Coverage for thb/characters/eirin.py : 95%
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
4# -- stdlib --
5# -- third party --
6# -- own --
7from game.base import EventHandler
8from thb.actions import DropCards, GenericAction, LaunchCard, ShowCards, UserAction, migrate_cards
9from thb.actions import random_choose_card, ttags
10from thb.cards.base import Card, HiddenCard, PhysicalCard, Skill, TreatAs, VirtualCard, t_One
11from thb.cards.basic import Heal
12from thb.cards.definition import AttackCard
13from thb.characters.base import Character, register_character_to
14from thb.inputlets import ChooseOptionInputlet, ChoosePeerCardInputlet
17# -- code --
18class EirinHeal(Heal):
19 pass
22class SkySilkAction(UserAction):
23 def apply_action(self):
24 src, tgt = self.source, self.target
25 g = self.game
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)
31 g.process_action(DropCards(src, tgt, [c]))
32 if tgt.dead: return False
34 action = 'draw'
35 if tgt.life < tgt.maxlife:
36 action = g.user_input([tgt], ChooseOptionInputlet(self, ('heal', 'draw'))) or 'draw'
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))
46 drop = [c for c in cl if 'basic' in c.category]
47 get = [c for c in cl if c not in drop]
49 if get:
50 migrate_cards(get, tgt.cards)
52 if drop:
53 migrate_cards(drop, g.deck.droppedcards)
55 ttags(src)['sky_silk'] = True
56 return True
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']
63class SkySilk(Skill):
64 associated_action = SkySilkAction
65 skill_category = ('character', 'active')
66 target = t_One()
67 usage = 'drop'
69 def check(self):
70 cl = self.associated_cards
71 return bool(not cl)
74class LunaStringPlaceCard(GenericAction):
75 def __init__(self, target, card):
76 self.source = self.target = target
77 self.card = card
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
93class LunaStringLaunchCardHandler(EventHandler):
94 interested = ('action_before',)
95 execute_before = ('SolidShieldHandler',)
97 def handle(self, evt_type, act):
98 if evt_type == 'action_before': 98 ↛ 126line 98 didn't jump to line 126, because the condition on line 98 was never false
99 if not isinstance(act, LaunchCard):
100 return act
102 c = act.card
104 def walk(c):
105 if c.is_card(LunaString):
106 return c
108 if c.is_card(PhysicalCard) or c.is_card(HiddenCard):
109 return None
111 assert isinstance(c, VirtualCard)
113 for c1 in c.associated_cards:
114 r = walk(c1)
115 if r: 115 ↛ 116line 115 didn't jump to line 116, because the condition on line 115 was never true
116 return r
118 return None
120 c = walk(c)
122 if c:
123 g = self.game
124 g.process_action(LunaStringPlaceCard(c.character, c))
126 return act
129class LunaString(TreatAs, Skill):
130 treat_as = AttackCard
132 skill_category = ('character', 'active')
133 no_reveal = True
134 use_action = LunaStringPlaceCard # UseCard hook
136 color = Card.NOTSET
137 suit = Card.NOTSET
138 number = 0
140 cost_detached = False
142 def check(self):
143 if self.cost_detached: 143 ↛ 144line 143 didn't jump to line 144, because the condition on line 143 was never true
144 return True
146 cl = self.associated_cards
147 if not len(cl) == 1:
148 return False
150 c = cl[0]
152 if not (c.is_card(PhysicalCard) or c.is_card(HiddenCard)): # HiddenCard for other viewpoints 152 ↛ 153line 152 didn't jump to line 153, because the condition on line 152 was never true
153 return False
155 return c.resides_in.type in ('cards', 'showncards')
158@register_character_to('common', '-kof')
159class Eirin(Character):
160 skills = [SkySilk, LunaString]
161 eventhandlers = [LunaStringLaunchCardHandler]
162 maxlife = 3