'use strict'; function Game (cfg) { // 基本属性 this.seed = cfg.seed; this.onGameover = cfg.onGameover; this.winner = null; this.cards = []; // 私有 this._r = new Random(Random.engines.mt19937().seed(cfg.seed)); this._frameCount = 0; this._sources = []; this._framedBeforeBlock = false; // 表示处理块前,是否有帧发生. // 在驱逐力量0以下的 SIGNI 前置 false, 在 handleFrameEnd() 中置 true. // 因为第一次驱逐力量0以下的 SIGNI 后,可能发生常时效果改变, // 导致剩下的 SIGNI 力量也变为0以下. // 于是不断驱逐力量0以下的 SIGNI 直至 _framedBeforeBlock 为 false. // 储存的数据 this.objList = []; this.hostMsgObjs = []; this.guestMsgObjs = []; this.dataObj = {}; // 储存游戏对象(card,player 等)绑定的数据,回合结束时清空 this.trashingCards = []; this.trashingCharms = []; // 注册 this.register(this); // 玩家 this.hostPlayer = new Player(this,cfg.hostIO,cfg.hostMainDeck,cfg.hostLrigDeck); this.guestPlayer = new Player(this,cfg.guestIO,cfg.guestMainDeck,cfg.guestLrigDeck); this.hostPlayer.opponent = this.guestPlayer; this.guestPlayer.opponent = this.hostPlayer; this.turnPlayer = this.hostPlayer; // 组件 this.phase = new Phase(this); this.constEffectManager = new ConstEffectManager(this); this.effectManager = new EffectManager(this); this.triggeringEffects = []; this.triggeringEvents = []; // 附加属性 this.trashWhenPowerBelowZero = false; this.spellToCutIn = null; // <ブルー・パニッシュ> }; Game.checkDeck = function (cfg,mayusRoom) { if (!isObj(cfg)) return false; if (!isArr(cfg.mainDeck) || !isArr(cfg.lrigDeck)) return false; // 主卡组正好40张 if (cfg.mainDeck.length !== 40) return false; // LRIG卡组最多10张 if (cfg.lrigDeck.length > 10) return false; // toInfo var mainDeckInfos = []; var lrigDeckInfos = []; var burstCount = 0; var flagLrig = false; for (var i = 0; i < cfg.mainDeck.length; i++) { var pid = cfg.mainDeck[i]; var info = CardInfo[pid]; if (!info) return false; info = CardInfo[info.cid]; if (!info) return false; if (info.cardType === 'LRIG') return false; if (info.cardType === 'ARTS') return false; if (info.cardType === 'RESONA') return false; if (info.burstEffect) { burstCount++; } mainDeckInfos.push(info); } for (var i = 0; i < cfg.lrigDeck.length; i++) { var pid = cfg.lrigDeck[i]; var info = CardInfo[pid]; if (!info) return false; info = CardInfo[info.cid]; if (!info) return false; if (info.cardType === 'SIGNI') return false; if (info.cardType === 'SPELL') return false; if ((info.cardType === 'LRIG') && (info.level === 0)) { flagLrig = true; } lrigDeckInfos.push(info); } var infos = mainDeckInfos.concat(lrigDeckInfos); // LRIG卡组至少有一张0级LRIG if (!flagLrig) return false; // 生命迸发恰好20张 if (burstCount !== 20) return false; // 相同的卡最多放入4张 var legal = [mainDeckInfos,lrigDeckInfos].every(function (infos) { var bucket = {}; infos.forEach(function (info) { if (info.sideA) { info = CardInfo[info.sideA] } if (info.cid in bucket) { bucket[info.cid]++; } else { bucket[info.cid] = 1; } }); for (var cid in bucket) { if (bucket[cid] > 4) return false; } return true; },this); if (!legal) return false; if (!mayusRoom) return true; return Game.checkMayusRoom(infos); }; Game.checkMayusRoom = function (infos) { // 禁止 狐狸+修复 和 狐狸+Three out if (infos.some(function (info) { return info.cid === 33; // 狐狸 }) && infos.some(function (info) { return (info.cid === 34) || (info.cid === 84); // 修复, three out })) { return false; } // 禁止 V・@・C+共鸣・进军 和 V・@・C+共鸣 if (infos.some(function (info) { return info.cid === 1202; // V・@・C }) && infos.some(function (info) { return (info.cid === 884) || (info.cid === 1369); // 共鸣・进军, 共鸣 })) { return false; } // 禁止 Lock+割裂 和 Lock+精挖 if (infos.some(function (info) { return info.cid === 534; // Lock }) && infos.some(function (info) { return (info.cid === 408) || (info.cid === 570); // 割裂, 精挖 })) { return false; } // 禁止 Ar+魔术手 if (infos.some(function (info) { return info.cid === 814; // Ar }) && infos.some(function (info) { return (info.cid === 1090); // 魔术手 })) { return false; } // 禁止 双麻油 if (infos.some(function (info) { return info.cid === 649; // 創世の巫女 マユ }) && infos.some(function (info) { return (info.cid === 1562); // 真名の巫女 マユ })) { return false; } // 禁止 台风蛇 if (infos.some(function (info) { return info.cid === 957; // 台風一過 }) && infos.some(function (info) { return (info.cid === 1652); // コードアンシエンツ ヘルボロス })) { return false; } // 限制 var limitMap = { 37: 2, // <忘得ぬ幻想 ヴァルキリー> 34: 2, // <修復> 178: 2, // <先駆の大天使 アークゲイン> 534: 1, // <ロック・ユー> 689: 1, // <RAINY> 474: 0, // <ノー・ゲイン> 23: 0, // <大器晩成> 689: 0, // <RAINY> 1030: 0, // <四面楚火> 1457: 0, // <サーバント Z> }; for (var i = 0; i < infos.length; i++) { var info = infos[i]; var cid = info.cid; if (cid in limitMap) { limitMap[cid]--; if (limitMap[cid] < 0) return false; } } return true; }; Game.prototype.start = function () { this.allocateSid(this.hostPlayer,this.objList); this.allocateSid(this.guestPlayer,this.objList); this.setupEffects(); this.outputInitMsg(this.hostPlayer); this.outputInitMsg(this.guestPlayer); this.phase.setup(); this.sendMsgQueue(); }; Game.prototype.setupEffects = function () { [this.hostPlayer,this.guestPlayer].forEach(function (player) { concat(player.mainDeck.cards,player.lrigDeck.cards).forEach(function (card) { card.setupEffects(); },this); },this); }; Game.prototype.outputInitMsg = function (player) { var opponent = player.opponent; player.output({ type: 'INIT', content: { player: player, opponent: opponent, playerZones: { mainDeck: player.mainDeck, lrigDeck: player.lrigDeck, handZone: player.handZone, lrigZone: player.lrigZone, signiZones: player.signiZones, enerZone: player.enerZone, checkZone: player.checkZone, trashZone: player.trashZone, lrigTrashZone: player.lrigTrashZone, lifeClothZone: player.lifeClothZone, excludedZone: player.excludedZone, mainDeckCards: player.mainDeck.cards, lrigDeckCards: player.lrigDeck.cards, lrigDeckCardInfos: player.lrigDeck.cards.map(function (card) { return { pid: card.pid, isSide: !!card.sideA } },this) }, opponentZones: { mainDeck: opponent.mainDeck, lrigDeck: opponent.lrigDeck, handZone: opponent.handZone, lrigZone: opponent.lrigZone, signiZones: opponent.signiZones, enerZone: opponent.enerZone, checkZone: opponent.checkZone, trashZone: opponent.trashZone, lrigTrashZone: opponent.lrigTrashZone, lifeClothZone: opponent.lifeClothZone, excludedZone: opponent.excludedZone, mainDeckCards: opponent.mainDeck.cards, lrigDeckCards: opponent.lrigDeck.cards, lrigDeckCardInfos: opponent.lrigDeck.cards.map(function (card) { return { pid: 0, isSide: !!card.sideA } },this) } } }); }; Game.prototype.register = function (obj) { if (!obj) throw new TypeError(); obj.gid = obj._asid = obj._bsid = this.objList.push(obj); }; Game.prototype.allocateSid = function (player,objs) { var len = objs.length; objs.forEach(function (obj,i) { var r = this.rand(i,len-1); if ((player === this.hostPlayer)) { var tmp = obj._asid; obj._asid = objs[r]._asid; objs[r]._asid = tmp; } else { var tmp = obj._bsid; obj._bsid = objs[r]._bsid; objs[r]._bsid = tmp; } },this); }; Game.prototype.getSid = function (player,obj) { return (player === this.hostPlayer)? obj._asid : obj._bsid; }; Game.prototype.setSid = function (player,obj,sid) { if (player === this.hostPlayer) { obj._asid = sid; } else { obj._bsid = sid; } }; Game.prototype.getObjectBySid = function (player,sid) { if (!isNum(sid)) return null; for (var i = this.objList.length - 1; i >= 0; i--) { var obj = this.objList[i]; if (player === this.hostPlayer) { if (obj._asid === sid) return obj; } else { if (obj._bsid === sid) return obj; } } return null; }; Game.prototype.decideFirstPlayerAsyn = function () { return Callback.immediately().callback(this,function () { var firstPlayer = this.rand(0,1)? this.hostPlayer : this.guestPlayer; return firstPlayer; }); // var a,b; // return this.hostPlayer.rockPaperScissorsAsyn(this,function (v) { // a = v; // }).callback(this,function () { // return this.guestPlayer.rockPaperScissorsAsyn(this,function (v) { // b = v; // }); // }).callback(this,function () { // if (a === b) return this.decideFirstPlayerAsyn(); // var aWin = // ((a === 0) && (b === 2)) || // ((a === 1) && (b === 0)) || // ((a === 2) && (b === 2)); // var firstPlayer = aWin? this.hostPlayer : this.guestPlayer; // return firstPlayer; // }); }; Game.prototype.win = function (player) { if (player.opponent.wontLoseGame) return false; player.output({ type: 'WIN', content: {} }); player.opponent.output({ type: 'LOSE', content: {} }); this.winner = player; return true; }; Game.prototype.destroy = function () { this.hostPlayer.io.listener = null; this.guestPlayer.io.listener = null; }; Game.prototype.output = function (msgObj) { this.hostPlayer.output(msgObj); this.guestPlayer.output(msgObj); }; Game.prototype.packOutputs = function (func,thisp) { this.output({ type: 'PACKED_MSG_START', content: {} }); var rtn = func.call(thisp || this); this.output({ type: 'PACKED_MSG_END', content: {} }); return rtn; }; Game.prototype.outputColor = function () { var playerColor = this.turnPlayer.lrig.color; var opponentColor = this.turnPlayer.opponent.lrig.color; if (playerColor === 'colorless') { playerColor = 'white'; } if (opponentColor === 'colorless') { opponentColor = 'white'; } this.turnPlayer.output({ type: 'SET_COLOR', content: { selfColor: playerColor, opponentColor: opponentColor } }); this.turnPlayer.opponent.output({ type: 'SET_COLOR', content: { selfColor: opponentColor, opponentColor: playerColor } }); }; Game.prototype.sendMsgQueue = function () { this.hostPlayer.sendMsgQueue(); this.guestPlayer.sendMsgQueue(); }; Game.prototype.rand = function (min,max) { // return Math.floor(Math.random() * (max+1-min)) + min; return this._r.integer(min,max); }; Game.prototype.moveCards = function (cards,zone,arg) { if (!cards.length) return []; cards = cards.slice(); this.packOutputs(function () { this.frameStart(); cards = cards.filter(function (card) { return card.moveTo(zone,arg); },this); this.frameEnd(); }); return cards; }; Game.prototype.moveCardsAdvancedAsyn = function (cards,zones,args,force) { cards = cards.slice(); this.frameStart(); var source = this.getEffectSource(); var signis = []; // 可以被保护的 SIGNI var protectedSignis = []; // 确实被保护了的 SIGNI var succs = []; // 返回值,表示是否成功移动. 注: 被保护了也算成功移动. var protectedFlags = []; // 返回值,表示是否被保护 if (!force && source && !source.powerChangeBanned) { // <幻兽神 狮王> // 获得以被保护的 SIGNI signis = cards.filter(function (card,i) { return card.protectingShironakujis.length && (source.player === card.player.opponent) && inArr(card,card.player.signis) && !card.isEffectFiltered(source); },this); } return Callback.forEach(signis,function (signi) { // 获得保护该 SIGNI 的<幻水 蓝鲸> var protectingShironakujis = signi.protectingShironakujis; if (!protectingShironakujis.length) return; return signi.player.selectOptionalAsyn('PROTECT',[signi]).callback(this,function (c) { if (!c) return; signi.beSelectedAsTarget(); return signi.player.selectOptionalAsyn('_SHIRONAKUJI',protectingShironakujis).callback(this,function (shironakuji) { if (!shironakuji) return; shironakuji.beSelectedAsTarget(); // 保护 protectedSignis.push(signi); this.tillTurnEndAdd(source,shironakuji,'power',-6000); // 力量-6000,注意效果源 this.constEffectManager.compute(); // 强制计算,即使不是帧结束 }); }); },this).callback(this,function () { this.packOutputs(function () { cards.forEach(function (card,i) { if (inArr(card,protectedSignis)) { protectedFlags.push(true); succs.push(true); } else { protectedFlags.push(false); succs.push(card.moveTo(zones[i],args[i])); } },this); },this); this.frameEnd(); return {protectedFlags: protectedFlags, succs: succs}; }); }; // 只要有1只 SIGNI 被驱逐成功(包括代替)就返回 true. Game.prototype.banishCardsAsyn = function (cards,force,arg) { if (!arg) arg = {}; var attackingSigni = arg.attackingSigni || null; var control = { someBanished: false, }; var source = this.getEffectSource(); cards = cards.filter(function (card) { if (card.isEffectFiltered(source)) return false; if (!card.canBeBanished()) return false; return true; },this); if (!cards.length) return Callback.immediately(false); this.frameStart(); return this.protectBanishAsyn(cards,this.turnPlayer,control).callback(this,function (cards) { var zones = cards.map(function (card) { // <绿叁游 水滑梯> if (card.resonaBanishToTrash) return card.player.lrigTrashZone; // 原枪 if (card.player.banishTrash) return card.player.trashZone; // <原槍 エナジェ> if (inArr(source,card.player._RanergeOriginalSpear)) return card.player.trashZone; if (inArr(attackingSigni,card.player._RanergeOriginalSpear)) return card.player.trashZone; return card.player.enerZone; },this); var opposingSignis = cards.map(function (card) { return card.getOpposingSigni(); },this); return this.moveCardsAdvancedAsyn(cards,zones,[],force).callback(this,function (arg) { arg.protectedFlags.forEach(function (isProtected,i) { if (isProtected) return; if (!arg.succs[i]) return; var signi = cards[i]; // <幻獣神 ウルティム> var banishSource = attackingSigni || source; var count; if (banishSource) { if (banishSource.hasClass('空獣') || banishSource.hasClass('地獣')) { if (signi.player === banishSource.player.opponent) { count = this.getData(banishSource.player,'_UltimPhantomBeastDeity') || 0; this.setData(banishSource.player,'_UltimPhantomBeastDeity',++count); } } } var event = { card: signi, opposingSigni: opposingSignis[i], attackingSigni: attackingSigni, source: banishSource }; this.setData(signi.player,'flagSigniBanished',true); signi.onBanish.trigger(event); signi.player.onSigniBanished.trigger(event); },this); this.frameEnd(); return control.someBanished || !!arg.succs.length; }); }); }; Game.prototype.protectBanishAsyn = function (cards,player,control) { // 过滤不被驱逐的卡 cards = cards.filter(function (card) { if (card.isEffectFiltered()) return false; if (!card.canBeBanished()) return false; return true; },this); // 获得玩家受保护的卡及其保护措施 var cardList = []; var protectionTable = {}; cards.forEach(function (card) { if (card.player !== player) return; card.banishProtections.forEach(function (protection) { if (!protection.condition.call(protection.source,card)) return; if (protectionTable[card.gid]) { protectionTable[card.gid].push(protection); } else { cardList.push(card); protectionTable[card.gid] = [protection]; } },this); },this); // 选择要保护的卡以及保护措施 return player.selectOptionalAsyn('PROTECT',cardList).callback(this,function (card) { if (!card) { // 回合玩家处理完毕,处理非回合玩家. if (player === this.turnPlayer) { return this.protectBanishAsyn(cards,player.opponent,control); } // 非回合玩家处理完毕,结束处理. return cards; } card.beSelectedAsTarget(); var protections = protectionTable[card.gid]; return player.selectAsyn('CHOOSE_EFFECT',protections).callback(this,function (protection) { protection.source.activate(); return protection.actionAsyn.call(protection.source,card).callback(this,function () { control.someBanished = true; this.constEffectManager.compute(); // 强制计算,即使不是帧结束 cards = cards.filter(function (c) { if (c === card) return false; // 排除已被保护的卡 if (!inArr(c,c.player.signis)) return false; // 排除已不在场的卡 return true; },this); // 继续处理当前玩家. return this.protectBanishAsyn(cards,player,control); }); }); }); }; // Game.prototype.protectBanishAsyn = function () { // // 代替驱逐 // return Callback.forEach(cards,function (card) { // if (force) return; // if (card.isEffectFiltered()) return; // if (card.canNotBeBanished) return; // if (card.power <= 0 ) return; // // <堕落虚无 派蒙> // if (card.trashCharmInsteadOfBanish && card.charm) { // return card.player.selectOptionalAsyn('TRASH_CHARM',[card]).callback(this,function (c) { // if (!c) return; // someBanished = true; // card.charm.trash(); // removeFromArr(card,cards); // }); // } // // <核心代号 S・W・T> // if (card.discardSpellInsteadOfBanish) { // var spells = card.player.hands.filter(function (card) { // return card.type === 'SPELL'; // },this); // if (!spells.length) return; // return card.player.selectOptionalAsyn('PROTECT',[card]).callback(this,function (c) { // if (!c) return; // return card.player.selectAsyn('TRASH',spells).callback(this,function (spell) { // if (!spell) return; // someBanished = true; // spell.trash(); // removeFromArr(card,cards); // }); // }); // } // // <コードハート M・P・P> // if (card.protectingMpps.length) { // // 获得保护该 SIGNI 的<M・P・P> // var protectingMpps = card.protectingMpps.filter(function (mpp) { // if (!inArr(mpp,card.player.signis)) return false; // var spells = mpp.zone.cards.slice(1).filter(function (card) { // return (card !== mpp.charm); // },this); // return (spells.length >= 2); // },this); // if (!protectingMpps.length) return; // card.beSelectedAsTarget(); // 被驱逐的SIGNI闪烁 // return card.player.selectOptionalAsyn('PROTECT',protectingMpps).callback(this,function (mpp) { // if (!mpp) return; // var spells = mpp.zone.cards.slice(1).filter(function (card) { // return (card !== mpp.charm); // },this); // return card.player.selectSomeAsyn('TRASH',spells,2,2).callback(this,function (spells) { // mpp.activate(); // this.trashCards(spells); // someBanished = true; // removeFromArr(card,cards); // }); // }); // } // },this) // }; // Game.prototype.banishCards = function (cards) { // if (!cards.length) return; // cards = cards.slice(); // this.packOutputs(function () { // this.frameStart(); // cards.forEach(function (card) { // card.beBanished(); // },this); // this.frameEnd(); // }); // }; // Game.prototype.doBanishCards = function (cards) { // if (!cards.length) return; // cards = cards.slice(); // this.packOutputs(function () { // this.frameStart(); // cards.forEach(function (card) { // card.doBanish(); // },this); // this.frameEnd(); // }); // }; // Game.prototype.banishCardsAsyn = function (cards) { // if (!cards.length) return Callback.immediately(); // cards = cards.slice(); // return this.packOutputs(function () { // this.frameStart(); // return Callback.forEach(cards,function (card) { // return card.banishAsyn(); // },this).callback(this,function () { // this.frameEnd(); // }); // }); // }; Game.prototype.trashCardsAsyn = function (cards,arg) { if (!cards.length) return Callback.immediately(); cards = cards.slice(); var zones = cards.map(function (card) { if (inArr(card.type,['LRIG','ARTS'])) return card.player.lrigTrashZone; return card.player.trashZone; },this); var args = cards.map(function (card) { return arg || {}; },this); return this.moveCardsAdvancedAsyn(cards,zones,[],args); }; Game.prototype.bounceCardsAsyn = function (cards) { if (!cards.length) return Callback.immediately(); cards = cards.slice(); var zones = cards.map(function (card) { return card.player.handZone; },this); return this.moveCardsAdvancedAsyn(cards,zones,[]); }; Game.prototype.bounceCardsToDeckAsyn = function (cards) { if (!cards.length) return Callback.immediately(); cards = cards.slice(); var zones = cards.map(function (card) { if (inArr(this.type,['LRIG','ARTS'])) return card.player.lrigDeck; return card.player.mainDeck; },this); return this.moveCardsAdvancedAsyn(cards,zones,[]); }; Game.prototype.trashCards = function (cards,arg) { if (!cards.length) return; cards = cards.slice(); this.packOutputs(function () { this.frameStart(); cards = cards.filter(function (card) { return card.trash(arg); },this); this.frameEnd(); }); return cards; }; Game.prototype.excludeCards = function (cards) { if (!cards.length) return; cards = cards.slice(); this.packOutputs(function () { this.frameStart(); cards = cards.filter(function (card) { card.exclude(); },this); this.frameEnd(); }); return cards; }; Game.prototype.upCards = function (cards) { if (!cards.length) return; this.packOutputs(function () { this.frameStart(); cards = cards.filter(function (card) { return card.up(); },this); this.frameEnd(); }); return cards; }; Game.prototype.upCardsAsyn = function (cards) { if (!cards.length) return; var upCards = []; this.frameStart(); return Callback.forEach(cards,function (card) { return card.upAsyn().callback(this,function (succ) { if (succ) upCards.push(card); }); }).callback(this,function () { this.frameEnd(); return upCards; }); }; Game.prototype.downCards = function (cards) { if (!cards.length) return; this.packOutputs(function () { this.frameStart(); cards = cards.filter(function (card) { card.down(); },this); this.frameEnd(); }); return cards; }; // Game.prototype.informPower = function () { // var cards = concat(this.turnPlayer.signis,this.turnPlayer.opponent.signis); // var powers = cards.map(function (card) { // return card.power; // }); // this.output({ // type: 'POWER', // content: { // cards: cards, // powers: powers // } // }); // }; Game.prototype.outputCardStates = function () { var cards = concat(this.turnPlayer.signis,this.turnPlayer.opponent.signis); var signiInfos = cards.map(function (card) { return { card: card, power: card.power, states: card.getStates() } },this); cards = [this.turnPlayer.lrig,this.turnPlayer.opponent.lrig]; var lrigInfos = cards.map(function (card) { return { card: card, states: card.getStates() } },this); var zones = concat(this.turnPlayer.signiZones,this.turnPlayer.opponent.signiZones); var zoneInfos = zones.map(function (zone) { return { zone: zone, states: zone.getStates() } },this); this.output({ type: 'CARD_STATES', content: { signiInfos: signiInfos, lrigInfos: lrigInfos, zoneInfos: zoneInfos } }); }; Game.prototype.addConstEffect = function (cfg,dontCompute) { var constEffect = new ConstEffect(this.constEffectManager,cfg); if (!dontCompute) { this.handleFrameEnd(); } return constEffect; }; Game.prototype.newEffect = function (eff) { return new Effect(this.effectManager,eff); }; Game.prototype.frame = function (thisp,func) { if (arguments.length !== 2) { debugger; } this.frameStart(); func.call(thisp); this.frameEnd(); }; Game.prototype.frameStart = function () { this._frameCount++; }; Game.prototype.frameEnd = function () { if (this._frameCount <= 0) { debugger; console.warn('game._frameCount <= 0'); this._frameCount = 0; return; } this._frameCount--; this.handleFrameEnd(); }; Game.prototype.handleFrameEnd = function () { if (this._frameCount) return; this._framedBeforeBlock = true; this.constEffectManager.compute(); this.triggeringEffects.forEach(function (effect,idx) { effect.trigger(this.triggeringEvents[idx]); },this); this.triggeringEffects.length = 0; this.triggeringEvents.length = 0; }; Game.prototype.pushTriggeringEffect = function (effect,event) { this.triggeringEffects.push(effect); this.triggeringEvents.push(event); }; Game.prototype.pushEffectSource = function (source) { this._sources.push(source || null); }; Game.prototype.popEffectSource = function (source) { return this._sources.pop(); }; Game.prototype.getEffectSource = function () { if (!this._sources.length) return null; return this._sources[this._sources.length-1]; }; Game.prototype.blockAsyn = function (source,thisp,func) { if (arguments.length !== 3) { if (arguments.length === 2) { source = null; thisp = arguments[0]; func = arguments[1]; } else { debugger; console.warn('game.blockAsyn() 参数个数不正确'); } } this.blockStart(source); return Callback.immediately().callback(thisp,func).callback(this,function (rtn) { return this.blockEndAsyn().callback(this,function () { return rtn; }); }); }; // Game.prototype.block = function (source,thisp,func) { // this.pushEffectSource(source); // func.call(thisp); // this.popEffectSource(); // }; Game.prototype.blockStart = function (source) { this.pushEffectSource(source); }; Game.prototype.blockEndAsyn = function () { if (this._sources.length <= 0) { debugger; console.warn('game._sources.length <= 0'); return Callback.immediately(); } this.popEffectSource(); return this.handleBlockEndAsyn(); }; Game.prototype.handleBlockEndAsyn = function () { if (this._sources.length) return Callback.immediately(); this.frameStart(); return this.turnPlayer.resetSignisAsyn().callback(this,function () { return this.turnPlayer.opponent.resetSignisAsyn(); }).callback(this,function () { this.frameEnd(); return this.banishNonPositiveAsyn(); }).callback(this,function () { // 废弃【魅饰】和SIGNI下方的卡 this.trashCards(this.trashingCharms,{ isCharm: true }); this.trashCards(this.trashingCards); this.trashingCharms.length = 0; this.trashingCards.length = 0; return this.rebuildAsyn(); }).callback(this,function () { return this.effectManager.handleEffectsAsyn(); }); // return this.banishNonPositiveAsyn().callback(this,function () { // return this.rebuildAsyn().callback(this,function () { // return this.effectManager.handleEffectsAsyn(); // }); // }); }; Game.prototype.banishNonPositiveAsyn = function () { if (!this._framedBeforeBlock) return Callback.immediately(); this._framedBeforeBlock = false; var signis = concat(this.turnPlayer.signis,this.turnPlayer.opponent.signis).filter(function (signi) { return (signi.power <= 0); },this); if (!signis.length) Callback.immediately(); return Callback.immediately().callback(this,function () { this.pushEffectSource(null); if (this.trashWhenPowerBelowZero) { return this.trashCards(signis); } return this.banishCardsAsyn(signis,true); }).callback(this,function () { this.popEffectSource(); return this.banishNonPositiveAsyn(); }); }; Game.prototype.rebuildAsyn = function () { var players = [this.turnPlayer,this.turnPlayer.opponent].filter(function (player) { return !player.mainDeck.cards.length && player.trashZone.cards.length; },this); if (!players.length) return Callback.immediately(); return this.blockAsyn(this,function () { // console.log('game.rebuildAsyn()'); // 将废弃区的卡片放到牌组,洗切 this.frameStart(); return Callback.forEach(players,function (player) { var cards = player.trashZone.cards; return player.showCardsAsyn(cards,'CONFIRM_REFRESH_SELF').callback(this,function () { return player.opponent.showCardsAsyn(cards,'CONFIRM_REFRESH_OPPONENT'); }).callback(this,function () { player.rebuildCount++; this.moveCards(player.trashZone.cards,player.mainDeck); player.shuffle(); player.onRebuild.trigger({ rebuildCount: player.rebuildCount }); }); },this).callback(this,function () { this.frameEnd(); // 废弃一张生命护甲 var cards = []; players.forEach(function (player) { cards = cards.concat(player.lifeClothZone.getTopCards(1)); },this); this.trashCards(cards); }); // this.frame(this,function () { // players.forEach(function (player) { // player.rebuildCount++; // this.moveCards(player.trashZone.cards,player.mainDeck); // player.shuffle(); // player.onRebuild.trigger({ // rebuildCount: player.rebuildCount // }); // },this); // }); // // 废弃一张生命护甲 // var cards = []; // players.forEach(function (player) { // cards = cards.concat(player.lifeClothZone.getTopCards(1)); // },this); // this.trashCards(cards); }); }; Game.prototype.setAdd = function (source,obj,prop,value,isSet,arg) { if (!arg) arg = {}; if (!arg.forced) { if (obj.isEffectFiltered && obj.isEffectFiltered(source)) return; var card = null; if (obj.player && inArr(prop,Card.abilityProps)) { card = obj; } else if (isObj(value) && (value.constructor === Effect)) { card = value.source; if (card.isEffectFiltered(source)) return; } if (card) { if (card.canNotGainAbility || card.player.canNotGainAbility) return; if (card.canNotGainAbilityBySelfPlayer) { if (source.player === card.player) { return; } } } } var destroyTimming = [this.phase.onTurnEnd]; if (obj.type === 'SIGNI') { if (inArr(obj,obj.player.signis)) { destroyTimming.push(obj.onLeaveField); } } this.frameStart(); this.addConstEffect({ source: source, fixed: true, destroyTimming: destroyTimming, action: function (set,add) { var target = obj; if (obj.type === 'LRIG') { target = obj.player.lrig; } if (isSet) { set(target,prop,value,arg); } else { add(target,prop,value,arg); } } }); if (obj.triggerOnAffect) { obj.triggerOnAffect(source); } this.frameEnd(); }; Game.prototype.tillTurnEndSet = function (source,obj,prop,value,arg) { this.setAdd(source,obj,prop,value,true,arg); }; Game.prototype.tillTurnEndAdd = function (source,obj,prop,value,arg) { this.setAdd(source,obj,prop,value,false,arg); }; Game.prototype.getData = function (obj,key) { var hash = obj.gid + key; return this.dataObj[hash]; }; Game.prototype.setData = function (obj,key,value) { var hash = obj.gid + key; this.dataObj[hash] = value; }; Game.prototype.clearData = function () { this.dataObj = {}; }; Game.prototype.getOriginalValue = function (obj,prop) { return this.constEffectManager.getOriginalValue(obj,prop); }; Game.prototype.gameover = function (arg) { if (!arg) arg = {}; var win,surrender; if (arg.surrender === 'host') { surrender = true; win = false; } else if (arg.surrender === 'guest') { surrender = true; win = true; } else { surrender = false; win = (this.winner === this.hostPlayer); } if (!this.hostPlayer.lrig || !this.guestPlayer.lrig) { this.onGameover(null); return; } var replayObj = { win: win, surrender: surrender, selfLrig: this.hostPlayer.lrig.cid, opponentLrig: this.guestPlayer.lrig.cid, messagePacks: this.hostPlayer.messagePacks }; this.onGameover(replayObj); }; Game.prototype.getLiveMessagePacks = function () { return this.hostPlayer.messagePacks; }; global.Game = Game;