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

5import itertools 

6import random 

7 

8# -- third party -- 

9# -- own -- 

10from thb.cards import definition, spellcard 

11from thb.cards.base import PhysicalCard 

12from thb.meta.common import ui_meta 

13 

14 

15# -- code -- 

16@ui_meta(definition.DemolitionCard) 

17class DemolitionCard: 

18 # action_stage meta 

19 image = 'thb-card-demolition' 

20 name = '城管执法' 

21 description = ( 

22 '|R城管执法|r\n\n' 

23 '非延时符卡。\n' 

24 '出牌阶段,对一名其他角色使用,弃置其区域内的一张牌。\n\n' 

25 '|DB(画师:霏茶,CV:shourei小N)|r' 

26 ) 

27 

28 def is_action_valid(self, c, tl): 

29 if not tl: 

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

31 

32 tgt = tl[0] 

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

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

35 else: 

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

37 

38 def sound_effect(self, act): 

39 return 'thb-cv-card_demolition' 

40 

41 

42@ui_meta(spellcard.Demolition) 

43class Demolition: 

44 def effect_string(self, act): 

45 if not act.succeeded: return None 

46 

47 # This could happen when: 

48 # 1. Parsee uses Envy, recycled a diamond card 

49 # 2. The card is an equipment weared by Alice 

50 # 3. Alice fires DollBlast, drops the very same card 

51 # 4. Sometime here, cards are shuffled 

52 # 5. Demolition now holds a ShreddedCard 

53 if not isinstance(act.card, PhysicalCard): 

54 return None 

55 

56 return '|G【%s】|r卸掉了|G【%s】|r的%s。' % ( 

57 act.source.ui_meta.name, 

58 act.target.ui_meta.name, 

59 self.card_desc(act.card), 

60 ) 

61 

62 

63@ui_meta(definition.RejectCard) 

64class RejectCard: 

65 # action_stage meta 

66 name = '好人卡' 

67 image = 'thb-card-reject' 

68 description = ( 

69 '|R好人卡|r\n\n' 

70 '非延时符卡。\n' 

71 '一张符卡对一名目标角色生效前,你可以使用此牌来抵消该符卡对其目标角色产生的效果。\n\n' 

72 '|DB(画师:霏茶,CV:VV)' 

73 ) 

74 

75 def is_action_valid(self, c, tl): 

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

77 

78 def effect_string(self, act): 

79 return '|G【%s】|r为|G【%s】|r受到的|G%s|r使用了|G%s|r。' % ( 

80 act.source.ui_meta.name, 

81 act.target.ui_meta.name, 

82 act.force_action.target_act.associated_card.ui_meta.name, 

83 act.card.ui_meta.name, 

84 ) 

85 

86 def sound_effect(self, act): 

87 return 'thb-cv-card_reject' 

88 

89 def has_reject_card(self, p): 

90 from thb.cards.definition import RejectCard as RC 

91 if any([c.is_card(RC) for c in itertools.chain(p.cards, p.showncards)]): 

92 return True 

93 

94 from thb.characters import reimu 

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

96 return True 

97 

98 return False 

99 

100 

101@ui_meta(spellcard.RejectHandler) 

102class RejectHandler: 

103 # choose_card meta 

104 def choose_card_text(self, act, cards): 

105 c = act.target_act.associated_card 

106 name = c.ui_meta.name 

107 

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

109 act.target_act.target.ui_meta.name, 

110 name, 

111 ) 

112 

113 if act.cond(cards): 

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

115 else: 

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

117 

118 

119@ui_meta(definition.SealingArrayCard) 

120class SealingArrayCard: 

121 # action_stage meta 

122 name = '封魔阵' 

123 image = 'thb-card-sealarray' 

124 tag = 'thb-tag-sealarray' 

125 description = ( 

126 '|R封魔阵|r\n\n' 

127 '延时类符卡。\n' 

128 '出牌阶段,对一名其他角色使用,将此牌横置于该角色的判定区内。该角色的判定阶段,需进行一次判定然后弃置此牌。若判定结果不为红桃,跳过其出牌阶段。\n' 

129 '|B|R>> |r判定开始前,你可以使用【好人卡】抵消该符卡的效果(抵消后弃掉【封魔阵】)。\n\n' 

130 '|DB(画师:霏茶,CV:shourei小N)|r' 

131 ) 

