Coverage for thb/characters/cirno.py : 32%
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, DropCards, GenericAction, LaunchCard, LifeLost, UserAction
7from thb.actions import random_choose_card, ttags, user_choose_cards
8from thb.cards.base import Skill, VirtualCard
9from thb.cards.classes import AttackCard, DuelCard, 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 CirnoDropCards(GenericAction):
17 def __init__(self, source, target, cards):
18 self.source = source
19 self.target = target
20 self.cards = cards
22 def apply_action(self):
23 src, tgt = self.source, self.target
24 cards = self.cards
26 g = self.game
27 g.players.reveal(cards)
28 g.process_action(DropCards(src, tgt, cards))
29 return True
32class BakadesuAction(UserAction):
33 card_usage = 'launch'
35 def apply_action(self):
36 src, tgt = self.source, self.target
37 ttags(src)['bakadesu'] = True
39 cl = user_choose_cards(self, tgt, ('cards', 'showncards'))
40 g = self.game
41 if cl:
42 g.process_action(LaunchCard(tgt, [src], cl[0]))
43 else:
44 c = g.user_input([src], ChoosePeerCardInputlet(self, tgt, ('cards', 'showncards', 'equips')))
45 c = c or random_choose_card(g, [tgt.cards, tgt.showncards, tgt.equips])
46 c and g.process_action(CirnoDropCards(src, tgt, [c]))
48 return True
50 def is_valid(self):
51 src, tgt = self.source, self.target
52 if not LaunchCard(tgt, [src], AttackCard()).can_fire():
53 return False
55 return not ttags(src)['bakadesu']
57 def cond(self, cl):
58 if len(cl) != 1:
59 return False
60 c = cl[0]
62 return c.is_card(AttackCard) and (
63 c.resides_in is not None and c.resides_in.type in ('cards', 'showncards')
64 )
66 def ask_for_action_verify(self, p, cl, tl):
67 src, tgt = self.source, self.target
68 return LaunchCard(tgt, [src], cl[0]).can_fire()
71class Bakadesu(Skill):
72 associated_action = BakadesuAction
73 skill_category = ['character', 'active']
74 target = t_OtherOne()
76 def check(self):
77 return not self.associated_cards
80class PerfectFreeze(Skill):
81 associated_action = None
82 skill_category = ['character', 'passive']
83 target = t_None()
86class PerfectFreezeAction(UserAction):
87 card_usage = 'drop'
89 def __init__(self, source, target, damage):
90 self.source = source
91 self.target = target
92 self.damage = damage
94 def apply_action(self):
95 self.damage.cancelled = True
97 src, tgt = self.source, self.target
98 g = self.game
99 cl = user_choose_cards(self, tgt, ('cards', 'showncards', 'equips'))
100 c = cl[0] if cl else random_choose_card(g, [tgt.cards, tgt.showncards, tgt.equips])
102 if c:
103 damage = c.resides_in is not tgt.equips
104 g.process_action(CirnoDropCards(src, tgt, [c]))
106 if damage:
107 g.process_action(LifeLost(src, tgt, 1))
109 return True
111 def cond(self, cl):
112 if len(cl) != 1:
113 return False
115 if cl[0].is_card(VirtualCard):
116 return False
118 return True
120 def ask_for_action_verify(self, p, cl, tl):
121 return CirnoDropCards(self.source, self.target, cl).can_fire()
124class PerfectFreezeHandler(THBEventHandler):
125 interested = ['action_before']
127 execute_after = [
128 'RepentanceStickHandler',
129 'AyaRoundfanHandler',
130 ]
132 def handle(self, evt_type, act):
133 if evt_type == 'action_before' and isinstance(act, Damage): 133 ↛ 134line 133 didn't jump to line 134, because the condition on line 133 was never true
134 if act.cancelled:
135 return act
137 src, tgt = act.source, act.target
138 if not (src and src.has_skill(PerfectFreeze)):
139 return act
141 g = self.game
142 for lc in reversed(g.action_stack):
143 if isinstance(lc, LaunchCard):
144 break
145 else:
146 return act
148 if src is not lc.source:
149 return act
151 c = lc.card
152 if not c.is_card(AttackCard) and not c.is_card(DuelCard):
153 return act
155 if not g.user_input([src], ChooseOptionInputlet(self, (False, True))):
156 return act
158 g.process_action(PerfectFreezeAction(src, tgt, act))
160 return act
163@register_character_to('common')
164class Cirno(Character):
165 skills = [Bakadesu, PerfectFreeze]
166 eventhandlers = [PerfectFreezeHandler]
167 maxlife = 4