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 absolute_import, annotations, division, print_function, unicode_literals 

3 

4# -- stdlib -- 

5from enum import Enum 

6from itertools import chain, combinations, cycle 

7from typing import Any, Callable, Dict, List, cast 

8import logging 

9import random 

10 

11# -- third party -- 

12# -- own -- 

13from game.base import BootstrapAction, GameEnded, GameItem, InputTransaction, InterruptActionFlow 

14from game.base import NPC, Player 

15from thb.actions import ActionStage, ActionStageLaunchCard, CardChooser, DrawCards, DropCards 

16from thb.actions import FatetellStage, FinalizeStage, GenericAction, LaunchCard, PlayerDeath 

17from thb.actions import PlayerTurn, PrepareStage, RevealRole, ShuffleHandler, ask_for_action 

18from thb.cards.base import Card, Deck 

19from thb.cards.classes import AskForHeal, AttackCard, Demolition, DemolitionCard 

20from thb.cards.classes import ElementalReactorCard, ExinwanCard, FrozenFrogCard, GrazeCard 

21from thb.cards.classes import GreenUFOCard, Heal, HealCard, LaunchGraze, MomijiShieldCard 

22from thb.cards.classes import NazrinRodCard, RedUFOCard, Reject, RejectCard, RejectHandler 

23from thb.cards.classes import SinsackCard, WineCard 

24from thb.common import PlayerRole 

25from thb.inputlets import ActionInputlet, GalgameDialogInputlet 

26from thb.mode import THBEventHandler, THBattle 

27from utils.misc import BatchList 

28 

29 

30# -- code -- 

31log = logging.getLogger('THBattleNewbie') 

32 

33 

34class OneShotActionStage(ActionStage): 

35 one_shot = True 

36 

37 

38class DeathHandler(THBEventHandler): 

39 interested = ['action_apply'] 

40 

41 def handle(self, evt_type, act): 

42 if evt_type != 'action_apply': return act 42 ↛ exitline 42 didn't return from function 'handle', because the return on line 42 wasn't executed

43 if not isinstance(act, PlayerDeath): return act 43 ↛ 44line 43 didn't jump to line 44, because the condition on line 43 was never false

44 tgt = act.target 

45 

46 g = self.game 

47 a, b = g.players 

48 

49 raise GameEnded([a.player if b is tgt else b.player]) 

50 

51 

52class THBNewbieRole(Enum): 

53 HIDDEN = 0 

54 NEWBIE = 1 

55 BAKA = 2 

56 

57 

58class CirnoAI(object): 

59 

60 def __init__(self, trans, ilet): 

61 self.trans = trans 

62 self.ilet = ilet 

63 

64 def entry(self): 

65 ilet = self.ilet 

66 trans = self.trans 

67 p = ilet.actor 

68 

69 g = trans.game 

70 g.pause(1.2) 

71 

72 if trans.name == 'ActionStageAction': 

73 tl = g.players[1:] 

74 cl = list(p.showncards) + list(p.cards) 

75 if random.random() > 0.6: 

76 return False 

77 

78 for c in cl: 

79 if c.is_card(AttackCard): 

80 if self.try_launch(c, tl[:1]): return True 

81 

82 elif trans.name == 'Action' and isinstance(ilet, ActionInputlet): 

83 if not (ilet.categories and not ilet.candidates): 

84 return True 

85 

86 if isinstance(ilet.initiator, AskForHeal): 

87 return False 

88 

89 cond = cast(CardChooser, ilet.initiator).cond 

90 cl = list(p.showncards) + list(p.cards) 

91 _, C = chain, lambda r: combinations(cl, r) 

92 for c in _(C(1), C(2)): 

93 if cond(c): 

94 ilet.set_result(skills=[], cards=c, characters=[]) 

95 return True 

96 

97 elif trans.name == 'ChoosePeerCard': 

98 tgt = ilet.target 

99 if tgt.cards: 

100 ilet.set_card(tgt.cards[0]) 

