mnml/client/src/utils.jsx
2019-05-30 20:36:13 +10:00

464 lines
13 KiB
JavaScript

const preact = require('preact');
const { Component } = require('preact');
const get = require('lodash/get');
const anime = require('animejs').default;
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 genAvatar = name => {
let hash = 0;
if (name.length === 0) return hash;
// Probs don't need to hash using the whole string
for (let i = 0; i < name.length; i += 1) {
const chr = name.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash = hash % 10000;
}
return `${hash}`;
};
function requestAvatar(name) {
const id = genAvatar(name);
const req = new Request(`/assets/molecules/${id}.svg`);
return fetch(req)
.then(res => res.text())
.then(svg => svg);
}
const animations = {};
function animateConstruct(id) {
if (animations[id]) return false;
animations[id] = true;
const duration = anime.random(2000, 18000);
const target = document.getElementById(id);
return anime({
targets: target,
translateX: () => anime.random(-20, 20),
translateY: () => anime.random(0, -40),
rotate: () => anime.random(-15, 15),
duration,
direction: 'alternate',
easing: 'linear',
loop: true,
complete: () => animations[id] = false,
});
}
function clearAnimation(id) {
animations[id] = false;
}
class ConstructAvatar extends Component {
render() {
return (
<img
id={this.props.id}
src={`/molecules/${genAvatar(this.props.name)}.svg`}
height="500"
onError={event => event.target.setAttribute('src', '/molecules/726.svg')}
/>
);
}
componentDidMount() {
animateConstruct(this.props.id);
}
componentWillUnmount() {
clearAnimation(this.props.id);
}
}
function instanceConstruct(name, id) {
// useEffect(() => {
// animateConstruct(id);
// return () => clearAnimation(id);
// });
setTimeout(() => animateConstruct(id), 50);
return (
<div
className="avatar"
id={id}
style={{'background-image': `url(/molecules/${genAvatar(name)}.svg)`}}
/>
);
}
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 resoConstructHealth(resolution, currentGame) {
if (!resolution) return false;
const modifyHealth = construct => {
if (construct.id !== resolution.target.id) return false; // not target
const [type, event] = resolution.event;
if (type === 'Damage') {
const { amount, mitigation, colour } = event;
construct.green_life.value -= amount;
if (colour === 'Red') {
construct.red_life.value -= mitigation;
}
if (colour === 'Blue') {
construct.blue_life.value -= mitigation;
}
}
if (type === 'Healing') {
const { amount } = event;
construct.green_life.value += amount;
}
if (type === 'Recharge') {
const { red, blue } = event;
construct.red_life.value += red;
construct.blue_life.value += blue;
}
return true;
};
currentGame.players.forEach(player => player.constructs.forEach(modifyHealth));
return true;
}
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) {
if (colour === 'Red') {
return 'red-damage';
}
if (colour === 'Blue') {
return 'blue-damage';
}
if (colour === '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) {
return 'green-damage';
}
}
if (type === 'Inversion') {
const { skill } = event;
}
if (type === 'Reflection') {
const { skill } = event;
}
if (type === 'Effect') {
const { skill, effect, duration } = event;
if (source && startSkill) return 'active-skill';
if (target && endSkill) return 'active-skill';
}
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 } = event;
}
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) return 'purple-damage';
if (red > 0) return 'red-damage';
if (blue > 0) return 'blue-damage';
}
}
if (type === 'Evasion') {
const { skill, evasion_rating } = event;
}
return '';
}
function getCombatSequence(event) {
if (!event) return false;
// Skip combat animations depending on event type, expandable in future
const dotTicks = ['DecayTick', 'CorruptionTick', 'TriageTick', 'SiphonTick', 'StrangleTick'];
if (['Skill', 'AoeSkill'].includes(event[0])) return ['START_SKILL', 'END_SKILL'];
if (['Immunity'].includes(event[0])) return ['START_SKILL', 'POST_SKILL'];
if (['Removal'].includes(event[0])) return ['POST_SKILL'];
if (['Healing'].includes(event[0])
&& (event[1].skill.includes('Slay')
|| event[1].skill.includes('SiphonTick')
|| event[1].skill.includes('Purify')
|| event[1].skill.includes('Sleep'))) return ['POST_SKILL'];
if (['Recharge'].includes(event[0])
&& (event[1].skill.includes('Reflect'))) return ['POST_SKILL'];
if (event[0] === 'Effect'
&& (['Ruin', 'Taunt', 'Strangling', 'Parry'].includes(event[1].skill)
|| (event[1].skill.includes('Decay') && event[1].effect === 'Wither'))) return ['POST_SKILL'];
if (['Power'].includes(event[0])
&& ((event[1].skill.includes('Chaos') && event[1].colour === 'Red')
|| event[1].skill.includes('Silence')
|| event[1].skill.includes('Snare'))) return ['POST_SKILL'];
if (['Ko'].includes(event[0])
|| (event[1].skill.includes('Throw') && event[1].effect === 'Vulnerable')) return ['POST_SKILL'];
if (dotTicks.includes(event[1].skill)) return ['END_SKILL', 'POST_SKILL'];
return ['START_SKILL', '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, amount, mitigation, colour } = event;
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>&nbsp;</span>;
// uncomment for double borders in vbox;
// if (v) {
// return <div>{v}</div>;
// }
// return;
}
const COLOURS = [
'#a52a2a',
'#1FF01F',
'#3498db',
];
module.exports = {
animateConstruct,
stringSort,
convertItem,
numSort,
genAvatar,
ConstructAvatar,
instanceConstruct,
requestAvatar,
eventClasses,
getCombatSequence,
getCombatText,
resoConstructHealth,
NULL_UUID,
STATS,
COLOURS,
};