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 DropCards, GenericAction, LaunchCard, ShowCards, UserAction, migrate_cards 

8from thb.actions import random_choose_card, ttags 

9from thb.cards.base import Card, HiddenCard, PhysicalCard, Skill, TreatAs, VirtualCard, t_One 

10from thb.cards.basic import Heal 

11from thb.cards.definition import AttackCard 

12from thb.characters.base import Character, register_character_to 

13from thb.inputlets import ChooseOptionInputlet, ChoosePeerCardInputlet 

14from thb.mode import THBEventHandler 

15 

16 

17# -- code -- 

18class EirinHeal(Heal): 

19 pass 

20 

21 

22class SkySilkAction(UserAction): 

23 def apply_action(self): 

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

25 g = self.game 

26 

27 c = g.user_input([src], ChoosePeerCardInputlet(self, tgt, ['cards', 'showncards', 'equips'])) 

28 c = c or random_choose_card(g, [tgt.cards, tgt.showncards, tgt.equips]) 

29 g.players.reveal(c) 

30 

31 g.process_action(DropCards(src, tgt, [c])) 

32 if tgt.dead: return False 

33 

34 action = 'draw' 

35 if tgt.life < tgt.maxlife: 

36 action = g.user_input([tgt], ChooseOptionInputlet(self, ('heal', 'draw'))) or 'draw' 

37 

38 if action == 'heal': 

39 g.process_action(EirinHeal(src, tgt)) 

40 else: 

41 g.deck.getcards(3) 

42 g.deck.cards.rotate(3) 

43 cl = g.deck.getcards(3) 

44 g.process_action(ShowCards(tgt, cl)) 

45 

46 drop = [c for c in cl if 'basic' in c.category] 

47 get = [c for c in cl if c not in drop] 

48 

49 if get: 

50 migrate_cards(get, tgt.cards) 

51 

52 if drop: 

53 migrate_cards(drop, g.deck.droppedcards) 

54 

55 ttags(src)['sky_silk'] = True 

56 return True 

57 

58 def is_valid(self): 

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

60 return (tgt.cards or tgt.showncards or tgt.equips) and not ttags(src)['sky_silk'] 

61 

62 

63class SkySilk(Skill): 

64 associated_action = SkySilkAction 

65 skill_category = ('character', 'active') 

66 target = t_One() 

67 usage = 'drop' 

68 

69 def check(self): 

70 cl = self.associated_cards 

71 return bool(not cl) 

72 

73 

74class LunaStringPlaceCard(GenericAction): 

75 def __init__(self, target, card): 

76 self.source = self.target = target 

77 self.card = card 

78 

79 def apply_action(self): 

80 g = self.game 

81 tgt = self.target 

82 direction = g.user_input([tgt], ChooseOptionInputlet(self, ('front', 'back'))) 

83 sk = self.card 

84 assert sk.is_card(LunaString) 

85 c = sk.associated_cards[0] 

86 sk.associated_cards[:] = [] 

87 sk.cost_detached = True 

88 self.direction = direction # for ui 

89 migrate_cards([c], g.deck.cards, unwrap=True, direction=direction) 

90 return True 

91 

92 

93class LunaStringLaunchCardHandler(THBEventHandler): 

94 interested = ['action_before'] 

95 execute_before = ['SolidShieldHandler'] 

96 

97 def handle(self, evt_type, act): 

98 if evt_type == 'action_before': 

99 if not isinstance(act, LaunchCard): 

100 return act 

101 

102 c = act.card 

103 

104 def walk(c): 

105 if c.is_card(LunaString): 

106 return c 

107 

108 if c.is_card(PhysicalCard) or c.is_card(HiddenCard): 

109 return None 

110 

111 assert isinstance(c, VirtualCard) 

112 

113 for c1 in c.associated_cards: 

114 r = walk(c1) 

115 if r: 

116 return r 

117 

118 return None 

119 

120 c = walk(c) 

121 

122 if c: 

123 g = self.game 

124 assert isinstance(c, LunaString) 

125 g.process_action(LunaStringPlaceCard(c.character, c)) 

126 

127 return act 

128 

129 

130class LunaString(TreatAs, Skill): 

131 treat_as = AttackCard 

132 

133 skill_category = ('character', 'active') 

134 no_reveal = True 

135 use_action = LunaStringPlaceCard # UseCard hook 

136 

137 color = Card.NOTSET # type: ignore 

138 suit = Card.NOTSET # type: ignore 

139 number = 0 # type: ignore 

140 

141 cost_detached = False 

142 

143 def check(self): 

144 if self.cost_detached: 

145 return True 

146 

147 cl = self.associated_cards 

148 if not len(cl) == 1: 

149 return False 

150 

151 c = cl[0] 

152 

153 if not (c.is_card(PhysicalCard) or c.is_card(HiddenCard)): # HiddenCard for other viewpoints 

154 return False 

155 

156 return c.resides_in.type in ('cards', 'showncards') 

157 

158 

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

160class Eirin(Character): 

161 skills = [SkySilk, LunaString] 

162 eventhandlers = [LunaStringLaunchCardHandler] 

163 maxlife = 3