webxoss-client/DeckEditor/Rules.js

583 lines
17 KiB
JavaScript
Raw Normal View History

2016-10-23 07:28:51 +02:00
'use strict';
function TextualRule (prop,map,exact) {
this.prop = prop;
this.map = map;
this.exact = exact;
}
TextualRule.prototype.parse = function (words) {
var keywords = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var keyword = this.parseWord(word);
if (!keyword) continue;
keywords.push(keyword);
words.splice(i,1);
i--;
}
var prop = this.prop;
var exact = this.exact;
return function filter (info) {
if (!keywords.length) return true;
return keywords.some(function (keyword) {
var value = info[prop].toLowerCase();
if (exact) {
return value === keyword;
} else {
return value.indexOf(keyword) !== -1;
}
});
};
};
TextualRule.prototype.parseWord = function (word) {
for (var keyword in this.map) {
var matchWords = this.map[keyword];
if (matchWords.some(function (matchWord) {
return word === Localize.traditionalize(matchWord);
},this)) {
return keyword;
}
}
return null;
};
///////////////////////////////////////////////////////////////
//
// 枚举型规则,包括:
// ColorRule,TypeRule,RarityRule
// 匹配例子:
// "白 LRIG SR 红"
// "白色 精灵 lr red"
//
///////////////////////////////////////////////////////////////
var ColorRule = new TextualRule('color',{
'colorless': ['colorless','无','无色','無','incolore'],
'white': ['white','白','白色','白','белая','bianco'],
'black': ['black','黑','黑色','黒','чёрная','nero'],
'red': ['red','红','红色','赤','красная','rosso'],
'blue': ['blue','蓝','蓝色','青','синяя','blu'],
'green': ['green','绿','绿色','緑','зелёная','verde']
},false);
var TypeRule = new TextualRule('cardType',{
'lrig': ['l','lrig','分身','ルリグ','идел'],
'signi': ['s','signi','精灵','シグニ','запись'],
'spell': ['spell','魔法','スペル','магия'],
'arts': ['arts','必杀','技艺','アーツ','умение'],
'resona': ['resona','共鸣','レゾナ','отголосок']
},true);
var RarityRule = new TextualRule('rarity',{
'c': ['c'],
'r': ['r'],
'lc': ['lc'],
'sr': ['sr'],
'lr': ['lr'],
'st': ['st'],
'pr': ['pr'],
'sp': ['sp']
},true);
///////////////////////////////////////////////////////////////
//
// 效果(能力)型规则:
// SkillRule
// 匹配例子:
// "常","常时","常时效果","常时能力"
// "※","爆发","爆发效果","迸发","迸发效果"
//
///////////////////////////////////////////////////////////////
var SkillRule = {};
SkillRule.parse = function (words) {
var effectProps = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var effectProp = this.parseWord(word);
if (!effectProp) continue;
effectProps.push(effectProp);
words.splice(i,1);
i--;
}
return function filter (info) {
if (!effectProps.length) return true;
info = CardInfo[info.cid];
return effectProps.some(function (effectProp) {
var effectTexts = info[effectProp];
return effectTexts && effectTexts.length;
});
};
};
SkillRule.parseWord = TextualRule.prototype.parseWord;
SkillRule.map = {
'constEffectTexts': [
'【常】','常','常时','常时效果','常時能力',
'【常】','常','常時','常時效果','常時能力',
'[constant]','const','constant',
'[постоянно]','постоянно'
],
'startUpEffectTexts': [
'【出】','出','出场','出场效果','出场能力',
'【出】','出','出現','出現效果','出現能力',
'[on-play]','[onplay]','on-play','onplay',
'[при вводе]','при вводе'
],
'actionEffectTexts': [
'【起】','起','起动','起动效果','起动能力',
'【起】','起','起動','起動效果','起動能力',
'[action]','action',
'[действие]','действие'
],
'burstEffectTexts': [
'【※】','※','爆发','迸发','爆发效果','迸发效果','生命爆发','生命迸发','生命爆发效果','生命迸发效果',
'ライフバースト','バースト',
'burst','lifeburst','lb',
'вспышка','жизненная вспышка'
]
};
///////////////////////////////////////////////////////////////
//
// 无迸发规则:
// NoBurstRule
// 匹配例子:
// "无爆发","noburst"
//
///////////////////////////////////////////////////////////////
var NoBurstRule = {};
NoBurstRule.parse = function (words) {
var matched = false;
for (var i = 0; i < words.length; i++) {
var word = words[i];
if (!this.parseWord(word)) continue;
matched = true;
words.splice(i,1);
i--;
}
return function filter (info) {
if (!matched) return true;
info = CardInfo[info.cid];
return (info.cardType === 'SIGNI' || info.cardType === 'SPELL') &&
(!info.burstEffectTexts || !info.burstEffectTexts.length);
};
};
NoBurstRule.parseWord = TextualRule.prototype.parseWord;
NoBurstRule.map = {
'burstEffectTexts': [
'无爆发','无迸发','无爆发效果','无迸发效果','无生命爆发','无生命迸发','无生命爆发效果','无生命迸发效果',
'noburst',
'ライフバースト-','バースト-',
'迸发-','爆发-',
'burst-','lifeburst-','lb-',
'вспышка-','жизненная вспышка-'
]
};
///////////////////////////////////////////////////////////////
//
// CROSS规则:
// CrossRule
// 匹配例子:
// "cross","交错","クロス"等
//
///////////////////////////////////////////////////////////////
var CrossRule = {};
CrossRule.parse = function (words) {
var matched = false;
for (var i = 0; i < words.length; i++) {
var word = words[i];
if (!this.parseWord(word)) continue;
matched = true;
words.splice(i,1);
i--;
}
return function filter (info) {
if (!matched) return true;
info = CardInfo[info.cid];
return (info.cardType === 'SIGNI') &&
(info.crossLeft || info.crossRight);
};
};
CrossRule.parseWord = TextualRule.prototype.parseWord;
CrossRule.map = {
'cross': [
'cross','交错','クロス',
'[cross]','>cross<','【cross】','【交错】','【クロス】','[связь]'
]
};
///////////////////////////////////////////////////////////////
//
// 时点规则:
// TimmingRule
// 匹配例子:
// "【主要阶段】","主要阶段"
// "【魔法切入】","魔法切入"
//
///////////////////////////////////////////////////////////////
var TimmingRule = {};
TimmingRule.parse = function (words) {
var timmings = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var timming = this.parseWord(word);
if (!timming) continue;
timmings.push(timming);
words.splice(i,1);
i--;
}
return function filter (info) {
if (!timmings.length) return true;
return timmings.some(function (timming) {
if (!info.timmings) return false;
return inArr(timming,info.timmings);
});
};
};
TimmingRule.parseWord = TextualRule.prototype.parseWord;
TimmingRule.map = {
'mainPhase': [
'主要阶段','【主要阶段】','主要',
'メインフェイズ','【メインフェイズ】',
'[mainphase]','mainphase','main',
'[основнаяфаза]','основнаяфаза','основная'
],
'attackPhase': [
'攻击阶段','【攻击阶段】','攻击',
'アタックフェイズ','【アタックフェイズ】',
'[attackphase]','attackphase','attack',
'[фазаатаки]','фазаатаки','атака'
],
'spellCutIn': [
'魔法切入','【魔法切入】','切入',
'スペルカットイン','【スペルカットイン】',
'[spellcut-in]','[cut-in]','[spellcutin]','[cutin]','spellcutin','cutin','cut',
'[ответнамагию]','[ответ]','ответнамагию','ответ'
]
};
///////////////////////////////////////////////////////////////
//
// 限定规则:
// LimitingRule
// 匹配例子:
// "小玉"
// "小玉+"
//
///////////////////////////////////////////////////////////////
var LimitingRule = {};
LimitingRule.parse = function (words) {
var matchedClasses = [];
var flagNoLimiting = false;
for (var i = 0; i < words.length; i++) {
var word = words[i];
var classes = [
'タマ','花代','ユヅキ','ピルルク','エルドラ','ミルルン','緑子',
'アン','ウリス','イオナ','ウムル','リメンバ','タウィル','サシェ',
'ミュウ','アイヤイ','アルフォウ','ハナレ'
];
for (var j = 0; j < classes.length; j++) {
var cls = classes[j];
var matched = false;
var localizedClass = Localize('class',cls).toLowerCase();
if (word === localizedClass) {
matched = true;
} else if (word === localizedClass+'+') {
matched = true;
flagNoLimiting = true;
}
if (matched) {
matchedClasses.push(cls);
words.splice(i,1);
i--;
break;
}
}
}
return function filter (info) {
if (!matchedClasses.length) return true;
if (info.cardType === 'LRIG') {
return matchedClasses.some(function (cls) {
if (!info.classes) return false;
return inArr(cls,info.classes);
},this);
}
if (!info.limiting) return flagNoLimiting;
return inArr(info.limiting,matchedClasses);
};
};
///////////////////////////////////////////////////////////////
//
// 类别规则:
// ClassRule
// 匹配例子:
// "精武"
// "武装"
//
///////////////////////////////////////////////////////////////
var ClassRule = {};
ClassRule.parse = function (words) {
var matchedClasses = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var classes = [
'精像','天使','悪魔','美巧','精武','アーム','ウェポン','遊具',
'毒牙','精羅','鉱石','宝石','植物','原子','宇宙','精械','電機',
'古代兵器','迷宮','精生','水獣','空獣','地獣','龍獣','凶蟲','精元'
];
for (var j = 0; j < classes.length; j++) {
var cls = classes[j];
if (word === Localize('class',cls).toLowerCase().replace(' ','')) {
matchedClasses.push(cls);
words.splice(i,1);
i--;
break;
}
}
}
return function filter (info) {
if (!matchedClasses.length) return true;
return matchedClasses.some(function (cls) {
if (!info.classes) return false;
return inArr(cls,info.classes);
},this);
};
};
///////////////////////////////////////////////////////////////
//
// 数值型规则,包括:
// PowerRule,LevelRule,LimitRule
// 匹配格式:
// 相等: "关键字:=数值"
// 大于: "关键字:>=数值" 或 "关键字:数值+"
// 小于: "关键字:<=数值" 或 "关键字:数值-"
// 范围: "关键字:min~max" 或 "关键字:min-max"
// (注: 上述的":"和"="可省略.)
// 匹配例子(每行等价):
// "力量>100","Power>100","Power:100+","Power100+"
// "等级=4","level=4","level4","lv4","lv.4"
// "界限>7 界限<9","limit7+ 界限9-","界限:7-9","界限7~9"
//
///////////////////////////////////////////////////////////////
function NumericRule (prop,keywords) {
this.prop = prop;
this.keywords = keywords;
}
NumericRule.prototype.parseExpression = function (str) {
var match;
// 相等
match = str.match(/^=*(\d+)$/);
if (match) {
var num = parseInt(match[1]);
return [num,num];
}
// 大于
match = str.match(/^>=*(\d+)\+?$/);
if (!match) match = str.match(/^(\d+)\+$/);
if (match) {
var num = parseInt(match[1]);
return [num,Infinity];
}
// 小于
match = str.match(/^<=*(\d+)\-?$/);
if (!match) match = str.match(/^(\d+)\-$/);
if (match) {
var num = parseInt(match[1]);
return [-Infinity,num];
}
// 范围
match = str.match(/^(\d+)[\-\~](\d+)$/);
if (match) {
var a = parseInt(match[1]);
var b = parseInt(match[2]);
return [a,b];
}
return null
};
NumericRule.prototype.parseWord = function (word) {
for (var i = 0; i < this.keywords.length; i++) {
var keyword = Localize.traditionalize(this.keywords[i]);
if (word.indexOf(keyword) === 0) {
word = word.slice(keyword.length).replace(/^:/,'');
return this.parseExpression(word);
}
}
return null;
};
NumericRule.prototype.parse = function (words) {
var ranges = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var range = this.parseWord(word);
if (!range) continue;
ranges.push(range);
words.splice(i,1);
i--;
}
var prop = this.prop;
return function filter (info) {
if (!ranges.length) return true;
if ((info.cardType === 'SPELL') || (info.cardType === 'ARTS')) {
return false;
}
return ranges.every(function (range) {
var value = info[prop];
var min = range[0];
var max = range[1];
return (value >= min) && (value <= max);
});
};
};
var PowerRule = new NumericRule('power',['力量','パワー','power','сила']);
var LevelRule = new NumericRule('level',['等级','レベル','level','lv.','lv','уровень','livello']);
var LimitRule = new NumericRule('limit',['界限','リミット','limite','limit','ограничение']);
///////////////////////////////////////////////////////////////
//
// 数字规则: NumberRule.
// 匹配说明:
// 力量和等级的关键字可以省略,此时用NumberRule匹配.
// 数字小于10,则匹配为等级,否则匹配为力量.
// 匹配例子:
// "1000+ 3-" // 等价于"power>1000 level<3"
// ">3 2000~3000" // 等价于"level>3 power:2000~3000"
//
///////////////////////////////////////////////////////////////
var NumberRule = new NumericRule('',['']);
NumberRule.parse = function (words) {
var ranges = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var range = this.parseWord(word);
if (!range) continue;
ranges.push(range);
words.splice(i,1);
i--;
}
return function filter (info) {
return ranges.every(function (range) {
if ((info.cardType === 'SPELL') || (info.cardType === 'ARTS')) return false;
function matchLevel (num) {
return !isFinite(num) || (num < 10);
}
var value = range.every(matchLevel)? info.level : info.power;
var min = range[0];
var max = range[1];
return (value >= min) && (value <= max);
});
};
};
///////////////////////////////////////////////////////////////
//
// 画师规则: IllustRule.
// 匹配例子:
// "画师:pop"
// "Illust:藤真拓哉"
//
///////////////////////////////////////////////////////////////
var IllustRule = {};
IllustRule.parseWord = function (word) {
var match = word.match(/illust:?(.+)/);
if (!match) match = word.match(/画师:?(.+)/);
if (!match) match = word.match(/畫師:?(.+)/);
if (!match) return null;
return match[1];
}
IllustRule.parse = function (words) {
var illusts = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var illust = this.parseWord(word);
if (!illust) continue;
illusts.push(illust);
words.splice(i,1);
i--;
}
return function filter (info) {
if (!illusts.length) return true;
return illusts.some(function (illust) {
var cardIllust = info.illust.toLowerCase();
return cardIllust.indexOf(illust) !== -1;
});
};
};
///////////////////////////////////////////////////////////////
//
// ID规则: WxidRule.
// 匹配例子:
// "WD01-001","wd01-001","wD01001" // 仅匹配"WD01-001"
// "wx02-01","WX0201" // 匹配"WX02-010"至"WX02-019"
// "wd03-","WD03" // 匹配"WD03"的所有卡
//
///////////////////////////////////////////////////////////////
var WxidRule = {};
WxidRule.parseWord = function (word) {
var match = word.match(/^(wx\d{2}|wd\d{2}|pr|sp\d{2})-?(\d{0,3})$/);
if (!match) return null;
return match[1] + '-' + match[2];
}
WxidRule.parse = function (words) {
var idLimits = [];
for (var i = 0; i < words.length; i++) {
var word = words[i];
var limit = this.parseWord(word);
if (!limit) continue;
idLimits.push(limit);
words.splice(i,1);
i--;
}
return function filter (info) {
if (!idLimits.length) return true;
return idLimits.some(function (limit) {
return info.wxid.toLowerCase().indexOf(limit) === 0;
});
};
};
///////////////////////////////////////////////////////////////
//
// 卡名规则: NameRule.
// 匹配例子:
// "喷流 知识" // 卡名同时包含"喷流"和"知识".
// "喷流|知识" // 卡名包含"喷流"或"知识".
// "喷流||知识" // 卡名包含"喷流"或"知识".
// "喷流||的||知识" // 卡名包含"喷流"或"知识"或"的".
//
///////////////////////////////////////////////////////////////
var NameRule = {};
NameRule.dotRegex =
/[\u00B7\u0387\u05BC\u2022\u2027\u2219\u22C5\u30FB\uFF0E\uFF65]/g;
NameRule.fullWidth =
'' +
'' +
'';
NameRule.halfWidth =
'0123456789=@' +
'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
'abcdefghijklmnopqrstuvwxyz';
NameRule.normalizeString = function (str) {
var chars = str.replace(this.dotRegex,'').split('');
chars.forEach(function (char,idx) {
var i = this.fullWidth.indexOf(char);
chars[idx] = this.halfWidth[i] || char;
},this);
return chars.join('').toLowerCase();
};
NameRule.parse = function (words) {
var wordSets = [];
words.forEach(function (word) {
wordSets.push(word.split(/\|+/));
},this);
words.length = 0;
var that = this;
return function filter (info) {
return wordSets.every(function (wordSet) {
if (!wordSet.length) return true;
return wordSet.some(function (word) {
var name = that.normalizeString(Localize.cardName(info));
var w = that.normalizeString(word);
return (name.indexOf(w) !== -1);
},this);
},this);
};
};