diff --git a/client/src/components/animations.jsx b/client/src/components/animations.jsx
index dcec77d2..c8e7bb43 100644
--- a/client/src/components/animations.jsx
+++ b/client/src/components/animations.jsx
@@ -10,6 +10,7 @@ const Bash = require('./anims/bash');
const Block = require('./anims/block');
const Buff = require('./anims/buff');
const Debuff = require('./anims/debuff');
+const Curse = require('./anims/curse');
const Stun = require('./anims/stun');
const Heal = require('./anims/heal');
const Hex = require('./anims/hex');
@@ -102,6 +103,7 @@ function animations(props) {
case 'Bash': return ;
case 'Block': return ;
case 'Buff': return ;
+ case 'Curse': return ;
case 'Blast': return ;
case 'Debuff': return ;
case 'Strike': return ;
diff --git a/client/src/components/anims/bash.jsx b/client/src/components/anims/bash.jsx
new file mode 100644
index 00000000..57fae4aa
--- /dev/null
+++ b/client/src/components/anims/bash.jsx
@@ -0,0 +1,97 @@
+const preact = require('preact');
+const { Component } = require('preact');
+
+const anime = require('animejs').default;
+
+const { TIMES } = require('../../constants');
+
+class Bash extends Component {
+ constructor() {
+ super();
+ this.animations = [];
+ }
+
+ render() {
+ return (
+
+ );
+ }
+
+ componentDidMount() {
+ this.animations.push(anime({
+ targets: ['#bash'],
+ opacity: 1,
+
+ delay: TIMES.TARGET_DELAY_MS,
+ duration: TIMES.TARGET_DELAY_MS,
+ }));
+
+ this.animations.push(anime({
+ targets: ['#bash'],
+ scale: {
+ value: 1,
+ delay: TIMES.TARGET_DELAY_MS,
+ duration: TIMES.TARGET_DURATION_MS * 0.1,
+ endDelay: TIMES.TARGET_DURATION_MS * 0.9,
+ easing: 'easeInExpo',
+ },
+
+ keyframes: [
+ { translateX: 0, translateY: 0 },
+ { translateX: -5, translateY: 0 },
+ { translateX: 0, translateY: -5 },
+ { translateX: 5, translateY: 0 },
+ { translateX: 0, translateY: 5 },
+ { translateX: -2, translateY: 0 },
+ { translateX: 0, translateY: -2 },
+ { translateX: 2, translateY: 0 },
+ { translateX: 0, translateY: 2 },
+ ],
+
+ delay: TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS * 0.1,
+ duration: TIMES.TARGET_DURATION_MS * 0.2,
+ easing: 'easeOutSine',
+ }));
+
+ this.animations.push(anime({
+ targets: ['#bashFilter feTurbulence', '#bashFilter feDisplacementMap'],
+ baseFrequency: 2,
+ scale: 10,
+ numOctaves: 5,
+ easing: 'easeOutSine',
+
+ delay: TIMES.TARGET_DELAY_MS,
+ 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();
+ }
+ }
+}
+
+module.exports = Bash;
diff --git a/client/src/components/anims/curse.jsx b/client/src/components/anims/curse.jsx
new file mode 100644
index 00000000..2ccaf2aa
--- /dev/null
+++ b/client/src/components/anims/curse.jsx
@@ -0,0 +1,59 @@
+const preact = require('preact');
+const { Component } = require('preact');
+const anime = require('animejs').default;
+
+const { TIMES } = require('../../constants');
+
+class Curse extends Component {
+ constructor() {
+ super();
+ this.animations = [];
+ }
+
+ render() {
+ return (
+
+ );
+ }
+
+ componentDidMount() {
+ this.animations.push(anime({
+ targets: ['#curse'],
+ opacity: 1,
+
+ easing: 'easeOutExpo',
+ delay: TIMES.TARGET_DELAY_MS,
+ duration: TIMES.TARGET_DELAY_MS,
+ }));
+
+ this.animations.push(anime({
+ targets: ['#curse'],
+ scale: [0.5, 0.8],
+ strokeWidth: 0,
+ easing: 'easeInOutSine',
+ direction: 'alternate',
+ duration: TIMES.START_SKILL,
+ }));
+ }
+
+ // 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();
+ }
+ }
+}
+
+module.exports = Curse;