diff --git a/client/src/animations.socket.jsx b/client/src/animations.socket.jsx index 424114b1..881f1ea6 100644 --- a/client/src/animations.socket.jsx +++ b/client/src/animations.socket.jsx @@ -25,14 +25,18 @@ function createSocket(store) { function onDevResolutions(newRes) { const { game: currentGame } = store.getState(); + let id = 0; return eachSeries(newRes, (r, cb) => { if (['Disable', 'TargetKo'].includes(r.event[0])) return cb(); // Create sub events for combat animations const sequence = getCombatSequence(r); + id += 1; + return eachSeries(sequence, (stages, sCb) => { const stagedR = Object.create(r); stagedR.sequence = sequence; stagedR.stages = stages; + stagedR.id = id; let timeout = 0; if (stages.includes('START_SKILL') && stages.includes('END_SKILL')) { @@ -45,11 +49,13 @@ function createSocket(store) { return setTimeout(sCb, timeout); }, err => { if (err) console.error(err); + store.dispatch(actions.setAvatarAnimation({ id, source: false, target: false })); // Finished this resolution return cb(); }); }, err => { if (err) return console.error(err); + store.dispatch(actions.setAvatarAnimation({ id: -1, source: false, target: false })); store.dispatch(actions.setResolution(null)); // stop skipping resolutions store.dispatch(actions.setSkip(false)); diff --git a/client/src/components/animations.jsx b/client/src/components/animations.jsx index 564070fc..a290c9eb 100644 --- a/client/src/components/animations.jsx +++ b/client/src/components/animations.jsx @@ -73,13 +73,20 @@ function animations(props) { const sourceIsPlayer = playerTeamIds.includes(construct.id); const targetIsPlayer = playerTeamIds.includes(resolution.target.id); - if (resolution.source.id === construct.id && resolution.stages.includes('START_SKILL')) { + const skipSource = (resolution.source.id === resolution.target.id + && ['Invert', 'Banish'].includes(removeTier(event.skill))); + + if (!skipSource && resolution.source.id === construct.id && resolution.stages.includes('START_SKILL')) { const sameTeam = (sourceIsPlayer && targetIsPlayer) || (!sourceIsPlayer && !targetIsPlayer); - const y = sameTeam - ? 0 - : targetIsPlayer - ? 1 - : -1; + + let y = 0; + if (!sameTeam) y = targetIsPlayer ? 1 : -1; + + // const y = sameTeam + // ? 0 + // : targetIsPlayer + // ? 1 + // : -1; const i = sourceIsPlayer ? playerTeamIds.findIndex(c => c === construct.id) @@ -90,8 +97,14 @@ function animations(props) { : otherTeamIds.findIndex(c => c === resolution.target.id); const x = j - i; - if (avatarAnimation.id !== resolution.id) { - setAvatarAnimation({ id: resolution.id, animTargetId: construct.id, x, y }); + if (!avatarAnimation.source) { + setAvatarAnimation({ + source: true, + target: avatarAnimation.target, + animTargetId: construct.id, + type: 'sourceCast', + params: { x, y }, + }); } } const targetTeam = targetIsPlayer ? playerTeamIds : otherTeamIds; @@ -100,7 +113,6 @@ function animations(props) { // target animation const anim = text => { - console.log(text); if (!text || !resolution.sequence[0].includes('END_SKILL')) return false; const skill = removeTier(text); @@ -130,14 +142,34 @@ function animations(props) { case 'Curse': return ; case 'Decay': return ; case 'DecayTick': return ; - case 'Invert': return ; + case 'Invert': { + if (!avatarAnimation.target) { + setAvatarAnimation({ + source: avatarAnimation.source, + target: true, + id: resolution.id, + animTargetId: construct.id, + type: 'invert', + }); + } break; + } case 'Purge': return ; case 'Silence': return ; case 'Restrict': return ; // Stun Base case 'Stun': return ; - case 'Banish': return ; + case 'Banish': { + if (!avatarAnimation.target) { + setAvatarAnimation({ + source: avatarAnimation.source, + target: true, + id: resolution.id, + animTargetId: construct.id, + type: 'banish', + }); + } break; + } case 'Bash': return ; case 'Absorb': return ; case 'Sleep': return ; diff --git a/client/src/components/anims/banish.jsx b/client/src/components/anims/banish.jsx index 081b5537..62d3096a 100644 --- a/client/src/components/anims/banish.jsx +++ b/client/src/components/anims/banish.jsx @@ -1,42 +1,17 @@ -const preact = require('preact'); -const { Component } = require('preact'); - const anime = require('animejs').default; + const { TIMES } = require('../../constants'); -class Banish extends Component { - constructor(props) { - super(); - this.id = props.id; - this.animations = []; - } - - render() { - // Need this so unmount triggers - return ; - } - - componentDidMount() { - this.animations.push(anime({ - targets: [document.getElementById(this.id)], - scaleY: 0, - fill: '#fff', - easing: 'easeOutElastic', - delay: TIMES.TARGET_DELAY_MS, - duration: TIMES.TARGET_DURATION_MS * 0.5, - direction: 'alternate', - })); - } - - // 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(); - } - } +function Banish(id) { + return anime({ + targets: [document.getElementById(id)], + scaleY: 0, + fill: '#fff', + easing: 'easeOutElastic', + delay: TIMES.TARGET_DELAY_MS, + duration: TIMES.TARGET_DURATION_MS * 0.45, + direction: 'alternate', + }); } module.exports = Banish; diff --git a/client/src/components/anims/idle.jsx b/client/src/components/anims/idle.jsx new file mode 100644 index 00000000..0972b4ac --- /dev/null +++ b/client/src/components/anims/idle.jsx @@ -0,0 +1,18 @@ +const anime = require('animejs').default; + +function idle(id) { + 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, + }); +} + +module.exports = idle; diff --git a/client/src/components/anims/invert.jsx b/client/src/components/anims/invert.jsx index 34d383cc..57a787d8 100644 --- a/client/src/components/anims/invert.jsx +++ b/client/src/components/anims/invert.jsx @@ -1,41 +1,16 @@ -const preact = require('preact'); -const { Component } = require('preact'); - const anime = require('animejs').default; + const { TIMES } = require('../../constants'); -class Invert extends Component { - constructor(props) { - super(); - this.id = props.id; - this.animations = []; - } - - render() { - // Need this so unmount triggers - return ; - } - - componentDidMount() { - this.animations.push(anime({ - targets: [document.getElementById(this.id)], - rotate: 180, - delay: TIMES.TARGET_DELAY_MS, - duration: TIMES.TARGET_DURATION_MS * 0.45, - easing: 'easeInOutElastic', - direction: 'alternate', - })); - } - - // 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(); - } - } +function Invert(id) { + return anime({ + targets: [document.getElementById(id)], + rotate: 180, + delay: TIMES.TARGET_DELAY_MS, + duration: TIMES.TARGET_DURATION_MS * 0.45, + easing: 'easeInOutElastic', + direction: 'alternate', + }); } module.exports = Invert; diff --git a/client/src/components/anims/source.cast.jsx b/client/src/components/anims/source.cast.jsx new file mode 100644 index 00000000..c6690ca9 --- /dev/null +++ b/client/src/components/anims/source.cast.jsx @@ -0,0 +1,17 @@ +const anime = require('animejs').default; + +const { TIMES } = require('../../constants'); + +function sourceCast(id, params) { + const { x, y } = params; + return anime({ + targets: [document.getElementById(id)], + translateX: x * 200, + translateY: y * 200, + easing: 'easeInOutElastic', + direction: 'alternate', + duration: TIMES.SOURCE_DURATION_MS, + }); +} + +module.exports = sourceCast; diff --git a/client/src/components/construct.jsx b/client/src/components/construct.jsx index b70f0161..3419ca5b 100644 --- a/client/src/components/construct.jsx +++ b/client/src/components/construct.jsx @@ -3,7 +3,11 @@ const { Component } = require('preact'); const { connect } = require('preact-redux'); const anime = require('animejs').default; -const { TIMES } = require('../constants'); +const banish = require('./anims/banish'); +const idleAnimation = require('./anims/idle'); +const invert = require('./anims/invert'); +const sourceCast = require('./anims/source.cast'); + const genAvatar = name => { let hash = 0; @@ -17,28 +21,6 @@ const genAvatar = name => { return `${hash}`; }; -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; -} const addState = connect( function receiveState(state) { @@ -48,10 +30,15 @@ const addState = connect( ); class ConstructAvatar extends Component { - constructor() { + constructor(props) { super(); + // The animation ids are a check to ensure that animations are not repeated + // When a new construct animation is communicated with state it will have a corresponding Id + // which is a count of how many resoluttions have passed this.animId = 0; + this.source = false; this.animations = []; + this.avatar = genAvatar(props.name); } render() { @@ -59,61 +46,45 @@ class ConstructAvatar extends Component {
); } componentDidMount() { - this.idle = animateConstruct(this.props.id); + this.idle = idleAnimation(this.props.id); + this.animations.push(this.idle); } componentWillReceiveProps(nextProps) { - if (nextProps.avatarAnimation.id !== this.animId && nextProps.avatarAnimation.animTargetId === this.props.id) { + const animations = nextProps.avatarAnimation; + if (animations.id === -1) this.animId = 0; // The current set of resolutions ended reset to 0 + if (animations.id !== this.animId && animations.animTargetId === this.props.id) { this.animId = nextProps.avatarAnimation.id; - this.idle.pause(); - const anim = anime({ - targets: [document.getElementById(this.props.id)], - translateY: nextProps.avatarAnimation.y * 200, - translateX: nextProps.avatarAnimation.x * 200, - easing: 'easeInOutElastic', - direction: 'alternate', - duration: TIMES.SOURCE_DURATION_MS, - }); - anim.finished.then(this.idle.play); + const selectAnim = () => { + switch (animations.type) { + case 'banish': return banish(this.props.id); + case 'invert': return invert(this.props.id); + case 'sourceCast': return sourceCast(this.props.id, animations.params); + default: return null; + } + }; + const anim = selectAnim(); + if (anim) { + this.idle.pause(); + this.animations.push(anim); + anim.finished.then(this.idle.play); + } } - // console.log(this.props); - // console.log(nextProps); } componentWillUnmount() { - clearAnimation(this.props.id); - } -} - -class ConstructImg extends Component { - render() { - return ( - event.target.setAttribute('src', '/molecules/726.svg')} - /> - ); - } - - componentDidMount() { - animateConstruct(this.props.id); - } - - componentWillUnmount() { - clearAnimation(this.props.id); + for (let i = this.animations.length - 1; i >= 0; i--) { + this.animations[i].reset(); + } } } module.exports = { ConstructAvatar: addState(ConstructAvatar), - ConstructImg, }; diff --git a/client/src/events.jsx b/client/src/events.jsx index ec20bef3..392dda99 100644 --- a/client/src/events.jsx +++ b/client/src/events.jsx @@ -84,12 +84,13 @@ function registerEvents(store) { if (err) console.error(err); // Clear the anim classes store.dispatch(actions.setResolution('clear')); + store.dispatch(actions.setAvatarAnimation({ id, source: false, target: false })); // Finished this resolution small delay for reset return setTimeout(cb, 5); }); }, err => { if (err) return console.error(err); - store.dispatch(actions.setAvatarAnimation({ id: -1 })); + store.dispatch(actions.setAvatarAnimation({ id: -1, source: false, target: false })); store.dispatch(actions.setResolution(null)); // stop skipping resolutions store.dispatch(actions.setSkip(false)); diff --git a/client/src/reducers.jsx b/client/src/reducers.jsx index abd7ad03..9a154c51 100644 --- a/client/src/reducers.jsx +++ b/client/src/reducers.jsx @@ -15,7 +15,7 @@ module.exports = { activeConstruct: createReducer(null, 'SET_ACTIVE_CONSTRUCT'), activeItem: createReducer(null, 'SET_ACTIVE_VAR'), activeSkill: createReducer(null, 'SET_ACTIVE_SKILL'), - avatarAnimation: createReducer({ id: -1 }, 'SET_AVATAR_ANIMATION'), + avatarAnimation: createReducer({ id: -1, source: false, target: false }, 'SET_AVATAR_ANIMATION'), combiner: createReducer([null, null, null], 'SET_COMBINER'), constructs: createReducer([], 'SET_CONSTRUCTS'), constructDeleteId: createReducer(null, 'SET_CONSTRUCT_DELETE_ID'),