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 thb.actions import Damage, DrawCards, DropCards, GenericAction, LaunchCard, MaxLifeChange 

8from thb.actions import PrepareStage, UserAction, migrate_cards, random_choose_card 

9from thb.cards.base import Skill 

10from thb.cards.classes import t_None, t_OtherOne 

11from thb.characters.base import Character, register_character_to 

12from thb.inputlets import ChooseOptionInputlet, ChoosePeerCardInputlet 

13from thb.mode import THBEventHandler 

14 

15 

16# -- code -- 

17class RiversideAction(UserAction): 

18 def apply_action(self): 

19 g = self.game 

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

21 src.tags['riverside_tag'] = src.tags['turn_count'] 

22 tgt.tags['riverside_target'] = g.turn_count 

23 minhp = min([p.life for p in g.players if not p.dead]) 

24 if tgt.life == minhp: 

25 has_card = tgt.cards or tgt.showncards or tgt.equips 

26 if has_card and g.user_input([src], ChooseOptionInputlet(self, ('drop', 'draw'))) == 'drop': 

27 self.action = 'drop' 

28 catnames = ('cards', 'showncards', 'equips') 

29 card = g.user_input([src], ChoosePeerCardInputlet(self, tgt, catnames)) 

30 card = card or random_choose_card(g, [tgt.cards, tgt.showncards, tgt.equips]) 

31 g.players.reveal(card) 

32 g.process_action(DropCards(src, tgt, [card])) 

33 else: 

34 self.action = 'draw' 

35 g.process_action(DrawCards(src, 1)) 

36 

37 return True 

38 

39 def is_valid(self): 

40 src = self.source 

41 # Fire with Exinwan -> dead -> skills cleared -> assertion fail 

42 # assert src.has_skill(Riverside) 

43 return not src.tags['riverside_tag'] >= src.tags['turn_count'] 

44 

45 

46class RiversideHandler(THBEventHandler): 

47 interested = ['calcdistance'] 

48 

49 def handle(self, evt_type, arg): 

50 if evt_type == 'calcdistance': 

51 src, card, dist = arg 

52 if not src.has_skill(Riverside): 

53 return arg 

54 

55 turn_count = self.game.turn_count 

56 for p in dist: 

57 if p.tags.get('riverside_target') == turn_count: 

58 dist[p] -= 10000 

59 

60 return arg 

61 

62 

63class Riverside(Skill): 

64 associated_action = RiversideAction 

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

66 target = t_OtherOne() 

67 usage = 'drop' 

68 

69 def check(self): 

70 cl = self.associated_cards 

71 if len(cl) != 1: return False 

72 return cl[0].resides_in.type in ('cards', 'showncards', 'equips') 

73 

74 

75class ReturningAwake(GenericAction): 

76 def apply_action(self): 

77 g = self.game 

78 tgt = self.target 

79 tgt.skills.remove(Returning) 

80 tgt.skills.append(FerryFee) 

81 g.process_action(MaxLifeChange(tgt, tgt, -1)) 

82 

83 return True 

84 

85 

86class ReturningHandler(THBEventHandler): 

87 interested = ['action_before'] 

88 

89 def handle(self, evt_type, act): 

90 if evt_type == 'action_before' and isinstance(act, PrepareStage): 

91 tgt = act.target 

92 if not tgt.has_skill(Returning): return act 

93 g = self.game 

94 ncards = len(tgt.cards) + len(tgt.showncards) 

95 if tgt.life <= 2 and tgt.life < ncards: 

96 g.process_action(ReturningAwake(tgt, tgt)) 

97 

98 return act 

99 

100 

101class Returning(Skill): 

102 associated_action = None 

103 skill_category = ['character', 'passive', 'awake'] 

104 target = t_None() 

105 

106 

107class FerryFee(Skill): 

108 associated_action = None 

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

110 target = t_None() 

111 distance = 1 

112 

113 

114class FerryFeeEffect(UserAction): 

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

116 self.source = source 

117 self.target = target 

118 self.card = card 

119 

120 def apply_action(self): 

121 src = self.source 

122 card = self.card 

123 src.reveal(card) 

124 migrate_cards([card], src.cards, unwrap=True) 

125 return True 

126 

127 

128class FerryFeeHandler(THBEventHandler): 

129 interested = ['action_after'] 

130 

131 def handle(self, evt_type, act): 

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

133 src = act.source 

134 tgt = act.target 

135 if not (src and src.has_skill(FerryFee)): return act 

136 if not (tgt.cards or tgt.showncards or tgt.equips): return act 

137 g = self.game 

138 dist = LaunchCard.calc_distance(g, src, FerryFee(src)) 

139 if not dist.get(tgt, 10000) <= 0: return act 

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

141 g = self.game 

142 catnames = ('cards', 'showncards', 'equips') 

143 card = g.user_input([src], ChoosePeerCardInputlet(self, tgt, catnames)) 

144 card = card or random_choose_card(g, [tgt.cards, tgt.showncards, tgt.equips]) 

145 if not card: return act 

146 g.process_action(FerryFeeEffect(src, tgt, card)) 

147 

148 return act 

149 

150 

151@register_character_to('common') 

152class Komachi(Character): 

153 skills = [Riverside, Returning] 

154 eventhandlers = [RiversideHandler, ReturningHandler, FerryFeeHandler] 

155 maxlife = 4