From a6f654fac1de769480ea6d065517748bcfd198e6 Mon Sep 17 00:00:00 2001 From: Mashy Date: Fri, 6 Dec 2019 18:09:11 +1000 Subject: [PATCH] send cast in event, split up cast and hit --- client/src/animations.utils.jsx | 85 +++++++++---------- client/src/components/anims/attack.jsx | 6 +- .../components/game.construct.anim.text.jsx | 12 +-- .../components/game.construct.effect.box.jsx | 9 +- client/src/components/game.construct.life.jsx | 9 +- client/src/events.jsx | 29 +++---- core/src/construct.rs | 27 ++++-- core/src/game.rs | 8 +- core/src/skill.rs | 78 +++++++++++------ 9 files changed, 144 insertions(+), 119 deletions(-) diff --git a/client/src/animations.utils.jsx b/client/src/animations.utils.jsx index abd1cb0d..248d3517 100644 --- a/client/src/animations.utils.jsx +++ b/client/src/animations.utils.jsx @@ -1,71 +1,66 @@ -function getObjects(resolution, game, account) { - const [type, event] = resolution.variant; - +const direction = (game, account, source, target) => { const playerTeam = game.players.find(t => t.id === account.id); const playerTeamIds = playerTeam.constructs.map(c => c.id); const otherTeam = game.players.find(t => t.id !== account.id); const otherTeamIds = otherTeam.constructs.map(c => c.id); - const sourceIsPlayer = playerTeamIds.includes(event.source); - const targetIsPlayer = playerTeamIds.includes(resolution.target); - - const targetting = () => { - if (type === 'AoeSkill') { - if (targetIsPlayer) return playerTeamIds; - return otherTeamIds; - } - return [resolution.target]; - }; + const sourceIsPlayer = playerTeamIds.includes(source); + const targetIsPlayer = playerTeamIds.includes(target); const sameTeam = (sourceIsPlayer && targetIsPlayer) || (!sourceIsPlayer && !targetIsPlayer); let y = 0; if (!sameTeam) y = targetIsPlayer ? 1 : -1; const i = sourceIsPlayer - ? playerTeamIds.findIndex(c => c === event.source) - : otherTeamIds.findIndex(c => c === event.source); + ? playerTeamIds.findIndex(c => c === source) + : otherTeamIds.findIndex(c => c === source); const j = targetIsPlayer - ? playerTeamIds.findIndex(c => c === resolution.target) - : otherTeamIds.findIndex(c => c === resolution.target); + ? playerTeamIds.findIndex(c => c === target) + : otherTeamIds.findIndex(c => c === target); const x = j - i; - const direction = { x, y }; - // const targetTeam = targetIsPlayer ? playerTeamIds : otherTeamIds; + return { x, y }; +}; - const getFocusTargets = () => { - if (type === 'HitCast') { - const { source } = event; - const { target } = resolution; - if (source !== target) return [source, target]; - return [target]; - } - if (type === 'AoeSkill') { - if (targetIsPlayer) return playerTeamIds; - return otherTeamIds; - } - return [resolution.target]; - }; +function getAnimSource(resolution, game, account) { + const { source, target } = resolution.cast; const animSource = { animation: 'sourceCast', - constructId: event.source, - direction, + constructId: source, + direction: direction(game, account, source, target), }; + return animSource; +} +function getAnimTarget(resolution, game, account) { + const { source, target, skill } = resolution.cast; + const player = resolution.cast.player === account; const animTarget = { - skill: event.skill, - constructId: targetting(), - player: playerTeamIds.includes(resolution.target), - direction, + constructId: resolution.cast.target, + player, + direction: direction(game, account, source, target), + skill, }; - return { - animSource, - animTarget, - focusTargets: getFocusTargets(), - skill: event.skill, - }; + return animTarget; +} + +function getFocusTargets(resolution, game, account) { + if (resolution.variant[1] === 'AoeHit') { + const playerTeam = game.players.find(t => t.id === account.id); + const playerTeamIds = playerTeam.constructs.map(c => c.id); + const otherTeam = game.players.find(t => t.id !== account.id); + const otherTeamIds = otherTeam.constructs.map(c => c.id); + if (resolution.cast.player === account) return playerTeamIds; + return otherTeamIds; + } + + const { source, target } = resolution.cast; + return [source, target]; } module.exports = { - getObjects, + getAnimSource, + getAnimTarget, + getFocusTargets, }; diff --git a/client/src/components/anims/attack.jsx b/client/src/components/anims/attack.jsx index 65756c1f..0f5dd317 100644 --- a/client/src/components/anims/attack.jsx +++ b/client/src/components/anims/attack.jsx @@ -46,8 +46,8 @@ class Attack extends Component { 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, + delay: () => anime.random(0, TIMES.TARGET_DURATION_MS / 4), + duration: TIMES.TARGET_DURATION_MS * 5 / 4, })); } @@ -59,9 +59,7 @@ class Attack extends Component { for (let i = this.animations.length - 1; i >= 0; i--) { this.animations[i].reset(); } - this.props.animCb && this.props.animCb(); } - } module.exports = addState(Attack); diff --git a/client/src/components/game.construct.anim.text.jsx b/client/src/components/game.construct.anim.text.jsx index ad39219d..b54d9cd1 100644 --- a/client/src/components/game.construct.anim.text.jsx +++ b/client/src/components/game.construct.anim.text.jsx @@ -7,7 +7,7 @@ const shapes = require('./shapes'); const { removeTier } = require('../utils'); const { TIMES } = require('./../constants'); -const addState = connect(({ animText, animSkill, itemInfo }) => ({ animText, animSkill, itemInfo })); +const addState = connect(({ animText, itemInfo }) => ({ animText, itemInfo })); class AnimText extends preact.Component { shouldComponentUpdate(newProps) { @@ -17,7 +17,7 @@ class AnimText extends preact.Component { componentDidUpdate(prevProps) { const { animText, construct } = this.props; - if (animText && animText !== prevProps.animText && animText.target === construct.id) { + if (animText && animText !== prevProps.animText && animText.variant[1].target === construct.id) { anime({ targets: '.combat-text', top: '40%', @@ -28,10 +28,10 @@ class AnimText extends preact.Component { } render() { - const { construct, animText, animSkill, itemInfo } = this.props; - if (animText && animText.target === construct.id) { + const { construct, animText, itemInfo } = this.props; + if (animText && animText.variant[1].target === construct.id) { const itemSourceDescription = () => { - const itemSource = itemInfo.combos.filter(c => c.item === removeTier(animSkill)); + const itemSource = itemInfo.combos.filter(c => c.item === removeTier(animText.cast.skill)); const itemSourceInfo = itemSource.length ? `${itemSource[0].components[0]} ${itemSource[0].components[1]} ${itemSource[0].components[2]}` : false; @@ -82,7 +82,7 @@ class AnimText extends preact.Component { return (
-

{animSkill}

+

{animText.cast.skill}

{itemSourceDescription()} {generateAnimText()}
diff --git a/client/src/components/game.construct.effect.box.jsx b/client/src/components/game.construct.effect.box.jsx index a6215475..598c2ae8 100644 --- a/client/src/components/game.construct.effect.box.jsx +++ b/client/src/components/game.construct.effect.box.jsx @@ -21,11 +21,10 @@ const addState = connect( class GameConstructEffects extends preact.Component { shouldComponentUpdate(newProps) { - if (newProps.animText !== this.props.animText) { - if (newProps.animText && newProps.animText.constructId === this.props.construct.id) { - const [type] = newProps.animText.variant; - if (type === 'Effect' || type === 'Removal') return true; - } + if (newProps.animText && newProps.animText !== this.props.animText) { + const [type, info] = newProps.animText.variant; + if (info.target === this.props.construct.id + && (type === 'Effect' || type === 'Removal')) return true; } if (newProps.construct !== this.props.construct) return true; return false; diff --git a/client/src/components/game.construct.life.jsx b/client/src/components/game.construct.life.jsx index 680f657c..fe2f76a1 100644 --- a/client/src/components/game.construct.life.jsx +++ b/client/src/components/game.construct.life.jsx @@ -7,11 +7,10 @@ const addState = connect(({ animText }) => ({ animText })); class GameConstructLife extends preact.Component { shouldComponentUpdate(newProps) { - if (newProps.animText !== this.props.animText) { - if (newProps.animText && newProps.animText.target === this.props.construct.id) { - const [type] = newProps.animText.variant; - if (type === 'Damage' || type === 'Healing' || type === 'Recharge') return true; - } + if (newProps.animText && newProps.animText !== this.props.animText) { + const [type, info] = newProps.animText.variant; + if (info.target === this.props.construct.id + && (type === 'Damage' || type === 'Healing' || type === 'Recharge')) return true; } if (newProps.construct !== this.props.construct) return true; return false; diff --git a/client/src/events.jsx b/client/src/events.jsx index ca91776d..ba78e4d2 100644 --- a/client/src/events.jsx +++ b/client/src/events.jsx @@ -5,7 +5,7 @@ const eachSeries = require('async/eachSeries'); const sample = require('lodash/sample'); const actions = require('./actions'); -const animations = require('./animations.utils'); +const { getAnimSource, getAnimTarget, getFocusTargets } = require('./animations.utils'); const { infoToast, errorToast } = require('./utils'); const { tutorialVbox } = require('./tutorial.utils'); const { TIMES } = require('./constants'); @@ -90,24 +90,19 @@ function registerEvents(store) { const newRes = game.events[currentGame.events.length]; return eachSeries(newRes, (r, cb) => { const timeout = r.delay; + const focusTargets = getFocusTargets(r, game, account); + store.dispatch(actions.setAnimFocus(focusTargets)); - if (r.variant[0].includes('Hit')) { - let animTimeOut = TIMES.TARGET_DURATION_MS; - const anims = animations.getObjects(r, game, account); - - if (r.variant[0] === 'HitCast') { - animTimeOut += TIMES.TARGET_DELAY_MS; - store.dispatch(actions.setAnimSource(anims.animSource)); - } - - store.dispatch(actions.setAnimFocus(anims.focusTargets)); - store.dispatch(actions.setAnimSkill(anims.skill)); - store.dispatch(actions.setAnimTarget(anims.animTarget)); + if (r.variant[0] === 'Cast') { + const animSource = getAnimSource(r, game, account); + store.dispatch(actions.setAnimSource(animSource)); store.dispatch(actions.setAnimText(null)); - setTimeout(() => { - store.dispatch(actions.setAnimSource(null)); - store.dispatch(actions.setAnimTarget(null)); - }, animTimeOut); + setTimeout(() => store.dispatch(actions.setAnimSource(null)), TIMES.SOURCE_DURATION_MS); + } else if (r.variant[0].includes('Hit')) { + const animTarget = getAnimTarget(r, game, account); + store.dispatch(actions.setAnimTarget(animTarget)); + store.dispatch(actions.setAnimText(null)); + setTimeout(() => store.dispatch(actions.setAnimTarget(null)), TIMES.TARGET_DURATION_MS); } else { store.dispatch(actions.setAnimText(r)); } diff --git a/core/src/construct.rs b/core/src/construct.rs index 937a377f..84f1b9e2 100644 --- a/core/src/construct.rs +++ b/core/src/construct.rs @@ -423,11 +423,11 @@ impl Construct { match skill.cd { Some(cd) => { skill.cd = Some(cd.saturating_add(turns)); - events.push(EventVariant::CooldownIncrease { turns, skill: skill.skill }) + events.push(EventVariant::CooldownIncrease { target: self.id, turns }) }, None => { skill.cd = Some(turns); - events.push(EventVariant::CooldownIncrease { turns, skill: skill.skill }) + events.push(EventVariant::CooldownIncrease { target: self.id, turns }) }, } } @@ -548,7 +548,7 @@ impl Construct { pub fn recharge(&mut self, red_amount: usize, blue_amount: usize) -> Vec { let mut events = vec![]; - + let target = self.id; if self.is_ko() { return events; } match self.affected(Effect::Invert) { @@ -565,7 +565,7 @@ impl Construct { let blue = new_blue_life - current_blue_life; if red != 0 || blue != 0 { - events.push(EventVariant::Recharge { red, blue }); + events.push(EventVariant::Recharge { target, red, blue }); } }, true => { @@ -592,6 +592,7 @@ impl Construct { let red_damage_amount = red_current_green_life - self.green_life(); events.push(EventVariant::Damage { + target, amount: red_damage_amount, mitigation: red_mitigation, colour: Colour::Red, @@ -621,6 +622,7 @@ impl Construct { let blue_damage_amount = blue_current_green_life - self.green_life(); events.push(EventVariant::Damage { + target, amount: blue_damage_amount, mitigation: blue_mitigation, colour: Colour::Blue, @@ -635,6 +637,7 @@ impl Construct { pub fn deal_green_damage(&mut self, amount: usize) -> Vec { let mut events = vec![]; if self.is_ko() { return events; } + let target = self.id; let mods = self.effects.iter() .filter(|e| e.effect.modifications().contains(&Stat::GreenDamageTaken)) @@ -654,6 +657,7 @@ impl Construct { let overhealing = modified_power - healing; events.push(EventVariant::Healing { + target, amount: healing, overhealing, }); @@ -667,6 +671,7 @@ impl Construct { let delta = current_green_life - self.green_life(); events.push(EventVariant::Damage { + target, amount: delta, mitigation: 0, colour: Colour::Green, @@ -682,6 +687,7 @@ impl Construct { let mut events = vec![]; if self.is_ko() { return events; } + let target = self.id; let mods = self.effects.iter() .filter(|e| e.effect.modifications().contains(&Stat::RedDamageTaken)) @@ -710,6 +716,7 @@ impl Construct { events.push( EventVariant::Damage { + target, amount: delta, mitigation, colour: Colour::Red, @@ -717,7 +724,7 @@ impl Construct { } ); if self.is_ko() { - events.push(EventVariant::Ko {}); + events.push(EventVariant::Ko { target }); } }, true => { @@ -736,6 +743,7 @@ impl Construct { if healing > 0 { events.push( EventVariant::Healing { + target, amount: healing, overhealing: overhealing - recharge, } @@ -743,7 +751,7 @@ impl Construct { } if recharge > 0 { - events.push(EventVariant::Recharge { red: recharge, blue: 0 }); + events.push(EventVariant::Recharge { target, red: recharge, blue: 0 }); } } }; @@ -755,6 +763,7 @@ impl Construct { let mut events = vec![]; if self.is_ko() { return events; } + let target = self.id; let mods = self.effects.iter() .filter(|e| e.effect.modifications().contains(&Stat::BlueDamageTaken)) @@ -778,6 +787,7 @@ impl Construct { let delta = current_green_life - self.green_life(); events.push(EventVariant::Damage { + target, amount: delta, mitigation, colour: Colour::Blue, @@ -798,11 +808,11 @@ impl Construct { let recharge = self.blue_life.value - current_life; if healing > 0 { - events.push(EventVariant::Healing { amount: healing, overhealing }); + events.push(EventVariant::Healing { target, amount: healing, overhealing }); } if recharge > 0 { - events.push(EventVariant::Recharge { red: 0, blue: recharge }); + events.push(EventVariant::Recharge { target, red: 0, blue: recharge }); } } }; @@ -828,6 +838,7 @@ impl Construct { // todo modified durations cause of buffs let result = EventVariant::Effect { + target: self.id, effect: effect.effect, duration: effect.duration, display: EventConstruct::new(self) diff --git a/core/src/game.rs b/core/src/game.rs index effe4285..f351a70d 100644 --- a/core/src/game.rs +++ b/core/src/game.rs @@ -41,7 +41,7 @@ pub enum Colour { #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub enum Action { Hit { cast: Cast }, - HitCast { cast: Cast }, + Cast { cast: Cast }, Damage { cast: Cast, construct: Uuid, values: Vec, colour: Colour }, Effect { cast: Cast, construct: Uuid, effect: ConstructEffect }, IncreaseCooldowns { cast: Cast, construct: Uuid, turns: usize }, @@ -507,7 +507,7 @@ impl Game { for action in actions { match action { Action::Hit { cast } => self.hit(cast), - Action::HitCast { cast } => self.hit_cast(cast), + Action::Cast { cast } => self.cast(cast), Action::Damage { cast, construct, values, colour } => self.damage(cast, construct, values, colour), Action::Effect { cast, construct, effect } => self.effect(cast, construct, effect), Action::IncreaseCooldowns { cast, construct, turns } => self.increase_cooldowns(cast, construct, turns), @@ -517,8 +517,8 @@ impl Game { self } - fn hit_cast(&mut self, cast: Cast) -> &mut Game { - self.event_add(vec![EventVariant::HitCast()], cast); + fn cast(&mut self, cast: Cast) -> &mut Game { + self.event_add(vec![EventVariant::Cast()], cast); self } diff --git a/core/src/skill.rs b/core/src/skill.rs index 298c762f..a8b1db8f 100644 --- a/core/src/skill.rs +++ b/core/src/skill.rs @@ -109,10 +109,13 @@ impl Cast { } pub fn actions(&self) -> Vec { - let mut actions = match self.skill.is_tick() { - false => vec![Action::HitCast { cast: *self } ], - true => vec![Action::Hit { cast: *self } ] - }; + let mut actions = vec![]; + + if self.skill.cast_animation() { + actions.push(Action::Cast { cast: *self }); + } + + actions.push(Action::Hit { cast: *self }); let mut rest = match self.skill { Skill::Amplify => vec![ @@ -234,29 +237,23 @@ pub struct Event { #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum EventVariant { + Cast (), Hit (), - HitCast (), + HitAoe (), - Damage { amount: usize, mitigation: usize, colour: Colour, display: EventConstruct }, - Effect { effect: Effect, duration: u8, display: EventConstruct }, - Removal { effect: Option, display: EventConstruct }, + Damage { target: Uuid, amount: usize, mitigation: usize, colour: Colour, display: EventConstruct }, + Effect { target: Uuid, effect: Effect, duration: u8, display: EventConstruct }, + Removal { target: Uuid, effect: Option, display: EventConstruct }, - Healing { amount: usize, overhealing: usize }, - Recharge { red: usize, blue: usize }, - Inversion { skill: Skill }, - Reflection { skill: Skill }, - AoeSkill { skill: Skill }, - Skill { skill: Skill }, - TargetKo { skill: Skill }, - // skill not necessary but makes it neater as all events are arrays in js - Ko (), + Healing { target: Uuid, amount: usize, overhealing: usize }, + Recharge { target: Uuid, red: usize, blue: usize }, + Inversion { target: Uuid }, + Reflection { target: Uuid }, + Ko { target: Uuid }, + + CooldownIncrease { target: Uuid, turns: usize }, + CooldownDecrease { target: Uuid, turns: usize }, Forfeit (), - Incomplete (), - // not used - Evasion { skill: Skill, evasion_rating: usize }, - - CooldownIncrease { turns: usize, skill: Skill }, - CooldownDecrease { turns: usize, skill: Skill }, } impl EventVariant { @@ -269,7 +266,7 @@ impl EventVariant { match self { EventVariant::Hit() => target_duration - combat_text_overlap, - EventVariant::HitCast() => target_delay + target_duration - combat_text_overlap, + EventVariant::Cast() => target_delay, _ => combat_text_delay, } } @@ -1053,6 +1050,24 @@ impl Skill { } } + pub fn cast_animation(&self) -> bool { + match self { + Skill::ElectrocuteTick | + Skill::ElectrocuteTickPlus | + Skill::ElectrocuteTickPlusPlus | + Skill::DecayTick | + Skill::DecayTickPlus | + Skill::DecayTickPlusPlus | + Skill::SiphonTick | + Skill::SiphonTickPlus | + Skill::SiphonTickPlusPlus | + Skill::TriageTick | + Skill::TriageTickPlus | + Skill::TriageTickPlusPlus => false, + _ => true + } + } + pub fn defensive(&self) -> bool { match self { Skill::Amplify| @@ -1568,7 +1583,20 @@ mod tests { let cast = Cast::new(Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4(), Skill::Attack); let actions = cast.actions(); - + match actions[0] { + Action::Hit { cast } => { + assert_eq!(cast.skill, Skill::Attack); + }, + _ => panic!("{:?}", actions), + }; + + match actions[1] { + Action::Damage { cast: _, construct: _, values: _, colour } => { + assert_eq!(colour, Colour::Red); + }, + _ => panic!("{:?}", actions), + }; + } // #[test]