132 

133 def is_action_valid(self, c, tl): 

134 if len(tl) != 1: 

135 return (False, '请选择封魔阵的目标') 

136 t = tl[0] 

137 if self.me is t: 

138 return (False, '你不能跟自己过不去啊!') 

139 

140 return (True, '画个圈圈诅咒你!') 

141 

142 def sound_effect(self, act): 

143 return 'thb-cv-card_sealarray' 

144 

145 

146@ui_meta(spellcard.SealingArray) 

147class SealingArray: 

148 def effect_string(self, act): 

149 tgt = act.target 

150 if act.succeeded: 

151 return '|G【%s】|r被困在了封魔阵中。' % tgt.ui_meta.name 

152 else: 

153 return '封魔阵没有布置完善,|G【%s】|r侥幸逃了出来!' % tgt.ui_meta.name 

154 

155 

156@ui_meta(definition.FrozenFrogCard) 

157class FrozenFrogCard: 

158 # action_stage meta 

159 name = '冻青蛙' 

160 image = 'thb-card-frozenfrog' 

161 tag = 'thb-tag-frozenfrog' 

162 description = ( 

163 '|R冻青蛙|r\n\n' 

164 '延时类符卡。\n' 

165 '出牌阶段,对一名其他角色使用,将此牌横置于该角色的判定区内。该角色的判定阶段,需进行一次判定然后弃置此牌。若判定结果不为黑桃,跳过其摸牌阶段。\n' 

166 '|B|R>> |r判定开始前,你可以使用【好人卡】抵消该符卡的效果(抵消后弃掉【冻青蛙】)。\n\n' 

167 '|DB(画师:霏茶,CV:shourei小N)|r' 

168 ) 

169 

170 def is_action_valid(self, c, tl): 

171 if len(tl) != 1: 

172 return (False, '请选择冻青蛙的目标') 

173 t = tl[0] 

174 if self.me is t: 

175 return (False, '你不能跟自己过不去啊!') 

176 

177 return (True, '伸手党什么的,冻住就好了!') 

178 

179 def sound_effect(self, act): 

180 return 'thb-cv-card_frozenfrog' 

181 

182 

183@ui_meta(spellcard.FrozenFrog) 

184class FrozenFrog: 

185 def effect_string(self, act): 

186 tgt = act.target 

187 if act.succeeded: 

188 return '|G【%s】|r被冻住了……' % tgt.ui_meta.name 

189 else: 

190 return '幻想乡今天大晴,|G【%s】|r没有被冻住~' % tgt.ui_meta.name 

191 

192 

193@ui_meta(definition.NazrinRodCard) 

194class NazrinRodCard: 

195 # action_stage meta 

196 name = '寻龙尺' 

197 image = 'thb-card-nazrinrod' 

198 description = ( 

199 '|R寻龙尺|r\n\n' 

200 '非延时符卡。\n' 

201 '出牌阶段使用,从牌堆摸两张牌。\n\n' 

202 '|DB(画师:霏茶,CV:VV)|r' 

203 ) 

204 

205 def is_action_valid(self, c, tl): 

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

207 

208 def sound_effect(self, act): 

209 return 'thb-cv-card_nazrinrod' 

210 

211 

212@ui_meta(definition.SinsackCard) 

213class SinsackCard: 

214 # action_stage meta 

215 name = '罪袋' 

216 image = 'thb-card-sinsack' 

217 tag = 'thb-tag-sinsack' 

218 description = ( 

219 '|R罪袋|r\n\n' 

220 '延时类符卡。\n' 

221 '出牌阶段,对你使用,将此牌横置于你的判定区内。判定区内有此牌的角色的判定阶段,需进行一次判定:\n' 

222 '|B|R>> |r若判定结果为黑桃1-8,则目标角色受到3点无来源伤害,然后将其置入弃牌堆。\n' 

223 '|B|R>> |r若判定结果不在此范围,则将其移动到下家的判定区内。\n' 

224 '|B|R>> |r判定开始前,你可以使用|B好人卡|r抵消该符卡的效果,并将该|G罪袋|r直接传递给下家。\n\n' 

225 '|DB(画师:霏茶,CV:VV/大白)|r' 

226 ) 