101 elif tgt.showncards: 

102 ilet.set_card(tgt.showncards[0]) 

103 

104 def try_launch(self, c, tl, skills=[]): 

105 p = self.ilet.actor 

106 act = ActionStageLaunchCard(p, tl, c) 

107 if act.can_fire(): 

108 self.ilet.set_result(skills=skills, cards=[c], characters=tl) 

109 return True 

110 

111 return False 

112 

113 @classmethod 

114 def ai_main(cls, trans, ilet): 

115 cls(trans, ilet).entry() 

116 

117 

118class AdhocEventHandler(THBEventHandler): 

119 def __init__(self, hook: Callable[[str, Any], Any]): 

120 self._handle = hook # typing: ignore 

121 

122 def handle(self, evt_type: str, arg: Any) -> Any: 

123 return self._handle(evt_type, arg) 

124 

125 

126class THBattleNewbieBootstrap(BootstrapAction): 

127 game: THBattleNewbie 

128 

129 def __init__(self, params: Dict[str, Any], 

130 items: Dict[Player, List[GameItem]], 

131 players: BatchList[Player]): 

132 self.source = self.target = None 

133 self.players = players 

134 self.params = params 

135 self.items = items 

136 

137 def apply_action(self): 

138 g = self.game 

139 

140 from thb.characters.meirin import Meirin 

141 from thb.characters.cirno import Cirno 

142 

143 # ----- Init ----- 

144 pl = self.players 

145 g.deck = Deck(g) 

146 

147 cirno_p, meirin_p = pl 

148 

149 g.roles = { 

150 cirno_p: PlayerRole(THBNewbieRole), 

151 meirin_p: PlayerRole(THBNewbieRole), 

152 } 

153 g.roles[cirno_p].set(THBNewbieRole.BAKA) 

154 g.roles[meirin_p].set(THBNewbieRole.NEWBIE) 

155 

156 g.process_action(RevealRole(cirno_p, g.roles[cirno_p], pl)) 

157 g.process_action(RevealRole(meirin_p, g.roles[meirin_p], pl)) 

158 

159 cirno = Cirno(cirno_p) 

160 meirin = Meirin(meirin_p) 

161 

162 g.players = BatchList([cirno, meirin]) 

163 

164 g.refresh_dispatcher() 

165 g.emit_event('switch_character', (None, cirno)) 

166 g.emit_event('switch_character', (None, meirin)) 

167 

168 g.emit_event('game_begin', g) 

169 # ----- End Init ----- 

170 

171 pt = PlayerTurn(cirno) 

172 pt.pending_stages = [ScriptedStage] 

173 g.process_action(pt) 

174 

175 for i, idx in enumerate(cycle([1, 0])): 

176 p = g.players[idx] 

177 if i >= 6000: break 

178 try: 

179 g.process_action(PlayerTurn(p)) 

180 except InterruptActionFlow: 

181 pass 

182 

183 return True 

184 

185 

186class ScriptedStage(GenericAction): 

187 def __init__(self, target): 

188 self.source = target 

189 self.target = target 

190 

191 def apply_action(self): 

192 g = self.game 

193 

194 from thb.characters.meirin import Meirin 

195 from thb.characters.cirno import Cirno 

196 from thb.characters.sakuya import Sakuya 

197 

198 g.dispatcher.remove_by_cls(ShuffleHandler) 

199 

200 def dialog(character, dialog, voice): 

201 if voice is not None: 201 ↛ 204line 201 didn't jump to line 204, because the condition on line 201 was never false

202 voice = 'thb-cv-newbie-%s-%s' % (character.__name__.lower(), ('000' + str(voice))[-3:]) 

203 

204 g.user_input([meirin], GalgameDialogInputlet(g, character, dialog, voice), timeout=60) 

205 

206 def inject_eh(hook: Callable[[str, Any], Any]): 

207 eh = AdhocEventHandler(hook) 

208 g.dispatcher.add_adhoc(eh) 

209 return eh 

