Coverage for thb/meta/cards/spellcard.py : 54%
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 --
5import itertools
6import random
8# -- third party --
9# -- own --
10from thb.cards import definition, spellcard
11from thb.meta.common import ui_meta
14# -- code --
15@ui_meta(definition.DemolitionCard)
16class DemolitionCard:
17 # action_stage meta
18 image = 'thb-card-demolition'
19 name = '城管执法'
20 description = (
21 '|R城管执法|r\n\n'
22 '非延时符卡。\n'
23 '出牌阶段,对一名其他角色使用,弃置其区域内的一张牌。\n\n'
24 '|DB(画师:霏茶,CV:shourei小N)|r'
25 )
27 def is_action_valid(self, c, tl):
28 if not tl: 28 ↛ 29line 28 didn't jump to line 29, because the condition on line 28 was never true
29 return (False, '请选择拆除目标')
31 tgt = tl[0]
32 if not sum([len(l) for l in [tgt.cards, tgt.showncards, tgt.equips, tgt.fatetell]]): 32 ↛ 33line 32 didn't jump to line 33, because the condition on line 32 was never true
33 return (False, '这货已经没有牌了')
34 else:
35 return (True, '嗯,你的牌太多了')
37 def sound_effect(self, act):
38 return 'thb-cv-card_demolition'
41@ui_meta(spellcard.Demolition)
42class Demolition:
43 def effect_string(self, act):
44 if not act.succeeded: return None
45 return '|G【%s】|r卸掉了|G【%s】|r的%s。' % (
46 act.source.ui_meta.name,
47 act.target.ui_meta.name,
48 self.card_desc(act.card),
49 )
52@ui_meta(definition.RejectCard)
53class RejectCard:
54 # action_stage meta
55 name = '好人卡'
56 image = 'thb-card-reject'
57 description = (
58 '|R好人卡|r\n\n'
59 '非延时符卡。\n'
60 '一张符卡对一名目标角色生效前,你可以使用此牌来抵消该符卡对其目标角色产生的效果。\n\n'
61 '|DB(画师:霏茶,CV:VV)'
62 )
64 def is_action_valid(self, c, tl):
65 return (False, '你不能主动出好人卡')
67 def effect_string(self, act):
68 return '|G【%s】|r为|G【%s】|r受到的|G%s|r使用了|G%s|r。' % (
69 act.source.ui_meta.name,
70 act.target.ui_meta.name,
71 act.force_action.target_act.associated_card.ui_meta.name,
72 act.card.ui_meta.name,
73 )
75 def sound_effect(self, act):
76 return 'thb-cv-card_reject'
78 def has_reject_card(self, p):
79 from thb.cards.definition import RejectCard as RC
80 if any([c.is_card(RC) for c in itertools.chain(p.cards, p.showncards)]):
81 return True
83 from thb.characters import reimu
84 if p.has_skill(reimu.SpiritualAttack) and not p.dead:
85 return True
87 return False
90@ui_meta(spellcard.RejectHandler)
91class RejectHandler:
92 # choose_card meta
93 def choose_card_text(self, g, act, cards):
94 c = act.target_act.associated_card
95 name = c.ui_meta.name
97 s = '【%s】受到的【%s】' % (
98 act.target_act.target.ui_meta.name,
99 name,
100 )
102 if act.cond(cards):
103 return (True, '对不起,你是一个好人(%s)' % s)
104 else:
105 return (False, '请选择一张好人卡(%s)' % s)
108@ui_meta(definition.SealingArrayCard)
109class SealingArrayCard:
110 # action_stage meta
111 name = '封魔阵'
112 image = 'thb-card-sealarray'
113 tag_anim = lambda c: 'thb-tag-sealarray' 113 ↛ exitline 113 didn't run the lambda on line 113
114 description = (
115 '|R封魔阵|r\n\n'
116 '延时类符卡。\n'
117 '出牌阶段,对一名其他角色使用,将此牌横置于该角色的判定区内。该角色的判定阶段,需进行一次判定然后弃置此牌。若判定结果不为红桃,跳过其出牌阶段。\n'
118 '|B|R>> |r判定开始前,你可以使用【好人卡】抵消该符卡的效果(抵消后弃掉【封魔阵】)。\n\n'
119 '|DB(画师:霏茶,CV:shourei小N)|r'
120 )
122 def is_action_valid(self, c, tl):
123 if len(tl) != 1:
124 return (False, '请选择封魔阵的目标')
125 t = tl[0]
126 if self.me is t: 126 ↛ 129line 126 didn't jump to line 129, because the condition on line 126 was never false
127 return (False, '你不能跟自己过不去啊!')
129 return (True, '画个圈圈诅咒你!')
131 def sound_effect(self, act):
132 return 'thb-cv-card_sealarray'
135@ui_meta(spellcard.SealingArray)
136class SealingArray:
137 def effect_string(self, act):
138 tgt = act.target
139 if act.succeeded:
140 return '|G【%s】|r被困在了封魔阵中。' % tgt.ui_meta.name
141 else:
142 return '封魔阵没有布置完善,|G【%s】|r侥幸逃了出来!' % tgt.ui_meta.name
145@ui_meta(definition.FrozenFrogCard)
146class FrozenFrogCard:
147 # action_stage meta
148 name = '冻青蛙'
149 image = 'thb-card-frozenfrog'
150 tag_anim = lambda c: 'thb-tag-frozenfrog' 150 ↛ exitline 150 didn't run the lambda on line 150
151 description = (
152 '|R冻青蛙|r\n\n'
153 '延时类符卡。\n'
154 '出牌阶段,对一名其他角色使用,将此牌横置于该角色的判定区内。该角色的判定阶段,需进行一次判定然后弃置此牌。若判定结果不为黑桃,跳过其摸牌阶段。\n'
155 '|B|R>> |r判定开始前,你可以使用【好人卡】抵消该符卡的效果(抵消后弃掉【冻青蛙】)。\n\n'
156 '|DB(画师:霏茶,CV:shourei小N)|r'
157 )
159 def is_action_valid(self, c, tl):
160 if len(tl) != 1:
161 return (False, '请选择冻青蛙的目标')
162 t = tl[0]
163 if self.me is t:
164 return (False, '你不能跟自己过不去啊!')
166 return (True, '伸手党什么的,冻住就好了!')
168 def sound_effect(self, act):
169 return 'thb-cv-card_frozenfrog'
172@ui_meta(spellcard.FrozenFrog)
173class FrozenFrog:
174 def effect_string(self, act):
175 tgt = act.target
176 if act.succeeded:
177 return '|G【%s】|r被冻住了……' % tgt.ui_meta.name
178 else:
179 return '幻想乡今天大晴,|G【%s】|r没有被冻住~' % tgt.ui_meta.name
182@ui_meta(definition.NazrinRodCard)
183class NazrinRodCard:
184 # action_stage meta
185 name = '寻龙尺'
186 image = 'thb-card-nazrinrod'
187 description = (
188 '|R寻龙尺|r\n\n'
189 '非延时符卡。\n'
190 '出牌阶段使用,从牌堆摸两张牌。\n\n'
191 '|DB(画师:霏茶,CV:VV)|r'
192 )
194 def is_action_valid(self, c, tl):
195 return (True, '看看能找到什么好东西~')
197 def sound_effect(self, act):
198 return 'thb-cv-card_nazrinrod'
201@ui_meta(definition.SinsackCard)
202class SinsackCard:
203 # action_stage meta
204 name = '罪袋'
205 image = 'thb-card-sinsack'
206 tag_anim = lambda c: 'thb-tag-sinsack' 206 ↛ exitline 206 didn't run the lambda on line 206
207 description = (
208 '|R罪袋|r\n\n'
209 '延时类符卡。\n'
210 '出牌阶段,对你使用,将此牌横置于你的判定区内。判定区内有此牌的角色的判定阶段,需进行一次判定:\n'
211 '|B|R>> |r若判定结果为黑桃1-8,则目标角色受到3点无来源伤害,然后将其置入弃牌堆。\n'
212 '|B|R>> |r若判定结果不在此范围,则将其移动到下家的判定区内。\n'
213 '|B|R>> |r判定开始前,你可以使用|B好人卡|r抵消该符卡的效果,并将该|G罪袋|r直接传递给下家。\n\n'
214 '|DB(画师:霏茶,CV:VV/大白)|r'
215 )
217 def is_action_valid(self, c, tl):
218 return (True, '别来找我!')
220 def sound_effect(self, act):
221 return 'thb-cv-card_sinsack'
224@ui_meta(spellcard.Sinsack)
225class Sinsack:
226 def effect_string(self, act):
227 tgt = act.target
228 if act.succeeded:
229 return '罪袋终于找到了机会,将|G【%s】|r推倒了…' % tgt.ui_meta.name
232@ui_meta(spellcard.SinsackDamage)
233class SinsackDamage:
234 def sound_effect(self, act):
235 return 'thb-cv-card_sinsack_effect'
238@ui_meta(definition.YukariDimensionCard)
239class YukariDimensionCard:
240 # action_stage meta
241 image = 'thb-card-yukaridimension'
242 name = '隙间'
244 description = (
245 '|R隙间|r\n\n'
246 '非延时符卡。\n'
247 '出牌阶段,对距离为1的一名其他角色使用,获得其区域内的一张牌。\n\n'
248 '|DB(画师:霏茶,CV:VV)|r'
249 )
251 def is_action_valid(self, c, tl):
252 if not tl: 252 ↛ 253line 252 didn't jump to line 253, because the condition on line 252 was never true
253 return (False, '请选择目标')
255 target = tl[0]
256 if not (target.cards or target.showncards or target.equips or target.fatetell): 256 ↛ 257line 256 didn't jump to line 257, because the condition on line 256 was never true
257 return (False, '这货已经没有牌了')
258 else:
259 return (True, '请把胖次给我!')
261 def sound_effect(self, act):
262 return 'thb-cv-card_dimension'
265@ui_meta(spellcard.YukariDimension)
266class YukariDimension:
267 def effect_string(self, act):
268 src, tgt = act.source, act.target
269 if act.succeeded:
270 return '|G【%s】|r透过隙间拿走了|G【%s】|r的1张牌。' % (
271 src.ui_meta.name,
272 tgt.ui_meta.name
273 )
276@ui_meta(definition.DuelCard)
277class DuelCard:
278 # action_stage meta
279 image = 'thb-card-duel'
280 name = '弹幕战'
281 description = (
282 '|R弹幕战|r\n\n'
283 '非延时符卡。\n'
284 '出牌阶段,对一名其他角色使用,由目标角色开始,轮流打出一张【弹幕】。首先不打出【弹幕】的一方受到另一方造成的1点伤害。\n\n'
285 '|DB(画师:霏茶,CV:小羽)|r'
286 )
288 def is_action_valid(self, c, tl):
289 if not tl:
290 return (False, '请选择弹幕战的目标')
292 return (True, '来,战个痛快!')
294 def sound_effect(self, act):
295 return 'thb-cv-card_duel'
298@ui_meta(definition.MapCannonCard)
299class MapCannonCard:
300 image = 'thb-card-mapcannon'
301 name = '地图炮'
302 description = (
303 '|R地图炮|r\n\n'
304 '群体符卡。非延时符卡。\n'
305 '出牌阶段,对除你以外的所有其他角色使用,目标角色需依次打出一张【擦弹】,否则该角色受到1点伤害。\n\n'
306 '|DB(画师:霏茶,CV:VV)|r'
307 )
309 def is_action_valid(self, c, tl):
310 return (True, '一个都不能跑!')
312 def sound_effect(self, act):
313 return 'thb-cv-card_mapcannon'
316@ui_meta(definition.DemonParadeCard)
317class DemonParadeCard:
318 image = 'thb-card-demonparade'
319 name = '百鬼夜行'
320 description = (
321 '|R百鬼夜行|r\n\n'
322 '群体符卡。非延时符卡。\n'
323 '出牌阶段,对除你以外的所有其他角色使用,目标角色需依次打出一张|G弹幕|r,否则该角色受到1点伤害。\n\n'
324 '|DB(画师:霏茶,CV:小羽)|r'
325 )
327 def is_action_valid(self, c, tl):
328 return (True, '一只鬼,两只鬼,三只鬼……')
330 def sound_effect(self, act):
331 return 'thb-cv-card_demonparade'
334@ui_meta(definition.FeastCard)
335class FeastCard:
336 # action_stage meta
337 image = 'thb-card-feast'
338 name = '宴会'
339 description = (
340 '|R宴会|r\n\n'
341 '群体符卡。非延时符卡。\n'
342 '出牌阶段,对所有角色使用。已受伤的角色回复一点体力,未受伤的角色获得|B喝醉|r状态。\n\n'
343 '|DB(画师:霏茶,CV:VV)|r'
344 )
346 def is_action_valid(self, c, tl):
347 return (True, '开宴啦~~')
349 def sound_effect(self, act):
350 return random.choice([
351 'thb-cv-card_feast1',
352 'thb-cv-card_feast2',
353 'thb-cv-card_feast3',
354 ])
357@ui_meta(definition.HarvestCard)
358class HarvestCard:
359 # action_stage meta
360 image = 'thb-card-harvest'
361 name = '五谷丰登'
362 description = (
363 '|R五谷丰登|r\n\n'
364 '群体符卡。非延时符卡。\n'
365 '出牌阶段,对所有角色使用,你从牌堆顶亮出等同于现存角色数量的牌,目标角色依次选择并获得这些牌中的一张。\n\n'
366 '|DB(画师:霏茶,CV:VV)|r'
367 )
369 def is_action_valid(self, c, tl):
370 return (True, '麻薯会有的,节操是没有的!')
372 def sound_effect(self, act):
373 return 'thb-cv-card_harvest'
376@ui_meta(spellcard.HarvestEffect)
377class HarvestEffect:
378 def effect_string(self, act):
379 if not act.succeeded: return None
380 tgt = act.target
381 c = act.card
382 return '|G【%s】|r获得了|G%s|r。' % (
383 tgt.ui_meta.name,
384 self.card_desc(c),
385 )
388@ui_meta(definition.DollControlCard)
389class DollControlCard:
390 # action_stage meta
391 name = '人形操控'
392 image = 'thb-card-dollcontrol'
393 description = (
394 '|R人形操控|r\n\n'
395 '非延时符卡。\n'
396 '出牌阶段,对装备区内有武器牌的一名其他角色使用,令其选择一项:对其攻击范围内一名由你指定的角色使用一张【弹幕】,或将武器交给你。\n\n'
397 '|DB(画师:霏茶,CV:小羽)|r'
398 )
399 custom_ray = True
401 def is_action_valid(self, c, tl):
402 n = len(tl)
403 if n == 0:
404 return (False, '请选择被控者')
406 if tl[0] is self.me:
407 return (False, '你不可以控制你自己')
409 if all(e.equipment_category != 'weapon' for e in tl[0].equips):
410 return (False, '被控者没有武器!')
412 if n == 1:
413 return (False, '请选择被控者的攻击目标')
414 elif n == 2:
415 from thb.actions import LaunchCard
416 from thb.cards.definition import AttackCard
417 c = AttackCard()
418 lc = LaunchCard(tl[0], [tl[1]], c)
419 if not lc.can_fire():
420 return (False, '被控者无法向目标出【弹幕】!')
421 return (True, '乖,听话!')
423 def sound_effect(self, act):
424 return 'thb-cv-card_dollcontrol'
427@ui_meta(spellcard.DollControl)
428class DollControl:
429 # choose card meta
430 def choose_card_text(self, g, act, cards):
431 if act.cond(cards):
432 return (True, '那好吧…')
433 else:
434 return (False, '请出【弹幕】(否则你的武器会被拿走)')
436 def ray(self, act):
437 src = act.source
438 tl = act.tl
439 return [(src, tl[0]), (tl[0], tl[1])]
442@ui_meta(definition.DonationBoxCard)
443class DonationBoxCard:
444 # action_stage meta
445 name = '赛钱箱'
446 image = 'thb-card-donationbox'
447 description = (
448 '|R赛钱箱|r\n\n'
449 '非延时符卡。\n'
450 '出牌阶段,对至多两名其他角色使用,目标角色需将自己的一张牌置入你的明牌区。\n\n'
451 '|DB(画师:霏茶,CV:shourei小N)|r'
452 )
454 def is_action_valid(self, c, tl):
455 n = len(tl)
456 if not n:
457 return (False, '请选择1-2名玩家')
459 for t in tl:
460 if not (t.cards or t.showncards or t.equips):
461 return (False, '目标没有可以给你的牌')
463 return (True, '纳奉!纳奉!')
465 def sound_effect(self, act):
466 return 'thb-cv-card_donationbox'
469@ui_meta(spellcard.DonationBoxEffect)
470class DonationBoxEffect:
471 # choose card meta
472 def choose_card_text(self, g, act, cards):
473 if act.cond(cards):
474 return (True, '这是抢劫啊!')
475 else:
476 return (False, '请选择一张牌(否则会随机选择一张)')