433 lines
13 KiB
JavaScript
433 lines
13 KiB
JavaScript
const preact = require('preact');
|
|
const get = require('lodash/get');
|
|
|
|
const shapes = require('./components/shapes');
|
|
|
|
const stringSort = (k, desc) => {
|
|
if (desc) {
|
|
return (a, b) => {
|
|
if (!get(a, k)) return 1;
|
|
if (!get(b, k)) return -1;
|
|
return get(b, k).localeCompare(get(a, k));
|
|
};
|
|
}
|
|
return (a, b) => {
|
|
if (!get(a, k)) return 1;
|
|
if (!get(b, k)) return -1;
|
|
return get(a, k).localeCompare(get(b, k));
|
|
};
|
|
};
|
|
|
|
const numSort = (k, desc) => {
|
|
if (desc) {
|
|
return (a, b) => {
|
|
if (!get(a, k)) return 1;
|
|
if (!get(b, k)) return -1;
|
|
return get(b, k) - get(a, k);
|
|
};
|
|
}
|
|
return (a, b) => {
|
|
if (!get(a, k)) return 1;
|
|
if (!get(b, k)) return -1;
|
|
return get(a, k) - get(b, k);
|
|
};
|
|
};
|
|
|
|
const NULL_UUID = '00000000-0000-0000-0000-000000000000';
|
|
|
|
const STATS = {
|
|
RedPower: {
|
|
stat: 'red_power',
|
|
colour: 'red',
|
|
svg: shapes.circle,
|
|
},
|
|
GreenPower: {
|
|
stat: 'green_power',
|
|
colour: 'green',
|
|
svg: shapes.circle,
|
|
},
|
|
BluePower: {
|
|
stat: 'blue_power',
|
|
colour: 'blue',
|
|
svg: shapes.circle,
|
|
},
|
|
Speed: {
|
|
stat: 'speed',
|
|
colour: 'white',
|
|
svg: shapes.triangle,
|
|
},
|
|
RedLife: {
|
|
stat: 'red_life',
|
|
colour: 'red',
|
|
svg: shapes.square,
|
|
},
|
|
GreenLife: {
|
|
stat: 'green_life',
|
|
colour: 'green',
|
|
svg: shapes.square,
|
|
},
|
|
BlueLife: {
|
|
stat: 'blue_life',
|
|
colour: 'blue',
|
|
svg: shapes.square,
|
|
},
|
|
};
|
|
|
|
function eventClasses(resolution, construct) {
|
|
if (!resolution) return '';
|
|
const startSkill = resolution.stage === 'START_SKILL';
|
|
const endSkill = resolution.stage === 'END_SKILL';
|
|
const postSkill = resolution.stage === 'POST_SKILL';
|
|
const source = construct.id === resolution.source.id;
|
|
const target = construct.id === resolution.target.id;
|
|
// not involved at all. blur them
|
|
if (!(source || target)) return 'unfocus';
|
|
|
|
// not the target. just ignore for now
|
|
// if (construct.id !== resolution.target.id) return '';
|
|
|
|
const [type, event] = resolution.event;
|
|
|
|
if (type === 'Ko') {
|
|
if (target) return 'ko';
|
|
}
|
|
|
|
if (type === 'Disable') {
|
|
const { skill, disable } = event;
|
|
}
|
|
|
|
if (type === 'Immunity') {
|
|
const { skill, immunity } = event;
|
|
}
|
|
|
|
if (type === 'TargetKo') {
|
|
const { skill } = event;
|
|
}
|
|
|
|
if (type === 'Damage') {
|
|
const { skill, amount, mitigation, colour } = event;
|
|
// Highlight the flow of damage from source -> target
|
|
if (source && startSkill) return 'active-skill';
|
|
if (target && endSkill) return 'active-skill';
|
|
|
|
// Deal damage to construct and return effect
|
|
if (target && postSkill) {
|
|
construct.green_life.value = resolution.target.green;
|
|
if (colour === 'Red') {
|
|
construct.red_life.value = resolution.target.red;
|
|
return 'red-damage';
|
|
}
|
|
if (colour === 'Blue') {
|
|
construct.blue_life.value = resolution.target.blue;
|
|
return 'blue-damage';
|
|
}
|
|
if (colour === 'Green') {
|
|
construct.green_life.value = resolution.target.green;
|
|
return 'green-damage';
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (type === 'Healing') {
|
|
const { skill, amount, overhealing } = event;
|
|
if (source && startSkill) return 'active-skill';
|
|
if (target && endSkill) return 'active-skill';
|
|
if (target && postSkill) {
|
|
construct.green_life.value = resolution.target.green;
|
|
return 'green-damage';
|
|
|
|
}
|
|
}
|
|
|
|
if (type === 'Inversion') {
|
|
const { skill } = event;
|
|
}
|
|
|
|
if (type === 'Reflection') {
|
|
const { skill } = event;
|
|
}
|
|
|
|
if (type === 'Effect') {
|
|
const { skill, effect, duration, construct_effects: constructEffects } = event;
|
|
if (source && startSkill) return 'active-skill';
|
|
if (target && endSkill) return 'active-skill';
|
|
if (target && postSkill) construct.effects = constructEffects;
|
|
}
|
|
|
|
if (type === 'Skill') {
|
|
const { skill } = event;
|
|
// Highlight the flow of damage from source -> target
|
|
if (source && startSkill) return 'active-skill';
|
|
if (target && endSkill) return 'active-skill';
|
|
}
|
|
|
|
if (type === 'Removal') {
|
|
const { effect, construct_effects: constructEffects } = event;
|
|
if (source && startSkill) return 'active-skill';
|
|
if (target && endSkill) return 'active-skill';
|
|
if (target && postSkill) construct.effects = constructEffects;
|
|
}
|
|
|
|
if (type === 'Recharge') {
|
|
const { skill, red, blue } = event;
|
|
if (source && startSkill) return 'active-skill';
|
|
if (target && endSkill) return 'active-skill';
|
|
if (target && postSkill) {
|
|
if (red > 0 && blue > 0) {
|
|
construct.red_life.value = resolution.target.red;
|
|
construct.blue_life.value = resolution.target.blue;
|
|
return 'purple-damage';
|
|
}
|
|
if (red > 0) {
|
|
construct.red_life.value = resolution.target.red;
|
|
return 'red-damage';
|
|
}
|
|
if (blue > 0) {
|
|
construct.blue_life.value = resolution.target.blue;
|
|
return 'blue-damage';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (type === 'Evasion') {
|
|
const { skill, evasion_rating } = event;
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
function getCombatSequence(resolution) {
|
|
if (!resolution.event) return false;
|
|
if (resolution.event[0] === 'Inversion') return false;
|
|
if (resolution.event[0] === 'Skill') return ['START_SKILL', 'END_SKILL'];
|
|
if (resolution.event[0] === 'Ko') return ['POST_SKILL'];
|
|
|
|
switch (resolution.stages) {
|
|
case 1: return ['START_SKILL', 'DELAY', 'END_SKILL'];
|
|
case 2: return ['START_SKILL', 'POST_SKILL'];
|
|
case 3: return ['START_SKILL'];
|
|
case 4: return ['END_SKILL', 'POST_SKILL'];
|
|
case 5: return ['END_SKILL'];
|
|
case 6: return ['POST_SKILL'];
|
|
case 7: return false;
|
|
default: return ['START_SKILL', 'DELAY', 'END_SKILL', 'POST_SKILL'];
|
|
}
|
|
}
|
|
|
|
function getCombatText(construct, resolution) {
|
|
if (!resolution) return ['', ''];
|
|
|
|
|
|
const [type, event] = resolution.event;
|
|
const source = construct.id === resolution.source.id;
|
|
const target = construct.id === resolution.target.id;
|
|
const startSkill = resolution.stage === 'START_SKILL';
|
|
const endSkill = resolution.stage === 'END_SKILL';
|
|
const postSkill = resolution.stage === 'POST_SKILL';
|
|
|
|
if (type === 'Ko') {
|
|
if (postSkill && target) return ['KO!', ''];
|
|
}
|
|
|
|
if (type === 'Disable') {
|
|
const { skill, disable } = event;
|
|
if (startSkill && source) return [`${skill}`, ''];
|
|
if (endSkill && target) return [`${skill}`, ''];
|
|
if (postSkill && target) return [`${disable}`, ''];
|
|
}
|
|
|
|
if (type === 'Immunity') {
|
|
const { skill, immunity } = event;
|
|
if (startSkill && source) return [`${skill}`, ''];
|
|
if (endSkill && target) return [`${skill}`, ''];
|
|
if (postSkill && target) return ['IMMUNE', ''];
|
|
}
|
|
|
|
if (type === 'TargetKo') {
|
|
const { skill } = event;
|
|
if (startSkill && source) return [`${skill}`, ''];
|
|
if (endSkill && target) return [`${skill}`, ''];
|
|
}
|
|
|
|
if (type === 'Damage') {
|
|
const { skill, mitigation, colour } = event;
|
|
let { amount } = event;
|
|
if (colour === 'Green') amount *= -1;
|
|
const mitigationText = mitigation
|
|
? `(${mitigation})`
|
|
: '';
|
|
if (startSkill && source) return [`${skill}`, `${skill.toLowerCase()}-cast`];
|
|
if (endSkill && target) return [`${skill}`, `${skill.toLowerCase()}-hit`];
|
|
if (postSkill && target) return [`${amount} ${mitigationText}`, ''];
|
|
}
|
|
|
|
if (type === 'Healing') {
|
|
const { skill, amount, overhealing } = event;
|
|
if (startSkill && source) return [`${skill}`, ''];
|
|
if (endSkill && target) return [`${skill}`, ''];
|
|
if (postSkill && target) return [`${amount} (${overhealing} OH)`, ''];
|
|
}
|
|
|
|
if (type === 'Inversion') {
|
|
const { skill } = event;
|
|
if (startSkill && source) return [`${skill}`, ''];
|
|
if (endSkill && target) return [`${skill}`, ''];
|
|
if (postSkill && target) return ['INVERT', ''];
|
|
}
|
|
|
|
if (type === 'Reflection') {
|
|
const { skill } = event;
|
|
if (startSkill && source) return [`${skill}`, ''];
|
|
if (endSkill && target) return [`${skill}`, ''];
|
|
if (postSkill && target) return ['REFLECT', ''];
|
|
}
|
|
|
|
if (type === 'Effect') {
|
|
const { skill, effect, duration } = event;
|
|
if (startSkill && source) return [`${skill}`, ''];
|
|
if (endSkill && target) return [`${skill}`, ''];
|
|
if (postSkill && target) return [`+ ${effect} ${duration}T`, ''];
|
|
}
|
|
|
|
if (type === 'Recharge') {
|
|
const { skill, red, blue } = event;
|
|
if (startSkill && source) return [`${skill}`, ''];
|
|
if (endSkill && target) return [`${skill}`, ''];
|
|
if (postSkill && target) return [`+${red}R ${blue}B`, ''];
|
|
}
|
|
|
|
if (type === 'Skill' || type === 'AoeSkill') {
|
|
const { skill } = event;
|
|
if (startSkill && source) return [`${skill}`, ''];
|
|
if (endSkill && target) return [`${skill}`, ''];
|
|
}
|
|
|
|
if (type === 'Removal') {
|
|
const { effect } = event;
|
|
if (postSkill && target) return [`-${effect}`, ''];
|
|
}
|
|
|
|
|
|
return '';
|
|
}
|
|
|
|
function convertItem(v) {
|
|
if (['Red', 'Green', 'Blue'].includes(v)) {
|
|
return (
|
|
shapes.vboxColour(v.toLowerCase())
|
|
);
|
|
}
|
|
return v || <span> </span>;
|
|
// uncomment for double borders in vbox;
|
|
// if (v) {
|
|
// return <div>{v}</div>;
|
|
// }
|
|
// return;
|
|
}
|
|
|
|
const COLOURS = [
|
|
'#a52a2a',
|
|
'#1FF01F',
|
|
'#3498db',
|
|
];
|
|
|
|
const TARGET_COLOURS = {
|
|
BLUE: '#004bfe',
|
|
CYAN: '#27e7c0',
|
|
PURPLE: '#61008c',
|
|
YELLOW: '#fdfe02',
|
|
ORANGE: '#ff9215',
|
|
PINK: '#e766b6',
|
|
GRAY: '#9d9ea0',
|
|
LBLUE: '#87c6f2',
|
|
GREEN: '#166c4f',
|
|
BROWN: '#583108',
|
|
};
|
|
|
|
function randomPoints(numPoints, radius, width, height) {
|
|
const points = [];
|
|
let infCheck = 0;
|
|
while (points.length < numPoints) {
|
|
const c = [
|
|
Math.floor(Math.random() * (width - 2 * radius) + radius),
|
|
Math.floor(Math.random() * (height - 2 * radius) + radius),
|
|
];
|
|
let overlapping = false;
|
|
for (let j = 0; j < points.length; j += 1) {
|
|
const o = points[j];
|
|
const dx = c[0] - o[0];
|
|
const dy = c[1] - o[1];
|
|
const d = Math.floor(Math.sqrt(dx * dx + dy * dy));
|
|
if (d < radius) {
|
|
overlapping = true;
|
|
}
|
|
}
|
|
if (!overlapping) {
|
|
points.push(c);
|
|
infCheck = 0;
|
|
} else {
|
|
infCheck += 1;
|
|
if (infCheck > 50) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return points;
|
|
}
|
|
|
|
const removeTier = skill => {
|
|
if (skill.includes('Strike')) return 'Strike';
|
|
if (skill.includes('Heal')) return 'Heal';
|
|
if (skill.includes('Blast')) return 'Blast';
|
|
if (skill.includes('Chaos')) return 'Chaos';
|
|
if (skill.includes('Slay')) return 'Slay';
|
|
if (skill.includes('Siphon')) return 'Siphon';
|
|
|
|
if (skill.includes('Snare')) return 'Snare';
|
|
if (skill.includes('Purge')) return 'Purge';
|
|
if (skill.includes('Silence')) return 'Silence';
|
|
if (skill.includes('Curse')) return 'Curse';
|
|
if (skill.includes('Decay')) return 'Decay';
|
|
if (skill.includes('Invert')) return 'Invert';
|
|
|
|
if (skill.includes('Taunt')) return 'Taunt';
|
|
if (skill.includes('Triage')) return 'Triage';
|
|
if (skill.includes('Scatter')) return 'Scatter';
|
|
if (skill.includes('Haste')) return 'Haste';
|
|
if (skill.includes('Impurity')) return 'Impurity';
|
|
if (skill.includes('Amplify')) return 'Amplify';
|
|
|
|
if (skill.includes('Parry')) return 'Parry';
|
|
if (skill.includes('Purify')) return 'Purify';
|
|
if (skill.includes('Corrupt')) return 'Corrupt';
|
|
if (skill.includes('Clutch')) return 'Clutch';
|
|
if (skill.includes('Reflect')) return 'Reflect';
|
|
if (skill.includes('Recharge')) return 'Recharge';
|
|
|
|
if (skill.includes('Bash')) return 'Bash';
|
|
if (skill.includes('Sleep')) return 'Sleep';
|
|
if (skill.includes('Ruin')) return 'Ruin';
|
|
if (skill.includes('Throw')) return 'Throw';
|
|
if (skill.includes('Hex')) return 'Hex';
|
|
if (skill.includes('Banish')) return 'Banish';
|
|
|
|
return skill;
|
|
};
|
|
|
|
|
|
module.exports = {
|
|
stringSort,
|
|
convertItem,
|
|
numSort,
|
|
eventClasses,
|
|
getCombatSequence,
|
|
getCombatText,
|
|
NULL_UUID,
|
|
STATS,
|
|
COLOURS,
|
|
TARGET_COLOURS,
|
|
randomPoints,
|
|
removeTier,
|
|
};
|