210 

211 def remove_eh(eh): 

212 try: 

213 g.dispatcher.remove_adhoc(eh) 

214 except Exception: 

215 raise 

216 pass 

217 

218 def fail(): 

219 dialog(Meirin, '喂剧本不是这么写的啊,重来重来!', 1) 

220 

221 cirno, meirin = g.players # update 

222 turn = PlayerTurn.get_current(g) 

223 

224 dialog(Meirin, '一个pad,两个pad,三个pad……', 2) 

225 dialog(Sakuya, '(唰', None) 

226 dialog(Meirin, '啊,我头上的是……', 3) 

227 dialog(Sakuya, '别做白日梦,起床了起床了。那边那个妖精可又来门口找麻烦了,为了你的晚餐考虑,还是去解决一下吧?', 1) 

228 dialog(Meirin, '是是是……这都已经是第999次了吧,那家伙真是不知道什么叫做放弃吗……', 4) 

229 

230 dialog(Cirno, '俺又来啦,这次绝对要打赢你!赌上大酱的100场陪练!', 1) 

231 dialog(Meirin, '前面998次你也都是这么说的……好了,废话少说,放马过来吧!', 5) 

232 dialog(Cirno, '正合我意!', 2) 

233 

234 turn.target = cirno 

235 

236 c = g.deck.inject(AttackCard, Card.SPADE, 1) 

237 g.process_action(DrawCards(cirno, 1)) 

238 dialog(Cirno, '“吃我大弹幕啦!”', 3) 

239 g.process_action(LaunchCard(cirno, [meirin], c)) 

240 

241 # 红美铃受到一点伤害 

242 

243 dialog(Meirin, '呜哇!?', 6) 

244 dialog(Sakuya, '怎么搞的,被一只妖精弄伤了?', 2) 

245 dialog(Meirin, '不不不,那个咲夜你听我说,这是偷袭……', 7) 

246 dialog(Sakuya, '嗯……明明游戏已经开始了,不打起十二分的精神迎战可是不行的啊。在玩thb的时候请注意队友的感受,不要挂机喔。', 3) 

247 dialog(Meirin, '啥啊又在对着不存在的人说些莫名其妙的东西……', 8) 

248 dialog(Sakuya, '嗯?', 4) 

249 dialog(Meirin, '我可什么都没说!', 9) 

250 

251 # 红美铃的回合【目的:使用基本牌(麻薯,弹幕)】 

252 turn.target = meirin 

253 c = g.deck.inject(HealCard, Card.HEART, 2) 

254 g.process_action(DrawCards(meirin, 1)) 

255 

256 while c in meirin.cards: 

257 text = ( 

258 '总之,这里先回复……\n' 

259 '(请使用麻薯)\n' 

260 '(在PC版中鼠标移动到卡牌/人物上,或者手机版中长按卡牌/人物头像,就会弹出说明,很有用的)' 

261 ) 

262 dialog(Meirin, text, 10) 

263 g.process_action(OneShotActionStage(meirin)) 

264 

265 atkcard = g.deck.inject(AttackCard, Card.SPADE, 3) 

266 g.process_action(DrawCards(meirin, 1)) 

267 

268 while atkcard in meirin.cards: 

269 text = ( 

270 '好,状态全满!那边的妖精!吃我大弹幕啦!\n' 

271 '(请首先点击弹幕,然后点击琪露诺,最后点击出牌)\n' 

272 '(在PC版中鼠标移动到卡牌/人物上,或者手机版中长按卡牌/人物头像,就会弹出说明,很有用的)' 

273 ) 

274 dialog(Meirin, text, 11) 

275 g.process_action(OneShotActionStage(meirin)) 

276 

277 dialog(Cirno, '哎呀!?', 4) 

278 dialog(Sakuya, '啊啦,干的不错。', 5) 

279 dialog(Meirin, '那是自然啦,对付一些这样的妖精还是不在话下的……', 12) 

280 dialog(Cirno, '喂!悄悄话说的也太大声了!!', 5) 

