# Coverage for thb/characters/ran.py : 98%

## 175 statements 172 run 3 missing 0 excluded 3 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 ActionLimitExceeded, ActionStageLaunchCard, Damage, DropCards, GenericAction

8 from thb.actions import PlayerTurn, PrepareStage, ttags, user_choose_cards

9 from thb.cards.base import DummyCard, Skill, VirtualCard, t_None

10 from thb.cards.classes import InstantSpellCardAction, Reject, SpellCardAction, TreatAs

11 from thb.characters.base import Character, register_character_to

12 from thb.inputlets import ChooseOptionInputlet, ProphetInputlet

13 from thb.mode import THBEventHandler

14

15

16# -- code --

17 class Prophet(Skill):

18 associated_action = None

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

20 target = t_None()

21

22

23 class ExtremeIntelligence(Skill):

24 associated_action = None

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

26 target = t_None()

27

28

29 class ProphetAction(GenericAction):

30 def apply_action(self):

31 tgt = self.target

32 g = self.game

33 n = min(len([p for p in g.players if not p.dead]), 5)

34 cards = g.deck.getcards(n)

35

36 assert cards == g.deck.getcards(n)

37

38 tgt.reveal(cards)

39

40 upcards, downcards = g.user_input([tgt], ProphetInputlet(self, cards), timeout=45) or [list(range(n)), []]

41

42 deck = g.deck.cards

43 for i, c in enumerate(downcards): 43 ↛ 44 line 43 didn't jump to line 44, because the loop on line 43 never started

44 deck[i] = c

45 deck.rotate(-len(downcards))

46

47 for i, c in enumerate(upcards):

48 deck[i] = c

49

50 assert g.deck.getcards(len(upcards)) == upcards

51

52 return True

53

54

55 class ProphetHandler(THBEventHandler):

56 interested = ['action_apply']

57

58 def handle(self, evt_type, act):

59 if evt_type == 'action_apply' and isinstance(act, PrepareStage):

60 tgt = act.target

61 if not tgt.has_skill(Prophet): return act

62 g = self.game

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

64 return act

65

66 g.process_action(ProphetAction(tgt, tgt))

67

68 return act

69

70

71 class ExtremeIntelligenceAction(GenericAction):

72 card_usage = 'drop'

73

74 def __init__(self, source, target, act):

75 self.source, self.target, self.action = \

76 source, act.target, act

77

78 def activate_action(self):

79 p = self.source

80 self.cards = cards = user_choose_cards(self, p, ('cards', 'showncards', 'equips'))

81 if not cards: return False

82 p.tags['ran_ei'] = p.tags['turn_count'] + 1

83 return True

84

85 def apply_action(self):

86 p = self.source

87 cards = self.cards

88 g = self.game

89 g.process_action(DropCards(p, p, cards))

90

91 act = self.action

92 nact = act.__class__(source=p, target=act.target)

93 try:

94 nact.target_list = act.target_list

95 except AttributeError:

96 pass

97

98 try:

99 # HACK: This is for actions triggered by ForEach action.

100 # Well, actually it's for Harvest since only this

101 # uses the attrib

102 nact.parent_action = act.parent_action

103 except AttributeError:

104 pass

105

106 try:

107 nact.associated_card = act.associated_card

108 except AttributeError:

109 pass

110

111 g.process_action(nact)

112 return True

113

114 def cond(self, cl):

115 return len(cl) == 1 and not cl[0].is_card(Skill)

116

117

118 class ExtremeIntelligenceHandler(THBEventHandler):

119 interested = ['action_after', 'game_begin']

120

121 def handle(self, evt_type, act):

122 if evt_type == 'action_after' and isinstance(act, InstantSpellCardAction):

123 if isinstance(act, Reject): return act

124 g = self.game

125

126 tgt = PlayerTurn.get_current(g).target

127 for p in g.players.exclude(tgt):

128 if p.dead:

129 continue

130

131 if not p.has_skill(ExtremeIntelligence):

132 continue

133

