# Coverage for thb/characters/seija.py : 39%

## 92 statements 43 run 49 missing 0 excluded 0 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 from typing import Sequence, Tuple, List

5

6# -- third party --

7# -- own --

8 from thb.actions import DrawCards, LaunchCard, Pindian, UserAction

9 from thb.cards.base import Skill, VirtualCard

10 from thb.cards.classes import AttackCard, BaseAttack, DuelCard, TreatAs, t_None

11 from thb.characters.base import Character, register_character_to

12 from thb.inputlets import ChooseOptionInputlet

13 from thb.mode import THBEventHandler, THBattle

14

15

16# -- code --

17 class InciteAttack(TreatAs, VirtualCard):

18 treat_as = AttackCard

19

20 def check(self):

21 return not self.associated_cards

22

23

24 class InciteFailAttack(TreatAs, VirtualCard):

25 treat_as = AttackCard

26 distance = 99999

27

28 def check(self):

29 return not self.associated_cards

30

31

32 class InciteSilentFailAction(UserAction):

33 def __init__(self, source, target):

34 self.source = source

35 self.target = target

36

37 def apply_action(self):

38 return True

39

40

41 class InciteAction(UserAction):

42 def apply_action(self):

43 src = self.source

44 tags = src.tags

45 tgt, victim = self.target_list

46

47 tags['incite_tag'] = tags['turn_count']

48

49 g = self.game

50 if g.process_action(Pindian(src, tgt)):

51 g.process_action(LaunchCard(tgt, [victim], InciteAttack(tgt)))

52

53 else:

54 if g.user_input([tgt], ChooseOptionInputlet(self, (False, True))):

55 g.process_action(LaunchCard(tgt, [src], InciteFailAttack(tgt)))

56 else:

57 g.process_action(InciteSilentFailAction(src, tgt))

58

59 return True

60

61 def is_valid(self):

62 src = self.source

63 tags = src.tags

64 if tags['turn_count'] <= tags['incite_tag']:

65 return False

66

67 tgt, victim = self.target_list

68 Pindian(src, tgt).action_shootdown_exception()

69 return LaunchCard(tgt, [victim], InciteAttack(tgt)).can_fire()

70

71

72 class Incite(Skill):

73 associated_action = InciteAction

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

75 usage = 'none'

76

77 def target(self, src: Character, tl: Sequence[Character]) -> Tuple[List[Character], bool]:

78 tl = [t for t in tl if not t.dead and t is not src]

79

80 if not tl:

81 return ([], False)

82

83 tl_, valid = AttackCard().target(tl[0], tl[1:])

84 tl = tl[:1]

85 tl.extend(tl_)

86 return tl, valid

87

88 def check(self):

89 return not self.associated_cards

90

91

92 class Reversal(Skill):

93 associated_action = None

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

95 target = t_None()

96

97

98 class ReversalDuel(TreatAs, VirtualCard):

99 treat_as = DuelCard

100

101 def check(self):

102 return not self.associated_cards

103

104

105 class ReversalHandler(THBEventHandler):

106 interested = ['action_before']

107 execute_before = [

108 'HouraiJewelHandler',

109 'RejectHandler',

110 'FreakingPowerHandler',

111 'RoukankenEffectHandler',

112 ]

113

114 execute_after = ['DeathSickleHandler']

115

116 def handle(self, evt_type, act):

117 if evt_type == 'action_before' and isinstance(act, BaseAttack):

118 if hasattr(act, 'roukanken_tag'):

119 return act

120

121 src = act.source

122 tgt = act.target

123 g = self.game

124

125 if not tgt.has_skill(Reversal):

126 return act

127

128 if not g.user_input([tgt], ChooseOptionInputlet(self, (False, True))):

129 return act

130

131 def nhand(p):

132 return len(p.cards) + len(p.showncards)

133

134 g.process_action(DrawCards(tgt, 1))

135 if nhand(tgt) > nhand(src):

136 g.process_action(LaunchCard(src, [tgt], ReversalDuel(src)))

137 act.cancelled = True

138

139 return act

140

141

142@register_character_to('common', '-kof')

143 class Seija(Character):

144 skills = [Incite, Reversal]

145 eventhandlers = [ReversalHandler]

146 maxlife = 3