281 

282 # 琪露诺的回合【目的:使用基本牌(擦弹)】【使用太极(1)】 

283 turn.target = cirno 

284 g.deck.inject(HealCard, Card.HEART, 4) 

285 g.process_action(DrawCards(cirno, 1)) 

286 while True: 

287 if meirin.life < meirin.maxlife: 

288 g.process_action(Heal(meirin, meirin, meirin.maxlife - meirin.life)) 

289 

290 if meirin.cards: 

291 g.process_action(DropCards(meirin, meirin, meirin.cards)) 

292 

293 atkcard = g.deck.inject(AttackCard, Card.SPADE, 5) 

294 g.process_action(DrawCards(cirno, 1)) 

295 graze = g.deck.inject(GrazeCard, Card.DIAMOND, 6) 

296 g.process_action(DrawCards(meirin, 1)) 

297 

298 dialog(Cirno, '这回就轮到我了!接招!超⑨武神霸斩!', 6) 

299 

300 lc = LaunchCard(cirno, [meirin], atkcard) 

301 

302 @inject_eh 

303 def resp(evt_type: str, act: Any): 

304 if evt_type == 'choose_target' and act[0] is lc: 

305 g.pause(1) 

306 dialog(Meirin, '来的正好!', 13) 

307 

308 elif evt_type == 'action_after' and isinstance(act, LaunchGraze) and act.succeeded: 

309 g.pause(1) 

310 dialog(Cirno, '我的计谋竟被……', 7) 

311 text = ( 

312 '还没完呐!\n' 

313 '(使用技能,震飞琪露诺一张手牌)\n' 

314 '(每一名角色都有自己独特的技能,灵活利用好这些技能是你取得胜利的关键所在)' 

315 ) 

316 dialog(Meirin, text, 14) 

317 

318 return act 

319 

320 g.process_action(lc) 

321 remove_eh(resp) 

322 

323 if graze in meirin.cards or cirno.cards: 

324 fail() 

325 continue 

326 

327 break 

328 

329 dialog(Cirno, '切,再让你嘚瑟一会儿,我还有自己的杀手锏呢……', 8) 

330 dialog(Meirin, '在这种时候放空话可是谁都不会相信的啦。', 15) 

331 green = g.deck.inject(GreenUFOCard, Card.DIAMOND, 7) 

332 g.process_action(DrawCards(cirno, 1)) 

333 dialog(Cirno, '总之不能再让你这么自在地用弹幕打到俺了……看我的杀手锏!', 9) 

334 g.process_action(LaunchCard(cirno, [cirno], green)) 

335 

336 dialog(Meirin, '咦……', 16) 

337 dialog(Sakuya, '不会连这个你也不清楚吧。', 6) 

338 dialog(Meirin, '我记得是记得啦,似乎是我现在够不到琪露诺了之类的而琪露诺可以够到我……吧?说回来,这样的解释似乎很不科学诶,为什么我够不到但是对方够得到我啊,这个|G绿色UFO|r到底是什么东西……', 17) 

339 dialog(Sakuya, '……你只要好好打完这局就可以了,再问这么多为什么,我可不保证你的脑门上会多出什么奇怪的金属制品。', 7) 

340 dialog(Meirin, '是!', 18) 

341 

342 # 红美铃的回合【目的:使用延时符卡(冻青蛙),使用太极(2),使用红色UFO】' 

343 turn.target = meirin 

344 frozen = g.deck.inject(FrozenFrogCard, Card.SPADE, 8) 

345 g.process_action(DrawCards(meirin, 1)) 

346 

347 dialog(Meirin, '咦,这张牌是……', 19) 

348 dialog(Sakuya, '这是|G冻青蛙|r。\n(咳嗽了一声)|G冻青蛙|r是一种|R延时符卡|r,它和|G封魔阵|r一样在使用时并不会立即发生作用,只有轮到了那个角色的行动回合时,才会进行一次判定来执行该符卡的后续效果。', 8) 

