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