Coverage for thb/characters/komachi.py : 99%
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 -*-
3# -- stdlib --
4# -- third party --
5# -- own --
6from thb.actions import Damage, DrawCards, DropCards, GenericAction, LaunchCard, MaxLifeChange
7from thb.actions import PlayerTurn, UserAction, migrate_cards, random_choose_card
8from thb.cards.base import Skill
9from thb.cards.classes import t_None, t_OtherOne
10from thb.characters.base import Character, register_character_to
11from thb.inputlets import ChooseOptionInputlet, ChoosePeerCardInputlet
12from thb.mode import THBEventHandler
15# -- code --
16class RiversideAction(UserAction):
17 def apply_action(self):
18 g = self.game
19 src, tgt = self.source, self.target
20 src.tags['riverside_tag'] = src.tags['turn_count']
21 tgt.tags['riverside_target'] = g.turn_count
22 minhp = min([p.life for p in g.players if not p.dead])
23 if tgt.life == minhp:
24 has_card = tgt.cards or tgt.showncards or tgt.equips
25 if has_card and g.user_input([src], ChooseOptionInputlet(self, ('drop', 'draw'))) == 'drop':
26 self.action = 'drop'
27 catnames = ('cards', 'showncards', 'equips')
28 card = g.user_input([src], ChoosePeerCardInputlet(self, tgt, catnames))
29 card = card or random_choose_card(g, [tgt.cards, tgt.showncards, tgt.equips])
30 g.players.reveal(card)
31 g.process_action(DropCards(src, tgt, [card]))
32 else:
33 self.action = 'draw'
34 g.process_action(DrawCards(src, 1))
36 return True
38 def is_valid(self):
39 src = self.source
40 # Fire with Exinwan -> dead -> skills cleared -> assertion fail
41 # assert src.has_skill(Riverside)
42 return not src.tags['riverside_tag'] >= src.tags['turn_count']
45class RiversideHandler(THBEventHandler):
46 interested = ['calcdistance']
48 def handle(self, evt_type, arg):
49 if evt_type == 'calcdistance': 49 ↛ 59line 49 didn't jump to line 59, because the condition on line 49 was never false
50 src, card, dist = arg
51 if not src.has_skill(Riverside):
52 return arg
54 turn_count = self.game.turn_count
55 for p in dist:
56 if p.tags.get('riverside_target') == turn_count:
57 dist[p] -= 10000
59 return arg
62class Riverside(Skill):
63 associated_action = RiversideAction
64 skill_category = ['character', 'active']
65 target = t_OtherOne()
66 usage = 'drop'
68 def check(self):
69 cl = self.associated_cards
70 if len(cl) != 1: return False
71 return cl[0].resides_in.type in ('cards', 'showncards', 'equips')
74class ReturningAwake(GenericAction):
75 def apply_action(self):
76 g = self.game
77 tgt = self.target
78 tgt.skills.remove(Returning)
79 tgt.skills.append(FerryFee)
80 g.process_action(MaxLifeChange(tgt, tgt, -1))
82 return True
85class ReturningHandler(THBEventHandler):
86 interested = ['action_before']
88 def handle(self, evt_type, act):
89 if evt_type == 'action_before' and isinstance(act, PlayerTurn):
90 tgt = act.target
91 if not tgt.has_skill(Returning): return act
92 g = self.game
93 ncards = len(tgt.cards) + len(tgt.showncards)
94 if tgt.life <= 2 and tgt.life < ncards:
95 g.process_action(ReturningAwake(tgt, tgt))
97 return act
100class Returning(Skill):
101 associated_action = None
102 skill_category = ['character', 'passive', 'awake']
103 target = t_None()
106class FerryFee(Skill):
107 associated_action = None
108 skill_category = ['character', 'passive']
109 target = t_None()
110 distance = 1
113class FerryFeeEffect(UserAction):
114 def __init__(self, source, target, card):
115 self.source = source
116 self.target = target
117 self.card = card
119 def apply_action(self):
120 src = self.source
121 card = self.card
122 src.reveal(card)
123 migrate_cards([card], src.cards, unwrap=True)
124 return True
127class FerryFeeHandler(THBEventHandler):
128 interested = ['action_after']
130 def handle(self, evt_type, act):
131 if evt_type == 'action_after' and isinstance(act, Damage):
132 src = act.source
133 tgt = act.target
134 if not (src and src.has_skill(FerryFee)): return act
135 if not (tgt.cards or tgt.showncards or tgt.equips): return act
136 g = self.game
137 dist = LaunchCard.calc_distance(g, src, FerryFee(src))
138 if not dist.get(tgt, 10000) <= 0: return act
139 if g.user_input([src], ChooseOptionInputlet(self, (False, True))):
140 g = self.game
141 catnames = ('cards', 'showncards', 'equips')
142 card = g.user_input([src], ChoosePeerCardInputlet(self, tgt, catnames))
143 card = card or random_choose_card(g, [tgt.cards, tgt.showncards, tgt.equips])
144 if not card: return act 144 ↛ exitline 144 didn't return from function 'handle', because the return on line 144 wasn't executed
145 g.process_action(FerryFeeEffect(src, tgt, card))
147 return act
150@register_character_to('common')
151class Komachi(Character):
152 skills = [Riverside, Returning]
153 eventhandlers = [RiversideHandler, ReturningHandler, FerryFeeHandler]
154 maxlife = 4