349 dialog(Meirin, '原来是这样……那就先贴到她脸上再说!', 20) 

350 

351 g.process_action(OneShotActionStage(meirin)) 

352 

353 dialog(Meirin, '这是怎么回事…为什么不能使用…那就先来一发|G弹幕|r好了!', 21) 

354 

355 atkcard = g.deck.inject(AttackCard, Card.SPADE, 9) 

356 g.process_action(DrawCards(meirin, 1)) 

357 

358 g.process_action(OneShotActionStage(meirin)) 

359 

360 dialog(Meirin, '咲夜咲夜咲夜,我没法打她啊!', 22) 

361 dialog(Sakuya, '……你忘了琪露诺的|G绿色UFO|r吗。现在从你这边看来和琪露诺的距离为2,也就是,赤手空拳的距离1是没有办法用|G弹幕|r打中她的。我记得鬼族有她们的方法,但是很显然你并不会。', 9) 

362 dialog(Meirin, '好吧……所以?', 23) 

363 dialog(Sakuya, '(掏出飞刀', None) 

364 dialog(Meirin, '好啦好啦,我不问啦!', 24) 

365 

366 red = g.deck.inject(RedUFOCard, Card.HEART, 10) 

367 g.process_action(DrawCards(meirin, 1)) 

368 

369 while red in meirin.cards: 

370 text = ( 

371 '哈,我找到了这个!\n' 

372 '(红色UFO可以拉近其他角色与你的距离,快装备上吧)' 

373 ) 

374 dialog(Meirin, text, 25) 

375 g.process_action(OneShotActionStage(meirin)) 

376 

377 dialog(Sakuya, '你这不是知道UFO的规则嘛。', 10) 

378 dialog(Meirin, '只是想趁这次机会和咲夜多说说话啦,平时总是一副大忙人的样子来无影去无踪的……', 26) 

379 dialog(Sakuya, '你以为奉承一下我就会给你涨工资吗。', 11) 

380 dialog(Meirin, '啊哈,啊哈哈。', 27) 

381 

382 g.pause(1) 

383 

384 g.deck.inject(NazrinRodCard, Card.HEART, 11) 

385 graze = g.deck.inject(GrazeCard, Card.DIAMOND, 12) 

386 g.process_action(DrawCards(cirno, 2)) 

387 

388 while True: 

389 if atkcard not in meirin.cards: 

390 atkcard = g.deck.inject(AttackCard, Card.SPADE, 9) 

391 g.process_action(DrawCards(meirin, 1)) 

392 

393 if graze not in cirno.cards: 

394 graze = g.deck.inject(GrazeCard, Card.DIAMOND, 12) 

395 g.process_action(DrawCards(cirno, 1)) 

396 

397 dialog(Cirno, '喂,读条要过了你们在那儿干嘛呢,说好的弹幕呢!', 10) 

398 dialog(Meirin, '我这辈子还没听过这么欠扁的要求!吃我一弹!', 28) 

399 

400 @inject_eh 

401 def handle_longquan(evt_type: str, act: Any): 

402 if evt_type == 'action_after' and isinstance(act, LaunchGraze) and act.target is cirno: 

403 g.pause(1) 

404 dialog(Cirno, '你以为只有你会闪开吗!', 11) 

405 dialog(Meirin, '确实谁都会闪啦,不过接下来的,可就不是谁都会的咯?', 29) 

406 

407 return act 

408 

409 g.process_action(OneShotActionStage(meirin)) 

410 remove_eh(handle_longquan) 

411 

412 if atkcard in meirin.cards: 

413 continue 

414 

415 if cirno.cards: 

416 fail() 

417 continue 

418 

419 break 

420 

421 g.pause(1) 

422 dialog(Cirno, '呜哇你赖皮!', 12) 

423 dialog(Meirin, '哪来的什么赖皮不赖皮,我有自己的能力,但是你也有啊。', 30) 