227 

228 def is_action_valid(self, c, tl): 

229 return (True, '别来找我!') 

230 

231 def sound_effect(self, act): 

232 return 'thb-cv-card_sinsack' 

233 

234 

235@ui_meta(spellcard.Sinsack) 

236class Sinsack: 

237 def effect_string(self, act): 

238 tgt = act.target 

239 if act.succeeded: 

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

241 

242 

243@ui_meta(spellcard.SinsackDamage) 

244class SinsackDamage: 

245 def sound_effect(self, act): 

246 return 'thb-cv-card_sinsack_effect' 

247 

248 

249@ui_meta(definition.YukariDimensionCard) 

250class YukariDimensionCard: 

251 # action_stage meta 

252 image = 'thb-card-yukaridimension' 

253 name = '隙间' 

254 

255 description = ( 

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

257 '非延时符卡。\n' 

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

259 '|DB(画师:霏茶,CV:VV)|r' 

260 ) 

261 

262 def is_action_valid(self, c, tl): 

263 if not tl: 

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

265 

266 target = tl[0] 

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

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

269 else: 

270 return (True, '请把胖次给我!') 

271 

272 def sound_effect(self, act): 

273 return 'thb-cv-card_dimension' 

274 

275 

276@ui_meta(spellcard.YukariDimension) 

277class YukariDimension: 

278 def effect_string(self, act): 

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

280 if act.succeeded: 

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

282 src.ui_meta.name, 

283 tgt.ui_meta.name 

284 ) 

285 

286 

287@ui_meta(definition.DuelCard) 

288class DuelCard: 

289 # action_stage meta 

290 image = 'thb-card-duel' 

291 name = '弹幕战' 

292 description = ( 

293 '|R弹幕战|r\n\n' 

294 '非延时符卡。\n' 

295 '出牌阶段,对一名其他角色使用,由目标角色开始,轮流打出一张【弹幕】。首先不打出【弹幕】的一方受到另一方造成的1点伤害。\n\n' 

296 '|DB(画师:霏茶,CV:小羽)|r' 

297 ) 

298 

299 def is_action_valid(self, c, tl): 

300 if not tl: 

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

302 

303 return (True, '来,战个痛快!') 

304 

305 def sound_effect(self, act): 

306 return 'thb-cv-card_duel' 

307 

308 

309@ui_meta(definition.MapCannonCard) 

310class MapCannonCard: 

311 image = 'thb-card-mapcannon' 

312 name = '地图炮' 

313 description = ( 

314 '|R地图炮|r\n\n' 

315 '群体符卡。非延时符卡。\n' 

316 '出牌阶段,对除你以外的所有其他角色使用,目标角色需依次打出一张【擦弹】,否则该角色受到1点伤害。\n\n' 

317 '|DB(画师:霏茶,CV:VV)|r' 

318 ) 

319 

320 def is_action_valid(self, c, tl): 

321 return (True, '一个都不能跑!') 

322 

323 def sound_effect(self, act): 

324 return 'thb-cv-card_mapcannon' 

325 

326 

327@ui_meta(definition.DemonParadeCard) 

328class DemonParadeCard: 

329 image = 'thb-card-demonparade' 

330 name = '百鬼夜行' 

331 description = ( 

332 '|R百鬼夜行|r\n\n' 

333 '群体符卡。非延时符卡。\n' 

334 '出牌阶段,对除你以外的所有其他角色使用,目标角色需依次打出一张|G弹幕|r,否则该角色受到1点伤害。\n\n' 

335 '|DB(画师:霏茶,CV:小羽)|r' 

336 ) 

337 

338 def is_action_valid(self, c, tl): 

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

340 

341 def sound_effect(self, act): 

342 return 'thb-cv-card_demonparade' 

343 

344 

345@ui_meta(definition.FeastCard) 

346class FeastCard: 

347 # action_stage meta 

348 image = 'thb-card-feast' 

349 name = '宴会' 

350 description = ( 

351 '|R宴会|r\n\n' 

352 '群体符卡。非延时符卡。\n' 

353 '出牌阶段,对所有角色使用。已受伤的角色回复一点体力,未受伤的角色获得|B喝醉|r状态。\n\n' 

354 '|DB(画师:霏茶,CV:VV)|r' 

355 ) 

