forked from mirrors/webxoss-core
1080 lines
No EOL
30 KiB
JavaScript
1080 lines
No EOL
30 KiB
JavaScript
'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.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.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;
|
||
}
|
||
// 限制
|
||
var limitMap = {
|
||
37: 2, // <忘得ぬ幻想 ヴァルキリー>
|
||
34: 2, // <修復>
|
||
178: 2, // <先駆の大天使 アークゲイン>
|
||
534: 1, // <ロック・ユー>
|
||
689: 1, // <RAINY>
|
||
474: 0, // <ノー・ゲイン>
|
||
23: 0, // <大器晩成>
|
||
};
|
||
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: player.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.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 () {
|
||
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;
|
||
if (obj.canNotGainAbility) {
|
||
if (inArr(prop,Card.abilityProps)) return;
|
||
}
|
||
if (isObj(value) && (value.constructor === Effect)) {
|
||
if (value.source.canNotGainAbility) 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; |