424 dialog(Cirno, '可是我又不会用!你这不是赖皮是什么嘛!', 13) 

425 dialog(Meirin, '……', None) 

426 dialog(Sakuya, '……还是不要跟笨蛋说话的比较好,智商会下降的。', 12) 

427 

428 while frozen in meirin.cards: 

429 dialog(Meirin, '那么,把这张|G冻青蛙|r也贴上去吧!', 31) 

430 g.process_action(ActionStage(meirin)) 

431 

432 turn.target = cirno 

433 g.deck.inject(SinsackCard, Card.SPADE, 13) 

434 g.process_action(FatetellStage(cirno)) 

435 

436 g.pause(2) 

437 

438 dialog(Sakuya, '所谓“笨蛋的运气总会很不错”吗。', 13) 

439 dialog(Cirno, '哼,俺可是自带⑨翅膀的天才呀!', 14) 

440 dialog(Meirin, '可惜依旧是笨蛋。', 32) 

441 

442 shield = g.deck.inject(MomijiShieldCard, Card.SPADE, 1) 

443 g.process_action(DrawCards(cirno, 1)) 

444 dialog(Cirno, '无路赛……我要出王牌啦!', 15) 

445 g.process_action(LaunchCard(cirno, [cirno], shield)) 

446 

447 dialog(Meirin, '这是……', 33) 

448 dialog(Cirno, '这是我在妖怪之山的妖精那里【借】来的宝物,怎么样,接下来你就没办法用弹幕伤到俺了吧~', 16) 

449 dialog(Sakuya, '只是黑色的弹幕无效而已。', 14) 

450 dialog(Meirin, '对呀对呀。', 34) 

451 dialog(Sakuya, '而且,只要拆掉的话不就好了吗。', 15) 

452 dialog(Meirin, '对呀对……啥?', 35) 

453 dialog(Sakuya, '真是的,你之前的998局到底是怎么赢的……', 16) 

454 

455 # 红美铃的回合【目的:使用符卡(城管执法,好人卡)】 

456 turn.target = meirin 

457 

458 demolition = g.deck.inject(DemolitionCard, Card.CLUB, 2) 

459 g.process_action(DrawCards(meirin, 1)) 

460 cirnoreject = g.deck.inject(RejectCard, Card.CLUB, 3) 

461 g.process_action(DrawCards(cirno, 1)) 

462 

463 while demolition in meirin.cards: 

464 dialog(Meirin, '咲夜咲夜!', 36) 

465 dialog(Sakuya, '是啦,这就是我说的那张符卡了。和其他的|R非延时符卡|r使用方法都一样,属于发动之后就会立即生效的类型。', 17) 

466 dialog(Meirin, '……不是很好理解诶。', 37) 

467 

468 dialog(Sakuya, '用用看就知道了。快去卸掉她的|G天狗盾|r吧。', 18) 

469 

470 @inject_eh 

471 def handle_rejectcard(evt_type: str, act: Any): 

472 if evt_type == 'action_before' and isinstance(act, Demolition): 

473 g.pause(1.5) 

474 dialog(Cirno, '哈哈,早知道你会用这一招,怎能让你轻易得逞!', 17) 

475 g.process_action(LaunchCard(cirno, [cirno], cirnoreject, Reject(cirno, act))) 

476 

477 elif evt_type == 'action_before' and isinstance(act, Reject) and act.associated_card is cirnoreject: 

478 g.pause(1.5) 

479 dialog(Meirin, '这又是怎么回事……好像|G城管执法|r并没有起作用?', 38) 

480 dialog(Sakuya, '咦,这笨蛋居然知道用|G好人卡|r啊……', 19) 

481 dialog(Cirno, '什么笨蛋,老娘是天才,天~才~', 18) 

482 text = ( 

483 '|G好人卡|r的效果是|R抵消符卡效果|r,也就是说,你的|G城管执法|r的效果被无效化了。\n' 

484 '(在PC版中鼠标移动到卡牌/人物上,或者手机版中长按卡牌/人物头像,就会弹出说明,很有用的)' 

485 ) 