134 if p.tags['ran_ei'] >= p.tags['turn_count'] + 1:

135 continue

136

137 try:

138 tl = act.target_list

139 except AttributeError:

140 tl = [act.target]

141

142 if any(t.dead for t in tl):

143 return act

144

145 if not act.can_fire():

146 return act # act cannot fire again

147

148 nact = ExtremeIntelligenceAction(p, act.target, act)

149

150 if nact.activate_action():

151 g.process_action(nact)

152

153 elif evt_type == 'game_begin':

154 g = self.game

155 for p in g.players:

156 if isinstance(p, Ran):

157 p.tags['ran_ei'] = 0 # for ui

158

159 return act

160

161

162 class ExtremeIntelligenceKOF(TreatAs, Skill):

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

164

165 def __init__(self, player):

166 Skill.__init__(self, player)

167 self.treat_as = ttags(player).get('ran_eikof_card') or DummyCard

168

169 def check(self):

170 cards = self.associated_cards

171 if len(cards) != 1: return False

172 c = cards[0]

173 if c.resides_in is None: return False 173 ↛ exit line 173 didn't return from function 'check', because the return on line 173 wasn't executed

174 if c.resides_in.type not in ('cards', 'showncards'): return False

175 return True

176

177

178 class ExtremeIntelligenceKOFHandler(THBEventHandler):

179 interested = ['action_apply', 'action_shootdown']

180

181 def handle(self, evt_type, act):

182 if evt_type == 'action_apply' and isinstance(act, ActionStageLaunchCard):

183 if not act.card.is_card(VirtualCard):

184 src = act.source

185 if not src.has_skill(ExtremeIntelligenceKOF): return act

186 c = act.card

187 if 'instant_spellcard' not in c.category: return act

188 ttags(src)['ran_eikof_card'] = c.__class__

189

190 elif act.card.is_card(ExtremeIntelligenceKOF):

191 src = act.source

192 ttags(src)['ran_eikof_tag'] = True

193

194 elif evt_type == 'action_shootdown' and isinstance(act, ActionStageLaunchCard) and act.card.is_card(ExtremeIntelligenceKOF):

195 src = act.source

196 tt = ttags(src)

197 if tt['ran_eikof_tag'] or not tt['ran_eikof_card']:

198 raise ActionLimitExceeded

199

200 return act

201

202

203 class NakedFox(Skill):

204 associated_action = None

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

206 target = t_None()

207

208

209 class NakedFoxAction(GenericAction):

210 def __init__(self, dmg):

211 self.source = self.target = dmg.target

212 self.dmgact = dmg

213 self.dmgamount = dmg.amount # for UI

214

215 def apply_action(self):

216 self.dmgact.amount -= 1

217 return True

218

219

220 class NakedFoxHandler(THBEventHandler):

221 interested = ['action_before']

222

223 def handle(self, evt_type, act):

224 if evt_type == 'action_before' and isinstance(act, Damage):

225 g = self.game

226 tgt = act.target

227 if not tgt.has_skill(NakedFox): return act

228 pact = g.action_stack[-1]

229 if not isinstance(pact, SpellCardAction): return act

230 if tgt.cards or tgt.showncards: return act

231 if act.amount < 1: return act 231 ↛ exit line 231 didn't return from function 'handle', because the return on line 231 wasn't executed

232

233 g.process_action(NakedFoxAction(act))

234 return act

235

236 return act

237

238

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

240 class Ran(Character):

241 skills = [Prophet, ExtremeIntelligence, NakedFox]

242 eventhandlers = [

243 ProphetHandler,

244 ExtremeIntelligenceHandler,

245 NakedFoxHandler,

246 ]

247 maxlife = 3

248

249

250@register_character_to('kof')

251 class RanKOF(Character):

252 skills = [Prophet, ExtremeIntelligenceKOF, NakedFox]

253 eventhandlers = [

254 ProphetHandler,

255 ExtremeIntelligenceKOFHandler,

256 NakedFoxHandler,

257 ]

258 maxlife = 3