Hide keyboard shortcuts

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

2from __future__ import annotations 

3 

4# -- stdlib -- 

5# -- third party -- 

6# -- own -- 

7from game.base import ActionShootdown 

8from thb.actions import ActionStageLaunchCard, Damage, FinalizeStage, LaunchCard, PlayerTurn 

9from thb.actions import UserAction 

10from thb.cards.base import Skill 

11from thb.cards.classes import AttackCard, DuelCard, Heal, HealCard, PhysicalCard, t_None 

12from thb.characters.base import Character, register_character_to 

13from thb.inputlets import ChooseOptionInputlet 

14from thb.mode import THBEventHandler 

15 

16 

17# -- code -- 

18class Lunatic(Skill): 

19 associated_action = None 

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

21 target = t_None() 

22 

23 

24class Discarder(Skill): 

25 associated_action = None 

26 distance = 1 

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

28 target = t_None() 

29 

30 

31class MahjongDrug(Skill): 

32 associated_action = None 

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

34 target = t_None() 

35 

36 

37class LunaticAction(UserAction): 

38 def apply_action(self): 

39 self.target.skills.append(Discarder) 

40 self.target.tags['reisen_discarder'] = True # for tag 

41 return True 

42 

43 

44class LunaticHandler(THBEventHandler): 

45 interested = ['action_after'] 

46 

47 def handle(self, evt_type, act): 

48 if evt_type == 'action_after' and isinstance(act, Damage): 

49 src = act.source 

50 if not src: return act 

51 if not src.has_skill(Lunatic): return act 

52 

53 tgt = act.target 

54 if tgt.dead or tgt.has_skill(Discarder): return act 

55 

56 g = self.game 

57 for lc in reversed(g.action_stack): 

58 if isinstance(lc, LaunchCard): 

59 break 

60 else: 

61 return act 

62 

63 c = lc.card 

64 if not (c.is_card(AttackCard) or c.is_card(DuelCard)): 

65 return act 

66 

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

68 g.process_action(LunaticAction(src, tgt)) 

69 

70 return act 

71 

72 

73class DiscarderAttackOnly(ActionShootdown): 

74 pass 

75 

76 

77class DiscarderDistanceLimit(ActionShootdown): 

78 pass 

79 

80 

81class DiscarderHandler(THBEventHandler): 

82 interested = ['action_after', 'action_shootdown'] 

83 

84 def handle(self, evt_type, act): 

85 if evt_type == 'action_shootdown' and isinstance(act, ActionStageLaunchCard): 

86 src = act.source 

87 if not src.has_skill(Discarder): return act 

88 g = self.game 

89 current = PlayerTurn.get_current(g).target 

90 if src is not current: return act 

91 

92 self.card = c = act.card 

93 if not c.is_card(AttackCard): 

94 if c.is_card(PhysicalCard) or 'treat_as' in c.category: 

95 raise DiscarderAttackOnly 

96 else: 

97 return act 

98 

99 dist = LaunchCard.calc_distance(g, src, Discarder(src)) 

100 dist.pop(src, '') 

101 nearest = max(min(dist.values()), 0) 

102 avail = {p for p in dist if dist[p] <= nearest} 

103 

104 if not set(act.target_list) <= avail: 

105 raise DiscarderDistanceLimit 

106 

107 elif evt_type == 'action_after' and isinstance(act, FinalizeStage): 

108 tgt = act.target 

109 if tgt.has_skill(Discarder): 

110 tgt.skills.remove(Discarder) 

111 tgt.tags['reisen_discarder'] = False # for tag 

112 

113 return act 

114 

115 

116class MahjongDrugAction(UserAction): 

117 def apply_action(self): 

118 self.target.tags['wine'] = True 

119 return True 

120 

121 def is_valid(self): 

122 return not self.target.dead 

123 

124 

125class MahjongDrugHandler(THBEventHandler): 

126 interested = ['action_after'] 

127 

128 def handle(self, evt_type, act): 

129 if evt_type == 'action_after' and isinstance(act, Heal): 

130 tgt = act.target 

131 if not tgt.has_skill(MahjongDrug): return act 

132 card = getattr(act, 'associated_card', None) 

133 if not card or not card.is_card(HealCard): return act 

134 

135 g = self.game 

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

137 g.process_action(MahjongDrugAction(tgt, tgt)) 

138 

139 return act 

140 

141 

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

143class Reisen(Character): 

144 skills = [Lunatic, MahjongDrug] 

145 eventhandlers = [DiscarderHandler, LunaticHandler, MahjongDrugHandler] 

146 maxlife = 4 

147 

148 

149@register_character_to('kof') 

150class ReisenKOF(Character): 

151 skills = [Lunatic] 

152 eventhandlers = [DiscarderHandler, LunaticHandler] 

153 maxlife = 4