diff --git a/Card.js b/Card.js index ce309a9..8dcf361 100644 --- a/Card.js +++ b/Card.js @@ -829,6 +829,10 @@ Card.prototype.moveTo = function (zone,arg) { moveEvent.isCharm = true; signi.charm = null; } + // 处理陷阱 + if (card === card.zone.trap) { + card.zone.trap = null; + } } } @@ -841,7 +845,7 @@ Card.prototype.moveTo = function (zone,arg) { // 进入 SIGNI 区 if (zone.getActualCards().length) { // rise - if (card.rise) { + if (!arg.bottom && card.rise) { // 被 rise 的卡“离场” signi = zone.getActualCards()[0]; removeFromArr(signi,signi.player.signis); @@ -908,6 +912,7 @@ Card.prototype.moveTo = function (zone,arg) { // 手牌中非公开的卡,在游戏逻辑中是"背面朝上"的,即: // 对方不能查看;己方能查看(这是因为手牌区是 checkable 的). // 但在客户端中,手牌里的卡即使逻辑上是背面朝上的,仍显示为正面朝上. + // PS: 增加了陷阱,陷阱和手牌一样,是仅己方玩家可见的。 card.player.output({ type: 'MOVE_CARD', content: { @@ -915,7 +920,7 @@ Card.prototype.moveTo = function (zone,arg) { pid: (card.isFaceup || zone.checkable)? card.pid : 0, zone: zone, up: arg.up, - faceup: zone.inhand? true : arg.faceup, + faceup: (arg.isTrap || zone.inhand)? true : arg.faceup, bottom: arg.bottom, isSide: arg.isSide } @@ -1124,8 +1129,8 @@ Card.prototype.attackAsyn = function () { }).callback(this,function () { // 选择攻击的区域 var zones = []; - var opposingZone = this.player.opponent.signiZones[2-index]; var index = this.player.signiZones.indexOf(this.zone); + var opposingZone = this.player.opponent.signiZones[2-index]; if (this.canAttackAnySigniZone) { zones = this.player.opponent.signiZones; } else if (this.canAttackNearbySigniZone) { @@ -1184,79 +1189,91 @@ Card.prototype.attackAsyn = function () { } this.game.frameEnd(); }).callback(this,function () { - // 强制结束回合 - if (this.game.phase.checkForcedEndTurn()) return; - // 此时,攻击的卡可能已不在场上 - if (!inArr(card,player.signis)) return; - // 被无效化 - if (event.prevented) return; // 攻击时发动的起动效果 (<被侵犯的神判 安=Fifth>) return opponent.useOnAttackActionEffectAsyn(event).callback(this,function () { - // 攻击的卡不在场上或攻击被无效化,结束处理. + // 强制结束回合 + if (this.game.phase.checkForcedEndTurn()) return; + // 攻击的卡不在场上,结束处理. if (!inArr(card,player.signis)) return; - if (event.prevented) return; - // 若攻击的目标存在,进行战斗; - // (暗杀的情况下,目标为正对面的 SIGNI 时,不战斗) + // 处理陷阱 var opposingSigni = card.getOpposingSigni(); - var target = attackedZone.getActualCards()[0] || null; - var battle = true; - if (!target) battle = false; - if (card.assassin && (target === opposingSigni)) return false; - if (battle) { - // 战斗 - // 触发"进行战斗"时点 - var onBattleEvent = { - card: card, - target: target, - }; - return this.game.blockAsyn(this,function () { - this.game.frameStart(); - card.onBattle.trigger(onBattleEvent); - target.onBattle.trigger(onBattleEvent); - this.game.frameEnd(); - }).callback(this,function () { - // 此时,攻击的卡可能已不在场上 - if (!inArr(card,player.signis)) return; - // 受攻击的卡也可能已不在场上 - // 注意: 根据事务所QA,此时不击溃对方的生命护甲(即使有 lancer ). (<大剣 レヴァテイン>) - if (!inArr(target,target.player.signis)) return; - // 结算战斗伤害 - if (card.power >= target.power) { - // 保存此时的 lancer 属性作为参考, - // 因为驱逐被攻击的 SIGNI 后,攻击侧的 lancer 可能改变. - var lancer = card.lancer; - return this.game.blockAsyn(this,function () { - return target.banishAsyn({attackingSigni: card}).callback(this,function (succ) { - if (succ && lancer) { - crashArg.lancer = lancer; - return opponent.crashAsyn(1,crashArg); - } - }); - }); - } - }).callback(this,function () { - if (onBattleEvent._1877 && inArr(target,target.player.signis)) { - return this.game.blockAsyn(onBattleEvent._1877,this,function () { - target.moveTo(target.player.mainDeck,{bottom: true}); - }); - } + var index = this.player.signiZones.indexOf(this.zone); + var opposingZone = this.player.opponent.signiZones[2-index]; + var trap = opposingZone.trap + return Callback.immediately().callback(this,function () { + if (opposingSigni || !trap) return; + return this.player.selectOptionalAsyn('LAUNCH',[trap]).callback(this,function (card) { + if (!card) return; + card.beSelectedAsTarget(); + return card.trap.actionAsyn.call(card).callback(this,function () { + card.trash(); + }); }); - } else { - // 伤害 - if (target !== opposingSigni) return; - if (event.wontBeDamaged || opponent.wontBeDamaged) return; - crashArg.damage = true; - if (opponent.lifeClothZone.cards.length) { - var count = 1; - if (this.doubleCrash) count = 2; - if (this.tripleCrash) count = 3; - crashArg.doubleCrash = this.doubleCrash; - return opponent.crashAsyn(count,crashArg); + }).callback(this,function () { + // 攻击被无效,结束处理 + if (event.prevented) return; + // 若攻击的目标存在,进行战斗; + // (暗杀的情况下,目标为正对面的 SIGNI 时,不战斗) + var target = attackedZone.getActualCards()[0] || null; + var battle = true; + if (!target) battle = false; + if (card.assassin && (target === opposingSigni)) return false; + if (battle) { + // 战斗 + // 触发"进行战斗"时点 + var onBattleEvent = { + card: card, + target: target, + }; + return this.game.blockAsyn(this,function () { + this.game.frameStart(); + card.onBattle.trigger(onBattleEvent); + target.onBattle.trigger(onBattleEvent); + this.game.frameEnd(); + }).callback(this,function () { + // 此时,攻击的卡可能已不在场上 + if (!inArr(card,player.signis)) return; + // 受攻击的卡也可能已不在场上 + // 注意: 根据事务所QA,此时不击溃对方的生命护甲(即使有 lancer ). (<大剣 レヴァテイン>) + if (!inArr(target,target.player.signis)) return; + // 结算战斗伤害 + if (card.power >= target.power) { + // 保存此时的 lancer 属性作为参考, + // 因为驱逐被攻击的 SIGNI 后,攻击侧的 lancer 可能改变. + var lancer = card.lancer; + return this.game.blockAsyn(this,function () { + return target.banishAsyn({attackingSigni: card}).callback(this,function (succ) { + if (succ && lancer) { + crashArg.lancer = lancer; + return opponent.crashAsyn(1,crashArg); + } + }); + }); + } + }).callback(this,function () { + if (onBattleEvent._1877 && inArr(target,target.player.signis)) { + return this.game.blockAsyn(onBattleEvent._1877,this,function () { + target.moveTo(target.player.mainDeck,{bottom: true}); + }); + } + }); } else { - if (card.game.win(player)) return Callback.never(); - return; + // 伤害 + if (target !== opposingSigni) return; + if (event.wontBeDamaged || opponent.wontBeDamaged) return; + crashArg.damage = true; + if (opponent.lifeClothZone.cards.length) { + var count = 1; + if (this.doubleCrash) count = 2; + if (this.tripleCrash) count = 3; + crashArg.doubleCrash = this.doubleCrash; + return opponent.crashAsyn(count,crashArg); + } else { + if (card.game.win(player)) return Callback.never(); + return; + } } - } + }); }); }).callback(this,function () { // "这场战斗结束之后,就回老家结婚" (划掉) @@ -1531,6 +1548,16 @@ Card.prototype.charmTo = function (signi) { this.game.frameEnd(); }; +Card.prototype.trapTo = function(zone) { + if (zone.trap) zone.trap.trash(); + zone.trap = this; + this.moveTo(zone,{ + faceup: false, + up: signi.isUp, + isTrap: true, + }); +}; + Card.prototype.getAccedCards = function () { if (!inArr(this,this.player.signis)) return []; return this.zone.cards.filter(function (card) { diff --git a/CardInfo.js b/CardInfo.js index 7517ebd..63ae459 100644 --- a/CardInfo.js +++ b/CardInfo.js @@ -127029,7 +127029,7 @@ var CardInfo = { actionAsyn: function (card) { return card.banishAsyn().callback(this,function (succ) { if (!succ) return; - // TODO... + return this.player.setTrapFromDeckTopAsyn(3); }); } },{ @@ -127039,7 +127039,16 @@ var CardInfo = { }); }, actionAsyn: function (card) { - // TODO... + return card.banishAsyn().callback(this,function (succ) { + if (!succ) return; + var zones = this.player.signiZones.filter(function (zone) { + return zone.trap; + },this); + return this.player.selectAsyn('TARGET',zones).callback(this,function (zone) { + if (!zone) return; + zone.trap.moveTo(this.player.handZone); + }); + }); } }] }, @@ -128424,7 +128433,7 @@ var CardInfo = { this.player.informCards(cards); return this.player.selectOptionalAsyn('TARGET',cards).callback(this,function (card) { if (!card) return; - // TODO... + return this.player.setTrapFromDeckTopAsyn(2); }); } }], @@ -128443,7 +128452,6 @@ var CardInfo = { burstEffect: { actionAsyn: function () { return this.player.opponent.discardAsyn(1).callback(this,function () { - // TODO... if (this.player.getTraps().length) { var filter = function (card) { return card.isUp; @@ -129316,7 +129324,12 @@ var CardInfo = { source: this, description: '2068-attached-1', actionAsyn: function () { - // TODO... + return this.player.selectOptionalAsyn('TARGET',this.player.hands).callback(this,function (card) { + if (!card) return; + return this.player.selectAsyn('TARGET',this.player.signiZones).callback(this,function (zone) { + card.trapTo(zone); + }); + }); } }]; return this.player.selectAsyn('LAUNCH',effects).callback(this,function (effect) { @@ -129389,7 +129402,7 @@ var CardInfo = { startUpEffects: [{ actionAsyn: function () { if (this.player.getTraps().length) return; - // TODO... + return this.player.setTrapFromDeckTopAsyn(2); }, }], }, @@ -129551,8 +129564,9 @@ var CardInfo = { extraTexts_en: [ "[Trap]: You may put 1 of your other [Trap] on the field into the trash, and pay [Blue]. If you do, banish 1 of your opponent's SIGNI." ], - // TODO... trap: { + // TODO... + // not cost costBlue: 1, costCondition: function () { return this.getTraps().some(function (card) { @@ -129608,10 +129622,21 @@ var CardInfo = { source: this, description: '2071-burst-2', actionAsyn: function () { - var cards = this.player.mainDeck.getTopCards(2); - if (cards.length) return; + var cards = this.mainDeck.getTopCards(count); this.player.informCards(cards); - // TODO... + var done = false; + return Callback.loop(this,2,function () { + if (done) return; + return this.player.selectOptionalAsyn('TARGET',cards).callback(this,function (card) { + if (!card) return done = true; + removeFromArr(card,cards); + return this.player.selectAsyn('TARGET',this.player.signiZones).callback(this,function (zone) { + card.trapTo(zone); + }); + }); + }).callback(this,function () { + this.game.trashCards(cards); + }); } }]; return this.player.selectAsyn('LAUNCH',effects).callback(this,function (effect) { @@ -130479,7 +130504,6 @@ var CardInfo = { "[Trap]: Down 2 of your opponent's SIGNI." ], trap: { - costBlue: 1, actionAsyn: function () { var cards = this.player.opponent.signis.filter(function (signi) { return signi.isUp; @@ -130822,7 +130846,7 @@ var CardInfo = { ], spellEffect: { actionAsyn: function () { - // TODO... + return this.player.setTrapFromDeckTopAsyn(4); } }, }, @@ -132454,13 +132478,7 @@ var CardInfo = { source: this, description: '1396-const-0', actionAsyn: function () { - var cards = this.player.mainDeck.getTopCards(2); - if (!cards.length) return; - this.player.informCards(cards); - return this.player.selectOptionalAsyn('TARGET',cards).callback(this,function (card) { - if (!card) return; - // TODO... - }); + return this.player.setTrapFromDeckTopAsyn(2); } }); add(this.player,'onMainPhaseStart',effect); @@ -135244,7 +135262,7 @@ var CardInfo = { source: this, description: '2167-const-0', actionAsyn: function () { - // !TODO... + // TODO... } }); add(this,'onBanish',effect); diff --git a/Player.js b/Player.js index 46e99a5..4a693f7 100644 --- a/Player.js +++ b/Player.js @@ -2339,4 +2339,35 @@ Player.prototype.infectZoneAsyn = function() { }); }; +Player.prototype.setTrapFromDeckTopAsyn = function(count,max) { + if (!isNum(max)) max = 1; + var cards = this.mainDeck.getTopCards(count); + this.informCards(cards); + var done = false; + return Callback.loop(this,max,function () { + if (done) return; + return this.selectOptionalAsyn('TARGET',cards).callback(this,function (card) { + if (!card) return done = true; + removeFromArr(card,cards); + return this.selectAsyn('TARGET',this.signiZones).callback(this,function (zone) { + card.trapTo(zone); + }); + }); + }).callback(this,function () { + var len = cards.length; + if (!len) return; + return this.selectSomeAsyn('SET_ORDER',cards,len,len,true).callback(this,function (cards) { + this.mainDeck.moveCardsToBottom(cards); + }); + }); +}; + +Player.prototype.getTraps = function() { + return this.player.signiZones.filter(function (zone) { + return zone.trap; + }).map(function (zone) { + return zone.trap; + }); +}; + global.Player = Player; \ No newline at end of file diff --git a/Zone.js b/Zone.js index b6b774d..bd77d0b 100644 --- a/Zone.js +++ b/Zone.js @@ -36,6 +36,7 @@ function Zone (game,player,name,args,pids) { this.disabled = false; // <ワーム・ホール> this.powerDown = false; // <黒幻蟲 サソリス> this.virus = false; + this.trap = null; } Zone.prototype.getTopCards = function (n) {