webxoss-core/Card.js
2017-06-28 18:22:06 +08:00

1845 lines
No EOL
54 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.beforeUseAsyn = info.beforeUseAsyn || null;
// 生命迸发效果
this.burstEffects = this.cookEffect(info.burstEffect,'burst');
// 常时效果(因为内容会被修改,这里用 Object.create 创建不同的引用)
this.constEffects = (info.constEffects || []).map(function (constEffect) {
return Object.create(constEffect)
},this);
// 出场效果
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.betAdvancedAsyn = info.betAdvancedAsyn || null;
this.bettedCost = info.bettedCost || null;
this.rise = info.rise;
this.acce = !!info.acce;
this.acceingCard = null;
this.maxAcceCount = 1;
this.trap = info.trap || null;
this.shadow = false;
this.layer = !!info.layer;
this.layerMarks = [];
// 杂项
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.onAcced = new Timming(game);
// 附加的属性
this.canNotAttack = false;
this.mustAttack = 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.canNotBeBouncedByEffect = 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.powerDecreaseProtected = false; // PR-360
this._GustaftCenterBallista = false; // <弩中砲 グスタフト>
this.colorLost = false; // <侍从 ∞>
this.banishProtections = [];
this.upProtections = [];
this.canAttackAnySigniZone = false;
this.canAttackNearbySigniZone = false;
this.disableBurst = false;
// 注意hasAbility
}
Card.abilityProps = [
'canNotAttack',
'lancer',
'doubleCrash',
'tripleCrash',
'assassin',
'canNotBeBanished',
'canNotBeBanishedByEffect',
'canAttackAnySigniZone',
'canAttackNearbySigniZone',
'trap',
'layer',
];
var mixins = {
acce: {
activatedInEnerZone: true,
useCondition: function () {
return this.player.signis.some(function (signi) {
return signi.canBeAcced();
},this);
},
costChange: function () {
var obj = Object.create(this);
obj.costChange = null;
var flag = this.player.signis.some(function (signi) {
return signi.canBeAcced() && signi._2313;
},this);
if (flag) {
obj.costGreen -= 1;
if (obj.costGreen < 0) obj.costGreen = 0;
}
return obj;
},
costChangeAsyn: function () {
var cost = Object.create(this);
cost.costChange = null;
var reducedCost = Object.create(this);
reducedCost.costGreen -= 1;
if (reducedCost.costGreen < 0) reducedCost.costGreen = 0;
var cards = this.player.signis.filter(function (signi) {
return signi.canBeAcced();
},this);
if (!this.player.enoughCost(cost)) {
cards = cards.filter(function (signi) {
return signi._2313;
},this);
}
return this.player.selectTargetAsyn(cards).callback(this,function (card) {
this._data = card;
return (card && card._2313) ? reducedCost : cost;
});
},
actionAsyn: function () {
if (!this._data) return;
this.acceTo(this._data);
},
},
};
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('-');
if (eff.mixin) {
var mixin = mixins[eff.mixin];
for(var porp in mixin) {
effect[prop] = mixin[prop];
}
}
return effect;
},this);
};
Card.prototype.setupConstEffects = function () {
var layerMark = this.gid;
if (this.layer) {
this.game.addConstEffect({
source: this,
createTimming: this.onEnterField,
once: false,
destroyTimming: this.onLeaveField2,
action: function (set,add) {
this.player.signis.forEach(function (signi) {
if (signi.hasClass('怪異')) {
add(signi,'layerMarks',layerMark);
}
});
},
},true);
}
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) {
// 对于【自】效果,自动添加效果源和效果描述。
var options = Object.create(eff.effect);
if (!options.source) options.source = this;
if (!options.description) options.description = this.cid+'-'+'const-'+idx;
// CardInfo 中提供的是效果描述,而这里创建为效果实例,
// 并暴露在 card.constEffects[i].effect 上,供<験英の応援 #ゴウカク#>之类的效果获取。
var effect = this.game.newEffect(options);
eff.effect = effect;
if (isStr(eff.auto)) {
action = function (set,add) {
add(this,eff.auto,effect);
};
} else {
action = function (set,add) {
eff.auto.call(this,add,effect);
};
}
}
this.game.addConstEffect({
source: this,
createTimming: createTimming,
once: once,
destroyTimming: destroyTimming,
cross: !!eff.cross,
wisdom: eff.wisdom,
fixed: !!eff.fixed,
condition: eff.condition,
action: action,
},true);
if (eff.layer) {
this.player.mainDeck.cards.forEach(function (signi) {
if (!signi.hasClass('怪異')) return;
this.game.addConstEffect({
source: signi,
createTimming: signi.onEnterField,
once: true,
destroyTimming: signi.onLeaveField2,
cross: false,
wisdom: false,
fixed: false,
condition: function () {
return inArr(layerMark,this.layerMarks) && eff.condition.call(this);
},
},true);
},this);
}
},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;
if (arg.isSummon) arg.bottom = false;
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;
}
}
// "不能因对战对手的效果从场上返回手牌。"
if (card.player.canNotBeBouncedByEffect && source && (source.player !== card.player)) {
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),
power: card.power, // 移动前的力量
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.getSigni();
if (signi && card === signi.charm) {
moveEvent.isCharm = true;
signi.charm = null;
}
// 处理陷阱
if (card === card.zone.trap) {
card.zone.trap = null;
}
// 处理 acce
card.acceingCard = 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 (arg.isSummon) {
if (card.rise) {
// 被 rise 的卡“离场”
signi = zone.getSigni();
removeFromArr(signi,signi.player.signis);
signi.frozen = false;
signi.fieldData = {};
signi.fieldTurnData = {};
charm = signi.charm;
signi.charm = null;
signi.onLeaveField2.trigger({});
moveEvent.riseTarget = signi;
}
// 出场
enterFieldEvent = moveEvent;
zone.player.signis.push(card);
} else {
// 放置到 SIGNI 下方的卡
// 目前不需要处理
}
}
} 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 的).
// 但在客户端中,手牌里的卡即使逻辑上是背面朝上的,仍显示为正面朝上.
// PS: 增加了陷阱,陷阱是 chekcable 的,背面朝上,但己方玩家可见的。
card.player.output({
type: 'MOVE_CARD',
content: {
card: card,
pid: (card.isFaceup || arg.isTrap || 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 === leaveFieldEvent.oldZone.trap) return;
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.getNonTrapCards().concat(zone.trap || []);
oldZone.cards = zone.getNonTrapCards().concat(oldZone.trap || []);
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;
var attackedZones = [];
var index = this.player.signiZones.indexOf(this.zone);
var opposingZone = this.getOpposingZone();
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 zones = [];
if (this.canAttackAnySigniZone) {
zones = this.player.opponent.signiZones;
} else if (this.canAttackNearbySigniZone) {
if (index === 1) {
zones = this.player.opponent.signiZones;
} else {
zones = [
opposingZone,
this.player.opponent.signiZones[1],
];
}
}
if (!zones.length) return attackedZones.push(opposingZone);
return this.player.selectAsyn('TARGET',zones).callback(this,function (zone) {
attackedZones.push(zone);
// FIX ME: show selected zone.
var card = zone.getSigni();
if (card) card.beSelectedAsTarget();
});
})
.callback(this,function () {
// <具英の鋭針 #コンパス#>
if (!card._2278) return;
if (attackedZones[0] !== opposingZone) return;
var zones = [];
if (index === 1) {
zones = [this.player.opponent.signiZones[0],this.player.opponent.signiZones[2]];
} else {
zones = [this.player.opponent.signiZones[1]];
}
return this.player.selectOptionalAsyn('TARGET',zones).callback(this,function (zone) {
if (zone) attackedZones.push(zone);
// FIX ME: show selected zone.
var card = zone.getSigni();
if (card) card.beSelectedAsTarget();
});
}).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 () {
// 攻击时发动的起动效果 (<被侵犯的神判 安=Fifth>)
return opponent.useOnAttackActionEffectAsyn(event).callback(this,function () {
// 强制结束回合
if (this.game.phase.checkForcedEndTurn()) return;
// 攻击的卡不在场上,结束处理.
if (!inArr(card,player.signis)) return;
// 处理陷阱
var opposingSigni = card.getOpposingSigni();
opposingZone = card.getOpposingZone();
var trap = opposingZone.trap
return Callback.immediately().callback(this,function () {
if (opposingSigni || !trap) return;
return this.player.opponent.selectOptionalAsyn('LAUNCH',[trap]).callback(this,function (card) {
if (!card) return;
return card.handleTrapAsyn(event);
});
}).callback(this,function () {
// 攻击被无效,结束处理
if (event.prevented) return;
// 处理战斗或伤害
return Callback.forEach(attackedZones,function (attackedZone) {
// 强制结束回合
if (this.game.phase.checkForcedEndTurn()) return;
// 攻击的卡不在场上,结束处理.
if (!inArr(card,player.signis)) return;
var target = attackedZone.getSigni() || null;
var battle = true;
if (!target) battle = false;
if (card.assassin && (attackedZone === opposingZone)) battle = false;
if (battle) {
// 战斗
// 触发"进行战斗"时点
var onBattleEvent = {
card: card,
target: target,
};
return this.game.blockAsyn(this,function () {
this.game.frameStart();
card.onBattle.trigger(onBattleEvent);
target.onBattle.trigger(onBattleEvent);
this.game.frameEnd();
}).callback(this,function () {
// 此时,攻击的卡可能已不在场上
if (!inArr(card,player.signis)) return;
// 受攻击的卡也可能已不在场上
// 注意: 根据事务所QA,此时不击溃对方的生命护甲(即使有 lancer ). (<大剣 レヴァテイン>)
if (!inArr(target,target.player.signis)) return;
// 结算战斗伤害
if (card.power >= target.power) {
// 保存此时的 lancer 属性作为参考,
// 因为驱逐被攻击的 SIGNI 后,攻击侧的 lancer 可能改变.
var lancer = card.lancer;
return this.game.blockAsyn(this,function () {
return target.banishAsyn({attackingSigni: card}).callback(this,function (succ) {
if (succ && lancer) {
crashArg.lancer = lancer;
return opponent.crashAsyn(1,crashArg);
}
});
});
}
}).callback(this,function () {
if (onBattleEvent._1877 && inArr(target,target.player.signis)) {
return this.game.blockAsyn(onBattleEvent._1877,this,function () {
target.moveTo(target.player.mainDeck,{bottom: true});
});
}
});
} else {
// 伤害
if (attackedZone !== opposingZone) {
// 攻击非正面的区域
if (!this.game.getData(card.player,'damageWhenAttackSigniZone')) {
return;
}
}
if (event.wontBeDamaged || opponent.wontBeDamaged) return;
crashArg.damage = true;
if (opponent.lifeClothZone.cards.length) {
var count = 1;
if (this.doubleCrash) count = 2;
if (this.tripleCrash) count = 3;
crashArg.doubleCrash = this.doubleCrash;
return opponent.crashAsyn(count,crashArg);
} else {
if (card.game.win(player)) return Callback.never();
return;
}
}
},this);
});
});
}).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(false,this.rise).callback(this,function (zone) {
card.moveTo(zone,{
isSummon: true,
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 = 1;
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 () {
var zone = this.getOpposingZone();
if (!zone) return null;
return zone.getSigni() || null;
};
Card.prototype.getOpposingZone = function () {
if (!inArr(this,this.player.signis)) return null;
var idx = 2 - this.player.signiZones.indexOf(this.zone);
return this.player.opponent.signiZones[idx];
};
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.trapTo = function(zone) {
if (zone.trap) zone.trap.trash();
zone.trap = this;
this.moveTo(zone,{
faceup: false,
isTrap: true,
});
};
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.getAccedCards().length < this.maxAcceCount;
};
Card.prototype.acceTo = function (signi) {
if (!signi.canBeAcced()) return;
this.acceingCard = signi;
this.game.frameStart();
this.moveTo(signi.zone,{
faceup: true,
up: signi.isUp,
});
signi.onAcced.trigger();
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;
}
}
var count = this.game.getData(this,'banishProtectCount') || 0
if (count) {
this.game.setData(this,'banishProtectCount',count-1);
return false;
}
return true;
};
Card.prototype.canGainAbility = function (source) {
var canNotGainAbility =
this.canNotGainAbility ||
(this.player.signiCanNotGainAbility && this.type === 'SIGNI') ||
(this.canNotGainAbilityBySelfPlayer && source && source.player === this.player);
return !canNotGainAbility;
};
Card.prototype.isInfected = function() {
return this.zone.virus;
};
Card.prototype.handleTrapAsyn = function(event) {
// 注意 event 是可选的!
return Callback.immediately().callback(this,function () {
if (this.zone.cards.indexOf(this) === 0) {
this.faceup();
} else {
return this.player.opponent.showCardsAsyn([this]);
}
}).callback(this,function () {
if (!this.trap) return;
return this.game.blockAsyn(this,function () {
this.player.onTrapTriggered.trigger();
return this.trap.actionAsyn.call(this,event);
})
}).callback(this,function () {
this.trash();
});
};
Card.prototype.getBottomCards = function() {
if (!inArr(this,this.player.signis)) return;
return this.zone.cards.filter(function (card) {
return (card !== this) &&
(card !== this.charm) &&
(card !== this.zone.trap) &&
!card.acceingCard;
},this);
};
global.Card = Card;