Merge branch 'animations-test' into develop

This commit is contained in:
Mashy 2019-06-28 11:42:21 +10:00
commit 96a0d3f30c
31 changed files with 1503 additions and 2342 deletions

View File

@ -40,16 +40,24 @@ buy supporter pack
account account
balance balance
* incoming txs table / payments * accounting
id id
ref reference / hash etc
currency currency
amount credit
debit
* conversions
id
tx ref accounting
conversion rate conversion rate
converted value converted value
* mtx table * txs
* mtx table
id
item_name
* define $cost for ingame bits * define $cost for ingame bits
* pay for rerolls * pay for rerolls

20
client/animations.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<title>mnml.gg</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name=apple-mobile-web-app-capable content=yes>
<meta name=apple-mobile-web-app-status-bar-style content=black>
<meta name="description" content="mnml pvp tbs">
<meta name="author" content="ntr@smokestack.io">
<link rel="manifest" href="manifest.webmanifest">
<link rel="stylesheet" href="./node_modules/izitoast/dist/css/iziToast.min.css"></script>
<link rel="stylesheet" href="assets/styles/normalize.css">
<link rel="stylesheet" href="assets/styles/skeleton.css">
</head>
</head>
<body>
</body>
<script src="./animations.test.js"></script>
</html>

View File

@ -0,0 +1,9 @@
require('./assets/styles/styles.css');
require('./assets/styles/styles.mobile.css');
require('./assets/styles/instance.css');
require('./assets/styles/instance.mobile.css');
require('./assets/styles/game.css');
require('./assets/styles/anims.css');
// kick it off
require('./src/animations.test');

View File

