# Coverage for thb/characters/youmu.py : 24%

## 107 statements 39 run 68 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 from __future__ import annotations

3

4# -- stdlib --

5# -- third party --

6# -- own --

7 from thb.actions import ActionStage, Damage, DropCards, UserAction, migrate_cards

8 from thb.actions import random_choose_card

9 from thb.cards.base import Skill

10 from thb.cards.classes import Attack, BaseDuel, InstantSpellCardAction, LaunchGraze, UseAttack

11 from thb.cards.classes import t_None

12 from thb.characters.base import Character

13 from thb.inputlets import ChooseIndividualCardInputlet

14 from thb.mode import THBEventHandler

15 from utils.misc import classmix

16

17

18# -- code --

19 class MijincihangzhanAttack(Attack):

20 pass

21

22

23 class MijincihangzhanDuelMixin(InstantSpellCardAction):

24 # 迷津慈航斩 弹幕战

25 def apply_action(self):

26 g = self.game

27 source = self.source

28 target = self.target

29

30 d = (source, target)

31 while True:

32 d = (d[1], d[0])

33 if d[1].has_skill(Nitoryuu):

34 if not (

35 g.process_action(UseAttack(d[0])) and

36 g.process_action(UseAttack(d[0]))

37 ): break

38 else:

39 if not g.process_action(UseAttack(d[0])): break

40

41 g.process_action(Damage(d[1], d[0], amount=1))

42 return d[1] is source

43

44

45 class NitoryuuWearEquipmentAction(UserAction):

46 def __init__(self, source, target, card):

47 self.source = source

48 self.target = target

49 self.card = card

50

51 def apply_action(self):

52 g = self.game

53 card = self.card

54 tgt = self.target

55 g = self.game

56

57 weapons = [e for e in tgt.equips if e.equipment_category == 'weapon']

58 if len(weapons) > 1:

59 e = g.user_input([tgt], ChooseIndividualCardInputlet(self, weapons))

60 e = e or random_choose_card(g, [weapons])

61 g.process_action(DropCards(tgt, tgt, [e]))

62

63 migrate_cards([card], tgt.equips)

64

65 return True

66

67

68 class NitoryuuWearEquipmentHandler(THBEventHandler):

69 interested = ['wear_equipment']

70

71 def handle(self, evt_type, arg):

72 we, tgt, c, rst = arg

73 if not evt_type == 'wear_equipment': return arg

74 if not tgt.has_skill(Nitoryuu): return arg

75 if 'equipment' not in c.category: return arg

76 if c.equipment_category != 'weapon': return arg

77

78 g = self.game

79 g.process_action(NitoryuuWearEquipmentAction(tgt, tgt, c))

80 return we, tgt, c, 'handled'

81

82

83 class YoumuHandler(THBEventHandler):

84 interested = ['action_apply', 'action_before', 'attack_aftergraze', 'card_migration']

85 execute_before = ['ScarletRhapsodySwordHandler', 'LaevateinHandler', 'HouraiJewelHandler']

86 execute_after = ['VitalityHandler']

87

88 def handle(self, evt_type, act):

89 if evt_type == 'action_before':

90 if isinstance(act, Attack):

91 if not act.source.has_skill(Mijincihangzhan): return act

92 act.__class__ = classmix(MijincihangzhanAttack, act.__class__)

93 elif isinstance(act, BaseDuel):

94 if not isinstance(act, MijincihangzhanDuelMixin):

95 act.__class__ = classmix(MijincihangzhanDuelMixin, act.__class__)

96

97 elif evt_type == 'action_apply' and isinstance(act, ActionStage):

98 p = act.target

99 p.tags['vitality'] += bool(p.has_skill(Nitoryuu) and self.weapons(p) >= 2)

100

101 elif evt_type == 'card_migration':

102

103 act, cards, _from, to, _ = arg = act

104

105 for cl in (_from, to):

106 if cl.type != 'equips': continue

107 p = cl.owner

108 if not p.has_skill(Nitoryuu): continue

109

110 n = self.weapons(p)

111 dn = len(cards)

112 if not any(getattr(c, 'equipment_category', None) == 'weapon' for c in cards): continue

113

114 if cl is _from and (dn + n) >= 2 and n <= 1:

115 adjust = -1

116 elif cl is to and (n - dn) <= 1 and n >= 2:

117 adjust = 1

118 else:

119 adjust = 0

120

121 p.tags['vitality'] += adjust

122

123 return arg

124

125 elif evt_type == 'attack_aftergraze':

126 act, rst = arg = act

127 if rst: return arg

128 if not isinstance(act, MijincihangzhanAttack): return arg

129

130 g = self.game

131 return act, not g.process_action(LaunchGraze(act.target))

132

133 return act

134

135 @staticmethod

136 def weapons(p):

137 return sum([c.equipment_category == 'weapon' for c in p.equips])

138

139

140 class Mijincihangzhan(Skill):

141 # 迷津慈航斩

142 associated_action = None

143 skill_category = ['character', 'passive', 'compulsory']

144 target = t_None()

145

146

147 class Nitoryuu(Skill):

148 # 二刀流

149 associated_action = None

150 skill_category = ['character', 'passive', 'compulsory']

151 target = t_None()

152

153 def check(self):

154 return not self.associated_cards

155

156

157# @register_character_to('common')

158 class Youmu(Character):

159 skills = [Mijincihangzhan, Nitoryuu]

160 eventhandlers = [YoumuHandler, NitoryuuWearEquipmentHandler]

161 maxlife = 4