diff --git a/client/animations.test.js b/client/animations.test.js index 11f0a948..96a91b4e 100644 --- a/client/animations.test.js +++ b/client/animations.test.js @@ -1,6 +1,6 @@ require('./assets/styles/styles.css'); require('./assets/styles/styles.mobile.css'); -require('./assets/styles/instance.css'); +require('./assets/styles/instance.less'); require('./assets/styles/instance.mobile.css'); require('./assets/styles/game.css'); diff --git a/client/assets/styles/game.css b/client/assets/styles/game.css index d6ccd3d8..7fc39187 100644 --- a/client/assets/styles/game.css +++ b/client/assets/styles/game.css @@ -234,65 +234,54 @@ */} .game-construct.red-damage { -/* filter: drop-shadow(0 0 0.2em red); -*/ color: red; + color: #a52a2a; /*ensure construct doesn't get opacity lowered because of being KO before the KO animation*/ opacity: 1; - - /*border-color: red;*/ } .red-damage button { - /*border: 1px solid red;*/ - color: red; + color: #a52a2a; } .red-damage text { - fill: red; + fill: #a52a2a; } .red-damage .stats { - /*border-top: 1px solid red;*/ + /*border-top: 1px solid #a52a2a;*/ } .game-construct.blue-damage { -/* filter: drop-shadow(0 0 0.2em blue); -*/ color: blue; + color: #3498db; opacity: 1; - /*border-color: blue;*/ } .blue-damage button { - /*border: 1px solid blue;*/ - color: blue; + color: #3498db; } .blue-damage text { - fill: blue; + fill: #3498db; } .blue-damage .stats { - /*border-top: 1px solid blue;*/ } .game-construct.green-damage { -/* filter: drop-shadow(0 0 0.2em green); -*/ color: green; + color: #1FF01F; opacity: 1; - /*border-color: green;*/ } .green-damage button { - /*border: 1px solid green;*/ - color: green; + color: #1FF01F; } .green-damage text { - fill: green; + fill: #1FF01F; } .green-damage .stats { - /*border-top: 1px solid green;*/ + /*border-top: 1px solid #1FF01F;*/ } .game-construct.purple-damage { diff --git a/client/assets/styles/instance.less b/client/assets/styles/instance.less index 4b7cd9de..04077c41 100644 --- a/client/assets/styles/instance.less +++ b/client/assets/styles/instance.less @@ -287,66 +287,80 @@ flex: 1 0 100%; } +// .equipping { +// position: relative; +// } + +// .equipping::before { +// content: ''; +// position: absolute; +// top: 2px; +// left: 50%; +// width: 100%; +// height: 2px; +// transform-origin: center; +// background-color: whitesmoke; +// animation: equipping-skill 2s infinite ease-out alternate; +// opacity: 0; +// } + +// .equipping::after { +// content: ''; +// position: absolute; +// bottom: 2px; +// left: 50%; +// width: 100%; +// height: 2px; +// transform-origin: center; +// background-color: whitesmoke; +// animation: equipping-skill 2s infinite ease-out alternate; +// opacity: 0; +// animation-delay: 0.75s +// } + +// @keyframes equipping-skill { +// from { +// transform: translate(-50%, 0) scaleX(0); +// } + +// to { +// transform: translate(-50%, 0) scaleX(0.75); +// opacity: 1; +// } +// } + +// .equip-spec { +// position: relative; +// stroke: #333; +// } + +// .equip-spec::after { +// content: ''; +// position: absolute; +// bottom: 2px; +// left: 50%; +// width: 100%; +// height: 2px; +// transform-origin: center; +// background-color: whitesmoke; +// animation: equipping-skill 2s infinite ease-out alternate; +// opacity: 0; +// } + .equipping { - position: relative; + animation: equipping 2s infinite ease-in-out alternate; } -.equipping::before { - content: ''; - position: absolute; - top: 2px; - left: 50%; - width: 100%; - height: 2px; - transform-origin: center; - background-color: whitesmoke; - animation: equipping-skill 2s infinite ease-out alternate; - opacity: 0; -} - -.equipping::after { - content: ''; - position: absolute; - bottom: 2px; - left: 50%; - width: 100%; - height: 2px; - transform-origin: center; - background-color: whitesmoke; - animation: equipping-skill 2s infinite ease-out alternate; - opacity: 0; - animation-delay: 0.75s -} - -@keyframes equipping-skill { +@keyframes equipping { from { - transform: translate(-50%, 0) scaleX(0); + border-color: #444; } to { - transform: translate(-50%, 0) scaleX(0.75); - opacity: 1; + border-color: whitesmoke; } } -.equip-spec { - position: relative; - stroke: #333; -} - -.equip-spec::after { - content: ''; - position: absolute; - bottom: 2px; - left: 50%; - width: 100%; - height: 2px; - transform-origin: center; - background-color: whitesmoke; - animation: equipping-skill 2s infinite ease-out alternate; - opacity: 0; -} - .thresholds { display: flex; flex-flow: column; diff --git a/client/assets/styles/vbox.less b/client/assets/styles/vbox.less index f1947ce3..4af09140 100644 --- a/client/assets/styles/vbox.less +++ b/client/assets/styles/vbox.less @@ -78,9 +78,18 @@ background: @white; border: 1px solid @white; + // overwrite the classes on white svg elements ellipse.white { stroke: black; } + + rect.white { + stroke: black; + } + + polygon.white { + stroke: black; + } } } diff --git a/client/package.json b/client/package.json index fbf72b65..7fcacae2 100644 --- a/client/package.json +++ b/client/package.json @@ -5,7 +5,7 @@ "main": "index.js", "scripts": { "start": "parcel watch index.html --out-dir /var/lib/mnml/public/current", - "anims": "parcel watch animations.html --out-dir /var/lib/mnml/public/current", + "anims": "parcel watch animations.html --no-hmr --out-dir /var/lib/mnml/public/current", "build": "parcel build index.html", "scss": "node-sass --watch assets/scss -o assets/styles", "lint": "eslint --fix --ext .jsx src/", diff --git a/client/src/animations.socket.jsx b/client/src/animations.socket.jsx index fb0666b4..c1567fa5 100644 --- a/client/src/animations.socket.jsx +++ b/client/src/animations.socket.jsx @@ -28,12 +28,9 @@ function createSocket(store) { if (animating) return false; store.dispatch(actions.setAnimating(true)); - return eachSeries(newRes, (r, cb) => { if (['Disable', 'TargetKo'].includes(r.event[0])) return cb(); - store.dispatch(actions.setResolution(r)); - // convert server enum into anims keywords // todo make serersideonly const sequence = animations.getSequence(r); @@ -41,23 +38,29 @@ function createSocket(store) { const anims = animations.getObjects(r, sequence, game, account); const text = animations.getText(r, sequence); + store.dispatch(actions.setAnimFocus(animations.getFocusTargets(r))); + if (sequence.includes('START_SKILL')) store.dispatch(actions.setAnimSource(anims.animSource)); - if (sequence.includes('END_SKILL')) store.dispatch(actions.setAnimTarget(anims.animTarget)); + if (sequence.includes('END_SKILL')) { + store.dispatch(actions.setAnimTarget(anims.animTarget)); + if (!['Banish', 'Invert'].includes(anims.animTarget.skill)) store.dispatch(actions.setAnimCb(cb)); + } if (sequence.includes('POST_SKILL')) { // timeout to prevent text classes from being added too soon setTimeout( () => store.dispatch(actions.setAnimText(text)), - timeout - 1000, + timeout - TIMES.POST_SKILL_DURATION_MS ); } - return setTimeout(() => { store.dispatch(actions.setAnimSource(null)); store.dispatch(actions.setAnimTarget(null)); store.dispatch(actions.setAnimText(null)); - return setTimeout(cb, 50); + store.dispatch(actions.setAnimFocus([])); + if (!sequence.includes('END_SKILL') + || ['Banish', 'Invert'].includes(anims.animTarget.skill)) return cb(); + return true; }, timeout); - }, err => { if (err) return console.error(err); // clear animation state diff --git a/client/src/animations.test.jsx b/client/src/animations.test.jsx index 3704c4f6..2127ab35 100644 --- a/client/src/animations.test.jsx +++ b/client/src/animations.test.jsx @@ -69,48 +69,48 @@ document.fonts.load('16pt "Jura"').then(() => { }); const SKILLS = [ - 'AbsorbI', - 'AbsorptionI', - 'AmplifyI', + 'Absorb', + 'Absorption', + 'Amplify', 'Attack', - 'BanishI', - 'BashI', - 'BlastI', + 'Banish', + 'Bash', + 'Blast', 'Block', - 'BreakI', + 'Break', 'Buff', - 'ChaosI', - 'CounterAttackI', - 'CounterI', - 'CurseI', + 'Chaos', + 'CounterAttack', + 'Counter', + 'Curse', 'Debuff', - 'DecayI', - 'DecayTickI', - 'ElectrifyI', - 'ElectrocuteI', - 'ElectrocuteTickI', - 'HasteI', + 'Decay', + 'DecayTick', + 'Electrify', + 'Electrocute', + 'ElectrocuteTick', + 'Haste', 'HasteStrike', - 'HealI', + 'Heal', 'HybridBlast', - 'HybridI', - 'InterceptI', - 'InvertI', - 'LinkI', - 'PurgeI', - 'PurifyI', - 'RechargeI', - 'ReflectI', - 'RestrictI', - 'RuinI', - 'SilenceI', - 'SiphonI', - 'SiphonTickI', - 'SlayI', - 'SleepI', - 'StrikeI', + 'Hybrid', + 'Intercept', + 'Invert', + 'Link', + 'Purge', + 'Purify', + 'Recharge', + 'Reflect', + 'Restrict', + 'Ruin', + 'Silence', + 'Siphon', + 'SiphonTick', + 'Slay', + 'Sleep', + 'Strike', 'Stun', - 'SustainI', - 'TriageI', - 'TriageTickI', + 'Sustain', + 'Triage', + 'TriageTick', ]; diff --git a/client/src/components/animations.jsx b/client/src/components/animations.jsx index d3fdc00f..9e1a4e80 100644 --- a/client/src/components/animations.jsx +++ b/client/src/components/animations.jsx @@ -3,6 +3,7 @@ const { Component } = require('preact'); const { connect } = require('preact-redux'); const Amplify = require('./anims/amplify'); +const Attack = require('./anims/attack'); const Absorb = require('./anims/absorb'); const Bash = require('./anims/bash'); const Blast = require('./anims/blast'); @@ -69,7 +70,7 @@ class ConstructAnimation extends Component { const chooseAnim = (skill) => { switch (skill) { // Attack base - case 'Attack': return ; + case 'Attack': return ; case 'Blast': return ; case 'Siphon': return ; case 'SiphonTick': return ; diff --git a/client/src/components/anims/attack.jsx b/client/src/components/anims/attack.jsx new file mode 100644 index 00000000..6e580daf --- /dev/null +++ b/client/src/components/anims/attack.jsx @@ -0,0 +1,67 @@ +const preact = require('preact'); +const { Component } = require('preact'); +const { connect } = require('preact-redux'); +const anime = require('animejs').default; + +const { TIMES, COLOURS } = require('../../constants'); + +const addState = connect( + function receiveState(state) { + const { animCb } = state; + return { animCb }; + } +); + +class Attack extends Component { + constructor(props) { + super(); + this.props = props; + this.animations = []; + } + + render() { + return ( + + + + + + + + ); + } + + componentDidMount() { + this.animations.push(anime({ + targets: ['#attack rect'], + easing: 'easeOutExpo', + y: [400, 200], + height: [100, 10, 0], + width: [12, 5, 0], + delay: () => anime.random(TIMES.TARGET_DELAY_MS, TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS / 2), + duration: TIMES.TARGET_DURATION_MS, + })); + } + + // this is necessary because + // skipping / timing / unmounting race conditions + // can cause the animations to cut short, this will ensure the values are reset + // because preact will recycle all these components + componentWillUnmount() { + for (let i = this.animations.length - 1; i >= 0; i--) { + this.animations[i].reset(); + } + this.props.animCb(); + } + +} + +module.exports = addState(Attack); diff --git a/client/src/components/anims/blast.jsx b/client/src/components/anims/blast.jsx index bc8a0ff8..73e6e237 100644 --- a/client/src/components/anims/blast.jsx +++ b/client/src/components/anims/blast.jsx @@ -2,9 +2,9 @@ const preact = require('preact'); const { Component } = require('preact'); const { connect } = require('preact-redux'); const anime = require('animejs').default; +const times = require('lodash/times'); -const { TIMES } = require('../../constants'); -const { randomPoints } = require('../../utils'); +const { TIMES, COLOURS } = require('../../constants'); const addState = connect( function receiveState(state) { @@ -30,76 +30,65 @@ function projectile(x, y, radius, colour) { class Blast extends Component { constructor(props) { super(); - this.team = props.team; this.animations = []; - const points = randomPoints(8, 60, { x: 0, y: 0, width: 300, height: 400 }); - this.charges = points.map(coord => projectile(coord[0], coord[1], 20, '#00aabb')); } render() { return ( - // {this.charges} + viewBox="0 0 300 300"> - - - - + + + + + + + - - - - - - - {this.charges} + + {times(50, () => ( + + + + + ))} + ); } componentDidMount() { - anime.set('#blast', { - translateX: -200 * this.props.direction.x, - translateY: -300 * this.props.direction.y, - }); - this.animations.push(anime({ - targets: '#blast', + targets: ['#blast'], opacity: [ - { value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.3 }, - { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.7, duration: TIMES.POST_SKILL_DURATION_MS }, + { value: 1, delay: TIMES.TARGET_DELAY_MS, duration: TIMES.TARGET_DURATION_MS * 0.2 }, + { value: 0, delay: TIMES.TARGET_DURATION_MS * 0.5, duration: TIMES.TARGET_DURATION_MS * 0.2 }, ], + easing: 'easeInOutSine', })); - anime.set('#explosion feDisplacementMap', { - scale: 1, - }); - this.animations.push(anime({ - targets: '#blast', - translateY: 0, - translateX: 0, - loop: false, + targets: ['#blast g'], + transform: () => ` + translate(${anime.random(-100, 100)} ${anime.random(-100, 100)}) + `, + style: { rotate: anime.random(-180, 180) }, + easing: 'easeOutCubic', delay: TIMES.TARGET_DELAY_MS, - duration: (TIMES.TARGET_DURATION_MS * 1 / 2), - easing: 'easeInQuad', - })); - - this.animations.push(anime({ - targets: '#explosion feDisplacementMap', - scale: 200, - loop: false, - delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 1 / 2, - duration: TIMES.TARGET_DURATION_MS * 1 / 2, - easing: 'easeInQuad', + duration: TIMES.TARGET_DURATION_MS, })); } + // this is necessary because + // skipping / timing / unmounting race conditions + // can cause the animations to cut short, this will ensure the values are reset + // because preact will recycle all these components componentWillUnmount() { for (let i = this.animations.length - 1; i >= 0; i--) { this.animations[i].reset(); diff --git a/client/src/components/anims/break.jsx b/client/src/components/anims/break.jsx index 289eddfa..f0fb7e07 100644 --- a/client/src/components/anims/break.jsx +++ b/client/src/components/anims/break.jsx @@ -106,7 +106,8 @@ class Break extends Component { componentWillUnmount() { for (let i = this.animations.length - 1; i >= 0; i--) { this.animations[i].reset(); - } this.props.animCb(); + } + this.props.animCb(); } } diff --git a/client/src/components/instance.constructs.jsx b/client/src/components/instance.constructs.jsx index bda246d2..5a027298 100644 --- a/client/src/components/instance.constructs.jsx +++ b/client/src/components/instance.constructs.jsx @@ -144,7 +144,7 @@ function Construct(props) { if (!s) { const equipping = specList.includes(vbox.bound[itemEquip]); - const classes = `${equipping ? 'equip-spec' : 'gray'} empty`; + const classes = `${equipping ? 'equipping' : 'gray'} empty`; return (