486 dialog(Sakuya, text, 20) 

487 dialog(Sakuya, '但是,|G好人卡|r的“无效符卡”的效果,本身也是符卡效果,是可以被|G好人卡|r抵消的!', 21) 

488 

489 meirinreject = g.deck.inject(RejectCard, Card.CLUB, 4) 

490 g.process_action(DrawCards(meirin, 1)) 

491 

492 while not act.cancelled: 

493 dialog(Meirin, '我知道了,我也用|G好人卡|r去抵消她的|G好人卡|r效果就好了!', 39) 

494 

495 rej = RejectHandler(g) 

496 rej.target_act = act 

497 with InputTransaction('AskForRejectAction', [meirin]) as trans: 

498 p, rst = ask_for_action(rej, [meirin], ('cards', 'showncards'), [], trans=trans) 

499 

500 if not p: continue 

501 assert rst 

502 cards, _ = rst 

503 assert cards[0] is meirinreject 

504 g.process_action(LaunchCard(meirin, [cirno], meirinreject, Reject(meirin, act))) 

505 

506 return act 

507 

508 g.process_action(OneShotActionStage(meirin)) 

509 remove_eh(handle_rejectcard) 

510 

511 if shield in cirno.equips: 

512 dialog(Sakuya, '喂,不是说要拆掉|G天狗盾|r吗,你明明装备着|G红色UFO|r的,她是在你的距离范围内的。', 22) 

513 dialog(Meirin, '哎呀,手抖了的说……', 40) 

514 else: 

515 dialog(Cirno, '呜哇你赔我的|G天狗盾|r!', 19) 

516 dialog(Meirin, '怪我咯!?', 41) 

517 

518 g.deck.inject(ExinwanCard, Card.CLUB, 3) 

519 g.process_action(DrawCards(meirin, 1)) 

520 

521 dialog(Meirin, '咦?咲夜,这张牌好奇怪……为什么是负面的效果?', 42) 

522 dialog(Sakuya, '是|G恶心丸|r啊……你的运气不太好哦。不过尽管说是一张负面效果的牌,但是看发动条件的话,是可以恶心到别人的。情况允许的话就留在手里好了,直接吃掉肯定是不合算的。', 23) 

523 

524 turn.target = cirno 

525 g.deck.inject(DemolitionCard, Card.CLUB, 4) 

526 g.process_action(DrawCards(cirno, 1)) 

527 

528 dialog(Cirno, '可恶,你到底有没有认真的在打啊!看我双倍奉还!', 20) 

529 g.process_action(LaunchCard(cirno, [meirin], demolition)) 

530 

531 dialog(Cirno, '呜哇,这是什么!不要来找我!', 21) 

532 dialog(Meirin, '哈哈,说好的双倍奉还呢?', 43) 

533 

534 # 美铃的觉醒和主动技能 

535 er = g.deck.inject(ElementalReactorCard, Card.DIAMOND, 5) 

536 atks = [g.deck.inject(AttackCard, Card.SPADE, i) for i in (6, 7, 8, 9)] 

537 g.process_action(DrawCards(cirno, 5)) 

538 dialog(Cirno, '真是伤脑筋……看来我要拿出真正的实力来了!', 22) 

539 g.process_action(LaunchCard(cirno, [cirno], er)) 

540 

541 dialog(Cirno, '接招啦!!', 23) 

542 

543 for c in atks[:-1]: 

544 g.pause(0.5) 

545 g.process_action(LaunchCard(cirno, [meirin], c)) 

546 

547 dialog(Meirin, '唔,不好,再这样下去就要撑不住了……', 44) 

548 wine = g.deck.inject(WineCard, Card.DIAMOND, 10) 

549 g.process_action(DrawCards(meirin, 1)) 

550 

551 while wine in meirin.cards: 

552 dialog(Meirin, '没办法,这瓶|G酒|r本来是想留着|R来一发2点伤害的弹幕|r用的,现在只能用来|R抵挡1点致命伤害|r了……', 45) 