356 

357 def is_action_valid(self, c, tl): 

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

359 

360 def sound_effect(self, act): 

361 return random.choice([ 

362 'thb-cv-card_feast1', 

363 'thb-cv-card_feast2', 

364 'thb-cv-card_feast3', 

365 ]) 

366 

367 

368@ui_meta(definition.HarvestCard) 

369class HarvestCard: 

370 # action_stage meta 

371 image = 'thb-card-harvest' 

372 name = '五谷丰登' 

373 description = ( 

374 '|R五谷丰登|r\n\n' 

375 '群体符卡。非延时符卡。\n' 

376 '出牌阶段,对所有角色使用,你从牌堆顶亮出等同于现存角色数量的牌,目标角色依次选择并获得这些牌中的一张。\n\n' 

377 '|DB(画师:霏茶,CV:VV)|r' 

378 ) 

379 

380 def is_action_valid(self, c, tl): 

381 return (True, '麻薯会有的,节操是没有的!') 

382 

383 def sound_effect(self, act): 

384 return 'thb-cv-card_harvest' 

385 

386 

387@ui_meta(spellcard.HarvestEffect) 

388class HarvestEffect: 

389 def effect_string(self, act): 

390 if not act.succeeded: return None 

391 tgt = act.target 

392 c = act.card 

393 return '|G【%s】|r获得了|G%s|r。' % ( 

394 tgt.ui_meta.name, 

395 self.card_desc(c), 

396 ) 

397 

398 

399@ui_meta(definition.DollControlCard) 

400class DollControlCard: 

401 # action_stage meta 

402 name = '人形操控' 

403 image = 'thb-card-dollcontrol' 

404 description = ( 

405 '|R人形操控|r\n\n' 

406 '非延时符卡。\n' 

407 '出牌阶段,对装备区内有武器牌的一名其他角色使用,令其选择一项:对其攻击范围内一名由你指定的角色使用一张【弹幕】,或将武器交给你。\n\n' 

408 '|DB(画师:霏茶,CV:小羽)|r' 

409 ) 

410 custom_ray = True 

411 

412 def is_action_valid(self, c, tl): 

413 n = len(tl) 

414 if n == 0: 

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

416 

417 if tl[0] is self.me: 

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

419 

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

421 return (False, '被控者没有武器!') 

422 

423 if n == 1: 

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

425 elif n == 2: 

426 from thb.actions import LaunchCard 

427 from thb.cards.definition import AttackCard 

428 c = AttackCard() 

429 lc = LaunchCard(tl[0], [tl[1]], c) 

430 if not lc.can_fire(): 

431 return (False, '被控者无法向目标出【弹幕】!') 

432 return (True, '乖,听话!') 

433 

434 def sound_effect(self, act): 

435 return 'thb-cv-card_dollcontrol' 

436 

437 

438@ui_meta(spellcard.DollControl) 

439class DollControl: 

440 # choose card meta 

441 def choose_card_text(self, act, cards): 

442 if act.cond(cards): 

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

444 else: 

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

446 

447 def ray(self, act): 

448 src = act.source 

449 tl = act.target_list 

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

451 

452 

453@ui_meta(definition.DonationBoxCard) 

454class DonationBoxCard: 

455 # action_stage meta 

456 name = '赛钱箱' 

457 image = 'thb-card-donationbox' 

458 description = ( 

459 '|R赛钱箱|r\n\n' 

460 '非延时符卡。\n' 

461 '出牌阶段,对至多两名其他角色使用,目标角色需将自己的一张牌置入你的明牌区。\n\n' 

462 '|DB(画师:霏茶,CV:shourei小N)|r' 

463 ) 

464 

465 def is_action_valid(self, c, tl): 

466 n = len(tl) 

467 if not n: 

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

469 

470 for t in tl: 

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

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

473 

474 return (True, '纳奉!纳奉!') 

475 

476 def sound_effect(self, act): 

477 return 'thb-cv-card_donationbox' 

478 

479 

480@ui_meta(spellcard.DonationBoxEffect) 

481class DonationBoxEffect: 

482 # choose card meta 

483 def choose_card_text(self, act, cards): 

484 if act.cond(cards): 

485 return (True, '这是抢劫啊!') 

486 else: 

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