# Coverage for thb/meta/cards/spellcard.py : 93%

## 232 statements 221 run 11 missing 0 excluded 7 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 import itertools

6 import random

7

8# -- third party --

9# -- own --

10 from thb.cards import definition, spellcard

11 from thb.meta.common import ui_meta

12

13

14# -- code --

15@ui_meta(definition.DemolitionCard)

16 class 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 )

26

27 def is_action_valid(self, c, tl):

28 if not tl:

29 return (False, '请选择拆除目标')

30

31 tgt = tl[0]

32 if not sum([len(l) for l in [tgt.cards, tgt.showncards, tgt.equips, tgt.fatetell]]):

33 return (False, '这货已经没有牌了')

34 else:

35 return (True, '嗯，你的牌太多了')

36

37 def sound_effect(self, act):

38 return 'thb-cv-card_demolition'

39

40

41@ui_meta(spellcard.Demolition)

42 class Demolition:

43 def effect_string(self, act):

44 if not act.succeeded: return None 44 ↛ exit line 44 didn't return from function 'effect_string', because the return on line 44 wasn't executed

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 )

50

51

52@ui_meta(definition.RejectCard)

53 class 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 )

63

64 def is_action_valid(self, c, tl):

65 return (False, '你不能主动出好人卡')

66

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 )

74

75 def sound_effect(self, act):

76 return 'thb-cv-card_reject'

77

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

82

83 from thb.characters import reimu

84 if p.has_skill(reimu.SpiritualAttack) and not p.dead:

85 return True

86

87 return False

88

89

90@ui_meta(spellcard.RejectHandler)

91 class RejectHandler:

92 # choose_card meta

93 def choose_card_text(self, act, cards):

94 c = act.target_act.associated_card

95 name = c.ui_meta.name

96

97 s = '【%s】受到的【%s】' % (

98 act.target_act.target.ui_meta.name,

99 name,

100 )

101

102 if act.cond(cards):

103 return (True, '对不起，你是一个好人(%s)' % s)

104 else:

105 return (False, '请选择一张好人卡（%s)' % s)

106

107

108@ui_meta(definition.SealingArrayCard)

109 class SealingArrayCard:

110 # action_stage meta

111 name = '封魔阵'

112 image = 'thb-card-sealarray'

113 tag_anim = lambda c: 'thb-tag-sealarray' 113 ↛ exit line 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 )

121

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:

127 return (False, '你不能跟自己过不去啊！')

128

129 return (True, '画个圈圈诅咒你！')

130

131 def sound_effect(self, act):

132 return 'thb-cv-card_sealarray'

133

134

135@ui_meta(spellcard.SealingArray)

136 class 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

143

144

145@ui_meta(definition.FrozenFrogCard)

146 class FrozenFrogCard:

147 # action_stage meta

148 name = '冻青蛙'

149 image = 'thb-card-frozenfrog'

150 tag_anim = lambda c: 'thb-tag-frozenfrog' 150 ↛ exit line 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 )

158

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, '你不能跟自己过不去啊！')

165

166 return (True, '伸手党什么的，冻住就好了！')

167

168 def sound_effect(self, act):

169 return 'thb-cv-card_frozenfrog'

170

171

172@ui_meta(spellcard.FrozenFrog)

173 class 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

180

181

182@ui_meta(definition.NazrinRodCard)

183 class 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 )

193

194 def is_action_valid(self, c, tl):

195 return (True, '看看能找到什么好东西~')

196

197 def sound_effect(self, act):

198 return 'thb-cv-card_nazrinrod'

199

200

201@ui_meta(definition.SinsackCard)

202 class SinsackCard:

203 # action_stage meta

204 name = '罪袋'

205 image = 'thb-card-sinsack'

206 tag_anim = lambda c: 'thb-tag-sinsack' 206 ↛ exit line 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 )

216

217 def is_action_valid(self, c, tl):

218 return (True, '别来找我！')

219

220 def sound_effect(self, act):

221 return 'thb-cv-card_sinsack'

222

223

224@ui_meta(spellcard.Sinsack)

225 class Sinsack:

226 def effect_string(self, act):

227 tgt = act.target

228 if act.succeeded:

229 return '罪袋终于找到了机会，将|G【%s】|r推倒了…' % tgt.ui_meta.name

230

231

232@ui_meta(spellcard.SinsackDamage)

233 class SinsackDamage:

234 def sound_effect(self, act):

235 return 'thb-cv-card_sinsack_effect'

236

237

238@ui_meta(definition.YukariDimensionCard)

239 class YukariDimensionCard:

240 # action_stage meta

241 image = 'thb-card-yukaridimension'

242 name = '隙间'

243