553 g.process_action(ActionStage(meirin)) 

554 

555 g.pause(0.5) 

556 g.process_action(LaunchCard(cirno, [meirin], atks[-1])) 

557 

558 g.pause(0.5) 

559 dialog(Cirno, '怎么样,老娘最强!', 24) 

560 

561 dialog(Meirin, '呼呼,撑过来了……', 46) 

562 dialog(Meirin, '咲夜,这是怎么回事?|G弹幕|r不应该|R一回合只能使用一次|r吗?', 47) 

563 dialog(Sakuya, '嗯……说的不错。|R一回合只能使用一次|r这个规则是没错啦,不过你看她的手上,是河童们研制的迷你|G八卦炉|r,只要装备上的话,一回合就可以使用任意次数的|G弹幕|r了。', 24) 

564 dialog(Meirin, '你这家伙好像【借】来了不少东西嘛……不过笨蛋就是笨蛋,我怎么可能会输在笨蛋的手上!', 48) 

565 

566 pt = PlayerTurn(meirin) 

567 pt.pending_stages = [PrepareStage, FinalizeStage] 

568 g.process_action(pt) # awake! 

569 g.pause(2) 

570 

571 dialog(Cirno, '你怎么会突然多出来一个技能,这一点也不公平!', 25) 

572 dialog(Sakuya, '“笨蛋就是笨蛋”这句话说的一点没错……你像这样打伤美铃,触发了美铃的|R觉醒技|r。|R觉醒技|r通常都是很厉害的技能,但是要满足一定条件以后才可以开始使用。据我所知,那个三途川的草鞋船夫也有类似的能力,而美铃的觉醒技,就是|G太极|r了。', 25) 

573 dialog(Cirno, '啊,原来是这样……不不,才不是笨蛋,老娘最强!', 26) 

574 

575 graze = g.deck.inject(GrazeCard, Card.DIAMOND, 11) 

576 g.process_action(DrawCards(meirin, 1)) 

577 

578 while graze in meirin.cards: 

579 text = ( 

580 '现在说不是笨蛋是不是有点晚啊……见识一下吧,这来自古老东方的两仪|G太极|r之力!\n' 

581 '(使用主动发动的技能:请点击技能按钮,然后选择擦弹,然后选择琪露诺,最后出牌)' 

582 ) 

583 dialog(Meirin, text, 49) 

584 g.process_action(OneShotActionStage(meirin)) 

585 

586 dialog(Cirno, '呜啊……', 27) 

587 dialog(Sakuya, '我要回去做晚饭了。一会儿到了饭点你还没有解决掉这个妖精,我可不会给你留吃的。', 26) 

588 dialog(Meirin, '喂咲夜你先等等……', 50) 

589 dialog(Sakuya, '(The World', None) 

590 dialog(Meirin, '……好吧。那边的妖精!', 51) 

591 dialog(Cirno, '诶?', 28) 

592 dialog(Meirin, '以晚饭之名!我要制裁你!', 52) 

593 dialog(Cirno, '虽然不知道为什么突然燃起了斗志,不过……', 29) 

594 dialog(Cirno, '来吧!老娘是不可能会输的!', 30) 

595 

596 g.process_action(Heal(cirno, cirno, cirno.maxlife - cirno.life)) 

597 g.process_action(Heal(meirin, meirin, meirin.maxlife - meirin.life)) 

598 g.process_action(DrawCards(cirno, 4)) 

599 g.process_action(DrawCards(meirin, 4)) 

600 

601 g.refresh_dispatcher() 

602 

603 return True 

604 

605 

606class THBattleNewbie(THBattle): 

607 n_persons = 1 

608 game_ehs = [DeathHandler] 

609 npc_players = [NPC('琪露诺', CirnoAI.ai_main)] 

610 bootstrap = THBattleNewbieBootstrap 

611 

612 def can_leave(g, p): 

613 return True