send cast in event, split up cast and hit

This commit is contained in:
Mashy 2019-12-06 18:09:11 +10:00
parent 7afb6e30bd
commit a6f654fac1
9 changed files with 144 additions and 119 deletions

View File

@ -1,71 +1,66 @@
function getObjects(resolution, game, account) { const direction = (game, account, source, target) => {
const [type, event] = resolution.variant;
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 otherTeam = game.players.find(t => t.id !== account.id); const otherTeam = game.players.find(t => t.id !== account.id);
const otherTeamIds = otherTeam.constructs.map(c => c.id); const otherTeamIds = otherTeam.constructs.map(c => c.id);
const sourceIsPlayer = playerTeamIds.includes(event.source); const sourceIsPlayer = playerTeamIds.includes(source);
const targetIsPlayer = playerTeamIds.includes(resolution.target); const targetIsPlayer = playerTeamIds.includes(target);
const targetting = () => {
if (type === 'AoeSkill') {
if (targetIsPlayer) return playerTeamIds;
return otherTeamIds;
}
return [resolution.target];
};
const sameTeam = (sourceIsPlayer && targetIsPlayer) || (!sourceIsPlayer && !targetIsPlayer); const sameTeam = (sourceIsPlayer && targetIsPlayer) || (!sourceIsPlayer && !targetIsPlayer);
let y = 0; let y = 0;
if (!sameTeam) y = targetIsPlayer ? 1 : -1; if (!sameTeam) y = targetIsPlayer ? 1 : -1;
const i = sourceIsPlayer const i = sourceIsPlayer
? playerTeamIds.findIndex(c => c === event.source) ? playerTeamIds.findIndex(c => c === source)
: otherTeamIds.findIndex(c => c === event.source); : otherTeamIds.findIndex(c => c === source);
const j = targetIsPlayer const j = targetIsPlayer
? playerTeamIds.findIndex(c => c === resolution.target) ? playerTeamIds.findIndex(c => c === target)
: otherTeamIds.findIndex(c => c === resolution.target); : otherTeamIds.findIndex(c => c === target);
const x = j - i; const x = j - i;
const direction = { x, y }; return { x, y };
// const targetTeam = targetIsPlayer ? playerTeamIds : otherTeamIds; };
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 = { const animSource = {
animation: 'sourceCast', animation: 'sourceCast',
constructId: event.source, constructId: source,
direction, 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 = { const animTarget = {
skill: event.skill, constructId: resolution.cast.target,
constructId: targetting(), player,
player: playerTeamIds.includes(resolution.target), direction: direction(game, account, source, target),
direction, skill,
}; };
return { return animTarget;
animSource, }
animTarget,
focusTargets: getFocusTargets(), function getFocusTargets(resolution, game, account) {
skill: event.skill, 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 = { module.exports = {
getObjects, getAnimSource,
getAnimTarget,
getFocusTargets,
}; };

View File

@ -46,8 +46,8 @@ class Attack extends Component {
y: [400, 200], y: [400, 200],
height: [100, 10, 0], height: [100, 10, 0],
width: [12, 5, 0], width: [12, 5, 0],
delay: () => anime.random(TIMES.TARGET_DELAY_MS, TIMES.TARGET_DELAY_MS + TIMES.TARGET_DURATION_MS / 2), delay: () => anime.random(0, TIMES.TARGET_DURATION_MS / 4),
duration: TIMES.TARGET_DURATION_MS, 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--) { for (let i = this.animations.length - 1; i >= 0; i--) {
this.animations[i].reset(); this.animations[i].reset();
} }
this.props.animCb && this.props.animCb();
} }
} }
module.exports = addState(Attack); module.exports = addState(Attack);

View File

@ -7,7 +7,7 @@ const shapes = require('./shapes');
const { removeTier } = require('../utils'); const { removeTier } = require('../utils');
const { TIMES } = require('./../constants'); 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 { class AnimText extends preact.Component {
shouldComponentUpdate(newProps) { shouldComponentUpdate(newProps) {
@ -17,7 +17,7 @@ class AnimText extends preact.Component {
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
const { animText, construct } = this.props; 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({ anime({
targets: '.combat-text', targets: '.combat-text',
top: '40%', top: '40%',
@ -28,10 +28,10 @@ class AnimText extends preact.Component {
} }
render() { render() {
const { construct, animText, animSkill, itemInfo } = this.props; const { construct, animText, itemInfo } = this.props;
if (animText && animText.target === construct.id) { if (animText && animText.variant[1].target === construct.id) {
const itemSourceDescription = () => { 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 const itemSourceInfo = itemSource.length
? `${itemSource[0].components[0]} ${itemSource[0].components[1]} ${itemSource[0].components[2]}` ? `${itemSource[0].components[0]} ${itemSource[0].components[1]} ${itemSource[0].components[2]}`
: false; : false;
@ -82,7 +82,7 @@ class AnimText extends preact.Component {
return ( return (
<div class="combat-text"> <div class="combat-text">
<h2><span>{animSkill}</span></h2> <h2><span>{animText.cast.skill}</span></h2>
<span>{itemSourceDescription()}</span> <span>{itemSourceDescription()}</span>
{generateAnimText()} {generateAnimText()}
</div> </div>

View File

@ -21,11 +21,10 @@ const addState = connect(
class GameConstructEffects extends preact.Component { class GameConstructEffects extends preact.Component {
shouldComponentUpdate(newProps) { shouldComponentUpdate(newProps) {
if (newProps.animText !== this.props.animText) { if (newProps.animText && newProps.animText !== this.props.animText) {
if (newProps.animText && newProps.animText.constructId === this.props.construct.id) { const [type, info] = newProps.animText.variant;
const [type] = newProps.animText.variant; if (info.target === this.props.construct.id
if (type === 'Effect' || type === 'Removal') return true; && (type === 'Effect' || type === 'Removal')) return true;
}
} }
if (newProps.construct !== this.props.construct) return true; if (newProps.construct !== this.props.construct) return true;
return false; return false;

View File

@ -7,11 +7,10 @@ const addState = connect(({ animText }) => ({ animText }));
class GameConstructLife extends preact.Component { class GameConstructLife extends preact.Component {
shouldComponentUpdate(newProps) { shouldComponentUpdate(newProps) {
if (newProps.animText !== this.props.animText) { if (newProps.animText && newProps.animText !== this.props.animText) {
if (newProps.animText && newProps.animText.target === this.props.construct.id) { const [type, info] = newProps.animText.variant;
const [type] = newProps.animText.variant; if (info.target === this.props.construct.id
if (type === 'Damage' || type === 'Healing' || type === 'Recharge') return true; && (type === 'Damage' || type === 'Healing' || type === 'Recharge')) return true;
}
} }
if (newProps.construct !== this.props.construct) return true; if (newProps.construct !== this.props.construct) return true;
return false; return false;

View File

@ -5,7 +5,7 @@ const eachSeries = require('async/eachSeries');
const sample = require('lodash/sample'); const sample = require('lodash/sample');
const actions = require('./actions'); const actions = require('./actions');
const animations = require('./animations.utils'); const { getAnimSource, getAnimTarget, getFocusTargets } = require('./animations.utils');
const { infoToast, errorToast } = require('./utils'); const { infoToast, errorToast } = require('./utils');
const { tutorialVbox } = require('./tutorial.utils'); const { tutorialVbox } = require('./tutorial.utils');
const { TIMES } = require('./constants'); const { TIMES } = require('./constants');
@ -90,24 +90,19 @@ function registerEvents(store) {
const newRes = game.events[currentGame.events.length]; const newRes = game.events[currentGame.events.length];
return eachSeries(newRes, (r, cb) => { return eachSeries(newRes, (r, cb) => {
const timeout = r.delay; const timeout = r.delay;
const focusTargets = getFocusTargets(r, game, account);
store.dispatch(actions.setAnimFocus(focusTargets));
if (r.variant[0].includes('Hit')) { if (r.variant[0] === 'Cast') {
let animTimeOut = TIMES.TARGET_DURATION_MS; const animSource = getAnimSource(r, game, account);
const anims = animations.getObjects(r, game, account); store.dispatch(actions.setAnimSource(animSource));
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));
store.dispatch(actions.setAnimText(null)); store.dispatch(actions.setAnimText(null));
setTimeout(() => { setTimeout(() => store.dispatch(actions.setAnimSource(null)), TIMES.SOURCE_DURATION_MS);
store.dispatch(actions.setAnimSource(null)); } else if (r.variant[0].includes('Hit')) {
store.dispatch(actions.setAnimTarget(null)); const animTarget = getAnimTarget(r, game, account);
}, animTimeOut); store.dispatch(actions.setAnimTarget(animTarget));
store.dispatch(actions.setAnimText(null));
setTimeout(() => store.dispatch(actions.setAnimTarget(null)), TIMES.TARGET_DURATION_MS);
} else { } else {
store.dispatch(actions.setAnimText(r)); store.dispatch(actions.setAnimText(r));
} }

View File

@ -423,11 +423,11 @@ impl Construct {
match skill.cd { match skill.cd {
Some(cd) => { Some(cd) => {
skill.cd = Some(cd.saturating_add(turns)); skill.cd = Some(cd.saturating_add(turns));
events.push(EventVariant::CooldownIncrease { turns, skill: skill.skill }) events.push(EventVariant::CooldownIncrease { target: self.id, turns })
}, },
None => { None => {
skill.cd = Some(turns); 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<EventVariant> { pub fn recharge(&mut self, red_amount: usize, blue_amount: usize) -> Vec<EventVariant> {
let mut events = vec![]; let mut events = vec![];
let target = self.id;
if self.is_ko() { return events; } if self.is_ko() { return events; }
match self.affected(Effect::Invert) { match self.affected(Effect::Invert) {
@ -565,7 +565,7 @@ impl Construct {
let blue = new_blue_life - current_blue_life; let blue = new_blue_life - current_blue_life;
if red != 0 || blue != 0 { if red != 0 || blue != 0 {
events.push(EventVariant::Recharge { red, blue }); events.push(EventVariant::Recharge { target, red, blue });
} }
}, },
true => { true => {
@ -592,6 +592,7 @@ impl Construct {
let red_damage_amount = red_current_green_life - self.green_life(); let red_damage_amount = red_current_green_life - self.green_life();
events.push(EventVariant::Damage { events.push(EventVariant::Damage {
target,
amount: red_damage_amount, amount: red_damage_amount,
mitigation: red_mitigation, mitigation: red_mitigation,
colour: Colour::Red, colour: Colour::Red,
@ -621,6 +622,7 @@ impl Construct {
let blue_damage_amount = blue_current_green_life - self.green_life(); let blue_damage_amount = blue_current_green_life - self.green_life();
events.push(EventVariant::Damage { events.push(EventVariant::Damage {
target,
amount: blue_damage_amount, amount: blue_damage_amount,
mitigation: blue_mitigation, mitigation: blue_mitigation,
colour: Colour::Blue, colour: Colour::Blue,
@ -635,6 +637,7 @@ impl Construct {
pub fn deal_green_damage(&mut self, amount: usize) -> Vec<EventVariant> { pub fn deal_green_damage(&mut self, amount: usize) -> Vec<EventVariant> {
let mut events = vec![]; let mut events = vec![];
if self.is_ko() { return events; } if self.is_ko() { return events; }
let target = self.id;
let mods = self.effects.iter() let mods = self.effects.iter()
.filter(|e| e.effect.modifications().contains(&Stat::GreenDamageTaken)) .filter(|e| e.effect.modifications().contains(&Stat::GreenDamageTaken))
@ -654,6 +657,7 @@ impl Construct {
let overhealing = modified_power - healing; let overhealing = modified_power - healing;
events.push(EventVariant::Healing { events.push(EventVariant::Healing {
target,
amount: healing, amount: healing,
overhealing, overhealing,
}); });
@ -667,6 +671,7 @@ impl Construct {
let delta = current_green_life - self.green_life(); let delta = current_green_life - self.green_life();
events.push(EventVariant::Damage { events.push(EventVariant::Damage {
target,
amount: delta, amount: delta,
mitigation: 0, mitigation: 0,
colour: Colour::Green, colour: Colour::Green,
@ -682,6 +687,7 @@ impl Construct {
let mut events = vec![]; let mut events = vec![];
if self.is_ko() { return events; } if self.is_ko() { return events; }
let target = self.id;
let mods = self.effects.iter() let mods = self.effects.iter()
.filter(|e| e.effect.modifications().contains(&Stat::RedDamageTaken)) .filter(|e| e.effect.modifications().contains(&Stat::RedDamageTaken))
@ -710,6 +716,7 @@ impl Construct {
events.push( events.push(
EventVariant::Damage { EventVariant::Damage {
target,
amount: delta, amount: delta,
mitigation, mitigation,
colour: Colour::Red, colour: Colour::Red,
@ -717,7 +724,7 @@ impl Construct {
} }
); );
if self.is_ko() { if self.is_ko() {
events.push(EventVariant::Ko {}); events.push(EventVariant::Ko { target });
} }
}, },
true => { true => {
@ -736,6 +743,7 @@ impl Construct {
if healing > 0 { if healing > 0 {
events.push( events.push(
EventVariant::Healing { EventVariant::Healing {
target,
amount: healing, amount: healing,
overhealing: overhealing - recharge, overhealing: overhealing - recharge,
} }
@ -743,7 +751,7 @@ impl Construct {
} }
if recharge > 0 { 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![]; let mut events = vec![];
if self.is_ko() { return events; } if self.is_ko() { return events; }
let target = self.id;
let mods = self.effects.iter() let mods = self.effects.iter()
.filter(|e| e.effect.modifications().contains(&Stat::BlueDamageTaken)) .filter(|e| e.effect.modifications().contains(&Stat::BlueDamageTaken))
@ -778,6 +787,7 @@ impl Construct {
let delta = current_green_life - self.green_life(); let delta = current_green_life - self.green_life();
events.push(EventVariant::Damage { events.push(EventVariant::Damage {
target,
amount: delta, amount: delta,
mitigation, mitigation,
colour: Colour::Blue, colour: Colour::Blue,
@ -798,11 +808,11 @@ impl Construct {
let recharge = self.blue_life.value - current_life; let recharge = self.blue_life.value - current_life;
if healing > 0 { if healing > 0 {
events.push(EventVariant::Healing { amount: healing, overhealing }); events.push(EventVariant::Healing { target, amount: healing, overhealing });
} }
if recharge > 0 { 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 // todo modified durations cause of buffs
let result = EventVariant::Effect { let result = EventVariant::Effect {
target: self.id,
effect: effect.effect, effect: effect.effect,
duration: effect.duration, duration: effect.duration,
display: EventConstruct::new(self) display: EventConstruct::new(self)

View File

@ -41,7 +41,7 @@ pub enum Colour {
#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)]
pub enum Action { pub enum Action {
Hit { cast: Cast }, Hit { cast: Cast },
HitCast { cast: Cast }, Cast { cast: Cast },
Damage { cast: Cast, construct: Uuid, values: Vec<Value>, colour: Colour }, Damage { cast: Cast, construct: Uuid, values: Vec<Value>, colour: Colour },
Effect { cast: Cast, construct: Uuid, effect: ConstructEffect }, Effect { cast: Cast, construct: Uuid, effect: ConstructEffect },
IncreaseCooldowns { cast: Cast, construct: Uuid, turns: usize }, IncreaseCooldowns { cast: Cast, construct: Uuid, turns: usize },
@ -507,7 +507,7 @@ impl Game {
for action in actions { for action in actions {
match action { match action {
Action::Hit { cast } => self.hit(cast), 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::Damage { cast, construct, values, colour } => self.damage(cast, construct, values, colour),
Action::Effect { cast, construct, effect } => self.effect(cast, construct, effect), Action::Effect { cast, construct, effect } => self.effect(cast, construct, effect),
Action::IncreaseCooldowns { cast, construct, turns } => self.increase_cooldowns(cast, construct, turns), Action::IncreaseCooldowns { cast, construct, turns } => self.increase_cooldowns(cast, construct, turns),
@ -517,8 +517,8 @@ impl Game {
self self
} }
fn hit_cast(&mut self, cast: Cast) -> &mut Game { fn cast(&mut self, cast: Cast) -> &mut Game {
self.event_add(vec![EventVariant::HitCast()], cast); self.event_add(vec![EventVariant::Cast()], cast);
self self
} }

View File

@ -109,10 +109,13 @@ impl Cast {
} }
pub fn actions(&self) -> Vec<Action> { pub fn actions(&self) -> Vec<Action> {
let mut actions = match self.skill.is_tick() { let mut actions = vec![];
false => vec![Action::HitCast { cast: *self } ],
true => vec![Action::Hit { cast: *self } ] if self.skill.cast_animation() {
}; actions.push(Action::Cast { cast: *self });
}
actions.push(Action::Hit { cast: *self });
let mut rest = match self.skill { let mut rest = match self.skill {
Skill::Amplify => vec![ Skill::Amplify => vec![
@ -234,29 +237,23 @@ pub struct Event {
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub enum EventVariant { pub enum EventVariant {
Cast (),
Hit (), Hit (),
HitCast (), HitAoe (),
Damage { amount: usize, mitigation: usize, colour: Colour, display: EventConstruct }, Damage { target: Uuid, amount: usize, mitigation: usize, colour: Colour, display: EventConstruct },
Effect { effect: Effect, duration: u8, display: EventConstruct }, Effect { target: Uuid, effect: Effect, duration: u8, display: EventConstruct },
Removal { effect: Option<Effect>, display: EventConstruct }, Removal { target: Uuid, effect: Option<Effect>, display: EventConstruct },
Healing { amount: usize, overhealing: usize }, Healing { target: Uuid, amount: usize, overhealing: usize },
Recharge { red: usize, blue: usize }, Recharge { target: Uuid, red: usize, blue: usize },
Inversion { skill: Skill }, Inversion { target: Uuid },
Reflection { skill: Skill }, Reflection { target: Uuid },
AoeSkill { skill: Skill }, Ko { target: Uuid },
Skill { skill: Skill },
TargetKo { skill: Skill }, CooldownIncrease { target: Uuid, turns: usize },
// skill not necessary but makes it neater as all events are arrays in js CooldownDecrease { target: Uuid, turns: usize },
Ko (),
Forfeit (), Forfeit (),
Incomplete (),
// not used
Evasion { skill: Skill, evasion_rating: usize },
CooldownIncrease { turns: usize, skill: Skill },
CooldownDecrease { turns: usize, skill: Skill },
} }
impl EventVariant { impl EventVariant {
@ -269,7 +266,7 @@ impl EventVariant {
match self { match self {
EventVariant::Hit() => target_duration - combat_text_overlap, EventVariant::Hit() => target_duration - combat_text_overlap,
EventVariant::HitCast() => target_delay + target_duration - combat_text_overlap, EventVariant::Cast() => target_delay,
_ => combat_text_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 { pub fn defensive(&self) -> bool {
match self { match self {
Skill::Amplify| Skill::Amplify|
@ -1568,7 +1583,20 @@ mod tests {
let cast = Cast::new(Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4(), Skill::Attack); let cast = Cast::new(Uuid::new_v4(), Uuid::new_v4(), Uuid::new_v4(), Skill::Attack);
let actions = cast.actions(); 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] // #[test]