@ -1,59 +0,0 @@
.anim-container {
position: absolute;
width: 100%;
min-height: 100%;
transform-style: preserve-3d;
}
.blast-cast {
position: absolute;
background: black;
opacity: .7;
border-radius: 50%;
}
@for $i from 1 through 100 {
@keyframes blast-cast-#{$i} {
100% {
transform: translate3d(-3em +random(200)/200, 0, 0);
}
}
}
@for $i from 1 through 100 {
.blast-cast:nth-child(#{$i}){
$size: random(30)+px;
height: $size;
width: $size;
transform: translate3d( (random(200) * 1px), (random(200) * 1px), (random(200) * 1px));
background: #15f4ee;
animation: blast-cast-#{$i} 0.7s;
}
}
.blast-hit {
position: absolute;
background: black;
opacity: .7;
border-radius: 50%;
}
@for $i from 1 through 100 {
@keyframes blast-hit-#{$i} {
100% {
transform: translate3d((random(200) * 1px), (random(200) * 1px), (random(200) * 1px));
}
}
}
@for $i from 1 through 100 {
.blast-hit:nth-child(#{$i}){
$size: random(30)+px;
height: $size;
width: $size;
transform: translate3d(-3em +random(200)/200, 0, 0);
animation: blast-hit-#{$i} 0.7s infinite;
background: #15f4ee;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -29,10 +29,13 @@
} }
.opponent .combat-text { .opponent .combat-text {
left: 40%;
top: 75%; top: 75%;
} }
.opponent .combat-anim {
top: 25%;
}
.opponent .game-construct { .opponent .game-construct {
align-items: flex-start; align-items: flex-start;
grid-template-rows: min-content min-content min-content minmax(min-content, 2fr); grid-template-rows: min-content min-content min-content minmax(min-content, 2fr);
@ -89,11 +92,11 @@
max-height: 10em; max-height: 10em;
} }
} }
/*
.resolving #targeting { .resolving #targeting {
opacity: 0; opacity: 0;
} }
*/
.game-construct .name { .game-construct .name {
width: 100%; width: 100%;
margin-bottom: 0.25em; margin-bottom: 0.25em;
@ -200,12 +203,27 @@
} }
.combat-text { .combat-text {
fill: whitesmoke;
font-size: 2em; font-size: 2em;
font-family: 'Jura'; font-family: 'Jura';
position: absolute; position: absolute;
top: 50%; object-fit: contain;
left: 50%; top: 15%;
}
.combat-text svg {
height: 7em;
}
.combat-anim {
font-size: 2em;
font-family: 'Jura';
position: absolute;
object-fit: contain;
top: 15%;
}
.combat-anim svg {
height: 7em;
} }
.game-construct.active-skill { .game-construct.active-skill {

View File

@ -178,10 +178,14 @@ a {
svg { svg {
fill: none; fill: none;
stroke: whitesmoke; stroke: whitesmoke;
stroke-width: 0; stroke-width: 5px;
height: 2em; height: 2em;
} }
.skill-animation {
height: 5em;
}
.team .avatar { .team .avatar {
object-fit: contain; object-fit: contain;
max-width: 100%; max-width: 100%;

View File

@ -3,7 +3,6 @@ require('./assets/styles/styles.mobile.css');
require('./assets/styles/instance.css'); require('./assets/styles/instance.css');
require('./assets/styles/instance.mobile.css'); require('./assets/styles/instance.mobile.css');
require('./assets/styles/game.css'); require('./assets/styles/game.css');
require('./assets/styles/anims.css');
// kick it off // kick it off
require('./src/app'); require('./src/app');

View File

@ -5,6 +5,7 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"start": "parcel index.html --host 0.0.0.0 --port 40080 --no-source-maps", "start": "parcel index.html --host 0.0.0.0 --port 40080 --no-source-maps",
"anims": "parcel animations.html --host 0.0.0.0 --port 40080 --no-source-maps",
"build": "rm -rf dist && parcel build index.html && cp -r assets/molecules/ dist/", "build": "rm -rf dist && parcel build index.html && cp -r assets/molecules/ dist/",
"scss": "node-sass --watch assets/scss -o assets/styles", "scss": "node-sass --watch assets/scss -o assets/styles",
"lint": "eslint --fix --ext .jsx src/", "lint": "eslint --fix --ext .jsx src/",
@ -13,6 +14,7 @@
"author": "", "author": "",
"license": "UNLICENSED", "license": "UNLICENSED",
"dependencies": { "dependencies": {
"anime": "^0.1.2",
"animejs": "^3.0.1", "animejs": "^3.0.1",
"async": "^2.6.2", "async": "^2.6.2",
"borc": "^2.0.3", "borc": "^2.0.3",

View File

@ -1,11 +0,0 @@
const preact = require('preact');
function animationDivs(classes) {
switch (classes) {
case 'blast-cast': return Array.from({ length: 100 }).map(j => <div key={j} class="blast-cast"></div>);
case 'blast-hit': return Array.from({ length: 100 }).map(j => <div key={j} class="blast-hit"></div>);
default: return <div>&nbsp;</div>;
}
}
module.exports = { animationDivs };

View File

@ -0,0 +1,16 @@
const preact = require('preact');
const TrippyTriangle = require('./components/svgs/trippy.triangle');
const Amplify = require('./components/svgs/amplify');
const Hex = require('./components/svgs/hex');
const Animations = () => (
<main>
<TrippyTriangle />
<Amplify />
<Hex />
</main>
);
// eslint-disable-next-line
preact.render(<Animations />, document.body);

View File

@ -0,0 +1,92 @@
const preact = require('preact');
const AttackCharge = require('./anims/attack.charge');
const Blast = require('./anims/blast');
const Heal = require('./anims/heal');
const Strike = require('./anims/strike');
const Chaos = require('./anims/chaos');
const Slay = require('./anims/slay');
const Siphon = require('./anims/siphon');
const SiphonTick = require('./anims/siphon.tick');
// const Test = require('./anims/test');
const { removeTier } = require('../utils');
const colours = {
red: '#a52a2a',
green: '#1FF01F',
blue: '#3498db',
purple: '#A25AC1',
yellow: '#d1c86a',
cyan: '#6AD1BF',
white: '#FFFFFF',
};
function animations(props) {
const { combatTextClass, combatText, stage, player, construct } = props;
const anim = text => {
if (stage === 'START_SKILL') {
const skill = removeTier(text);
switch (skill) {
// Attack Base
case 'Attack': return <AttackCharge id={construct.id} team={player} colour={colours.white}/>;
case 'Strike': return <AttackCharge id={construct.id} team={player} colour={colours.red}/>;
case 'Heal': return <AttackCharge id={construct.id} team={player} colour={colours.green}/>;
case 'Blast': return <AttackCharge id={construct.id} team={player} colour={colours.blue}/>;
case 'Chaos': return <AttackCharge id={construct.id} team={player} colour={colours.purple}/>;
case 'Slay': return <AttackCharge id={construct.id} team={player} colour={colours.yellow}/>;
case 'Siphon': return <AttackCharge id={construct.id} team={player} colour={colours.cyan}/>;
// Stun Base
// Block Base
// Buff Base
// Debuff Base
default: return false;
}
} else if (stage === 'END_SKILL') {
const skill = removeTier(text);
switch (skill) {
case 'Attack': return <Strike id={construct.id} stage={stage} team={player} colour={colours.white}/>;
case 'Blast': return <Blast id={construct.id} stage={stage} team={player}/>;
case 'Strike': return <Strike id={construct.id} stage={stage} team={player} colour={colours.red}/>;
case 'Chaos': return <Chaos id={construct.id} team={player} colour={colours.purple}/>;
case 'Slay': return <Slay id={construct.id} team={player} colour={colours.yellow}/>;
case 'Heal': return <Heal id={construct.id} stage={stage} team={player} colour={colours.red}/>;
case 'Siphon': return <Siphon id={construct.id} stage={stage} team={player} colour={colours.red}/>;
case 'SiphonTick': return <SiphonTick id={construct.id} stage={stage} team={player} colour={colours.red}/>;
default: return false;
}
}
return false;
};
if (combatText) {
const combatAnim = anim(combatText);
if (combatAnim) {
return (
<div class='combat-anim'>
{combatAnim}
</div>
);
}
return (
<div class={combatTextClass}>
{combatText}
</div>
);
}
/*
return (
<div class={combatTextClass}>
<Test id={construct.id} stage={stage} team={player}/>
</div>
);
*/
return (<div>&nbsp;</div>);
}
module.exports = animations;

View File

@ -0,0 +1,60 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
const charge = require('../svgs/charge');
const { TIMES } = require('../../constants');
const { randomPoints } = require('../../utils');
class AttackCharge extends Component {
constructor(props) {
super();
this.team = props.team;
this.colour = props.colour;
const points = randomPoints(7, 50, { x: 0, y: 0, width: 300, height: 400 });
this.charges = points.map(coord => charge(coord[0], coord[1], 6, this.colour));
}
render() {
return (
<svg
class={'skill-anim'}
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 400">
{this.charges}
</svg>
);
}
componentDidMount() {
const charges = document.querySelectorAll('#charge');
anime.set(charges, { fill: 'none' });
if (!this.team) {
anime.set('.skill-anim', {
rotate: 180,
});
} else {
anime.set('.skill-anim', {
rotate: 0,
});
}
this.anim = anime({
targets: charges,
fill: this.colour,
delay: anime.stagger(5, {
start: 100,
easing: 'linear',
}),
loop: false,
easing: 'easeInQuad',
});
}
componentWillUnmount() {
}
}
module.exports = AttackCharge;

View File

@ -0,0 +1,84 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
const dagger = require('../svgs/dagger');
const { TIMES } = require('../../constants');
const duration = TIMES.START_SKILL;
class Attack extends Component {
constructor(props) {
super();
this.props = props;
}
render() {
return (
<svg
class={'attack-anim'}
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 400">
{dagger(10, 250, 40, 150)}
{dagger(50, 250, 40, 150)}
{dagger(90, 250, 40, 150)}
{dagger(130, 250, 40, 150)}
{dagger(170, 250, 40, 150)}
{dagger(210, 250, 40, 150)}
{dagger(250, 250, 40, 150)}
</svg>
);
}
componentDidMount() {
let y = 0;
const daggers = document.querySelectorAll('.attack-anim .dagger');
if (this.props.stage === 'START_SKILL') {
anime.set(daggers, {
y: 250,
});
y = -150;
if (!this.props.team) {
anime.set('.attack-anim', {
rotate: 180,
});
} else {
anime.set('.attack-anim', {
rotate: 0,
});
}
}
if (this.props.stage === 'END_SKILL') {
anime.set(daggers, {
y: 400,
});
y = -150;
if (!this.props.team) {
anime.set('.attack-anim', {
rotate: 0,
});
} else {
anime.set('.attack-anim', {
rotate: 180,
});
}
}
anime({
targets: daggers,
delay: anime.stagger(250, {
start: 250,
grid: [1, 7],
from: 'center',
easing: 'linear',
}),
y,
duration,
});
}
}
module.exports = Attack;

View File

@ -0,0 +1,97 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
const { TIMES } = require('../../constants');
const { randomPoints } = require('../../utils');
const duration = TIMES.START_SKILL;
function projectile(x, y, radius, colour) {
return (
<circle
cx={x}
cy={y}
r={radius}
fill="url(#grad1)"
stroke-width="2"
stroke={colour}
id="projectile"
filter="url(#explosion)"
/>
);
}
class AttackCharge extends Component {
constructor(props) {
super();
this.team = props.team;
// this.colour = props.colour;
this.colour = '#00aabb';
const points = randomPoints(8, 60, { x: 0, y: 0, width: 300, height: 400 });
this.charges = points.map(coord => projectile(coord[0], coord[1], 20, this.colour));
}
render() {
return (
<svg
class={'skill-anim'}
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 400">
// {this.charges}
<defs>
<radialGradient id="grad1" cx="50%" cy="50%" r="70%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:rgb(255,255,255);stop-opacity:0.6" />
<stop offset="100%" style={`stop-color:${this.colour};stop-opacity:1`} />
</radialGradient>
</defs>
<filter id="explosion">
<feGaussianBlur stdDeviation="4"/>
<feTurbulence type="turbulence" baseFrequency="0.05" numOctaves="3" result="turbulence"/>
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="1" xChannelSelector="A" yChannelSelector="A"/>
</filter>
{this.charges}
</svg>
);
}
componentDidMount() {
if (!this.props.team) {
anime.set('.skill-anim', {
rotate: Math.random() * 180 + 90,
});
} else {
anime.set('.skill-anim', {
rotate: Math.random() * 180 + 270,
});
}
anime.set('.skill-anim', {
translateY: -200,
});
anime.set('#explosion feDisplacementMap', {
scale: 1,
});
anime({
targets: '.skill-anim',
translateY: 0,
loop: false,
duration: (duration * 1 / 2),
easing: 'easeInQuad',
});
anime({
targets: '#explosion feDisplacementMap',
scale: 100,
loop: false,
duration,
easing: 'easeInQuad',
});
}
}
module.exports = AttackCharge;

View File

@ -0,0 +1,114 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
const { TIMES } = require('../../constants');
const { randomPoints } = require('../../utils');
const duration = TIMES.START_SKILL;
function projectile(x, y, radius, colour) {
return (
<circle
cx={x}
cy={y}
r={radius}
fill="url(#grad1)"
stroke-width="2"
stroke={colour}
id="projectile"
filter="url(#explosion)"
/>
);
}
class AttackCharge extends Component {
constructor(props) {
super();
this.team = props.team;
// this.colour = props.colour;
this.colour = '#A25AC1';
const points = randomPoints(7, 30, { x: 0, y: 0, width: 300, height: 100 });
this.charges = points.map(coord => projectile(coord[0], coord[1], 14, this.colour));
}
render() {
return (
<svg
class={'skill-anim'}
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 400">
// {this.charges}
<defs>
<radialGradient id="grad1" cx="50%" cy="0%" r="85%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:#dba9a9;stop-opacity:0.6" />
<stop offset="100%" style={`stop-color:${this.colour};stop-opacity:1`} />
</radialGradient>
</defs>
<filter id="explosion">
<feGaussianBlur stdDeviation="4"/>
<feTurbulence type="turbulence" baseFrequency="0.01" numOctaves="3" result="turbulence"/>
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="1" xChannelSelector="A" yChannelSelector="A"/>
</filter>
{this.charges}
</svg>
);
}
componentDidMount() {
if (!this.props.team) {
anime.set('.skill-anim', {
rotate: Math.random() * 180 + 90,
});
} else {
anime.set('.skill-anim', {
rotate: Math.random() * 180 + 270,
});
}
const projectiles = document.querySelectorAll('#projectile');
projectiles.forEach(proj => {
const colour = Math.random() >= 0.5 ? '#a52a2a' : '#3498db';
anime.set(proj, {
stroke: colour,
});
});
anime.set('.skill-anim', {
translateY: -200,
translateX: 0,
});
anime.set('#explosion feDisplacementMap', {
scale: 1,
});
anime({
targets: '.skill-anim',
translateY: 0,
translateX: 0,
loop: false,
duration: (duration * 1 / 2),
easing: 'easeInQuad',
});
anime({
targets: '#explosion feDisplacementMap',
scale: 75,
loop: false,
delay: (duration * 2 / 3),
duration: (duration * 1 / 3),
easing: 'easeInQuad',
});
projectiles.forEach(proj => anime({
targets: proj,
cx: Math.random() * 250 + 25,
cy: Math.random() * 300 + 50,
duration: (duration * 2 / 3),
easing: 'easeInQuad',
}));
}
}
module.exports = AttackCharge;

View File

@ -0,0 +1,106 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
const { TIMES } = require('../../constants');
const { randomPoints } = require('../../utils');
const duration = TIMES.START_SKILL;
function projectile(x, y, radius, colour) {
return (
<circle
cx={x}
cy={y}
r={radius}
fill="url(#grad1)"
stroke-width="2"
stroke={colour}
id="projectile"
filter="url(#explosion)"
/>
);
}
class Heal extends Component {
constructor(props) {
super();
this.team = props.team;
// this.colour = props.colour;
this.colour = '#1FF01F';
const points = randomPoints(15, 30, { x: 0, y: 0, width: 300, height: 400 });
this.charges = points.map(coord => projectile(coord[0], coord[1], 14, this.colour));
}
render() {
return (
<svg
class={'skill-anim'}
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 400">
// {this.charges}
<defs>
<radialGradient id="grad1" cx="50%" cy="0%" r="85%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:#dba9a9;stop-opacity:0.6" />
<stop offset="100%" style={`stop-color:${this.colour};stop-opacity:1`} />
</radialGradient>
</defs>
<filter id="explosion">
<feGaussianBlur stdDeviation="4"/>
<feTurbulence type="turbulence" baseFrequency="0.01" numOctaves="3" result="turbulence"/>
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="1" xChannelSelector="A" yChannelSelector="A"/>
</filter>
{this.charges}
</svg>
);
}
componentDidMount() {
if (!this.props.team) {
anime.set('.skill-anim', {
rotate: Math.random() * 180 + 90,
});
} else {
anime.set('.skill-anim', {
rotate: Math.random() * 180 + 270,
});
}
anime.set('.skill-anim', {
translateY: -200,
translateX: 0,
});
anime.set('#explosion feDisplacementMap', {
scale: 100,
});
anime({
targets: '.skill-anim',
translateY: 0,
translateX: 0,
loop: false,
duration: (duration * 1 / 2),
easing: 'easeInQuad',
});
anime({
targets: '#explosion feDisplacementMap',
scale: 1,
loop: false,
duration: (duration * 1 / 2),
easing: 'easeInQuad',
});
anime({
targets: '#projectile',
cx: 150,
cy: 200,
delay: (duration * 1 / 2),
duration: (duration * 1 / 2),
easing: 'easeInQuad',
});
}
}
module.exports = Heal;

View File

@ -0,0 +1,45 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
const { TIMES } = require('../../constants');
const duration = TIMES.START_SKILL;
class AttackCharge extends Component {
render() {
return (
<svg
class={'skill-anim'}
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 400">
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
</filter>
<circle id="siphon" r="140" cx="150" cy="150" stroke="#3498db" stroke-width="2.5%" filter="url(#blur)"/>
</svg>
);
}
componentDidMount() {
anime.set('#siphon', {
r: '140',
stroke: '#3498db',
});
anime({
targets: '#siphon',
keyframes: [
{ r: '110', stroke: '#1FF01F' },
{ r: '80', stroke: '#1FF01F' },
{ r: '50', stroke: '#3498db' },
{ r: '20', stroke: '#3498db' },
],
duration,
easing: 'easeInCubic',
});
}
}
module.exports = AttackCharge;

View File

@ -0,0 +1,110 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
const { TIMES } = require('../../constants');
const duration = TIMES.START_SKILL;
function projectile(x, y, radius, colour) {
return (
<circle
cx={x}
cy={y}
r={radius}
fill="url(#grad1)"
stroke-width="0.1"
stroke={colour}
id="projectile"
/>
);
}
class SiphonTick extends Component {
constructor(props) {
super();
this.team = props.team;
// this.colour = props.colour;
this.colour = '#3498db';
const points = new Array(15).fill(0);
this.charges = points.map(() => projectile(150, 150, 7, '#1FF01F'));
}
render() {
return (
<svg
class={'skill-anim'}
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 400">
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
</filter>
<circle id="siphon" r="20" cx="150" cy="150" stroke="#3498db" stroke-width="2.5%" filter="url(#blur)"/>
<defs>
<radialGradient id="grad1" cx="50%" cy="0%" r="85%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:#3498db;stop-opacity:0.4" />
<stop offset="100%" style={'stop-color:#1FF01F;stop-opacity:1'} />
</radialGradient>
</defs>
<filter id="explosion">
<feGaussianBlur stdDeviation="4"/>
<feTurbulence type="turbulence" baseFrequency="0.001" numOctaves="3" result="turbulence"/>
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="1" xChannelSelector="A" yChannelSelector="A"/>
</filter>
{this.charges}
</svg>
);
}
componentDidMount() {
if (!this.props.team) {
anime.set('.skill-anim', {
rotate: Math.random() * 180 + 90,
});
} else {
anime.set('.skill-anim', {
rotate: Math.random() * 180 + 270,
});
}
anime.set('#projectile', {
cx: 150,
cy: 150,
});
anime.set('#siphon', {
r: '80',
stroke: '#3498db',
});
anime({
targets: '#siphon',
keyframes: [
{ r: '50', stroke: '#3498db' },
{ r: '20', stroke: '#3498db' },
{ r: '0', stroke: '#3498db' },
],
duration: duration * 2 / 3,
easing: 'easeInCubic',
});
const projectiles = document.querySelectorAll('#projectile');
projectiles.forEach(proj => {
anime({
targets: proj,
cx: Math.random() * 250 + 25,
cy: Math.random() * 200 - 100,
delay: (Math.random() * duration * 1 / 2),
duration,
easing: 'easeInQuad',
});
});
}
}
module.exports = SiphonTick;

View File

@ -0,0 +1,137 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
const { TIMES } = require('../../constants');
const { randomPoints } = require('../../utils');
const duration = TIMES.START_SKILL;
function projectile(x, y, radius, colour) {
return (
<circle
cx={x}
cy={y}
r={radius}
fill="url(#grad1)"
stroke-width="2"
stroke={colour}
id="projectile"
/>
);
}
function sword(colour) {
return (
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
<g transform="rotate(90, 640, 360) translate(0,720) scale(0.1,-0.1)" fill={colour} stroke={colour} filter="url(#explosion)" id="sword">
<path d="M4110 5071 c-294 -119 -436 -228 -457 -350 -4 -25 -7 -230 -6 -454 2 -380 1 -408 -15 -402 -9 4 -48 16 -87 26 -160 41 -215 44 -815 41 -719 -4 -1088 13 -1475 69 -270 38 -384 75 -468 148 -67 58 -111 75 -193 74 -104 -1 -204 -59 -264 -153 -130 -202 -159 -698 -59 -998 62 -188 174 -282 335 -282 77 0 114 14 174 67 87 77 198 114 456 152 386 56 724 73 1459 70 676 -2 728 1 937 68 15 4 16 -29 15 -419 -2 -500 -4 -492 93 -582 45 -41 99 -74 217 -133 87 -43 182 -86 212 -96 172 -55 197 16 78 226 -114 202 -160 347 -187 587 -16 144 -27 371 -18 376 4 3 299 0 655 -5 2030 -31 2627 -35 3953 -23 903 8 1433 17 1665 28 l340 16 450 79 c248 44 491 88 540 99 234 50 777 180 796 190 18 10 19 14 7 26 -7 8 -198 57 -423 109 -346 80 -483 106 -885 170 -417 66 -517 78 -815 100 -312 24 -424 27 -1370 36 -1039 11 -2548 7 -3645 -10 -327 -5 -748 -11 -934 -13 l-339 -3 2 75 c3 130 22 331 41 441 25 147 71 276 142 404 34 61 70 127 80 147 21 42 24 108 6 126 -23 23 -100 13 -198 -27z"/>
</g>
</svg>
);
}
class AttackCharge extends Component {
constructor(props) {
super();
this.team = props.team;
// this.colour = props.colour;
this.colour = '#a52a2a';
const points = new Array(30).fill(0);
this.charges = points.map(() => projectile(150, 420, 7, '#1FF01F'));
}
render() {
return (
<svg
class={'skill-anim'}
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 400">
<defs>
<radialGradient id="grad1" cx="50%" cy="0%" r="85%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:#dba9a9;stop-opacity:0.6" />
<stop offset="100%" style={`stop-color:${this.colour};stop-opacity:1`} />
</radialGradient>
</defs>
<filter id="explosion">
<feGaussianBlur stdDeviation="4"/>
<feTurbulence type="turbulence" baseFrequency="0.001" numOctaves="3" result="turbulence"/>
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="1" xChannelSelector="A" yChannelSelector="A"/>
</filter>
{sword(this.colour)}
{this.charges}
</svg>
);
}
componentDidMount() {
if (!this.props.team) {
anime.set('.skill-anim', {
rotate: Math.random() * 180 + 90,
});
} else {
anime.set('.skill-anim', {
rotate: Math.random() * 180 + 270,
});
}
anime.set('.skill-anim', {
translateY: -400,
translateX: 0,
});
anime.set('#explosion feDisplacementMap', {
scale: 100,
});
anime.set('#sword', {
fill: this.colour,
stroke: this.colour,
});
anime.set('#projectile', {
cx: 150,
cy: 420,
});
anime({
targets: '.skill-anim',
translateY: 0,
translateX: 0,
loop: false,
duration: (duration * 1 / 2),
easing: 'easeInQuad',
});
anime({
targets: '#explosion feDisplacementMap',
scale: 10000,
loop: false,
delay: (duration * 1 / 2),
duration: (duration * 1 / 2),
easing: 'easeInQuad',
});
anime({
targets: '#sword',
fill: '#1FF01F',
stroke: '#1FF01F',
delay: (duration * 3 / 4),
});
const projectiles = document.querySelectorAll('#projectile');
projectiles.forEach(proj => {
anime({
targets: proj,
cx: Math.random() * 250 + 25,
cy: Math.random() * 200 - 100,
delay: (duration * 2 / 3),
duration: (duration * 1 / 3),
easing: 'easeInQuad',
});
});
}
}
module.exports = AttackCharge;

View File

@ -0,0 +1,105 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
const { TIMES } = require('../../constants');
const duration = TIMES.START_SKILL;
function laser(dimensions, colour) {
const { x, y, length } = dimensions;
return (
<rect
width="18"
height={length}
x={x}
y={y}
fill="url(#grad1)"
stroke-width="2"
stroke={colour}
filter="url(#explosion)"
/>
);
}
class Strike extends Component {
constructor(props) {
super();
this.team = props.team;
// this.colour = props.colour;
this.colour = props.colour;
const coord = [0, 100, 200];
const points = coord.map(pos => ({
x: pos + Math.random() * 80,
y: 50 + Math.random() * 100,
length: 150 + Math.random() * 100,
}));
this.charges = points.map(pos => laser(pos, this.colour));
}
render() {
return (
<svg
class={'skill-anim'}
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 400">
// {this.charges}
<defs>
<radialGradient id="grad1" cx="50%" cy="0%" r="85%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:#dba9a9;stop-opacity:0.6" />
<stop offset="100%" style={`stop-color:${this.colour};stop-opacity:1`} />
</radialGradient>
</defs>
<filter id="explosion">
<feGaussianBlur stdDeviation="4"/>
<feTurbulence type="turbulence" baseFrequency="0.01" numOctaves="3" result="turbulence"/>
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="1" xChannelSelector="A" yChannelSelector="A"/>
</filter>
{this.charges}
</svg>
);
}
componentDidMount() {
if (!this.props.team) {
anime.set('.skill-anim', {
rotate: Math.random() * 180 + 90,
});
} else {
anime.set('.skill-anim', {
rotate: Math.random() * 180 + 270,
});
}
anime.set('.skill-anim', {
translateY: -200,
translateX: 0,
});
anime.set('#explosion feDisplacementMap', {
scale: 1,
});
anime({
targets: '.skill-anim',
translateY: 0,
translateX: 0,
loop: false,
duration: (duration * 1 / 2),
easing: 'easeInQuad',
});
anime({
targets: '#explosion feDisplacementMap',
scale: 200,
loop: false,
delay: (duration * 1 / 4),
duration,
easing: 'easeInQuad',
});
}
}
module.exports = Strike;

View File

@ -0,0 +1,112 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
const { TIMES } = require('../../constants');
const { randomPoints } = require('../../utils');
const duration = TIMES.START_SKILL;
function projectile(x, y, radius, colour) {
return (
<circle
cx={x}
cy={y}
r={radius}
fill="url(#grad1)"
stroke-width="0.1"
stroke={colour}
id="projectile"
/>
);
}
class AttackCharge extends Component {
constructor(props) {
super();
this.team = props.team;
// this.colour = props.colour;
this.colour = '#3498db';
const points = new Array(15).fill(0);
this.charges = points.map(() => projectile(150, 150, 7, '#1FF01F'));
}
render() {
return (
<svg
class={'skill-anim'}
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 400">
<filter id="blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
</filter>
<circle id="siphon" r="20" cx="150" cy="150" stroke="#3498db" stroke-width="2.5%" filter="url(#blur)"/>
<defs>
<radialGradient id="grad1" cx="50%" cy="0%" r="85%" fx="50%" fy="50%">
<stop offset="0%" style="stop-color:#3498db;stop-opacity:0.4" />
<stop offset="100%" style={'stop-color:#1FF01F;stop-opacity:1'} />
</radialGradient>
</defs>
<filter id="explosion">
<feGaussianBlur stdDeviation="4"/>
<feTurbulence type="turbulence" baseFrequency="0.001" numOctaves="3" result="turbulence"/>
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="1" xChannelSelector="A" yChannelSelector="A"/>
</filter>
{this.charges}
</svg>
);
}
componentDidMount() {
if (!this.props.team) {
anime.set('.skill-anim', {
rotate: Math.random() * 45 + 90,
});
} else {
anime.set('.skill-anim', {
rotate: Math.random() * 45 + 270,
});
}
anime.set('#projectile', {
cx: 150,
cy: 150,
});
anime.set('#siphon', {
r: '80',
stroke: '#3498db',
});
anime({
targets: '#siphon',
keyframes: [
{ r: '50', stroke: '#3498db' },
{ r: '20', stroke: '#3498db' },
{ r: '0', stroke: '#3498db' },
],
duration: duration * 2 / 3,
easing: 'easeInCubic',
});
const projectiles = document.querySelectorAll('#projectile');
projectiles.forEach(proj => {
anime({
targets: proj,
cx: Math.random() * 250 + 25,
cy: Math.random() * 200 - 100,
delay: (Math.random() * duration * 1 / 2),
duration,
easing: 'easeInQuad',
});
});
}
}
module.exports = AttackCharge;

View File

@ -5,9 +5,10 @@ const range = require('lodash/range');
const actions = require('../actions'); const actions = require('../actions');
const { STATS, eventClasses, getCombatText } = require('../utils'); const { STATS, eventClasses, getCombatText } = require('../utils');
const { ConstructAvatar } = require('./construct'); const { ConstructAvatar } = require('./construct');
const { animationDivs } = require('../animations'); const animations = require('./animations');
const shapes = require('./shapes'); const shapes = require('./shapes');
const SkillBtn = require('./skill.btn'); const SkillBtn = require('./skill.btn');
const addState = connect( const addState = connect(
@ -39,9 +40,10 @@ const addState = connect(
activeSkill, activeSkill,
selectSkillTarget, selectSkillTarget,
}; };
}, }
); );
function GameConstruct(props) { function GameConstruct(props) {
const { const {
i, i,
@ -75,9 +77,9 @@ function GameConstruct(props) {
const [combatText, combatClass] = getCombatText(construct, resolution); const [combatText, combatClass] = getCombatText(construct, resolution);
const combatTextClass = `combat-text ${combatClass}`; const combatTextClass = `combat-text ${combatClass}`;
const combatTextEl = combatText
? <div class={combatTextClass}>{combatText}</div> const stage = resolution ? resolution.stage : false;
: null; const combatInfo = animations({ combatTextClass, combatText, stage, player, construct });
const effects = construct.effects.length const effects = construct.effects.length
? construct.effects.map(c => <div key={c.effect}>{c.effect} - {c.duration}T</div>) ? construct.effects.map(c => <div key={c.effect}>{c.effect} - {c.duration}T</div>)
@ -86,12 +88,6 @@ function GameConstruct(props) {
const playerTeam = game.players.find(t => t.id === account.id); const playerTeam = game.players.find(t => t.id === account.id);
const playerTeamIds = playerTeam.constructs.map(c => c.id); const playerTeamIds = playerTeam.constructs.map(c => c.id);
const anim = (
<div class="anim-container">
{animationDivs(combatClass)}
</div>
);
return ( return (
<div <div
onClick={() => selectSkillTarget(construct.id)} onClick={() => selectSkillTarget(construct.id)}
@ -101,7 +97,7 @@ function GameConstruct(props) {
{crypSkills} {crypSkills}
<div class="stats"> {stats} </div> <div class="stats"> {stats} </div>
<ConstructAvatar name={construct.name} id={construct.id} /> <ConstructAvatar name={construct.name} id={construct.id} />
{combatTextEl} {combatInfo}
<div class="effects"> {effects} </div> <div class="effects"> {effects} </div>
</div> </div>
); );

View File

@ -0,0 +1,58 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
class Amplify extends Component {
constructor() {
super();
this.id = Math.random();
}
render() {
const path = 'M0,100 C100,-200 100,400 200,100';
return (
<svg
class='skill-animation blue'
version="1.1"
id="amplify"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 200 200">
<filter id="ampFilter">
<feTurbulence type="turbulence" baseFrequency="0.4" numOctaves="2" result="turbulence" style="transform: scale(1);"></feTurbulence>
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="2" xChannelSelector="R" yChannelSelector="G"></feDisplacementMap>
</filter>
<path filter='url("#ampFilter")' d={path} />
</svg>
);
}
componentDidMount() {
const altPath = 'M0,100 C100,400 100,-200 200,100';
anime({
targets: '#amplify path',
d: [{ value: altPath }],
stroke: ['#a52a2a', '#3498db'],
duration: 1000,
loop: true,
direction: 'alternate',
easing: 'easeInOutSine',
});
anime({
targets: ['#ampFilter feTurbulence', '#ampFilter feDisplacementMap'],
baseFrequency: 0.15,
scale: 4,
loop: true,
direction: 'alternate',
easing: 'easeInOutExpo',
});
}
componentWillUnmount() {
}
}
module.exports = Amplify;

View File

@ -0,0 +1,42 @@
const preact = require('preact');
module.exports = function charge(x, y, height, colour) {
return (
<svg>
<polygon
points={`
${x},${y - height}
${x - 2 * height},${y + 2 * height}
${x + 2 * height},${y + 2 * height}
`}
stroke={colour}
stroke-width="2"
id="charge"
/>
<polyline
points={`
${x + 2 * height},${y + 2 * height}
${x + 3.5 * height},${y + height}
${x},${y - 4 * height}
${x - 3.5 * height},${y + height}
${x - 2 * height},${y + 2 * height}
`}
stroke={colour}
stroke-width="2"
id="charge"
/>
<polyline
points={`
${x + 3.5 * height},${y + height}
${x + 5 * height},${y}
${x},${y - 7 * height}
${x - 5 * height},${y}
${x - 3.5 * height},${y + height}
`}
stroke={colour}
stroke-width="2"
id="charge"
/>
</svg>
);
};

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,58 @@
const preact = require('preact');
const { Component } = require('preact');
const anime = require('animejs').default;
// shamelessly lifted from teh anime docs
// https://animejs.com/documentation/#svgAttr
class Hex extends Component {
render() {
return (
<svg
class='hex-anim skill-animation blue'
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 128 128">
<filter id="hexFilter">
<feTurbulence type="turbulence" baseFrequency="0" numOctaves="2" result="turbulence" style="transform: scale(1);"></feTurbulence>
<feDisplacementMap in2="turbulence" in="SourceGraphic" scale="0" xChannelSelector="R" yChannelSelector="G"></feDisplacementMap>
</filter>
<polygon
points="64 124.94732621931382 8.574 96.00354944613788 8.62269130211947 32.03166105954991 64 4 119.37730869788052 32.03166105954991 119.426 96.00354944613788 " style="filter: url(&quot;#hexFilter&quot;); transform: scale(1);"
fill="currentColor"
></polygon>
</svg>
);
}
componentDidMount() {
anime({
targets: ['.hex-anim'],
scale: [1, 2],
loop: true,
direction: 'alternate',
easing: 'easeInOutExpo',
});
anime({
targets: ['.hex-anim polygon'],
points: '64 69.88600002141976 8.574 99.91603773440568 62.29420564057706 66.93105659089863 64 3.916037734405676 65.70579435942294 66.93105659089863 119.426 99.91603773440568',
loop: true,
direction: 'alternate',
easing: 'easeInOutExpo',
});
anime({
targets: ['#hexFilter feTurbulence', '#hexFilter feDisplacementMap'],
baseFrequency: 0.05,
scale: 15,
loop: true,
direction: 'alternate',
easing: 'easeInOutExpo',
});
}
}
module.exports = Hex;

View File

@ -1,36 +1,58 @@
const preact = require('preact'); const preact = require('preact');
const { Component } = require('preact');
class TrippyTriangle extends Component {
constructor() {
super();
this.state = { pct: 0, delta: 0 };
this.progress = this.progress.bind(this);
}
render() {
const { pct, delta } = this.state;
module.exports = function triangle(colours) {
if (colours.length === 1) {
return ( return (
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" > <svg
<polygon class={colours[0]} points="10,190 100,10 190,190"/> style={{
<polygon class={colours[0]} points="40,160 100,10 190,190"/> transform: `rotate3d(0, 1, 0, ${delta * 0.2}rad) translate(0px, ${-7.5 * delta}px)`,
<polygon class={colours[0]} points="70,130 100,10 190,190"/> }}
class='skill-animation'
version="1.1"
id="Layer_1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 300 300">
<g>
<polygon class='blue' points={`0,190 100,${pct} 190,190`}/>
<polygon class='blue' points={`40,170 100,${pct} 160,170`}/>
<polygon class='blue' points={`70,150 100,${pct} 130,150`}/>
</g>
</svg> </svg>
); );
} }
return ( progress() {
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 202.69 177.29" >
<clipPath id="firstColour">
<rect x="0" y="0" width="100" height="200" />
</clipPath>
<clipPath id="secondColour">
<rect x="100" y="0" width="100" height="200" />
</clipPath>
<polygon clip-path="url(#firstColour)" class={colours[0]} points="1.77,176.25 100.7,2.08 200.9,176.25 "/> requestAnimationFrame(this.progress);
<polygon clip-path="url(#firstColour)" class={colours[0]} points="23.99,163.23 100.78,28.05 178.54,163.23 "/> this.now = window.performance.now();
<polygon clip-path="url(#firstColour)" class={colours[0]} points="46.21,150.21 100.85,54.03 156.18,150.21 "/> const elapsed = this.now - this.then;
<polygon clip-path="url(#firstColour)" class={colours[0]} points="68.43,137.19 100.92,80 133.82,137.19 "/> if (elapsed > this.fpsInterval) {
<polygon clip-path="url(#firstColour)" class={colours[0]} points="90.65,124.17 100.99,105.98 111.46,124.17 "/> this.then = this.now - (elapsed % this.fpsInterval);
const delta = this.state.delta + Math.PI * 0.04;
const pct = 75 + 15 * Math.sin(delta);
this.setState({ pct, delta });
}
}
<polygon clip-path="url(#secondColour)" class={colours[1]} points="1.77,176.25 100.7,2.08 200.9,176.25 "/> componentDidMount() {
<polygon clip-path="url(#secondColour)" class={colours[1]} points="23.99,163.23 100.78,28.05 178.54,163.23 "/> const fps = 30;
<polygon clip-path="url(#secondColour)" class={colours[1]} points="46.21,150.21 100.85,54.03 156.18,150.21 "/> this.fpsInterval = 1000 / fps;
<polygon clip-path="url(#secondColour)" class={colours[1]} points="68.43,137.19 100.92,80 133.82,137.19 "/> this.then = window.performance.now();
<polygon clip-path="url(#secondColour)" class={colours[1]} points="90.65,124.17 100.99,105.98 111.46,124.17 "/> requestAnimationFrame(this.progress);
</svg> }
);
}; componentWillUnmount() {
// clearAnimation(this.props.id);
}
}
module.exports = TrippyTriangle;

View File

@ -84,9 +84,18 @@ class TargetSvg extends Component {
? null ? null
: outgoing.map(getPath); : outgoing.map(getPath);
let skill = '';
if (resolution) {
if (resolution.event[1]) ([, { skill }] = resolution.event);
}
const resolutionText = resolution
? <text x={`${width / 2.3}`} y={`${height / 2.3}`} font-family="Jura" font-size="2em"> {skill} </text>
: null;
return ( return (
<svg id="targeting" viewBox={`0 0 ${width} ${height}`} preserveAspectRatio="none" class="targeting-arrows"> <svg id="targeting" viewBox={`0 0 ${width} ${height}`} preserveAspectRatio="none" class="targeting-arrows">
{arrows} {arrows}
{resolutionText}
</svg> </svg>
); );
} }

View File

@ -1,8 +1,9 @@
module.exports = { module.exports = {
TIMES: { TIMES: {
START_SKILL: 700, START_SKILL: 1000,
END_SKILL: 700, END_SKILL: 1000,
POST_SKILL: 1000, POST_SKILL: 1000,
DELAY: 150,
}, },
INFO: { INFO: {

View File

@ -204,14 +204,14 @@ function getCombatSequence(resolution) {
if (resolution.event[0] === 'Ko') return ['POST_SKILL']; if (resolution.event[0] === 'Ko') return ['POST_SKILL'];
switch (resolution.stages) { switch (resolution.stages) {
case 1: return ['START_SKILL', 'END_SKILL']; case 1: return ['START_SKILL', 'DELAY', 'END_SKILL'];
case 2: return ['START_SKILL', 'POST_SKILL']; case 2: return ['START_SKILL', 'POST_SKILL'];
case 3: return ['START_SKILL']; case 3: return ['START_SKILL'];
case 4: return ['END_SKILL', 'POST_SKILL']; case 4: return ['END_SKILL', 'POST_SKILL'];
case 5: return ['END_SKILL']; case 5: return ['END_SKILL'];
case 6: return ['POST_SKILL']; case 6: return ['POST_SKILL'];
case 7: return false; case 7: return false;
default: return ['START_SKILL', 'END_SKILL', 'POST_SKILL']; default: return ['START_SKILL', 'DELAY', 'END_SKILL', 'POST_SKILL'];
} }
} }
@ -345,6 +345,85 @@ const TARGET_COLOURS = {
BROWN: '#583108', BROWN: '#583108',
}; };
function randomPoints(numPoints, radius, dimensions) {
const {
x,
y,
width,
height,
} = dimensions;
const points = [];
let infCheck = 0;
while (points.length < numPoints) {
const c = [
Math.floor(Math.random() * (x + width - 2 * radius) + x + radius),
Math.floor(Math.random() * (y + height - 2 * radius) + y + 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 > 100) {
break;
}
}
}
return points;
}
const removeTier = skill => {
if (skill.includes('SiphonTick')) return 'SiphonTick';
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 = { module.exports = {
stringSort, stringSort,
convertItem, convertItem,
@ -356,4 +435,6 @@ module.exports = {
STATS, STATS,
COLOURS, COLOURS,
TARGET_COLOURS, TARGET_COLOURS,
randomPoints,
removeTier,
}; };