Coverage for thb/characters/komachi.py : 31%
Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2from __future__ import annotations
4# -- stdlib --
5# -- third party --
6# -- own --
7from thb.actions import Damage, DrawCards, DropCards, GenericAction, LaunchCard, MaxLifeChange
8from thb.actions import PrepareStage, UserAction, migrate_cards, random_choose_card
9from thb.cards.base import Skill
10from thb.cards.classes import t_None, t_OtherOne
11from thb.characters.base import Character, register_character_to
12from thb.inputlets import ChooseOptionInputlet, ChoosePeerCardInputlet
13from thb.mode import THBEventHandler
16# -- code --
17class RiversideAction(UserAction):
18 def apply_action(self):
19 g = self.game
20 src, tgt = self.source, self.target
21 src.tags['riverside_tag'] = src.tags['turn_count']
22 tgt.tags['riverside_target'] = g.turn_count
23 minhp = min([p.life for p in g.players if not p.dead])
24 if tgt.life == minhp:
25 has_card = tgt.cards or tgt.showncards or tgt.equips
26 if has_card and g.user_input([src], ChooseOptionInputlet(self, ('drop', 'draw'))) == 'drop':
27 self.action = 'drop'
28 catnames = ('cards', 'showncards', 'equips')
29 card = g.user_input([src], ChoosePeerCardInputlet(self, tgt, catnames))
30 card = card or random_choose_card(g, [tgt.cards, tgt.showncards, tgt.equips])
31 g.players.reveal(card)
32 g.process_action(DropCards(src, tgt, [card]))
33 else:
34 self.action = 'draw'
35 g.process_action(DrawCards(src, 1))
37 return True
39 def is_valid(self):
40 src = self.source
41 # Fire with Exinwan -> dead -> skills cleared -> assertion fail
42 # assert src.has_skill(Riverside)
43 return not src.tags['riverside_tag'] >= src.tags['turn_count']
46class RiversideHandler(THBEventHandler):
47 interested = ['calcdistance']
49 def handle(self, evt_type, arg):
50 if evt_type == 'calcdistance':
51 src, card, dist = arg
52 if not src.has_skill(Riverside):
53 return arg
55 turn_count = self.game.turn_count
56 for p in dist:
57 if p.tags.get('riverside_target') == turn_count:
58 dist[p] -= 10000
60 return arg
63class Riverside(Skill):
64 associated_action = RiversideAction
65 skill_category = ['character', 'active']
66 target = t_OtherOne()
67 usage = 'drop'
69 def check(self):
70 cl = self.associated_cards
71 if len(cl) != 1: return False
72 return cl[0].resides_in.type in ('cards', 'showncards', 'equips')
75class ReturningAwake(GenericAction):
76 def apply_action(self):
77 g = self.game
78 tgt = self.target
79 tgt.skills.remove(Returning)
80 tgt.skills.append(FerryFee)
81 g.process_action(MaxLifeChange(tgt, tgt, -1))
83 return True
86class ReturningHandler(THBEventHandler):
87 interested = ['action_before']
89 def handle(self, evt_type, act):
90 if evt_type == 'action_before' and isinstance(act, PrepareStage):
91 tgt = act.target
92 if not tgt.has_skill(Returning): return act
93 g = self.game
94 ncards = len(tgt.cards) + len(tgt.showncards)
95 if tgt.life <= 2 and tgt.life < ncards:
96 g.process_action(ReturningAwake(tgt, tgt))
98 return act
101class Returning(Skill):
102 associated_action = None
103 skill_category = ['character', 'passive', 'awake']
104 target = t_None()
107class FerryFee(Skill):
108 associated_action = None
109 skill_category = ['character', 'passive']
110 target = t_None()
111 distance = 1
114class FerryFeeEffect(UserAction):
115 def __init__(self, source, target, card):
116 self.source = source
117 self.target = target
118 self.card = card
120 def apply_action(self):
121 src = self.source
122 card = self.card
123 src.reveal(card)
124 migrate_cards([card], src.cards, unwrap=True)
125 return True
128class FerryFeeHandler(THBEventHandler):
129 interested = ['action_after']
131 def handle(self, evt_type, act):
132 if evt_type == 'action_after' and isinstance(act, Damage):
133 src = act.source
134 tgt = act.target
135 if not (src and src.has_skill(FerryFee)): return act
136 if not (tgt.cards or tgt.showncards or tgt.equips): return act
137 g = self.game
138 dist = LaunchCard.calc_distance(g, src, FerryFee(src))
139 if not dist.get(tgt, 10000) <= 0: return act
140 if g.user_input([src], ChooseOptionInputlet(self, (False, True))):
141 g = self.game
142 catnames = ('cards', 'showncards', 'equips')
143 card = g.user_input([src], ChoosePeerCardInputlet(self, tgt, catnames))
144 card = card or random_choose_card(g, [tgt.cards, tgt.showncards, tgt.equips])
145 if not card: return act
146 g.process_action(FerryFeeEffect(src, tgt, card))
148 return act
151@register_character_to('common')
152class Komachi(Character):
153 skills = [Riverside, Returning]
154 eventhandlers = [RiversideHandler, ReturningHandler, FerryFeeHandler]
155 maxlife = 4