diff --git a/languages/en.js b/languages/en.js
index fccc946f..8e8a00f5 100644
--- a/languages/en.js
+++ b/languages/en.js
@@ -22,16 +22,40 @@
},
force_reload_data: `Force refresh data`,
skill_parse: {
- unknown_skill_type: ()=>`Unknown skill type.`,
- active_turns: (turns, activeElement)=> [activeElement,`, for ${turns} turns.`],
- random_skills: (skillList)=>[`Activates these random skills:`, skillList],
- delay: ()=> `Delays enemies' next move`,
- mass_attack: ()=> `plus Mass Attack`,
- leader_change: ()=> `Switches places with Leader Monster; use again to switch back`,
- no_skyfall: ()=> `No Skyfall Combos`,
+ skill: {
+ unknown: ()=>`Unknown skill type.`,
+ active_turns: (turns, activeElement)=> [activeElement,`, for ${turns} turns.`],
+ random_skills: (skillList)=>[`Activates these random skills:`, skillList],
+ delay: ()=> `Delays enemies' next move`,
+ mass_attack: ()=> `plus Mass Attack`,
+ leader_change: ()=> `Switches places with Leader Monster; use again to switch back`,
+ no_skyfall: ()=> `No Skyfall Combos`,
+ heal: (valueElement)=> [`Recovers `, valueElement, ` as HP`],
+ defense_break: (valueElement)=> [`Reduce enemies' defense by `, valueElement],
+ poison: (valueElement)=> [`Poisons all enemies, inflict damage equal to `, valueElement, ` every turn`],
+ time_extend: (valueElement)=> [`Orb move time `, valueElement],
+ },
value: {
- unknown_value: (type)=>`[ unknown value: ${type}]`,
- }
+ unknown: (type)=>`[ unknown value: ${type}]`,
+ mul_percent: (value) => `${value}%`,
+ mul_scale: (value) => `${value}x`,
+ const: (value, unit)=> `${value}${unit ? ` ${unit}` : ''}`,
+ mul_hp: (value, stats) => [stats,` ${value}x`],
+ mul_atk: (value, stats) => [stats,` ${value}x`],
+ mul_rcv: (value, stats) => [stats,` ${value}x`],
+ },
+ stats: {
+ unknown: (type)=>`[ unknown stats: ${type}]`,
+ maxhp: ()=>`Max HP`,
+ hp: ()=>`own HP`,
+ atk: ()=>`own ATK`,
+ rcv: ()=>`own RCV`,
+ teamatk: ()=>`Team ATK`,
+ teamrcv: ()=>`Team RCV`,
+ },
+ unit: {
+ seconds : `sec`,
+ },
},
}
diff --git a/languages/zh-CN.js b/languages/zh-CN.js
index e75fe4d4..d8193861 100644
--- a/languages/zh-CN.js
+++ b/languages/zh-CN.js
@@ -1,16 +1,40 @@
document.title = `智龙迷城${teamsCount}人队伍图制作工具`;
const localTranslating = {
skill_parse: {
- unknown_skill_type: ()=>`未知的技能类型`,
- active_turns: (turns, activeElement)=>[`${turns}回合内,`, activeElement],
- random_skills: (skillList)=>[`随机执行以下技能:`, skillList],
- delay: ()=> `将敌人的攻击延迟`,
- mass_attack: ()=> `所有攻击变为全体攻击`,
- leader_change: ()=> `将自身换为队长,再次使用则换回来`,
- no_skyfall: ()=> `天降的宝珠不会消除`,
+ skill: {
+ unknown: ()=>`未知的技能类型`,
+ active_turns: (turns, activeElement)=>[`${turns}回合内,`, activeElement],
+ random_skills: (skillList)=>[`随机执行以下技能:`, skillList],
+ delay: ()=> `将敌人的攻击延迟`,
+ mass_attack: ()=> `所有攻击变为全体攻击`,
+ leader_change: ()=> `将自身换为队长,再次使用则换回来`,
+ no_skyfall: ()=> `天降的宝珠不会消除`,
+ heal: (valueElement)=> [`回复`, valueElement, `的 HP`],
+ defense_break: (valueElement)=> [`敌方的防御力减少`, valueElement],
+ poison: (valueElement)=> [`使敌方全体中毒,每回合损失`, valueElement, `的 HP`],
+ time_extend: (valueElement)=> [`宝珠移动时间`, valueElement],
+ },
value: {
- unknown_value: (type)=>`[ 未知数值: ${type}]`,
- }
+ unknown: (type)=>`[ 未知数值: ${type}]`,
+ mul_percent: (value) => `${value}%`,
+ mul_scale: (value) => `x${value}倍`,
+ const: (value, unit)=> `${value}${unit ? ` ${unit}` : ''}`,
+ mul_hp: (value, stats) => [stats, `x${value}倍`],
+ mul_atk: (value, stats) => [stats, `x${value}倍`],
+ mul_rcv: (value, stats) => [stats, `x${value}倍`],
+ },
+ stats: {
+ unknown: (type)=>`[ 未知状态: ${type}]`,
+ maxhp: ()=>`最大HP`,
+ hp: ()=>`自身HP`,
+ atk: ()=>`自身攻击力`,
+ rcv: ()=>`自身回复力`,
+ teamatk: ()=>`队伍攻击力`,
+ teamrcv: ()=>`队伍回复力`,
+ },
+ unit: {
+ seconds : `秒`,
+ },
},
}
diff --git a/script-skill-parser.js b/script-skill-parser.js
index 7d9d22f9..fa6feda7 100644
--- a/script-skill-parser.js
+++ b/script-skill-parser.js
@@ -48,6 +48,14 @@ Attributes.orbs = function () {
];
}
+const SkillValue = {
+ isLess: function (value) {
+ if (value.kind === SkillValueKind.Percent) return value.value < 1;
+ if (value.kind === SkillValueKind.Constant) return value.value < 0;
+ return false;
+ }
+};
+
const SkillValueKind = {
Percent: 'mul',
Constant: 'const',
@@ -62,6 +70,8 @@ const SkillValueKind = {
xAwakenings: 'mul-awakenings',
};
+
+
const SkillPowerUpKind = {
Multiplier: 'mul',
ScaleAttributes: 'scale-attrs',
@@ -631,6 +641,21 @@ const parsers = {
},
};
+function _appendToFragment(fragment, arg){
+ if (Array.isArray(arg))
+ {
+ arg.forEach(element=>_appendToFragment(fragment, element));
+ }
+ else if (typeof arg == "string" || typeof arg == "number")
+ {
+ return fragment.appendChild(document.createTextNode(arg));
+ }
+ else
+ {
+ return fragment.appendChild(arg);
+ }
+}
+
function renderSkills(skills)
{
const ul = document.createElement("ul");
@@ -645,35 +670,25 @@ function renderSkills(skills)
function renderSkill(skill)
{
function appendToFragment(arg){
- if (Array.isArray(arg))
- {
- arg.forEach(element=>appendToFragment(element));
- }
- else if (typeof arg == "string")
- {
- return fragment.appendChild(document.createTextNode(arg));
- }
- else
- {
- return fragment.appendChild(arg);
- }
+ return _appendToFragment(fragment, arg);
}
- function createIcon(iconType){
+ function createIcon(iconType, className){
const idoc = document.createElement("icon");
- idoc.className = "icon-skill";
+ idoc.className = `icon-skill${className ? ` ${className}` : ''}`;
idoc.setAttribute("data-icon-type", iconType);
return idoc;
}
const fragment = document.createDocumentFragment();
if (typeof localTranslating == "undefined") return fragment;
- const tsp = localTranslating.skill_parse;
+ const tsps = localTranslating.skill_parse.skill;
+ const tspu = localTranslating.skill_parse.unit;
switch (skill.kind) {
case SkillKinds.Unknown: {
- appendToFragment(tsp.unknown_skill_type());
+ appendToFragment(tsps.unknown());
break;
}
case SkillKinds.ActiveTurns: {
- appendToFragment(tsp.active_turns(skill.turns, renderSkill(skill.skill)));
+ appendToFragment(tsps.active_turns(skill.turns, renderSkill(skill.skill)));
break;
}
case SkillKinds.RandomSkills: {
@@ -683,81 +698,50 @@ function renderSkill(skill)
const li = ul.appendChild(document.createElement("li"));
li.appendChild(renderSkills(subSkills));
});
- appendToFragment(tsp.random_skills(ul));
+ appendToFragment(tsps.random_skills(ul));
break;
}
case SkillKinds.Delay: {
appendToFragment(createIcon("delay"));
- appendToFragment(tsp.delay());
+ appendToFragment(tsps.delay());
break;
}
case SkillKinds.MassAttack: {
appendToFragment(createIcon("mass-attack"));
- appendToFragment(tsp.mass_attack());
+ appendToFragment(tsps.mass_attack());
break;
}
case SkillKinds.LeaderChange: {
appendToFragment(createIcon("leader-change"));
- appendToFragment(tsp.leader_change());
+ appendToFragment(tsps.leader_change());
break;
}
case SkillKinds.NoSkyfall: {
appendToFragment(createIcon("no-skyfall"));
- appendToFragment(tsp.no_skyfall());
+ appendToFragment(tsps.no_skyfall());
break;
}
case SkillKinds.Heal: {
- appendToFragment(renderValue(skill.value));
+ appendToFragment(createIcon("heal", "status-incr"));
+ appendToFragment(tsps.heal(renderValue(skill.value)));
break;
}
case SkillKinds.DefenseBreak: {
- appendToFragment(renderValue(skill.value));
+ appendToFragment(createIcon("defense-break"));
+ appendToFragment(tsps.defense_break(renderValue(skill.value)));
break;
}
case SkillKinds.Poison: {
- appendToFragment(renderValue(skill.value));
+ appendToFragment(createIcon("poison"));
+ appendToFragment(tsps.poison(renderValue(skill.value)));
break;
}
- /*
- case SkillKinds.Heal: {
- const { value } = skill as Skill.WithValue;
- return (
-
-
- {renderValue(value)}
-
- );
- }
- case SkillKinds.DefenseBreak: {
- const { value } = skill as Skill.WithValue;
- return (
-
-
- {renderValue(value)}
-
- );
- }
- case SkillKinds.Poison: {
- const { value } = skill as Skill.WithValue;
- return (
-
-
- {renderValue(value)}
-
- );
- }
case SkillKinds.TimeExtend: {
- const { value } = skill as Skill.WithValue;
- return (
-
-
- {renderValue(value, 'seconds')}
-
- );
+ appendToFragment(createIcon(SkillValue.isLess(skill.value) ? "status-time-decr" : "status-time-incr"));
+ appendToFragment(tsps.time_extend(renderValue(skill.value, tspu.seconds, { showsPlusSign:true, percentToScale:true })));
+ break;
}
+ /*
case SkillKinds.FollowAttack: {
const { value } = skill as Skill.WithValue;
return (
@@ -1108,32 +1092,193 @@ function renderSkill(skill)
return fragment;
};
+function renderStat(stat) {
+ function appendToFragment(arg){
+ return _appendToFragment(fragment, arg);
+ }
+ function newSpan(str , type)
+ {
+ const span = document.createElement("span");
+ span.className = "cardskill-stats";
+ span.setAttribute("data-cardskill-stats", type);
+ span.textContent = str;
+ return span;
+ }
+ const fragment = document.createDocumentFragment();
+ if (typeof localTranslating == "undefined") return fragment;
+ const tsps = localTranslating.skill_parse.stats;
+ console.log(stat)
+ if (tsps[stat])
+ appendToFragment(newSpan(tsps[stat](), stat));
+ else
+ appendToFragment(tsps.unknown(stat));
+ return fragment;
+}
+/*
+function renderAttrs(attrs: Attributes | Attributes[]) {
+ if (!Array.isArray(attrs))
+ attrs = [attrs];
+ return attrs.map(attr => {
+ if (attr >= Attributes.Heart)
+ return ;
+ return ;
+ });
+}
+
+function renderOrbs(attrs: Attributes | Attributes[]) {
+ if (!Array.isArray(attrs))
+ attrs = [attrs];
+ return attrs.map(attr => );
+}
+
+function renderTypes(types: Types | Types[]) {
+ if (!Array.isArray(types))
+ types = [types];
+ return types.map(type => );
+}
+
+
+function renderCondition(cond: SkillCondition) {
+ if (cond.hp) {
+ if (cond.hp.min === cond.hp.max)
+ return <>{renderStat('hp')} = {formatNumber(cond.hp.min * 100)}%>;
+ else if (cond.hp.min === 0)
+ return <>{renderStat('hp')} ≤ {formatNumber(cond.hp.max * 100)}%>;
+ else if (cond.hp.max === 1)
+ return <>{renderStat('hp')} ≥ {formatNumber(cond.hp.min * 100)}%>;
+ else
+ return <>{renderStat('hp')} ∈ [{formatNumber(cond.hp.min * 100)}%, {formatNumber(cond.hp.max * 100)}%]>;
+ } else if (cond.useSkill) {
+ return <>use skill>;
+ } else if (cond.multiplayer) {
+ return <>in multiplayer>;
+ } else if (cond.remainOrbs) {
+ return <>≤ {cond.remainOrbs.count} orbs remain>;
+ } else if (cond.exact) {
+ if (cond.exact.type === 'combo') {
+ return <>= {cond.exact.value} combos>;
+ } else if (cond.exact.type === 'match-length') {
+ return <>= {cond.exact.value} {cond.exact.attrs === 'enhanced' ? 'Enhanced' : renderAttrs(cond.exact.attrs)} orbs>;
+ }
+ } else if (cond.compo) {
+ return <>{cond.compo.type} [{cond.compo.ids.join()}] in team>;
+ }
+ return <>[ unknown condition ]>;
+}
+
+function renderPowerUp(powerUp: SkillPowerUp) {
+ function renderStats(hp: number, atk: number, rcv: number, mul = true) {
+ const operator = mul ? <>×> : <>+>;
+ let list: Array<['hp' | 'atk' | 'rcv', number]> = [['hp', hp], ['atk', atk], ['rcv', rcv]];
+ list = list.filter(([, value]) => value !== (mul ? 1 : 0));
+ if (list.length === 0) return null;
+
+ if (list.every(([, value]) => value === list[0][1])) {
+ return <>
+ {list.map(([name], i) => {i !== 0 && ', '}{renderStat(name)})}
+ {operator} {formatNumber(list[0][1])}
+ >;
+ } else {
+ return <>
+ {list.map(([name, value], i) => (
+
+ {i !== 0 ? '; ' : ''}
+ {renderStat(name)}
+ {operator} {formatNumber(value)}
+
+ ))}
+ >;
+ }
+ }
-function renderValue(_value, unit) {
+ switch (powerUp.kind) {
+ case SkillPowerUpKind.Multiplier: {
+ const { hp, atk, rcv } = powerUp as SkillPowerUp.Mul;
+ return renderStats(hp, atk, rcv);
+ }
+ case SkillPowerUpKind.ScaleAttributes: {
+ const { attrs, min, max, baseAtk, baseRcv, bonusAtk, bonusRcv } = powerUp as SkillPowerUp.ScaleAttrs;
+ return <>
+ ≥ {min} of [{renderAttrs(attrs)}] ⇒ {renderStats(1, baseAtk, baseRcv)}
+ {max !== min && <> for each ≤ {max} attributes: {renderStats(0, bonusAtk, bonusRcv, false)}>}
+ >;
+ }
+ case SkillPowerUpKind.ScaleCombos: {
+ const { min, max, baseAtk, baseRcv, bonusAtk, bonusRcv } = powerUp as SkillPowerUp.Scale;
+ return <>
+ ≥ {min} combos ⇒ {renderStats(1, baseAtk, baseRcv)}
+ {max !== min && <> for each ≤ {max} combos: {renderStats(0, bonusAtk, bonusRcv, false)}>}
+ >;
+ }
+ case SkillPowerUpKind.ScaleMatchAttrs: {
+ const { matches, min, max, baseAtk, baseRcv, bonusAtk, bonusRcv } = powerUp as SkillPowerUp.ScaleMultiAttrs;
+ return <>
+ ≥ {min} matches of [{matches.map((attrs, i) =>
+ {i !== 0 && ', '}{renderAttrs(attrs)}
+ )}] ⇒ {renderStats(1, baseAtk, baseRcv)}
+ {max !== min && <> for each ≤ {max} matches: {renderStats(0, bonusAtk, bonusRcv, false)}>}
+ >;
+ }
+ case SkillPowerUpKind.ScaleMatchLength: {
+ const { attrs, min, max, baseAtk, baseRcv, bonusAtk, bonusRcv } = powerUp as SkillPowerUp.ScaleAttrs;
+ return <>
+ ≥ {min} × {renderAttrs(attrs)} ⇒ {renderStats(1, baseAtk, baseRcv)}
+ {max !== min && <> for each ≤ {max} orbs: {renderStats(0, bonusAtk, bonusRcv, false)}>}
+ >;
+ }
+ case SkillPowerUpKind.ScaleCross: {
+ const { crosses } = powerUp as SkillPowerUp.ScaleCross;
+ return crosses.map(({ single, attr, mul }, i) =>
+ {i !== 0 && ', '}
+ {mul !== 1 && <>{renderStat('atk')} × {formatNumber(mul)} >}
+ {single ? 'when' : 'for each'} cross of {renderAttrs(attr)}
+ );
+ }
+ case SkillPowerUpKind.ScaleAwakenings: {
+ const { awakenings, value } = powerUp as SkillPowerUp.ScaleAwakenings;
+ return <>
+ {renderStat('atk')} × {formatNumber(value - 1)} for each {awakenings.map(id =>
+
+ )}
+ >;
+ }
+ default:
+ return <>[ unknown power up ]>;
+ }
+}
+*/
+function renderValue(_value, unit, option = {}) {
function appendToFragment(arg){
- if (Array.isArray(arg))
- {
- arg.forEach(element=>appendToFragment(element));
- }
- else if (typeof arg == "string")
- {
- return fragment.appendChild(document.createTextNode(arg));
- }
- else
- {
- return fragment.appendChild(arg);
- }
+ return _appendToFragment(fragment, arg);
}
const fragment = document.createDocumentFragment();
if (typeof localTranslating == "undefined") return fragment;
const tspv = localTranslating.skill_parse.value;
switch (_value.kind) {
- /*
+ case SkillValueKind.Percent: {
+ appendToFragment(option.percentToScale ?
+ tspv.mul_scale(_value.value.keepCounts()) :
+ tspv.mul_percent((_value.value * 100).keepCounts())
+ );
+ break;
+ }
+ case SkillValueKind.Constant: {
+ appendToFragment(tspv.const((option.showsPlusSign && _value.value >= 0 ? "+" : "") + _value.value.keepCounts(), unit));
+ break;
+ }
+ case SkillValueKind.xHP: {
+ appendToFragment(tspv.mul_hp(_value.value.keepCounts(), renderStat('hp')));
+ break;
+ }
+ case SkillValueKind.xATK: {
+ appendToFragment(tspv.mul_atk(_value.value.keepCounts(), renderStat('atk')));
+ break;
+ }
case SkillValueKind.xRCV: {
- _value.value
- const { value } = _value as SkillValue.Simple;
- return {formatNumber(value)} × {renderStat('rcv')};
+ appendToFragment(tspv.mul_rcv(_value.value.keepCounts(), renderStat('rcv')));
+ break;
}
+ /*
case SkillValueKind.Percent: {
const { value } = _value as SkillValue.Simple;
return {formatNumber(value * 100)}%;
@@ -1187,7 +1332,7 @@ function renderValue(_value, unit) {
*/
default: {
console.log(_value.kind, _value);
- appendToFragment(tspv.unknown_value(_value.kind));
+ appendToFragment(tspv.unknown(_value.kind));
}
}
return fragment;
diff --git a/style.css b/style.css
index 356c3620..40f6c279 100644
--- a/style.css
+++ b/style.css
@@ -2758,20 +2758,77 @@ table .orb-icon
vertical-align: bottom;
transform: scale(0.75);
margin: -4px;
+ position: relative;
+}
+.icon-skill.status-incr::after,
+.icon-skill.status-decr::after,
+.icon-skill.status-bind::after
+{
+ content: "";
+ display: inline-block;
+ width: 36px;
+ height: 36px;
+ background-image: url(images/icon-skills.fw.png);
+ background-repeat: no-repeat;
+ position: absolute;
+}
+.icon-skill.status-incr::after,
+.icon-skill.status-decr::after
+{
+ transform: scale(0.75);
+ margin: -4px;
+ right: -5px;
+ top: -3px;
+}
+.icon-skill.status-bind::after
+{
+ animation: hidden-visible-animate 0.5s infinite ease-in alternate;
+}
+.icon-skill.status-incr::after
+{
+ background-position-y:calc(-36px * 1);
+}
+.icon-skill.status-decr::after
+{
+ background-position-y:calc(-36px * 2);
+}
+.icon-skill.status-bind::after
+{
+ background-position-y:calc(-36px * 3);
}
.icon-skill[data-icon-type="delay"]
{
- background-position-y:calc(-36px * 0);
+ background-position-y:calc(-36px * 4);
}
.icon-skill[data-icon-type="mass-attack"]
{
- background-position-y:calc(-36px * 1);
+ background-position-y:calc(-36px * 5);
}
.icon-skill[data-icon-type="leader-change"]
{
- background-position-y:calc(-36px * 2);
+ background-position-y:calc(-36px * 6);
}
.icon-skill[data-icon-type="no-skyfall"]
{
- background-position-y:calc(-36px * 3);
+ background-position-y:calc(-36px * 7);
+}
+.icon-skill[data-icon-type="heal"]
+{
+ background-position-y:calc(-36px * 8);
+}
+.icon-skill[data-icon-type="defense-break"]
+{
+ background-position-y:calc(-36px * 9);
+}
+.icon-skill[data-icon-type="poison"]
+{
+ background-position-y:calc(-36px * 10);
+}
+.icon-skill[data-icon-type="status-time-decr"]
+{
+ background-position-y:calc(-36px * 11);
+}
+.icon-skill[data-icon-type="status-time-incr"]
+{
+ background-position-y:calc(-36px * 12);
}
\ No newline at end of file