# Coverage for thb/characters/cirno.py : 97%

## 110 statements 108 run 2 missing 0 excluded 2 partial

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 -*-

2

3# -- stdlib --

4# -- third party --

5# -- own --

6 from thb.actions import Damage, DropCards, GenericAction, LaunchCard, LifeLost, UserAction

7 from thb.actions import random_choose_card, ttags, user_choose_cards

8 from thb.cards.base import Skill, VirtualCard

9 from thb.cards.classes import AttackCard, DuelCard, t_None, t_OtherOne

10 from thb.characters.base import Character, register_character_to

11 from thb.inputlets import ChooseOptionInputlet, ChoosePeerCardInputlet

12 from thb.mode import THBEventHandler

13

14

15# -- code --

16 class CirnoDropCards(GenericAction):

17 def __init__(self, source, target, cards):

18 self.source = source

19 self.target = target

20 self.cards = cards

21

22 def apply_action(self):

23 src, tgt = self.source, self.target

24 cards = self.cards

25

26 g = self.game

27 g.players.reveal(cards)

28 g.process_action(DropCards(src, tgt, cards))

29 return True

30

31

32 class BakadesuAction(UserAction):

33 card_usage = 'launch'

34

35 def apply_action(self):

36 src, tgt = self.source, self.target

37 ttags(src)['bakadesu'] = True

38

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]))

47

48 return True

49

50 def is_valid(self):

51 src, tgt = self.source, self.target

52 if not LaunchCard(tgt, [src], AttackCard()).can_fire():

53 return False

54

55 return not ttags(src)['bakadesu']

56

57 def cond(self, cl):

58 if len(cl) != 1:

59 return False

60 c = cl[0]

61

62 return c.is_card(AttackCard) and (

63 c.resides_in is not None and c.resides_in.type in ('cards', 'showncards')

64 )

65

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()

69

70

71 class Bakadesu(Skill):

72 associated_action = BakadesuAction

73 skill_category = ['character', 'active']

74 target = t_OtherOne()

75

76 def check(self):

77 return not self.associated_cards

78

79

80 class PerfectFreeze(Skill):

81 associated_action = None

82 skill_category = ['character', 'passive']

83 target = t_None()

84

85

86 class PerfectFreezeAction(UserAction):

87 card_usage = 'drop'

88

89 def __init__(self, source, target, damage):

90 self.source = source

91 self.target = target

92 self.damage = damage

93

94 def apply_action(self):

95 self.damage.cancelled = True

96

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])

101

102 if c:

103 damage = c.resides_in is not tgt.equips

104 g.process_action(CirnoDropCards(src, tgt, [c]))

105

106 if damage:

107 g.process_action(LifeLost(src, tgt, 1))

108

109 return True

110

111 def cond(self, cl):

112 if len(cl) != 1:

113 return False

114

115 if cl[0].is_card(VirtualCard):

116 return False

117

118 return True

119

120 def ask_for_action_verify(self, p, cl, tl):

121 return CirnoDropCards(self.source, self.target, cl).can_fire()

122

123

124 class PerfectFreezeHandler(THBEventHandler):

125 interested = ['action_before']

126

127 execute_after = [

128 'RepentanceStickHandler',

129 'AyaRoundfanHandler',

130 ]

131

132 def handle(self, evt_type, act):

133 if evt_type == 'action_before' and isinstance(act, Damage):

134 if act.cancelled: 134 ↛ 135 line 134 didn't jump to line 135, because the condition on line 134 was never true

135 return act

136

137 src, tgt = act.source, act.target

138 if not (src and src.has_skill(PerfectFreeze)):

139 return act

140

141 g = self.game

142 for lc in reversed(g.action_stack): 142 ↛ 146 line 142 didn't jump to line 146, because the loop on line 142 didn't complete

143 if isinstance(lc, LaunchCard):

144 break

145 else:

146 return act

147

148 if src is not lc.source:

149 return act

150

151 c = lc.card

152 if not c.is_card(AttackCard) and not c.is_card(DuelCard):

153 return act

154

155 if not g.user_input([src], ChooseOptionInputlet(self, (False, True))):

156 return act

157

158 g.process_action(PerfectFreezeAction(src, tgt, act))

159

160 return act

161

162

163@register_character_to('common')

164 class Cirno(Character):

165 skills = [Bakadesu, PerfectFreeze]

166 eventhandlers = [PerfectFreezeHandler]

167 maxlife = 4