244 description = (

245 '|R隙间|r\n\n'

246 '非延时符卡。\n'

247 '出牌阶段，对距离为1的一名其他角色使用，获得其区域内的一张牌。\n\n'

248 '|DB（画师：霏茶，CV：VV）|r'

249 )

250

251 def is_action_valid(self, c, tl):

252 if not tl:

253 return (False, '请选择目标')

254

255 target = tl[0]

256 if not (target.cards or target.showncards or target.equips or target.fatetell):

257 return (False, '这货已经没有牌了')

258 else:

259 return (True, '请把胖次给我！')

260

261 def sound_effect(self, act):

262 return 'thb-cv-card_dimension'

263

264

265@ui_meta(spellcard.YukariDimension)

266 class YukariDimension:

267 def effect_string(self, act):

268 src, tgt = act.source, act.target

269 if act.succeeded: 269 ↛ exit line 269 didn't return from function 'effect_string', because the condition on line 269 was never false

270 return '|G【%s】|r透过隙间拿走了|G【%s】|r的1张牌。' % (

271 src.ui_meta.name,

272 tgt.ui_meta.name

273 )

274

275

276@ui_meta(definition.DuelCard)

277 class 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 )

287

288 def is_action_valid(self, c, tl):

289 if not tl:

290 return (False, '请选择弹幕战的目标')

291

292 return (True, '来，战个痛快！')

293

294 def sound_effect(self, act):

295 return 'thb-cv-card_duel'

296

297

298@ui_meta(definition.MapCannonCard)

299 class 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 )

308

309 def is_action_valid(self, c, tl):

310 return (True, '一个都不能跑！')

311

312 def sound_effect(self, act):

313 return 'thb-cv-card_mapcannon'

314

315

316@ui_meta(definition.DemonParadeCard)

317 class 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 )

326

327 def is_action_valid(self, c, tl):

328 return (True, '一只鬼，两只鬼，三只鬼……')

329

330 def sound_effect(self, act):

331 return 'thb-cv-card_demonparade'

332

333

334@ui_meta(definition.FeastCard)

335 class 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 )

345

346 def is_action_valid(self, c, tl):

347 return (True, '开宴啦~~')

348

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 ])

355

356

357@ui_meta(definition.HarvestCard)

358 class 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 )

368

369 def is_action_valid(self, c, tl):

370 return (True, '麻薯会有的，节操是没有的！')

371

372 def sound_effect(self, act):

373 return 'thb-cv-card_harvest'

374

375

376@ui_meta(spellcard.HarvestEffect)

377 class HarvestEffect:

378 def effect_string(self, act):

379 if not act.succeeded: return None 379 ↛ exit line 379 didn't return from function 'effect_string', because the return on line 379 wasn't executed

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 )

386

387

388@ui_meta(definition.DollControlCard)

389 class 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

400

401 def is_action_valid(self, c, tl):

402 n = len(tl)

403 if n == 0:

404 return (False, '请选择被控者')

405

406 if tl[0] is self.me:

407 return (False, '你不可以控制你自己')

408

409 if all(e.equipment_category != 'weapon' for e in tl[0].equips):

410 return (False, '被控者没有武器！')

411

412 if n == 1:

413 return (False, '请选择被控者的攻击目标')

414 elif n == 2: 414 ↛ exit line 414 didn't return from function 'is_action_valid', because the condition on line 414 was never false

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, '乖，听话！')

422

423 def sound_effect(self, act):

424 return 'thb-cv-card_dollcontrol'

425

426

427@ui_meta(spellcard.DollControl)

428 class DollControl:

429 # choose card meta

430 def choose_card_text(self, act, cards):

431 if act.cond(cards):

432 return (True, '那好吧…')

433 else:

434 return (False, '请出【弹幕】（否则你的武器会被拿走）')

435

436 def ray(self, act):

437 src = act.source

438 tl = act.tl

439 return [(src, tl[0]), (tl[0], tl[1])]

440

441

442@ui_meta(definition.DonationBoxCard)

443 class 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 )

453

454 def is_action_valid(self, c, tl):

455 n = len(tl)

456 if not n:

457 return (False, '请选择1-2名玩家')

458

459 for t in tl:

460 if not (t.cards or t.showncards or t.equips):

461 return (False, '目标没有可以给你的牌')

462

463 return (True, '纳奉！纳奉！')

464

465 def sound_effect(self, act):

466 return 'thb-cv-card_donationbox'

467

468

469@ui_meta(spellcard.DonationBoxEffect)

470 class DonationBoxEffect:

471 # choose card meta

472 def choose_card_text(self, act, cards):

473 if act.cond(cards):

474 return (True, '这是抢劫啊！')

475 else:

476 return (False, '请选择一张牌（否则会随机选择一张）')