forked from mirrors/webxoss-core
1599 lines
No EOL
47 KiB
JavaScript
1599 lines
No EOL
47 KiB
JavaScript
'use strict';
|
|
|
|
function Card (game,player,zone,pid,side) {
|
|
// 引用
|
|
this.game = game;
|
|
this.player = player;
|
|
this.owner = player;
|
|
this.zone = zone;
|
|
|
|
// 状态
|
|
this.isFaceup = false;
|
|
this.isUp = true;
|
|
|
|
// 注册
|
|
this.game.register(this);
|
|
this.game.cards.push(this);
|
|
|
|
var cid = CardInfo[pid].cid;
|
|
var info = CardInfo[cid];
|
|
// 基本数据
|
|
this.pid = pid;
|
|
this._info = info;
|
|
this.cid = info.cid;
|
|
this.name = info.name;
|
|
this.resona = (info.cardType === 'RESONA');
|
|
this.type = this.resona? 'SIGNI' : info.cardType;
|
|
this.color = info.color.split('/')[0];
|
|
this.otherColors = info.color.split('/').slice(1);
|
|
this.limitings = info.limiting? info.limiting.split('/') : [];
|
|
this.limit = info.limit;
|
|
this.level = info.level;
|
|
this.power = info.power;
|
|
this.classes = info.classes;
|
|
this.guardFlag = info.guardFlag;
|
|
this.costWhite = info.costWhite;
|
|
this.costBlack = info.costBlack;
|
|
this.costRed = info.costRed;
|
|
this.costBlue = info.costBlue;
|
|
this.costGreen = info.costGreen;
|
|
this.costColorless = info.costColorless;
|
|
this.costCoin = info.costCoin;
|
|
this.costAsyn = info.costAsyn;
|
|
this.costChange = info.costChange;
|
|
this.costChangeAsyn = info.costChangeAsyn;
|
|
this.costChangeAfterChoose = info.costChangeAfterChoose;
|
|
this.multiEner = info.multiEner;
|
|
this.burstIcon = !!info.burstEffect;
|
|
|
|
this.useCondition = info.useCondition;
|
|
this.growCondition = info.growCondition;
|
|
this.growActionAsyn = info.growActionAsyn;
|
|
|
|
// 效果相关的数据
|
|
this.getMinEffectCount = info.getMinEffectCount;
|
|
this.getMaxEffectCount = info.getMaxEffectCount;
|
|
// 魔法效果
|
|
this.spellEffects = this.cookEffect(info.spellEffect,'spell',1);
|
|
// 技艺效果
|
|
this.timmings = info.timmings || [];
|
|
this.artsEffects = this.cookEffect(info.artsEffect,'arts',1);
|
|
this.encore = info.encore || null;
|
|
this.chain = info.chain || null;
|
|
// 生命迸发效果
|
|
this.burstEffects = this.cookEffect(info.burstEffect,'burst');
|
|
// 常时效果
|
|
this.constEffects = info.constEffects || [];
|
|
// 出场效果
|
|
this.startUpEffects = this.cookEffect(info.startUpEffects,'startup');
|
|
// 起动效果
|
|
this.actionEffects = this.cookEffect(info.actionEffects,'action');
|
|
// 是否白板
|
|
this.withAbility = !!(this.burstEffects.length ||
|
|
this.constEffects.length ||
|
|
this.startUpEffects.length ||
|
|
this.actionEffects.length ||
|
|
this.multiEner ||
|
|
this.guardFlag);
|
|
|
|
// 共鸣相关
|
|
this.resonaPhases = concat(info.resonaPhase || []);
|
|
this.resonaCondition = info.resonaCondition;
|
|
this.resonaAsyn = null;
|
|
|
|
// CROSS相关
|
|
this.crossLeft = info.crossLeft || null;
|
|
this.crossRight = info.crossRight || null;
|
|
this.crossIcon = !!(info.crossLeft || info.crossRight);
|
|
this.crossed = null;
|
|
|
|
// 双面
|
|
if (side) {
|
|
this.sideA = info.sideA? side : null;
|
|
this.sideB = info.sideB? side : null;
|
|
} else {
|
|
this.sideA = info.sideA? new Card(game,player,zone,info.sideA,this) : null;
|
|
this.sideB = info.sideB? new Card(game,player,zone,info.sideB,this) : null;
|
|
}
|
|
|
|
// Lostorage
|
|
this.coin = info.coin || 0;
|
|
this.bet = info.bet || 0;
|
|
this.bettedCost = info.bettedCost || null;
|
|
this.rise = info.rise;
|
|
this.acce = !!info.acce;
|
|
this.acceingCard = null;
|
|
|
|
// 杂项
|
|
this.effectFilters = [];
|
|
this.registeredEffects = [];
|
|
this.charm = null; // 魅饰卡
|
|
this._data = null; // 私有的数据储存,约定: 只能在 CardInfo.js 自身的代码里访问
|
|
this.fieldData = {}; // 离场即清空的数据
|
|
this.fieldTurnData = {}; // 离场或回合结束即清空的数据
|
|
|
|
// 时点
|
|
this.onMove = new Timming(game);
|
|
this.onEnterField = new Timming(game);
|
|
this.onLeaveField = new Timming(game); // 常时效果中「离场时」的时点
|
|
this.onLeaveField2 = new Timming(game); // 包括被 rise 等而成为卡垫的时点
|
|
this.onBurst = new Timming(game);
|
|
this.onAttack = new Timming(game);
|
|
this.onStartUp = new Timming(game);
|
|
this.onPowerChange = new Timming(game);
|
|
this.onPowerUpdate = new Timming(game);
|
|
this.onBanish = new Timming(game);
|
|
this.onAffect = new Timming(game);
|
|
this.onUp = new Timming(game);
|
|
this.onDown = new Timming(game);
|
|
this.onBattle = new Timming(game);
|
|
this.onHeaven = new Timming(game);
|
|
this.onFreeze = new Timming(game);
|
|
this.onChangeSigniZone = new Timming(game);
|
|
this.onRised = new Timming(game);
|
|
|
|
// 附加的属性
|
|
this.canNotAttack = false;
|
|
this.lancer = false;
|
|
this.doubleCrash = false;
|
|
this.tripleCrash = false;
|
|
this.frozen = false;
|
|
this.forceSummonZone = false;
|
|
this.trashCharmInsteadOfBanish = false;
|
|
this.attachedCostColorless = 0; // <星占之巫女 忆·夜>
|
|
this.assassin = false;
|
|
this.canNotBeBanished = false;
|
|
this.abilityLost = false;
|
|
this.canNotBeBanishedByEffect = false;
|
|
this.protectingShironakujis = []; // <幻水 蓝鲸>
|
|
this.protectingMpps = []; // <コードハート M・P・P>
|
|
this.useBikouAsWhiteCost = false; // <启示的天惠 安=FORTH>
|
|
this.canNotBeTrashedBySelf = false; // <罗星 星宿一>
|
|
this.trashAsWhiteCost = false; // <美しき弦奏 コントラ>
|
|
this.summonConditions = []; // <罠砲 タイマーボム>
|
|
this._OrichalciaNaturalStone = false; // <罗石 金铜>
|
|
this._KosakiPhantomBeast = false; // <幻兽 小御先>
|
|
this._MikamuneSmallSword = false; // <小剑 三日月>
|
|
this.resonaBanishToTrash = false; // <绿叁游 水滑梯>
|
|
this.discardSpellInsteadOfBanish = false; // <核心代号 S・W・T>
|
|
this.attackCostColorless = 0; // <黑幻虫 水熊虫>
|
|
this.canNotGainAbility = false; // <迷宫代号 金阁>
|
|
this.canNotGainAbilityBySelfPlayer = false; // <城堡代号 凡尔赛宫>
|
|
this._SnoropNaturalPlantPrincess = false; // <罗植姬 雪花莲>
|
|
this._CodeLabyrinthLouvre = null; // <卢浮宫>
|
|
this.powerAddProtected = false; // <幻兽 苍龙>
|
|
this._GustaftCenterBallista = false; // <弩中砲 グスタフト>
|
|
this.colorLost = false; // <侍从 ∞>
|
|
this.banishProtections = [];
|
|
this.upProtections = [];
|
|
// 注意hasAbility
|
|
}
|
|
|
|
Card.abilityProps = [
|
|
'canNotAttack',
|
|
'lancer',
|
|
'doubleCrash',
|
|
'tripleCrash',
|
|
'assassin',
|
|
'canNotBeBanished',
|
|
'canNotBeBanishedByEffect'
|
|
];
|
|
|
|
Card.prototype.cookEffect = function (rawEffect,type,offset) {
|
|
if (!offset) offset = 0;
|
|
return concat(rawEffect || []).map(function (eff,idx) {
|
|
var effect = Object.create(eff);
|
|
effect.source = this;
|
|
effect.description = [this.cid,type,idx+offset].join('-');
|
|
return effect;
|
|
},this);
|
|
};
|
|
|
|
Card.prototype.setupConstEffects = function () {
|
|
this.constEffects.forEach(function (eff,idx) {
|
|
var createTimming,destroyTimming,once;
|
|
if (eff.duringGame) {
|
|
createTimming = null;
|
|
destroyTimming = null;
|
|
once = false;
|
|
} else if (eff.getCreateTimming) {
|
|
createTimming = eff.getCreateTimming.call(this);
|
|
destroyTimming = eff.getDestroyTimming.call(this);
|
|
once = eff.once;
|
|
} else {
|
|
createTimming = this.onEnterField;
|
|
destroyTimming = this.onLeaveField2;
|
|
once = false;
|
|
}
|
|
var action = eff.action
|
|
if (eff.auto) {
|
|
action = function (set,add) {
|
|
var effect = this.game.newEffect({
|
|
source: this,
|
|
description: this.cid+'-'+'const-'+idx,
|
|
actionAsyn: eff.actionAsyn,
|
|
});
|
|
add(this,eff.auto,effect);
|
|
};
|
|
}
|
|
this.game.addConstEffect({
|
|
source: this,
|
|
createTimming: createTimming,
|
|
once: once,
|
|
destroyTimming: destroyTimming,
|
|
cross: !!eff.cross,
|
|
fixed: !!eff.fixed,
|
|
condition: eff.condition,
|
|
action: action,
|
|
},true);
|
|
},this);
|
|
};
|
|
|
|
Card.prototype.setupStartUpEffects = function () {
|
|
this.startUpEffects.forEach(function (eff) {
|
|
if (this.type === 'SIGNI') {
|
|
if (eff.cross) {
|
|
eff.condition = function () {
|
|
return this.crossed && inArr(this,this.player.signis);
|
|
};
|
|
} else {
|
|
eff.condition = function () {
|
|
return inArr(this,this.player.signis);
|
|
};
|
|
}
|
|
}
|
|
var effect = new Effect(this.game.effectManager,eff);
|
|
this.game.addConstEffect({
|
|
source: this,
|
|
fixed: true,
|
|
action: function (set,add) {
|
|
add(this,'onStartUp',effect);
|
|
}
|
|
},true);
|
|
},this);
|
|
};
|
|
|
|
Card.prototype.setupBurstEffects = function () {
|
|
this.burstEffects.forEach(function (eff) {
|
|
eff.optional = true;
|
|
var effect = new Effect(this.game.effectManager,eff);
|
|
this.game.addConstEffect({
|
|
source: this,
|
|
fixed: true,
|
|
action: function (set,add) {
|
|
add(this,'onBurst',effect);
|
|
}
|
|
},true);
|
|
},this);
|
|
};
|
|
|
|
Card.prototype.setupEffects = function () {
|
|
this.setupConstEffects();
|
|
this.setupStartUpEffects();
|
|
this.setupBurstEffects();
|
|
};
|
|
|
|
Card.prototype.canGrow = function (ignoreCost) {
|
|
if (this.type !== 'LRIG') return false;
|
|
if (this.player.canNotGrow) return false;
|
|
if (this.cid === this.player.lrig.cid) return false;
|
|
if (this.growCondition) {
|
|
if (!this.growCondition()) {
|
|
return false;
|
|
}
|
|
}
|
|
if (!ignoreCost && !this.player.enoughCost(this.getGrowCostObj())) return false;
|
|
// LRIG type
|
|
if (this.classes.every(function (cls) {
|
|
return !this.player.lrig.hasClass(cls) && (cls !== '?'); // <紡ぐ者>
|
|
},this)) {
|
|
return false;
|
|
}
|
|
return (this.level <= this.player.lrig.level+1);
|
|
};
|
|
|
|
Card.prototype.getGrowCostObj = function () {
|
|
var obj = Object.create(this);
|
|
obj.costWhite -= this.player.reducedGrowCostWhite;
|
|
obj.costBlack -= this.player.reducedGrowCostBlack;
|
|
obj.costRed -= this.player.reducedGrowCostRed;
|
|
obj.costBlue -= this.player.reducedGrowCostBlue;
|
|
obj.costGreen -= this.player.reducedGrowCostGreen;
|
|
obj.costColorless -= this.player.reducedGrowCostColorless;
|
|
if (obj.costWhite < 0) obj.costWhite = 0;
|
|
if (obj.costBlack < 0) obj.costBlack = 0;
|
|
if (obj.costRed < 0) obj.costRed = 0;
|
|
if (obj.costBlue < 0) obj.costBlue = 0;
|
|
if (obj.costGreen < 0) obj.costGreen = 0;
|
|
if (obj.costColorless < 0) obj.costColorless = 0;
|
|
return obj;
|
|
};
|
|
|
|
Card.prototype.getChainedCostObj = function (obj) {
|
|
if (!obj) {
|
|
obj = Object.create(this);
|
|
}
|
|
if (!this.player.chain) return obj;
|
|
obj.costWhite -= this.player.chain.costWhite || 0;
|
|
obj.costBlack -= this.player.chain.costBlack || 0;
|
|
obj.costRed -= this.player.chain.costRed || 0;
|
|
obj.costBlue -= this.player.chain.costBlue || 0;
|
|
obj.costGreen -= this.player.chain.costGreen || 0;
|
|
obj.costColorless -= this.player.chain.costColorless || 0;
|
|
if (obj.costWhite < 0) obj.costWhite = 0;
|
|
if (obj.costBlack < 0) obj.costBlack = 0;
|
|
if (obj.costRed < 0) obj.costRed = 0;
|
|
if (obj.costBlue < 0) obj.costBlue = 0;
|
|
if (obj.costGreen < 0) obj.costGreen = 0;
|
|
if (obj.costColorless < 0) obj.costColorless = 0;
|
|
return obj;
|
|
};
|
|
|
|
Card.prototype.canSummon = function () {
|
|
return this.canSummonWith(this.player.signis);
|
|
};
|
|
|
|
Card.prototype.canSummonWith = function (signis) {
|
|
// 类型
|
|
if (this.type !== 'SIGNI') return false;
|
|
// rise
|
|
var riseTargets = []
|
|
if (this.rise) {
|
|
riseTargets = signis.filter(function (signi) {
|
|
return this.rise(signi)
|
|
},this)
|
|
if (!riseTargets.length) return false;
|
|
}
|
|
// <绿罗植 世界树>
|
|
if (this.player.canNotUseColorlessSigni) {
|
|
if (this.hasColor('colorless')) {
|
|
return false;
|
|
}
|
|
}
|
|
// summonConditions
|
|
var flag = this.summonConditions.some(function (condition) {
|
|
return !condition.call(this);
|
|
},this);
|
|
if (flag) return false;
|
|
// 限定
|
|
if (!this.checkLimiting()) return false;
|
|
// 等级限制
|
|
if (this.level > this.player.lrig.level) return false;
|
|
// SIGNI 数量限制
|
|
var length = signis.length;
|
|
if (this.rise) length--;
|
|
if (length >= this.player.getSigniAmountLimit()) {
|
|
return false;
|
|
}
|
|
// 界限限制
|
|
var totalLevel = signis.reduce(function (total,signi) {
|
|
return total + signi.level;
|
|
},this.level);
|
|
if (this.rise) {
|
|
// rise 减去等级最高的目标即可
|
|
totalLevel -= Math.max.apply(Math,riseTargets.map(function (signi) {
|
|
return signi.level;
|
|
}));
|
|
}
|
|
if (totalLevel > this.player.lrig.limit) return false;
|
|
// 召唤区限制
|
|
var zones = this.player.getSummonZones(signis,this.rise);
|
|
if (!zones.length) return false;
|
|
// 结束
|
|
return true;
|
|
};
|
|
|
|
Card.prototype.getSummonSolution = function (filter,count) {
|
|
var cards = this.player.signis.filter(function (card) {
|
|
return filter(card) && card.canTrashAsCost();
|
|
},this);
|
|
// 选项不足
|
|
if (cards.length < count) return null;
|
|
// 必须全选
|
|
if (this.player.signis.length === count) {
|
|
if (!this.canSummonWith([])) return null;
|
|
return function () {
|
|
return Callback.immediately().callback(this,function () {
|
|
var signis = this.player.signis.slice();
|
|
this.game.trashCards(signis);
|
|
return signis;
|
|
});
|
|
}.bind(this);
|
|
}
|
|
// 2或3选1
|
|
if (count === 1) {
|
|
// 遍历
|
|
cards = cards.filter(function (card) {
|
|
var signis = this.player.signis.filter(function (signi) {
|
|
return (signi !== card);
|
|
},this);
|
|
return this.canSummonWith(signis);
|
|
},this);
|
|
if (!cards.length) return null;
|
|
return function () {
|
|
return this.player.selectAsyn('TRASH',cards).callback(this,function (card) {
|
|
card.trash();
|
|
return [card];
|
|
});
|
|
}.bind(this);
|
|
}
|
|
// 3选2
|
|
if (count === 2) {
|
|
// signis.length === 3;
|
|
if (cards.length === 2) {
|
|
var signis = this.player.signis.filter(function (signi) {
|
|
return !inArr(signi,cards);
|
|
},this);
|
|
if (!this.canSummonWith(signis)) return null;
|
|
return function () {
|
|
return Callback.immediately().callback(this,function () {
|
|
this.game.trashCards(cards);
|
|
return cards;
|
|
});
|
|
}.bind(this);
|
|
}
|
|
if (cards.length === 3) {
|
|
var cards_left = cards.filter(function (card) {
|
|
return this.canSummonWith([card]);
|
|
},this);
|
|
if (!cards_left.length) return null;
|
|
if (cards_left.length === 1) {
|
|
cards = cards.filter(function (card) {
|
|
return card !== cards_left[0];
|
|
},this);
|
|
return function () {
|
|
return Callback.immediately().callback(this,function () {
|
|
this.game.trashCards(cards);
|
|
return cards;
|
|
});
|
|
}.bind(this);
|
|
}
|
|
if (cards_left.length === 2) {
|
|
return function () {
|
|
return this.player.selectAsyn('TRASH',cards).callback(this,function (c) {
|
|
var cards_trash = [c];
|
|
if (inArr(c,cards_left)) {
|
|
cards = cards.filter(function (card) {
|
|
return !inArr(card,cards_left);
|
|
});
|
|
} else {
|
|
cards = cards_left;
|
|
}
|
|
return this.player.selectAsyn('TRASH',cards).callback(this,function (c) {
|
|
cards_trash.push(c);
|
|
this.game.trashCards(cards_trash);
|
|
return cards_trash;
|
|
});
|
|
});
|
|
}.bind(this);
|
|
}
|
|
if (cards_left.length === 3) {
|
|
return function () {
|
|
return this.player.selectSomeAsyn('TRASH',cards,2,2).callback(this,function (cards) {
|
|
this.game.trashCards(cards);
|
|
return cards;
|
|
});
|
|
}.bind(this);
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
};
|
|
|
|
Card.prototype.canAttack = function () {
|
|
if (this.attackCostColorless) {
|
|
var obj = {
|
|
costColorless: this.attackCostColorless
|
|
};
|
|
if (!this.player.enoughCost(obj)) return false;
|
|
}
|
|
|
|
// <バインド・ウェポンズ>
|
|
if (this.type === 'SIGNI') {
|
|
var attackCount = this.fieldTurnData.attackCount || 0;
|
|
if (attackCount >= this.player.signiAttackCountLimit) return false;
|
|
} else {
|
|
var lrigAttackCount = this.game.getData(this.player,'lrigAttackCount') || 0;
|
|
if (lrigAttackCount >= this.player.lrigAttackCountLimit) return false;
|
|
}
|
|
|
|
// <白羅星 フルムーン>
|
|
if (this.type === 'SIGNI') {
|
|
var attackCount = this.game.getData(this.player,'signiAttackCount') || 0;
|
|
if (attackCount >= this.player.signiTotalAttackCountLimit) return false;
|
|
}
|
|
|
|
return (this.type === 'SIGNI' || this.type === 'LRIG') &&
|
|
(this.isUp) &&
|
|
(!this.canNotAttack);
|
|
};
|
|
|
|
Card.prototype.canTrashAsCost = function () {
|
|
if (this.canNotBeTrashedBySelf) return false;
|
|
if (this.player.trashSigniBanned && inArr(this,this.player.signis)) return false;
|
|
return true;
|
|
};
|
|
|
|
Card.prototype.canUse = function (timming,ignoreCost) {
|
|
if (inArr(this.cid,this.player.bannedCards)) return false;
|
|
if (!this.checkLimiting()) return false;
|
|
if (this.useCondition && !this.useCondition()) return false;
|
|
if (this.type === 'SPELL') {
|
|
// <绿罗植 世界树>
|
|
if (this.player.canNotUseColorlessSpell) {
|
|
if (this.hasColor('colorless')) {
|
|
return false;
|
|
}
|
|
}
|
|
if (this.player.spellBanned) return false;
|
|
if (ignoreCost) return true;
|
|
return this.enoughCost();
|
|
}
|
|
if (this.type === 'ARTS') {
|
|
if (this.player.artsBanned) return false;
|
|
if (!inArr(timming,this.timmings)) return false;
|
|
if (this.player.oneArtEachTurn && this.game.getData(this.player,'flagArtsUsed')) return false;
|
|
|
|
// cost 判断
|
|
if (ignoreCost) return true;
|
|
var cost = this.getChainedCostObj()
|
|
if (this.player.enoughCost(cost)) return true;
|
|
// bet 相关
|
|
if (this.bet && this.bettedCost) {
|
|
if (this.bet > this.player.coin) return false; // 避免出现coin不够却可以选择使用技艺。《孤立无炎》
|
|
cost = this.getChainedCostObj(this.bettedCost)
|
|
if (this.player.enoughCost(cost)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
Card.prototype.checkLimiting = function () {
|
|
// this.limiting === this.player.lrig.lrigType
|
|
if (!this.limitings.length) return true;
|
|
if (this.player.ignoreLimitingOfArtsAndSpell) {
|
|
if ((this.type === 'SPELL') || (this.type === 'ARTS')) {
|
|
return true;
|
|
}
|
|
}
|
|
if (this.player.ignoreLimitingOfLevel5Signi) {
|
|
if ((this.type === 'SIGNI') && (this.level === 5)) {
|
|
return true;
|
|
}
|
|
}
|
|
return this.limitings.some(function (limiting) {
|
|
return inArr(limiting,this.player.lrig.classes);
|
|
},this);
|
|
};
|
|
|
|
Card.prototype.enoughCost = function () {
|
|
return this.player.enoughCost(this);
|
|
};
|
|
|
|
Card.prototype.hasClass = function (cls) {
|
|
return this.classes.some(function (thisClass) {
|
|
return thisClass === cls;
|
|
},this);
|
|
};
|
|
|
|
// 注意:不保证唯一性
|
|
Card.prototype.getColors = function (ignoreColorless) {
|
|
var colors = this.otherColors.concat(this.color);
|
|
if (this.colorLost) {
|
|
colors = ['colorless'];
|
|
}
|
|
if (ignoreColorless) {
|
|
removeFromArr('colorless',colors);
|
|
}
|
|
if (!this._SnoropNaturalPlantPrincess) return colors;
|
|
if (!inArr(this,this.player.signis)) return colors;
|
|
this.player.enerZone.cards.forEach(function (card) {
|
|
card.otherColors.concat(card.color).forEach(function (color) {
|
|
if (color === 'colorless') return;
|
|
if (inArr(color,colors)) return;
|
|
colors.push(color);
|
|
},this);
|
|
});
|
|
return colors;
|
|
};
|
|
|
|
Card.prototype.hasColor = function (color) {
|
|
if (color === 'colorless') {
|
|
return !this.getColors(true).length;
|
|
}
|
|
return inArr(color,this.getColors(true));
|
|
};
|
|
|
|
Card.prototype.hasSameColorWith = function (card) {
|
|
var colors = this.getColors(true);
|
|
return card.getColors(true).some(function (color) {
|
|
return inArr(color,colors);
|
|
},this);
|
|
};
|
|
|
|
Card.prototype.hasBurst = function () {
|
|
return this.burstIcon || this.onBurst.effects.length;
|
|
};
|
|
|
|
Card.prototype.isEffectFiltered = function (source) {
|
|
if (!this.effectFilters.length) return false;
|
|
if (!source) source = this.game.getEffectSource();
|
|
if (!source) return false;
|
|
return this.effectFilters.some(function (filter) {
|
|
return !filter.call(this,source);
|
|
},this);
|
|
};
|
|
|
|
Card.prototype.up = function () {
|
|
if (this.isUp) return false;
|
|
if (this.isEffectFiltered()) return false;
|
|
this.isUp = true;
|
|
this.triggerOnAffect();
|
|
this.onUp.trigger({
|
|
card: this
|
|
});
|
|
this.game.output({
|
|
type: 'UP_CARD',
|
|
content: {card: this}
|
|
});
|
|
return true;
|
|
};
|
|
|
|
Card.prototype.upAsyn = function () {
|
|
if (this.isUp) return Callback.immediately(false);
|
|
if (this.isEffectFiltered()) return Callback.immediately(false);
|
|
var protections = this.upProtections.filter(function (protection) {
|
|
return protection.condition.call(protection.source);
|
|
},this);
|
|
if (!this.upProtections.length) return Callback.immediately(this.up());
|
|
return this.player.selectAsyn('CHOOSE_EFFECT',protections).callback(this,function (protection) {
|
|
protection.source.activate();
|
|
return protection.actionAsyn.call(protection.source,this);
|
|
}).callback(this,function () {
|
|
return true;
|
|
});
|
|
};
|
|
|
|
Card.prototype.down = function () {
|
|
if (!this.isUp) return false;
|
|
if (this.isEffectFiltered()) return false;
|
|
if (this.player.canNotBeDownedByOpponentEffect) {
|
|
var source = this.game.getEffectSource()
|
|
if (source && (source.player === this.player.opponent)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
this.game.frameStart();
|
|
this.isUp = false;
|
|
if (this.hasClass('植物')) {
|
|
var count = this.game.getData(this.player,'増武Count') || 0;
|
|
this.game.setData(this.player,'増武Count',count+1);
|
|
}
|
|
this.triggerOnAffect();
|
|
this.onDown.trigger({
|
|
card: this
|
|
});
|
|
this.game.frameEnd();
|
|
|
|
this.game.output({
|
|
type: 'DOWN_CARD',
|
|
content: {card: this}
|
|
});
|
|
return true;
|
|
};
|
|
|
|
Card.prototype.faceup = function () {
|
|
if (this.isFaceup) return false;
|
|
if (this.isEffectFiltered()) return false;
|
|
this.isFaceup = true;
|
|
this.triggerOnAffect();
|
|
this.game.output({
|
|
type: 'FACEUP_CARD',
|
|
content: {
|
|
card: this,
|
|
pid: this.pid
|
|
}
|
|
});
|
|
};
|
|
|
|
Card.prototype.facedown = function () {
|
|
if (!this.isFaceup) return false;
|
|
if (this.isEffectFiltered()) return false;
|
|
this.isFaceup = false;
|
|
this.triggerOnAffect();
|
|
this.game.output({
|
|
type: 'FACEDOWN_CARD',
|
|
content: {card: this}
|
|
});
|
|
};
|
|
|
|
Card.prototype.moveTo = function (zone,arg) {
|
|
// 共鸣
|
|
if (this.resona) {
|
|
if (!inArr(zone.name,['SigniZone','LrigTrashZone','LrigDeck','ExcludedZone'])) {
|
|
zone = this.player.lrigDeck;
|
|
arg = {}; // 忽略移动参数
|
|
}
|
|
}
|
|
// <コードアンチ カイヅカ>
|
|
if (this.fieldData.excludeWhenLeaveField) {
|
|
if (inArr(this,this.player.signis) && zone.name !== 'SigniZone') {
|
|
zone = this.player.excludedZone;
|
|
}
|
|
}
|
|
if (arg === undefined) arg = {};
|
|
if (arg.up === undefined) arg.up = zone.up;
|
|
if (arg.faceup === undefined) arg.faceup = zone.faceup;
|
|
if (arg.bottom === undefined) arg.bottom = zone.bottom;
|
|
|
|
var card = this;
|
|
|
|
// 效果过滤 (不会受到XXX的效果影响)
|
|
var source = this.game.getEffectSource();
|
|
if (this.isEffectFiltered(source)) return false;
|
|
// "不能抽卡,也不能将卡加入手牌"
|
|
if (card.player.addCardToHandBanned) {
|
|
if ((zone.name === 'HandZone') && (card.zone !== zone)) {
|
|
return false;
|
|
}
|
|
}
|
|
// "不能自己将这只 SIGNI 从场上放置到废弃区。"
|
|
if (card.canNotBeTrashedBySelf && source && (source.player === card.player)) {
|
|
if (inArr(card,card.player.signis) && (zone.name === 'TrashZone')) {
|
|
return false;
|
|
}
|
|
}
|
|
// "不能从场上返回手牌。"
|
|
if (card.player.canNotBeBounced) {
|
|
if (inArr(card,card.player.signis) && (zone.name === 'HandZone')) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 注: 交换 SIGNI 区请用 card.changeSigniZone(zone).
|
|
// 以下代码作废.
|
|
// 从 SIGNI 区移动到 SIGNI 区的情况比较特殊,单独处理.
|
|
// 全部卡片一起移动,不触发任何时点.
|
|
// 注意: 这里没考虑转移控制权的情况,目前WX还没有这种效果.
|
|
// if ((card.zone.name === 'SigniZone') && (zone.name === 'SigniZone')) {
|
|
// var cards = card.zone.cards.slice();
|
|
// card.zone.cards.length = 0;
|
|
// this.game.packOutputs(function () {
|
|
// cards.forEach(function (card) {
|
|
// zone.cards.push(card);
|
|
// card.zone = zone;
|
|
// var msgObj = {
|
|
// type: 'MOVE_CARD',
|
|
// content: {
|
|
// card: card,
|
|
// pid: card.isFaceup? card.pid : 0,
|
|
// zone: zone,
|
|
// up: card.isUp,
|
|
// faceup: card.isFaceup,
|
|
// bottom: true
|
|
// }
|
|
// };
|
|
// card.player.output(msgObj);
|
|
// card.player.opponent.output(msgObj);
|
|
// },this);
|
|
// },this);
|
|
// return;
|
|
// }
|
|
|
|
// 更新 player 的 hands,signis,lrig .
|
|
// 同时设置 onEnterField 等时点的事件对象.
|
|
var moveEvent = {
|
|
card: card,
|
|
isSigni: inArr(card,card.player.signis),
|
|
isCharm: arg.isCharm || false,
|
|
isCrossed: !!card.crossed,
|
|
riseTarget: null,
|
|
isUp: arg.up,
|
|
oldZone: card.zone,
|
|
newZone: zone,
|
|
resonaArg: arg.resonaArg || null,
|
|
isExceedCost: !!arg.isExceedCost,
|
|
source: source
|
|
};
|
|
var enterFieldEvent = null;
|
|
var leaveFieldEvent = null;
|
|
var lrigChangeEvent = null;
|
|
var charm = null;
|
|
|
|
/* 处理离开区域逻辑 */
|
|
if (card.zone.name === 'HandZone') {
|
|
// 离开手牌
|
|
removeFromArr(card,card.player.hands);
|
|
} else if (card.zone.name === 'SigniZone') {
|
|
// 离开 SigniZone
|
|
if (inArr(card,card.player.signis)) {
|
|
// 是 SIGNI
|
|
// 以下代码 rise 里复用了,虽然说 DRY 原则,但这里也不太好抽离,养肥了再抽吧(
|
|
leaveFieldEvent = moveEvent;
|
|
card.frozen = false;
|
|
card.fieldData = {};
|
|
card.fieldTurnData = {};
|
|
charm = card.charm;
|
|
card.charm = null;
|
|
removeFromArr(card,card.player.signis);
|
|
} else {
|
|
// 是 SIGNI 下方的卡,比如魅饰卡
|
|
// 处理魅饰卡
|
|
var signi = card.zone.getActualCards()[0];
|
|
if (signi && card === signi.charm) {
|
|
moveEvent.isCharm = true;
|
|
signi.charm = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 处理进入区域逻辑 */
|
|
if (zone.name === 'HandZone') {
|
|
// 进入手牌
|
|
zone.player.hands.push(card);
|
|
} else if (zone.name === 'SigniZone') {
|
|
if (card.zone.name !== 'SigniZone' || zone.player !== card.player) {
|
|
// 进入 SIGNI 区
|
|
if (zone.getActualCards().length) {
|
|
// rise
|
|
if (card.rise) {
|
|
// 被 rise 的卡“离场”
|
|
signi = zone.getActualCards()[0];
|
|
removeFromArr(signi,signi.player.signis);
|
|
signi.frozen = false;
|
|
signi.fieldData = {};
|
|
signi.fieldTurnData = {};
|
|
charm = signi.charm;
|
|
signi.charm = null;
|
|
signi.onLeaveField2.trigger({});
|
|
// 出场
|
|
arg.bottom = false;
|
|
enterFieldEvent = moveEvent;
|
|
enterFieldEvent.riseTarget = signi;
|
|
card.player.signis.push(card);
|
|
} else {
|
|
// 放置到 SIGNI 下面的卡
|
|
// (目前不用处理)
|
|
}
|
|
} else {
|
|
// 出场
|
|
enterFieldEvent = moveEvent;
|
|
zone.player.signis.push(card);
|
|
}
|
|
}
|
|
} else if ((zone.name === 'LrigZone') && !arg.bottom) {
|
|
// 进入 LrigZone
|
|
lrigChangeEvent = {
|
|
oldLrig: zone.player.lrig,
|
|
newLrig: card
|
|
};
|
|
zone.player.lrig = card;
|
|
}
|
|
|
|
// <混沌之键主 乌姆尔=FYRA>
|
|
if (this.game.getData(card,'zeroActionCostInTrash')) {
|
|
this.game.setData(card,'zeroActionCostInTrash',false);
|
|
}
|
|
|
|
// 移动卡片
|
|
removeFromArr(card,card.zone.cards);
|
|
card.isUp = arg.up;
|
|
card.isFaceup = arg.faceup;
|
|
if (arg.bottom) {
|
|
zone.cards.push(card);
|
|
} else {
|
|
zone.cards.unshift(card);
|
|
}
|
|
card.zone = zone;
|
|
|
|
// CROSS
|
|
if (enterFieldEvent || leaveFieldEvent) {
|
|
card.player.setCrossPair();
|
|
}
|
|
|
|
// 向客户端输出信息.
|
|
// 注意,客户端并不能获知所有信息,而且玩家双方获得的信息也不一定是一样的.
|
|
// 须隐藏或修改的信息有: pid 和 faceup.
|
|
// 对 pid 及 faceup 的说明:
|
|
// pid: 对于游戏逻辑中正面朝上的卡,双方都可见其pid. (也就是都知道是什么卡)
|
|
// 而对于游戏逻辑中背面朝上的卡,
|
|
// 对方玩家: 不能获知其pid,
|
|
// 己方玩家: 如果卡片区域是 checkable 的,可见其pid. 否则不能.
|
|
// faceup: 游戏逻辑中卡片的面向和客户端中的卡片面向是不同的. 不同点仅在于手牌.
|
|
// 手牌中非公开的卡,在游戏逻辑中是"背面朝上"的,即:
|
|
// 对方不能查看;己方能查看(这是因为手牌区是 checkable 的).
|
|
// 但在客户端中,手牌里的卡即使逻辑上是背面朝上的,仍显示为正面朝上.
|
|
card.player.output({
|
|
type: 'MOVE_CARD',
|
|
content: {
|
|
card: card,
|
|
pid: (card.isFaceup || zone.checkable)? card.pid : 0,
|
|
zone: zone,
|
|
up: arg.up,
|
|
faceup: zone.inhand? true : arg.faceup,
|
|
bottom: arg.bottom,
|
|
isSide: arg.isSide
|
|
}
|
|
});
|
|
card.player.opponent.output({
|
|
type: 'MOVE_CARD',
|
|
content: {
|
|
card: card,
|
|
pid: card.isFaceup? card.pid : 0,
|
|
zone: zone,
|
|
up: arg.up,
|
|
faceup: arg.faceup,
|
|
bottom: arg.bottom,
|
|
isSide: arg.isSide
|
|
}
|
|
});
|
|
|
|
// "混淆"手牌
|
|
// 说明:
|
|
// 手牌区是"玩家可随时洗切"的区域.这意味着:
|
|
// 卡片加入某玩家手牌后,须要对对方玩家"混淆"其手牌.
|
|
// "混淆"的方法描述详见 <关于各种id的说明.txt> .
|
|
if (card.zone.inhand) {
|
|
card.game.allocateSid(card.player.opponent,card.player.hands);
|
|
}
|
|
|
|
// 触发各种时点
|
|
card.game.frameStart();
|
|
card.onMove.trigger(moveEvent);
|
|
card.player.onCardMove.trigger(moveEvent);
|
|
if ((moveEvent.oldZone.name === 'HandZone') && (moveEvent.newZone.name === 'TrashZone')) {
|
|
this.game.setData(card.player,'hasDiscardedCard',true);
|
|
card.player.onDiscard.trigger(moveEvent);
|
|
}
|
|
card.triggerOnAffect();
|
|
if (enterFieldEvent) {
|
|
// card.player.onSignisChange.trigger();
|
|
card.player.onSummonSigni.trigger(moveEvent);
|
|
card.onEnterField.trigger(enterFieldEvent);
|
|
if (!(arg.dontTriggerStartUp || card.player.signiStartUpBanned)) {
|
|
card.onStartUp.trigger(enterFieldEvent);
|
|
}
|
|
// rise
|
|
if (enterFieldEvent.riseTarget) {
|
|
enterFieldEvent.riseTarget.onRised.trigger(enterFieldEvent);
|
|
}
|
|
} else if (leaveFieldEvent) {
|
|
// card.player.onSignisChange.trigger();
|
|
card.onLeaveField.trigger(leaveFieldEvent);
|
|
card.onLeaveField2.trigger(leaveFieldEvent);
|
|
card.player.onSigniLeaveField.trigger(leaveFieldEvent);
|
|
// SIGNI 离场时,下面的卡送入废弃区,
|
|
// 此处理在块结束时执行。
|
|
// http://www.takaratomy.co.jp/products/wixoss/rule/rule_rulechange/151211/index.html
|
|
leaveFieldEvent.oldZone.cards.forEach(function (card) {
|
|
if (card === charm) {
|
|
card.game.trashingCharms.push(card);
|
|
} else {
|
|
card.game.trashingCards.push(card);
|
|
}
|
|
},this);
|
|
} else if (lrigChangeEvent) {
|
|
// card.player.onLrigChange.trigger(lrigChangeEvent);
|
|
if (card.coin) {
|
|
card.player.gainCoins(card.coin)
|
|
}
|
|
var oldLrig = lrigChangeEvent.oldLrig;
|
|
if (oldLrig) oldLrig.onLeaveField2.trigger();
|
|
card.onEnterField.trigger(enterFieldEvent);
|
|
if (!(arg.dontTriggerStartUp || card.player.lrigStartUpBanned)) {
|
|
card.onStartUp.trigger(enterFieldEvent);
|
|
}
|
|
}
|
|
// 双面共鸣
|
|
var side = card.sideA || card.sideB;
|
|
if (side && !arg.isSide) {
|
|
var arg = {
|
|
isSide: true,
|
|
};
|
|
if (enterFieldEvent) {
|
|
side.moveTo(card.player.excludedZone,arg);
|
|
} else if (zone === card.player.lrigTrashZone) {
|
|
side.moveTo(zone,arg);
|
|
} else if (zone === card.player.lrigDeck) {
|
|
side.moveTo(zone,arg);
|
|
}
|
|
}
|
|
card.game.frameEnd();
|
|
|
|
return true;
|
|
};
|
|
|
|
Card.prototype.changeSigniZone = function (zone) {
|
|
if (!inArr(this,this.player.signis)) {
|
|
console.warn('card.changeSigniZone: card is not a SIGNI!');
|
|
return;
|
|
}
|
|
|
|
// 效果过滤 (不会受到XXX的效果影响)
|
|
if (this.isEffectFiltered()) return false;
|
|
var card = zone.cards[0];
|
|
if (card && card.isEffectFiltered()) return false;
|
|
|
|
// 交换 zone.cards
|
|
var oldZone = this.zone;
|
|
var tmp = oldZone.cards;
|
|
oldZone.cards = zone.cards;
|
|
zone.cards = tmp;
|
|
// 设置 card.zone
|
|
oldZone.cards.forEach(function (card) {
|
|
card.zone = oldZone;
|
|
},this);
|
|
zone.cards.forEach(function (card) {
|
|
card.zone = zone;
|
|
},this);
|
|
// CROSS
|
|
this.player.setCrossPair();
|
|
this.onChangeSigniZone.trigger();
|
|
if (card) card.onChangeSigniZone.trigger();
|
|
// 向客户端输出信息
|
|
function createMsgObj (card) {
|
|
return {
|
|
type: 'MOVE_CARD',
|
|
content: {
|
|
card: card,
|
|
pid: card.isFaceup? card.pid : 0,
|
|
zone: card.zone,
|
|
up: card.isUp,
|
|
faceup: card.isFaceup,
|
|
bottom: true
|
|
}
|
|
}
|
|
}
|
|
this.game.packOutputs(function () {
|
|
var cards = concat(oldZone.cards,zone.cards);
|
|
cards.forEach(function (card) {
|
|
var msgObj = createMsgObj(card);
|
|
this.player.output(msgObj);
|
|
this.player.opponent.output(msgObj);
|
|
},this);
|
|
},this);
|
|
this.game.handleFrameEnd();
|
|
return true;
|
|
};
|
|
|
|
Card.prototype.getCrossPairCids = function () {
|
|
var pairs = [];
|
|
if (this.crossLeft) pairs = pairs.concat(this.crossLeft);
|
|
if (this.crossRight) pairs = pairs.concat(this.crossRight);
|
|
return pairs;
|
|
};
|
|
|
|
Card.prototype.trash = function (arg) {
|
|
var zone = inArr(this.type,['LRIG','ARTS'])?
|
|
this.player.lrigTrashZone:
|
|
this.player.trashZone;
|
|
return this.moveTo(zone,arg);
|
|
};
|
|
|
|
Card.prototype.exclude = function () {
|
|
return this.moveTo(this.player.excludedZone);
|
|
};
|
|
|
|
Card.prototype.trashAsyn = function () {
|
|
return this.game.trashCardsAsyn([this]);
|
|
};
|
|
|
|
Card.prototype.bounceAsyn = function () {
|
|
return this.game.bounceCardsAsyn([this]);
|
|
};
|
|
|
|
Card.prototype.attackAsyn = function () {
|
|
var card = this;
|
|
var player = card.player;
|
|
var opponent = player.opponent;
|
|
var crashArg = {
|
|
source: this,
|
|
lancer: false,
|
|
doubleCrash: false,
|
|
attack: true,
|
|
};
|
|
var cost = null;
|
|
if (this.attackCostColorless) {
|
|
cost = {
|
|
costColorless: this.attackCostColorless
|
|
};
|
|
if (!this.player.enoughCost(cost)) {
|
|
throw 'Not enough attack cost!'
|
|
}
|
|
}
|
|
// <バインド・ウェポンズ>, <白羅星 フルムーン>
|
|
if (this.type === 'SIGNI') {
|
|
var attackCount = this.fieldTurnData.attackCount || 0;
|
|
this.fieldTurnData.attackCount = ++attackCount;
|
|
var signiAttackCount = this.game.getData(this.player,'signiAttackCount') || 0;
|
|
this.game.setData(this.player,'signiAttackCount',++signiAttackCount);
|
|
} else {
|
|
var lrigAttackCount = this.game.getData(this.player,'lrigAttackCount') || 0;
|
|
this.game.setData(this.player,'lrigAttackCount',++lrigAttackCount);
|
|
}
|
|
|
|
return Callback.immediately().callback(this,function () {
|
|
if (!cost) return;
|
|
return this.player.payCostAsyn(cost);
|
|
}).callback(this,function () {
|
|
// "下次攻击无效化"
|
|
var prevented = !!this.game.getData(this.game,'preventNextAttack');
|
|
if (prevented) {
|
|
this.game.setData(this.game,'preventNextAttack',false);
|
|
}
|
|
// <暴风警报>
|
|
player.attackCount++;
|
|
if (player._stormWarning && (player.attackCount <= 2)) {
|
|
prevented = true;
|
|
}
|
|
// onAttack 的事件对象
|
|
var event = {
|
|
prevented: prevented,
|
|
preventedByGuard: false,
|
|
card: card,
|
|
banishAttackingSigniSource: null,
|
|
wontBeDamaged: false,
|
|
_1877: false, // PR-305
|
|
};
|
|
if (card.type === 'SIGNI') {
|
|
// 触发"攻击时"时点
|
|
// 触发"onHeaven"时点
|
|
return this.game.blockAsyn(this,function () {
|
|
this.game.frameStart();
|
|
card.down();
|
|
card.onAttack.trigger(event);
|
|
card.player.onAttack.trigger(event);
|
|
var pairs = card.crossed;
|
|
var heaven = pairs && pairs.every(function (pair) {
|
|
return !pair.isUp;
|
|
},this);
|
|
if (heaven) {
|
|
pairs.forEach(function (pair) {
|
|
pair.onHeaven.trigger(event);
|
|
},this);
|
|
card.player.onHeaven.trigger(event);
|
|
}
|
|
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 (!inArr(card,player.signis)) return;
|
|
if (event.prevented) return;
|
|
// 若对面有 SIGNI 且攻击方无暗杀,则进行战斗;
|
|
// 否则造成伤害.
|
|
var opposingSigni = card.getOpposingSigni();
|
|
if (opposingSigni && !card.assassin) {
|
|
// 战斗
|
|
// 触发"进行战斗"时点
|
|
var onBattleEvent = {
|
|
card: card,
|
|
opposingSigni: opposingSigni
|
|
};
|
|
return this.game.blockAsyn(this,function () {
|
|
this.game.frameStart();
|
|
card.onBattle.trigger(onBattleEvent);
|
|
opposingSigni.onBattle.trigger(onBattleEvent);
|
|
this.game.frameEnd();
|
|
}).callback(this,function () {
|
|
// 此时,攻击的卡可能已不在场上
|
|
if (!inArr(card,player.signis)) return;
|
|
// 受攻击的卡也可能已不在场上
|
|
// 注意: 根据事务所QA,此时不击溃对方的生命护甲(即使有 lancer ). (<大剣 レヴァテイン>)
|
|
if (!inArr(opposingSigni,opposingSigni.player.signis)) return;
|
|
// 结算战斗伤害
|
|
if (card.power >= opposingSigni.power) {
|
|
// 保存此时的 lancer 属性作为参考,
|
|
// 因为驱逐被攻击的 SIGNI 后,攻击侧的 lancer 可能改变.
|
|
var lancer = card.lancer;
|
|
return this.game.blockAsyn(this,function () {
|
|
return opposingSigni.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(opposingSigni,opposingSigni.player.signis)) {
|
|
return this.game.blockAsyn(onBattleEvent._1877,this,function () {
|
|
opposingSigni.moveTo(opposingSigni.player.mainDeck,{bottom: true});
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
// 伤害
|
|
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 () {
|
|
// "这场战斗结束之后,就回老家结婚" (划掉)
|
|
// "战斗结束后,将进行攻击的 SIGNI 驱逐"
|
|
if (event.banishAttackingSigniSource) {
|
|
return this.game.blockAsyn(event.banishAttackingSigniSource,card,card.banishAsyn);
|
|
}
|
|
}).callback(this,function () {
|
|
if (event.prevented) {
|
|
return this.game.blockAsyn(this,function () {
|
|
player.onAttackPrevented.trigger(event);
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
return this.game.blockAsyn(this,function () {
|
|
this.game.frameStart();
|
|
card.down();
|
|
card.onAttack.trigger(event);
|
|
card.player.onAttack.trigger(event);
|
|
this.game.frameEnd();
|
|
}).callback(this,function () {
|
|
// 被无效化
|
|
if (event.prevented) return;
|
|
// TODO: 处理onAttackActionEffect
|
|
// 防御
|
|
return this.game.blockAsyn(this,function () {
|
|
return opponent.guardAsyn();
|
|
}).callback(this,function (succ) {
|
|
if (succ) {
|
|
event.prevented = true;
|
|
event.preventedByGuard = true;
|
|
return;
|
|
};
|
|
if (event.wontBeDamaged || opponent.wontBeDamaged) return;
|
|
if (player.opponent.wontBeDamagedByOpponentLrig) 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 () {
|
|
if (event.prevented) {
|
|
return this.game.blockAsyn(this,function () {
|
|
player.onAttackPrevented.trigger(event);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
Card.prototype.handleBurstEnd = function (crossLifeCloth) {
|
|
if (this.zone !== this.player.checkZone) return;
|
|
if (crossLifeCloth) {
|
|
// 改变规则 <幻水 希拉>
|
|
this.trash();
|
|
var card = this.player.mainDeck.cards[0];
|
|
if (!card) return;
|
|
card.moveTo(this.player.lifeClothZone);
|
|
} else {
|
|
this.moveTo(this.player.enerZone);
|
|
}
|
|
};
|
|
|
|
Card.prototype.banishAsyn = function (arg) {
|
|
return this.game.banishCardsAsyn([this],false,arg);
|
|
};
|
|
|
|
// Card.prototype.banishAsyn = function () {
|
|
// return Callback.immediately().callback(this,function () {
|
|
// if (this.isEffectFiltered()) return false;
|
|
// if (this.canNotBeBanished) return false;
|
|
// if (this.trashCharmInsteadOfBanish && this.charm) {
|
|
// return this.player.selectOptionalAsyn('TRASH_CHARM',[this]).callback(this,function (c) {
|
|
// if (!c) {
|
|
// return this.doBanish();
|
|
// }
|
|
// return this.charm.trash();
|
|
// });
|
|
// }
|
|
// return this.doBanish();
|
|
// });
|
|
// };
|
|
|
|
// Card.prototype.beBanished = function () {
|
|
// var succ;
|
|
// this.game.frameStart();
|
|
// if (this.player.banishTrash) {
|
|
// succ = this.moveTo(this.player.trashZone);
|
|
// } else {
|
|
// succ = this.moveTo(this.player.enerZone);
|
|
// }
|
|
// if (succ) {
|
|
// var event = {
|
|
// card: this
|
|
// };
|
|
// this.onBanish.trigger(event);
|
|
// this.player.onSigniBanished.trigger(event);
|
|
// }
|
|
// this.game.frameEnd();
|
|
// return succ;
|
|
// };
|
|
|
|
// Card.prototype.doBanish = function () {
|
|
// if (this.isEffectFiltered()) return false;
|
|
// if (this.canNotBeBanished) return false;
|
|
// if (this.canNotBeBanishedByEffect) {
|
|
// var source = this.game.getEffectSource();
|
|
// if (source && (source.player === this.player.opponent)) {
|
|
// return false;
|
|
// }
|
|
// }
|
|
// var succ;
|
|
// this.game.frameStart();
|
|
// if (this.player.banishTrash) {
|
|
// succ = this.moveTo(this.player.trashZone);
|
|
// } else {
|
|
// succ = this.moveTo(this.player.enerZone);
|
|
// }
|
|
// if (succ) {
|
|
// var event = {
|
|
// card: this
|
|
// };
|
|
// this.onBanish.trigger(event);
|
|
// this.player.onSigniBanished.trigger(event);
|
|
// }
|
|
// this.game.frameEnd();
|
|
// return succ;
|
|
// };
|
|
|
|
Card.prototype.summonAsyn = function (optional,dontTriggerStartUp,down) {
|
|
if (!this.canSummon()) return Callback.immediately();
|
|
return Callback.immediately().callback(this,function () {
|
|
if (optional) {
|
|
return this.player.selectOptionalAsyn('SUMMON_SIGNI',[this]);
|
|
} else {
|
|
// return this.player.selectAsyn('SUMMON_SIGNI',[this]);
|
|
return this;
|
|
}
|
|
}).callback(this,function (card) {
|
|
if (!card) return;
|
|
return this.player.selectSummonZoneAsyn().callback(this,function (zone) {
|
|
card.moveTo(zone,{
|
|
dontTriggerStartUp: dontTriggerStartUp,
|
|
up: !down
|
|
});
|
|
this.game.handleFrameEnd(); // 增加一个空帧,以进行两次常计算
|
|
});
|
|
});
|
|
};
|
|
|
|
Card.prototype.summonOptionalAsyn = function (dontTriggerStartUp) {
|
|
return this.summonAsyn(true,dontTriggerStartUp);
|
|
};
|
|
|
|
Card.prototype.growAsyn = function (ignoreCost) {
|
|
return Callback.immediately().callback(this,function () {
|
|
if (!this.growActionAsyn) return;
|
|
return this.growActionAsyn();
|
|
}).callback(this,function () {
|
|
if (ignoreCost || this.player.ignoreGrowCost) return;
|
|
return this.player.payCostAsyn(this.getGrowCostObj());
|
|
}).callback(this,function () {
|
|
var colorChanged = (this.color !== this.player.lrig.color);
|
|
this.moveTo(this.player.lrigZone,{
|
|
up: this.player.lrig.isUp
|
|
});
|
|
if (colorChanged) this.game.outputColor();
|
|
});
|
|
};
|
|
|
|
Card.prototype.freeze = function () {
|
|
if (this.isEffectFiltered()) return false;
|
|
var event = {
|
|
card: this,
|
|
forzen: this.frozen
|
|
};
|
|
this.frozen = true;
|
|
this.game.frameStart();
|
|
this.onFreeze.trigger(event);
|
|
this.player.onSigniFreezed.trigger(event);
|
|
this.triggerOnAffect();
|
|
this.game.frameEnd();
|
|
return true;
|
|
};
|
|
|
|
Card.prototype.banishSigniAsyn = function (power,min,max,above) {
|
|
if (!isNum(min)) min = 0;
|
|
if (!isNum(max)) max = 1;
|
|
// <罗石 金铜>
|
|
if (this._OrichalciaNaturalStone && (min === 0) && (max === 1) && (power === 7000) && !above) {
|
|
power = 15000;
|
|
}
|
|
var cards = this.player.opponent.signis;
|
|
if (isNum(power)) {
|
|
cards = cards.filter(function (card) {
|
|
return above? (card.power >= power) : (card.power <= power);
|
|
},this);
|
|
}
|
|
if (!cards.length) return Callback.immediately(false);
|
|
return this.player.selectSomeTargetsAsyn(cards,min,max).callback(this,function (cards) {
|
|
return this.game.banishCardsAsyn(cards);
|
|
});
|
|
};
|
|
|
|
Card.prototype.decreasePowerAsyn = function (power,filter) {
|
|
return this.player.selectOpponentSigniAsyn(filter).callback(this,function (card) {
|
|
if (!card) return null;
|
|
this.game.tillTurnEndAdd(this,card,'power',-power);
|
|
return card;
|
|
});
|
|
};
|
|
|
|
Card.prototype.trashWhenTurnEnd = function () {
|
|
this.fieldData.trashWhenTurnEnd = true;
|
|
};
|
|
|
|
// 获得所谓的类别,在wx中,类别组成为"大类:小类"
|
|
// 其中,某些卡可以拥有多种小类,而"精元"没有小类.
|
|
// 对于"精武:武装:武器"类别,返回["武装","武器"]
|
|
// 对于"精元"类别,返回["精元"]
|
|
Card.prototype.getClasses = function () {
|
|
if (this.classes.length === 1) return this.classes.slice(0);
|
|
return this.classes.slice(1);
|
|
};
|
|
|
|
Card.prototype.activate = function () {
|
|
if (!this.zone.faceup) return;
|
|
this.game.output({
|
|
type: 'ACTIVATE',
|
|
content: {
|
|
card: this
|
|
}
|
|
});
|
|
};
|
|
|
|
Card.prototype.beSelectedAsTarget = function () {
|
|
this.game.output({
|
|
type: 'CARD_SELECTED',
|
|
content : {
|
|
card: this
|
|
}
|
|
});
|
|
};
|
|
|
|
Card.prototype.getTotalEnerCost = function (original) {
|
|
return this.player.getTotalEnerCost(this,original);
|
|
};
|
|
|
|
Card.prototype.getOpposingSigni = function () {
|
|
if (!inArr(this,this.player.signis)) return null;
|
|
var idx = 2 - this.player.signiZones.indexOf(this.zone);
|
|
return this.player.opponent.signiZones[idx].getActualCards()[0] || null;
|
|
};
|
|
|
|
Card.prototype.charmTo = function (signi) {
|
|
if (signi.charm) return;
|
|
signi.charm = this;
|
|
this.game.frameStart();
|
|
this.moveTo(signi.zone,{
|
|
faceup: false,
|
|
up: signi.isUp
|
|
});
|
|
this.game.frameEnd();
|
|
};
|
|
|
|
Card.prototype.getAccedCards = function () {
|
|
if (!inArr(this,this.player.signis)) return [];
|
|
return this.zone.cards.filter(function (card) {
|
|
return card.acceingCard === this;
|
|
},this);
|
|
};
|
|
|
|
Card.prototype.isAcced = function () {
|
|
return this.getAccedCards().length;
|
|
};
|
|
|
|
Card.prototype.canBeAcced = function () {
|
|
return !this.isAcced();
|
|
};
|
|
|
|
Card.prototype.acceTo = function (signi) {
|
|
if (!signi.canBeAcced()) return;
|
|
this.acceingCard = signi;
|
|
this.game.frameStart();
|
|
this.moveTo(signi.zone,{
|
|
faceup: true,
|
|
up: signi.isUp,
|
|
});
|
|
this.game.frameEnd();
|
|
};
|
|
|
|
Card.prototype.getStates = function () {
|
|
var states = [];
|
|
if (this.frozen) states.push('frozen');
|
|
if (this.charm) states.push('charm');
|
|
if (this.lancer) states.push('lancer');
|
|
if (this.doubleCrash) states.push('doubleCrash');
|
|
if (this.canNotAttack) states.push('locked');
|
|
if (this.assassin) states.push('assassin');
|
|
return states;
|
|
};
|
|
|
|
Card.prototype.triggerOnAffect = function (source) {
|
|
if (!source) {
|
|
source = this.game.getEffectSource();
|
|
}
|
|
if (!source) return;
|
|
this.onAffect.trigger({
|
|
card: this,
|
|
source: source
|
|
});
|
|
};
|
|
|
|
// Card.prototype.loseAbility = function () {
|
|
// this.canNotAttack = false;
|
|
// this.lancer = false;
|
|
// this.doubleCrash = false;
|
|
// this.assassin = false;
|
|
// this.canNotBeBanished = false;
|
|
|
|
// this.onMove.effects.length = 0;
|
|
// this.onEnterField.effects.length = 0;
|
|
// this.onLeaveField.effects.length = 0;
|
|
// this.onBurst.effects.length = 0;
|
|
// this.onAttack.effects.length = 0;
|
|
// this.onStartUp.effects.length = 0;
|
|
// this.onPowerChange.effects.length = 0;
|
|
// this.onBanish.effects.length = 0;
|
|
// this.onAffect.effects.length = 0;
|
|
// this.onDown.effects.length = 0;
|
|
// this.onBattle.effects.length = 0;
|
|
// };
|
|
|
|
Card.prototype.hasAbility = function () {
|
|
var flag = Card.abilityProps.some(function (prop) {
|
|
return this[prop];
|
|
},this);
|
|
if (flag) return true;
|
|
if (!this.withAbility) return false;
|
|
return !this.abilityLost;
|
|
};
|
|
|
|
Card.prototype.canBeBanished = function () {
|
|
if (this.canNotBeBanished) return false;
|
|
if (this.player.canNotBeBanished) return false;
|
|
if (this.canNotBeBanishedByEffect) {
|
|
var source = this.game.getEffectSource();
|
|
if (source && (source.player === this.player.opponent)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
Card.prototype.canGainAbility = function (source) {
|
|
let canNotGainAbility =
|
|
this.canNotGainAbility ||
|
|
(this.player.signiCanNotGainAbility && this.type === 'SIGNI') ||
|
|
(this.canNotGainAbilityBySelfPlayer && source && source.player === this.player);
|
|
return !canNotGainAbility;
|
|
};
|
|
|
|
global.Card = Card; |