From 63983536d72ac29922aee4db16df67242c610b5d Mon Sep 17 00:00:00 2001 From: ntr Date: Fri, 2 Nov 2018 01:23:48 +1100 Subject: [PATCH 1/9] reeadme --- client/src/components/game.jsx | 5 +- server/README.md | 128 ++++++++++++++++++++++++++++++++- server/WORKLOG.md | 29 +++----- server/src/cryp.rs | 46 ++++++------ server/src/game.rs | 64 ++++++++++------- server/src/skill.rs | 82 +++++++++++++++++---- 6 files changed, 271 insertions(+), 83 deletions(-) diff --git a/client/src/components/game.jsx b/client/src/components/game.jsx index 71eeea98..82b8f954 100755 --- a/client/src/components/game.jsx +++ b/client/src/components/game.jsx @@ -155,6 +155,9 @@ function GamePanel(props) { return 'Game over'; } } + + const logs = game.log.reverse().map((l, i) => (
{l}
)); + return (
@@ -172,7 +175,7 @@ function GamePanel(props) {
-
log
+
{logs}
); diff --git a/server/README.md b/server/README.md index 07b9e5a4..dedf97d6 100755 --- a/server/README.md +++ b/server/README.md @@ -1,6 +1,6 @@ -# Cryps ("creeps") // Creeptography +# Cryps ("creeps") -## Setup +## Combat skill phase: 1.1 -> block (sp 10) -> on self @@ -20,3 +20,127 @@ resolve phase: 1.1 <- attack (no effect because of block) 2.2 <- attack (normal resolve) 1.1 <- hexed (no skills for the rest of this turn and next) + +## Dmg Chart + +| Physical | Magic | Modifiers | +| ------ | ------ | ------ | +| dmg | dmg | speed | +| evasion | resistance | cooldowns | +| reduction | absorption? | durations | + + +## Cryp Classes / Paths / Specialisations + +Organic / Combat +================ +Strength of the Individual / Attack & Defense +-------------------------- + +from viruses and bacteria to civilisations all organic cryps are contained within this group. +they are the original form of cryps to inhabit the universe. +they value individual strength and the ability to defend one's self. +having undergone natural selection they are combative by nature and feel threatened from all sides. +magic and advanced technology disturbs them as they are unable to understand it; +their response is to try and crush it and restore their place at the apex. +their fear is a manifestation of the emotions that are unique to organic cryps. +can be social and cooperative (primates) as well as isolated and agressive (wolves / snow leopards) + +* tactics and strategy + * rally + * physical damage + * rend / expose + * taunt +* martial arts and combat + * blocking + * evasion and redirection +* frenzy + +Artificial / Machines +===================== +Speed & Efficiency +------------------ + +artificial cryps are machines of any sort, from simple spring powered devices to vast self-aware networks. +whether created by organic cryps as tools or as ancient automated systems discovered deep in space, +artificial cryps are ubiquitous. +their artificial nature lends them to efficiency and instant reactions. +artificial cryps do not think, they simply act. +no motivation / emotions; just outcomes + +* efficiency + * reduced cooldowns + * increased speed +* replication + * drones / tokens +* technology + * information and analysis (other teams' makeup etc) + +Life / Vitality +=============== +Enhancement & Preservation +-------------------------- + +life based cryps value the sanctity of life above all else +they are defensive and gracious, seeking to minimise the damage done by others. +healing cryps they have allied themselves with, preventing incoming damage and rendering enemies harmless + +* healing + * hots + * direct healing +* buffs + * protection from effects + * damage reduction + +Death / Destruction +=================== +Damage & Destruction +------------------------- + +cryps that have given themselves over to death are obsessed with destruction in all of its forms +no price is too high, death centred cryps will gladly harm themselves and amplify this sacrifice +in the hopes of inflicting ruin on everything around them. + +* damage amplification +* nukes +* life leach +* life exchange +* poison + +Form / Matter +============= +Structure & Control +------------------- + +form focused cryps both enhance and exploit everything around them, +seeking to improve their allies abilities and expose weaknesses in their opponents, +they are builders and breakers, chasing perfection + +* Dispel, removal +* + +Void +===== +Power from an alternate reality +------------------------------- + +The void cryps originally entered into our universe from alternate reality. +their physical form is embedded with advanced technologies which allows manipulation of time and space. +the void cryps have been evolving in form since their arrival into the universe + +they value power over all else, their goal is to convert all sentient life into their void form + +manipulate physical reality to control and disrupt the flow of battle + +can take physical and astral forms + +* Banish +* Shadow damage +* Time control (reverse turn outcomes) +* increase cooldowns +* increase durations +* Slow + +## Styles +* Aztec +* Yokai / ukiyo-e \ No newline at end of file diff --git a/server/WORKLOG.md b/server/WORKLOG.md index 9b68c58e..7366c9bd 100755 --- a/server/WORKLOG.md +++ b/server/WORKLOG.md @@ -8,6 +8,8 @@ * move rpc functions out * unwrap account for all functions except list +* handle unserializable cryps + * Global rolls * Stats @@ -57,6 +59,11 @@ * run nginx as not root +# Art Styles +* Aztec +* Pixel +* Industrial + # Mechanic Ideas teams 1v1 2v2 3v3 @@ -89,6 +96,7 @@ gem td style attr combinations techno artists for the soundtrack + slimey ghostly @@ -114,24 +122,3 @@ gem td style attr combinations * 18: Restrictions breed creativity * 19: Your audience is good at recognizing problems and bad at solving them * 20: All the lessons connect - - - -skill phase: -1.1 -> block (sp 10) -> on self -1.2 -> attack (sp 5) -> on team 2 - -2.1 -> hex (sp 3) -> on team 1 -2.2 -> attack (sp 5) -> on team 1 - -target phase: -team 2 targets 1.2 on 2.2 - -team 1 targets 2.1 on 1.1 -team 1 targets 2.2 on 1.1 - -resolve phase: -1.1 <- block -1.1 <- attack (no effect because of block) -2.2 <- attack (normal resolve) -1.1 <- hexed (no skills for the rest of this turn and next) diff --git a/server/src/cryp.rs b/server/src/cryp.rs index da8fef4c..282675db 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -9,7 +9,8 @@ use failure::err_msg; use account::Account; use rpc::{CrypSpawnParams}; -use skill::{Skill, Cooldown, Roll}; +use game::{Log}; +use skill::{Skill, Cooldown, Roll, Effect}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct CrypSkill { @@ -29,15 +30,8 @@ impl CrypSkill { } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -pub enum Status { - Stunned, - Silenced, - Blocking, -} - -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -pub struct CrypStatus { - status: Status, +pub struct CrypEffect { + effect: Effect, duration: u8, } @@ -80,7 +74,7 @@ pub struct Cryp { pub xp: u64, pub lvl: u8, pub skills: Vec, - pub statuses: Vec, + pub effects: Vec, pub name: String, } @@ -103,7 +97,7 @@ impl Cryp { lvl: 0, xp: 0, skills: vec![CrypSkill::new(Skill::Attack)], - statuses: vec![], + effects: vec![], name: String::new() }; } @@ -165,7 +159,7 @@ impl Cryp { } pub fn is_stunned(&self) -> bool { - self.statuses.iter().any(|s| s.status == Status::Stunned) + self.effects.iter().any(|s| s.effect == Effect::Stun) } pub fn available_skills(&self) -> Vec<&CrypSkill> { @@ -207,17 +201,17 @@ impl Cryp { self } - pub fn reduce_statuses(&mut self) -> &mut Cryp { - self.statuses = self.statuses.clone().into_iter().filter_map(|mut s| { + pub fn reduce_effect_durations(&mut self) -> &mut Cryp { + self.effects = self.effects.clone().into_iter().filter_map(|mut s| { s.duration = s.duration.saturating_sub(1); if s.duration == 0 { return None; } - println!("reduced status {:?}", s); + println!("reduced effect {:?}", s); return Some(s); - }).collect::>(); + }).collect::>(); self } @@ -246,20 +240,26 @@ impl Cryp { return roll; } - pub fn stun(&mut self, _roll: Roll) -> &mut Cryp { - if !self.statuses.iter().any(|s| s.status == Status::Blocking) { - self.statuses.push(CrypStatus { status: Status::Stunned, duration: Skill::Stun.duration() }); + pub fn stun(&mut self, _roll: Roll, log: &mut Log) -> &mut Cryp { + if !self.effects.iter().any(|s| s.effect == Effect::Block) { + let stun = CrypEffect { effect: Effect::Stun, duration: Skill::Stun.duration() }; + self.effects.push(stun); + log.push(format!("{:?} is {:?} for {:?}T", self.name, stun.effect, stun.duration)) + } else { + log.push(format!("{:?} blocks.", self.name)) } self } - pub fn attack(&mut self, roll: Roll) -> &mut Cryp { + pub fn attack(&mut self, roll: Roll, log: &mut Log) -> &mut Cryp { self.hp.reduce(roll.result); self } - pub fn block(&mut self, _roll: Roll) -> &mut Cryp { - self.statuses.push(CrypStatus { status: Status::Blocking, duration: Skill::Block.duration() }); + pub fn block(&mut self, _roll: Roll, log: &mut Log) -> &mut Cryp { + let effect = CrypEffect { effect: Effect::Block, duration: Skill::Block.duration() }; + self.effects.push(effect); + log.push(format!("{:?} is {:?} for {:?}T", self.name, effect.effect, effect.duration)); self } diff --git a/server/src/game.rs b/server/src/game.rs index 728dd219..2241d983 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -12,6 +12,8 @@ use rpc::{GameStateParams, GameSkillParams, GamePveParams, GamePvpParams, GameTa use cryp::{Cryp, cryp_get}; use skill::{Skill, Cast}; +pub type Log = Vec; + #[derive(Debug,Clone,Serialize,Deserialize)] pub struct Team { id: Uuid, @@ -47,7 +49,7 @@ pub enum Phase { Start, Skill, Target, - Damage, + Resolve, Finish, } @@ -94,11 +96,18 @@ impl Game { self } - // check team not already in fn add_team(&mut self, team: Team) -> Result<&mut Game, Error> { if self.teams.len() == self.team_num { return Err(err_msg("maximum number of teams")); } + + if self.teams.iter().any(|t| t.id == team.id) { + return Err(err_msg("team already in game")); + } + + let team_description = team.cryps.iter().map(|c| c.name.clone()).collect::>().join(", "); + self.log.push(format!("{:?} has joined the game.", team_description)); + self.teams.push(team); Ok(self) @@ -139,13 +148,17 @@ impl Game { } fn start(&mut self) -> &mut Game { + self.log.push("Game starting...".to_string()); + self.skill_phase_start(); self } fn skill_phase_start(&mut self) -> &mut Game { - if ![Phase::Start, Phase::Damage].contains(&self.phase) { - panic!("game not in damage or start phase"); + self.log.push("".to_string()); + + if ![Phase::Start, Phase::Resolve].contains(&self.phase) { + panic!("game not in Resolve or start phase"); } self.phase = Phase::Skill; @@ -175,8 +188,6 @@ impl Game { self } - // skills can target any team, but we have to check if the caller is the owner of the cryp - // and that the cryp has the skill they are trying to add fn add_skill(&mut self, team_id: Uuid, source_cryp_id: Uuid, target_team_id: Option, skill: Skill) -> Result { if self.phase != Phase::Skill { return Err(err_msg("game not in skill phase")); @@ -207,7 +218,7 @@ impl Game { // check here as well so uncastable spells don't go on the stack if !skill.castable(&cryp) { - return Err(err_msg("cryp cannot cast spell")); + return Err(err_msg("cryp cannot cast that skill")); } } @@ -228,16 +239,16 @@ impl Game { // for every team .all(|t| self.stack.iter() // the number of skills they have cast - .filter(|s| s.source_team_id == t.id) - .collect::>() + .filter(|s| s.source_team_id == t.id).collect::>() // should equal the number required this turn .len() == t.skills_required() ) } - // move all skills into their target team's targets list fn target_phase_start(&mut self) -> &mut Game { assert!(self.skill_phase_finished()); + self.log.push("".to_string()); + if self.phase != Phase::Skill { panic!("game not in skill phase"); } @@ -250,7 +261,7 @@ impl Game { // all cryps are stunned or otherwise inactive if self.target_phase_finished() { - self.damage_phase_start(); + self.resolve_phase_start(); } self @@ -311,12 +322,14 @@ impl Game { // requires no input // just do it - fn damage_phase_start(&mut self) -> &mut Game { + fn resolve_phase_start(&mut self) -> &mut Game { if self.phase != Phase::Target { panic!("game not in target phase"); } + assert!(self.target_phase_finished()); - self.phase = Phase::Damage; + self.phase = Phase::Resolve; + self.log.push("".to_string()); self.resolve_skills(); @@ -330,8 +343,8 @@ impl Game { } fn resolve_skills(&mut self) -> &mut Game { - if self.phase != Phase::Damage { - panic!("game not in damage phase"); + if self.phase != Phase::Resolve { + panic!("game not in Resolve phase"); } self.stack.sort_unstable_by_key(|s| s.skill.speed()); @@ -342,16 +355,18 @@ impl Game { let mut source = self.cryp_by_id(skill.source_cryp_id).unwrap().clone(); let mut target = self.cryp_by_id(skill.target_cryp_id.unwrap()).unwrap().clone(); - let resolution = skill.resolve(&mut source, &mut target); + self.log.push(format!("{:?} uses {:?} on {:?}", source.name, skill.skill, target.name)); + let resolution = skill.resolve(&mut source, &mut target, &mut self.log); self.resolved.push(*resolution); + self.update_cryp(&mut source); self.update_cryp(&mut target); return *resolution; }).collect::>(); - // now damage has all been assigned + // now Resolve has all been assigned // handle cooldowns and statuses self.progress_durations(); @@ -378,7 +393,7 @@ impl Game { } // always reduce durations - cryp.reduce_statuses(); + cryp.reduce_effect_durations(); self.update_cryp(&mut cryp); } @@ -455,7 +470,7 @@ pub fn game_target(params: GameTargetParams, tx: &mut Transaction, account: &Acc game.add_target(account.id, params.cryp_id, params.skill_id)?; if game.target_phase_finished() { - game.damage_phase_start(); + game.resolve_phase_start(); } game_update(&game, tx)?; @@ -753,7 +768,7 @@ mod tests { assert!(game.target_phase_finished()); - game.damage_phase_start(); + game.resolve_phase_start(); assert!([Phase::Skill, Phase::Finish].contains(&game.phase)); @@ -780,7 +795,7 @@ mod tests { game.add_target(y_team.id, y_cryp.id, x_stun_id).unwrap(); assert!(game.target_phase_finished()); - game.damage_phase_start(); + game.resolve_phase_start(); // should auto progress back to skill phase assert!(game.phase == Phase::Skill); @@ -807,7 +822,7 @@ mod tests { game.add_target(x_team.id, x_cryp.id, y_attack_id).unwrap(); game.add_target(y_team.id, y_cryp.id, x_stun_id).unwrap(); - game.damage_phase_start(); + game.resolve_phase_start(); // should auto progress back to skill phase assert!(game.phase == Phase::Skill); @@ -823,7 +838,7 @@ mod tests { game.target_phase_start(); // game.add_target(x_team.id, x_cryp.id, y_block_id).unwrap(); // game.add_target(y_team.id, y_cryp.id, x_block_id).unwrap(); - // game.damage_phase_start(); + // game.resolve_phase_start(); assert!(game.team_by_id(y_team.id).cryps[0].skill_on_cd(Skill::Block).is_some()); assert!(game.team_by_id(x_team.id).cryps[0].skill_on_cd(Skill::Block).is_some()); @@ -855,10 +870,11 @@ mod tests { game.add_target(x_team.id, x_cryp.id, y_attack_id).unwrap(); - game.damage_phase_start(); + game.resolve_phase_start(); // should not be stunned because of block assert!(game.team_by_id(x_team.id).cryps[0].is_stunned() == false); + println!("{:#?}", game.log); } } diff --git a/server/src/skill.rs b/server/src/skill.rs index 63e710f9..12fc63d2 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -2,6 +2,7 @@ use uuid::Uuid; use cryp::{Cryp, CrypSkill, CrypStat}; +use game::{Log}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct Roll { @@ -15,9 +16,16 @@ pub type Cooldown = Option; pub enum Skill { Attack, Block, - Heal, Stun, Dodge, + Banish, + Heal, + Accuracy, + Evasion, + Amplify, + HoT, + DoT, + // used by tests, no cd, no dmg TestTouch, TestStun, @@ -28,10 +36,20 @@ impl Skill { pub fn cd(&self) -> Cooldown { match self { Skill::Attack => None, + Skill::Block => Some(1), Skill::Dodge => Some(1), + Skill::Amplify => Some(1), + Skill::Evasion => Some(1), + Skill::Accuracy => Some(1), + Skill::Heal => Some(2), Skill::Stun => Some(2), + Skill::DoT => Some(2), + Skill::HoT => Some(2), + + Skill::Banish => Some(3), + Skill::TestTouch => None, Skill::TestStun => None, Skill::TestBlock => None, @@ -40,11 +58,21 @@ impl Skill { pub fn speed(&self) -> u8 { match self { + Skill::Dodge => 12, Skill::Attack => 10, + Skill::Block => 5, - Skill::Dodge => 5, + Skill::Amplify => 5, + Skill::Accuracy => 5, + Skill::Evasion => 5, + Skill::HoT => 5, + Skill::DoT => 5, + Skill::Heal => 2, Skill::Stun => 2, + Skill::Banish => 2, + + // test skills Skill::TestTouch => 10, Skill::TestStun => 2, Skill::TestBlock => 5, @@ -58,6 +86,12 @@ impl Skill { Skill::Stun => cryp.str, Skill::Dodge => cryp.agi, Skill::Heal => cryp.int, + Skill::Banish => cryp.str, + Skill::Evasion => cryp.str, + Skill::Accuracy => cryp.str, + Skill::Amplify => cryp.str, + Skill::HoT => cryp.str, + Skill::DoT => cryp.str, // test skills Skill::TestTouch => cryp.int, @@ -125,27 +159,29 @@ impl Cast { }; } - pub fn resolve(&mut self, cryp: &mut Cryp, target: &mut Cryp) -> &mut Cast { + pub fn resolve(&mut self, cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> &mut Cast { let roll = cryp.roll(self.skill); - println!("{:?} -> {:?} -> {:?}", cryp.name, self.skill, target.name); - match self.skill { // the real deal - Skill::Stun => target.stun(roll), - Skill::Attack => target.attack(roll), - Skill::Block => target.block(roll), + Skill::Stun => target.stun(roll, log), + Skill::Attack => target.attack(roll, log), + Skill::Block => target.block(roll, log), Skill::Heal => target, Skill::Dodge => target, + Skill::Banish => target, + Skill::Accuracy => target, + Skill::Evasion => target, + Skill::Amplify => target, + Skill::HoT => target, + Skill::DoT => target, // Test Skills - Skill::TestStun => target.stun(roll), - Skill::TestBlock => target.block(roll), + Skill::TestStun => target.stun(roll, log), + Skill::TestBlock => target.block(roll, log), Skill::TestTouch => target, }; - // println!("{:?} gettin clapped for {:?}", target.name, roll.result); - self.roll = Some(roll); self } @@ -161,6 +197,28 @@ impl Cast { } } +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +pub enum Effect { + // Skill Effects + Stun, + Silence, + Banish, + Block, + Haste, + Slow, + Regen, + Degen, + Bleed, + Leech, + Airborne, + Immune, + + // Passives / items + Ghost, + Stone, + Evasion, +} + // #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] // pub enum Skill { // Stoney, From c4b20c786d4e0d01927aa69175261caa53779546 Mon Sep 17 00:00:00 2001 From: ntr Date: Fri, 2 Nov 2018 13:25:59 +1100 Subject: [PATCH 2/9] more flava --- server/README.md | 94 +++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 49 deletions(-) diff --git a/server/README.md b/server/README.md index dedf97d6..5cf144b4 100755 --- a/server/README.md +++ b/server/README.md @@ -30,21 +30,17 @@ resolve phase: | reduction | absorption? | durations | -## Cryp Classes / Paths / Specialisations +## Cryp Alignments -Organic / Combat +Natural Selection ================ -Strength of the Individual / Attack & Defense --------------------------- - -from viruses and bacteria to civilisations all organic cryps are contained within this group. -they are the original form of cryps to inhabit the universe. +Survival of the fittest / Strength of the Individual / Attack & Defense +----------------------------------------------------------------------- they value individual strength and the ability to defend one's self. having undergone natural selection they are combative by nature and feel threatened from all sides. magic and advanced technology disturbs them as they are unable to understand it; their response is to try and crush it and restore their place at the apex. -their fear is a manifestation of the emotions that are unique to organic cryps. -can be social and cooperative (primates) as well as isolated and agressive (wolves / snow leopards) +their fear is a manifestation of the emotions and prejudices they have grown in order to survive. * tactics and strategy * rally @@ -56,90 +52,90 @@ can be social and cooperative (primates) as well as isolated and agressive (wolv * evasion and redirection * frenzy -Artificial / Machines +Machine Cult ===================== -Speed & Efficiency ------------------- +Everything Connected / Speed & Efficiency +----------------------------------------- +members of the Machine Cult worship artificial machines of any sort, from simple spring powered devices to vast self-aware networks. +they value speed and efficiency above all else. +the individual has little significance for the machine cult, its members are rushing headlong into state of complete connectedness +they long to transcend beyond their physical limitations and become vaster and more powerful than the sum of each part -artificial cryps are machines of any sort, from simple spring powered devices to vast self-aware networks. -whether created by organic cryps as tools or as ancient automated systems discovered deep in space, -artificial cryps are ubiquitous. -their artificial nature lends them to efficiency and instant reactions. -artificial cryps do not think, they simply act. -no motivation / emotions; just outcomes +rigid, often serving a very specific purpose, not as adaptable as the other alignments +they and their machinations do not think, they simply act. +no motivation / no emotions / no tricks; just action. * efficiency * reduced cooldowns * increased speed * replication * drones / tokens -* technology - * information and analysis (other teams' makeup etc) -Life / Vitality +Non-Violence =============== Enhancement & Preservation -------------------------- -life based cryps value the sanctity of life above all else -they are defensive and gracious, seeking to minimise the damage done by others. -healing cryps they have allied themselves with, preventing incoming damage and rendering enemies harmless +the philosophy of nonviolence teaches that the sanctity of life is above all else +its adherants are defensive and gracious, seeking to minimise the damage done by others and doing no direct harm themselves. +they seek to prevent damage in any way possible * healing * hots * direct healing -* buffs +* defensive buffs * protection from effects * damage reduction -Death / Destruction +Path to Destruction =================== Damage & Destruction ------------------------- -cryps that have given themselves over to death are obsessed with destruction in all of its forms -no price is too high, death centred cryps will gladly harm themselves and amplify this sacrifice -in the hopes of inflicting ruin on everything around them. +cryps walking the path to destruction have forsaken themselves in order to gain ruinous power. +no price is too high, they gladly harm themselves and allies to amplify the destruction they wreak on everything around them +specialise in magical damage dealing * damage amplification * nukes * life leach * life exchange * poison +* aoe -Form / Matter + +The Tribunal ============= -Structure & Control +Fuck Magic ------------------- -form focused cryps both enhance and exploit everything around them, -seeking to improve their allies abilities and expose weaknesses in their opponents, -they are builders and breakers, chasing perfection +The Tribunal has decided that magic is an abomination. +Its purpose is to seek out and suppress any magical activity. +Additionally it purges magic from amongst its own ranks. * Dispel, removal -* +* Silence +* Magic resistance +* Information gathering + * team composition + * available skills etc -Void -===== -Power from an alternate reality -------------------------------- +Universal Chaos +=============== +The only constant is change. +---------------------------- -The void cryps originally entered into our universe from alternate reality. -their physical form is embedded with advanced technologies which allows manipulation of time and space. -the void cryps have been evolving in form since their arrival into the universe - -they value power over all else, their goal is to convert all sentient life into their void form - -manipulate physical reality to control and disrupt the flow of battle - -can take physical and astral forms +Cryps aligning themselves with the forces of chaos believe that constant change is the only truth in the universe. +They harness this power to manipulate physical reality as well as control and disrupt the flow of battle +They blend between physical and astral forms, constantly shifting throughout time and space. * Banish -* Shadow damage +* Chaos damage * Time control (reverse turn outcomes) * increase cooldowns * increase durations * Slow +* damage redirection ## Styles * Aztec From 0a1f34f41b9633a93265d5b4fed734475105dbc6 Mon Sep 17 00:00:00 2001 From: ntr Date: Fri, 2 Nov 2018 18:04:00 +1100 Subject: [PATCH 3/9] graph time --- ops/package.json | 3 +++ server/README.md | 7 +++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ops/package.json b/ops/package.json index 3ee5cdf7..6ac74db4 100755 --- a/ops/package.json +++ b/ops/package.json @@ -11,6 +11,9 @@ "author": "", "license": "UNLICENSED", "dependencies": { + "ascii-tree": "^0.3.0", + "cli-ascii-tree": "0.0.4", + "inquirer": "^6.2.0", "knex": "^0.15.2", "pg": "^7.4.3" } diff --git a/server/README.md b/server/README.md index 5cf144b4..adab2a60 100755 --- a/server/README.md +++ b/server/README.md @@ -109,9 +109,8 @@ The Tribunal Fuck Magic ------------------- -The Tribunal has decided that magic is an abomination. -Its purpose is to seek out and suppress any magical activity. -Additionally it purges magic from amongst its own ranks. +The Tribunal has ruled that magic is an abomination. +Its members now scour the lands in search of magic, censoring its teaching, purging its effects and slaying the heretics who wield it. * Dispel, removal * Silence @@ -126,7 +125,7 @@ The only constant is change. ---------------------------- Cryps aligning themselves with the forces of chaos believe that constant change is the only truth in the universe. -They harness this power to manipulate physical reality as well as control and disrupt the flow of battle +They harness its power to manipulate physical reality as well as control and disrupt the flow of battle. They blend between physical and astral forms, constantly shifting throughout time and space. * Banish From 58f186d2fdeafe53b8df50ba46712820c753c11f Mon Sep 17 00:00:00 2001 From: ntr Date: Thu, 8 Nov 2018 00:00:46 +1100 Subject: [PATCH 4/9] passives and lots and lots of skills --- client/cryps.css | 3 + client/index.js | 2 + client/src/components/game.jsx | 12 +- server/Cargo.toml | 1 + server/src/cryp.rs | 91 ++---- server/src/game.rs | 6 +- server/src/main.rs | 2 + server/src/passives.rs | 70 +++++ server/src/skill.rs | 532 ++++++++++++++++++++++++--------- 9 files changed, 508 insertions(+), 211 deletions(-) create mode 100644 client/cryps.css create mode 100644 server/src/passives.rs diff --git a/client/cryps.css b/client/cryps.css new file mode 100644 index 00000000..5055091d --- /dev/null +++ b/client/cryps.css @@ -0,0 +1,3 @@ +body { + background-color: #181818; +} diff --git a/client/index.js b/client/index.js index 0787daa8..6ad41876 100755 --- a/client/index.js +++ b/client/index.js @@ -1,2 +1,4 @@ +require('./cryps.css'); + // kick it off require('./src/main'); diff --git a/client/src/components/game.jsx b/client/src/components/game.jsx index 82b8f954..0a81b6a7 100755 --- a/client/src/components/game.jsx +++ b/client/src/components/game.jsx @@ -55,8 +55,8 @@ function GamePanel(props) { ); }); - const statuses = cryp.statuses.map((status, i) => ( -
{status} for {status.turns}T
+ const effects = cryp.effects.map((effect, i) => ( +
{effect} for {effect.turns}T
)); return ( @@ -84,7 +84,7 @@ function GamePanel(props) {
{cryp.xp} / {Math.pow(2, cryp.lvl + 1)} XP
- {statuses} + {effects} {skills} ); @@ -101,8 +101,8 @@ function GamePanel(props) { } function OpponentCrypCard(cryp) { - const statuses = cryp.statuses.map((status, i) => ( -
{status.status} for {status.turns}T
+ const effects = cryp.effects.map((effect, i) => ( +
{effect.effect} for {effect.turns}T
)); return ( @@ -127,7 +127,7 @@ function GamePanel(props) { - {statuses} + {effects} ); } diff --git a/server/Cargo.toml b/server/Cargo.toml index d64ff304..725929a2 100755 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -12,6 +12,7 @@ serde_cbor = "0.9" tungstenite = "0.6" bcrypt = "0.2" +petgraph = "0.4" dotenv = "0.9.0" env_logger = "*" diff --git a/server/src/cryp.rs b/server/src/cryp.rs index 282675db..5cc5248e 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -10,7 +10,7 @@ use failure::err_msg; use account::Account; use rpc::{CrypSpawnParams}; use game::{Log}; -use skill::{Skill, Cooldown, Roll, Effect}; +use skill::{Skill, Cooldown, Effect}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct CrypSkill { @@ -31,8 +31,8 @@ impl CrypSkill { #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct CrypEffect { - effect: Effect, - duration: u8, + pub effect: Effect, + pub duration: u8, } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -40,6 +40,8 @@ pub enum Stat { Str, Agi, Int, + PhysicalDmg, + SpellPower, Hp, Stam, } @@ -66,10 +68,9 @@ impl CrypStat { pub struct Cryp { pub id: Uuid, pub account: Uuid, - pub str: CrypStat, - pub agi: CrypStat, - pub int: CrypStat, - pub stam: CrypStat, + pub phys_dmg: CrypStat, + pub spell_dmg: CrypStat, + pub stamina: CrypStat, pub hp: CrypStat, pub xp: u64, pub lvl: u8, @@ -89,10 +90,9 @@ impl Cryp { return Cryp { id, account: id, - str: CrypStat { value: 0, stat: Stat::Str }, - agi: CrypStat { value: 0, stat: Stat::Agi }, - int: CrypStat { value: 0, stat: Stat::Int }, - stam: CrypStat { value: 0, stat: Stat::Stam }, + phys_dmg: CrypStat { value: 0, stat: Stat::Str }, + spell_dmg: CrypStat { value: 0, stat: Stat::Int }, + stamina: CrypStat { value: 0, stat: Stat::Stam }, hp: CrypStat { value: 0, stat: Stat::Hp }, lvl: 0, xp: 0, @@ -143,13 +143,17 @@ impl Cryp { false => 2_u64.pow(self.lvl.into()), }; + let min = match self.lvl == 1 { + true => 2_u64, + false => 2_u64.pow(self.lvl.saturating_sub(1).into()), + }; + self.xp = max; - self.str.set(rng.gen_range(1, max)); - self.agi.set(rng.gen_range(1, max)); - self.int.set(rng.gen_range(1, max)); - self.stam.set(rng.gen_range(1, max)); - self.hp.set(self.stam.value); + self.phys_dmg.set(rng.gen_range(min, max)); + self.spell_dmg.set(rng.gen_range(min, max)); + self.stamina.set(rng.gen_range(min, max)); + self.hp.set(self.stamina.value); self } @@ -217,52 +221,9 @@ impl Cryp { } pub fn rez(&mut self) -> &mut Cryp { - self.hp.set(self.stam.value); + self.hp.set(self.stamina.value); self } - - pub fn roll(&self, skill: Skill) -> Roll { - let mut rng = thread_rng(); - let base: u64 = rng.gen(); - - let stat = skill.stat(self); - - let mut roll = Roll { base, result: base }; - - println!("{:?}'s stats", self.name); - println!("{:064b} <- finalised", roll.result); - roll.result = roll.result & stat.value; - - println!("{:064b} & <- attribute roll", stat.value); - println!("{:064b} = {:?}", roll.result, roll.result); - println!(""); - - return roll; - } - - pub fn stun(&mut self, _roll: Roll, log: &mut Log) -> &mut Cryp { - if !self.effects.iter().any(|s| s.effect == Effect::Block) { - let stun = CrypEffect { effect: Effect::Stun, duration: Skill::Stun.duration() }; - self.effects.push(stun); - log.push(format!("{:?} is {:?} for {:?}T", self.name, stun.effect, stun.duration)) - } else { - log.push(format!("{:?} blocks.", self.name)) - } - self - } - - pub fn attack(&mut self, roll: Roll, log: &mut Log) -> &mut Cryp { - self.hp.reduce(roll.result); - self - } - - pub fn block(&mut self, _roll: Roll, log: &mut Log) -> &mut Cryp { - let effect = CrypEffect { effect: Effect::Block, duration: Skill::Block.duration() }; - self.effects.push(effect); - log.push(format!("{:?} is {:?} for {:?}T", self.name, effect.effect, effect.duration)); - self - } - } pub fn cryp_get(tx: &mut Transaction, id: Uuid, account_id: Uuid) -> Result { @@ -350,13 +311,13 @@ mod tests { // pub fn assign_str(&mut self, opp: &Cryp, plr_t: &mut Turn, opp_t: &Turn) -> &mut Cryp { - // // let final_str = opp_t.str.result.saturating_sub(plr_t.agi.result); - // // let blocked = opp_t.str.result.saturating_sub(final_str); + // // let final_str = opp_t.phys_dmg.result.saturating_sub(plr_t.agi.result); + // // let blocked = opp_t.phys_dmg.result.saturating_sub(final_str); - // let final_str = opp_t.str.result & !plr_t.agi.result; - // let blocked = opp_t.str.result & plr_t.agi.result; + // let final_str = opp_t.phys_dmg.result & !plr_t.agi.result; + // let blocked = opp_t.phys_dmg.result & plr_t.agi.result; - // plr_t.log.push(format!("{:064b} <- attacking roll {:?}", opp_t.str.result, opp_t.str.result)); + // plr_t.log.push(format!("{:064b} <- attacking roll {:?}", opp_t.phys_dmg.result, opp_t.phys_dmg.result)); // // plr_t.log.push(format!("{:064b} <- blocking roll {:?}", plr_t.agi.result, plr_t.agi.result)); // plr_t.log.push(format!("{:064b} <- final str {:?} ({:?} blocked)", final_str, final_str, blocked)); diff --git a/server/src/game.rs b/server/src/game.rs index 2241d983..d0dd47bd 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -356,14 +356,14 @@ impl Game { let mut target = self.cryp_by_id(skill.target_cryp_id.unwrap()).unwrap().clone(); self.log.push(format!("{:?} uses {:?} on {:?}", source.name, skill.skill, target.name)); - let resolution = skill.resolve(&mut source, &mut target, &mut self.log); - self.resolved.push(*resolution); + let resolution = skill.set_resolution(&mut source, &mut target, &mut self.log); + self.resolved.push(resolution.clone()); self.update_cryp(&mut source); self.update_cryp(&mut target); - return *resolution; + return resolution.clone(); }).collect::>(); // now Resolve has all been assigned diff --git a/server/src/main.rs b/server/src/main.rs index 7189ae3c..a52e5dfd 100755 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -5,6 +5,7 @@ extern crate env_logger; extern crate bcrypt; extern crate dotenv; +extern crate petgraph; extern crate postgres; extern crate r2d2; extern crate r2d2_postgres; @@ -21,6 +22,7 @@ mod cryp; mod game; mod net; mod skill; +mod passives; mod rpc; mod account; mod item; diff --git a/server/src/passives.rs b/server/src/passives.rs new file mode 100644 index 00000000..6ecc5d97 --- /dev/null +++ b/server/src/passives.rs @@ -0,0 +1,70 @@ +use petgraph::graph::{Graph, UnGraph, NodeIndex}; +use petgraph::dot::{Dot, Config}; + +#[derive(Debug,Clone,Copy,PartialEq,Eq,Hash,PartialOrd,Ord,Serialize,Deserialize)] +pub struct Passive { + id: &'static str, + allocated: bool, + effect: usize, +} + +impl Passive { + fn new(id: &'static str) -> Passive { + return Passive { + id, + allocated: false, + effect: 0, + }; + } +} + +pub fn create_passive_graph() -> UnGraph { + let mut gr = Graph::new_undirected(); + + let start = gr.add_node(Passive::new("START")); + let mut last; + let mut next; + + // Natural Selection nodes + next = gr.add_node(Passive::new("NS")); + gr.add_edge(start, next, ()); + last = next; + + next = gr.add_node(Passive::new("NSPD0000")); + gr.add_edge(last, next, ()); + last = next; + + next = gr.add_node(Passive::new("NSPD0001")); + gr.add_edge(last, next, ()); + last = next; + + next = gr.add_node(Passive::new("NSPD0002")); + gr.add_edge(last, next, ()); + last = next; + + next = gr.add_node(Passive::new("NSPD0003")); + gr.add_edge(last, next, ()); + last = next; + + next = gr.add_node(Passive::new("NSBLOCK")); + gr.add_edge(last, next, ()); + last = next; + + return gr; +} + +#[cfg(test)] +mod tests { + use passives::*; + + #[test] + fn create_graph() { + let _graph = create_passive_graph(); + // good shit; + // let nodes = graph.node_indices().collect::>(); + // println!("{:?}", nodes[0]); + // println!("{:?}", graph.node_weight(nodes[0])); + + // println!("{:?}", Dot::with_config(&graph, &[Config::EdgeNoLabel])); + } +} diff --git a/server/src/skill.rs b/server/src/skill.rs index 12fc63d2..1bbfad12 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -1,30 +1,179 @@ -// use rand::prelude::*; +use rand::prelude::*; use uuid::Uuid; -use cryp::{Cryp, CrypSkill, CrypStat}; use game::{Log}; +use cryp::{Cryp, CrypEffect}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -pub struct Roll { +pub struct Cast { + pub id: Uuid, + pub skill: Skill, + pub source_team_id: Uuid, + pub source_cryp_id: Uuid, + pub target_cryp_id: Option, + pub target_team_id: Uuid, + pub resolution: Option, +} + +impl Cast { + pub fn new(source_cryp_id: Uuid, source_team_id: Uuid, target_team_id: Option, skill: Skill) -> Cast { + + let (target_cryp_id, target_team_id) = match skill.self_targeting() { + true => (Some(source_cryp_id), source_team_id), + false => (None, target_team_id.unwrap()) + }; + + return Cast { + id: Uuid::new_v4(), + source_cryp_id, + source_team_id, + target_cryp_id, + target_team_id, + skill, + resolution: None, + }; + } + + pub fn set_resolution(&mut self, cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> &mut Cast { + self.resolution = Some(self.skill.resolve(cryp, target, log)); + self + } + + pub fn set_target(&mut self, cryp_id: Uuid) -> &mut Cast { + self.target_cryp_id = Some(cryp_id); + self + } + + pub fn used_cooldown(self) -> bool { + return self.skill.cd().is_some(); + } +} + +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +pub struct Resolution { pub base: u64, - pub result: u64, + pub result: Option, } pub type Cooldown = Option; +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +pub enum Effect { + // physical + Stun, + Block, + Bleed, + Leech, + Airborne, + Untouchable, + Deadly, + Vulnerable, + Fury, + Evasion, + Blind, + + // magic + Silence, + Banish, + Slow, + Haste, + Enslave, + Mesmerise, + + // magic immunity + Immune, + + // effects over time + Triage, + Decay, + Regen, + Degen, + + SpeedDrain, + SpeedIncrease, +} + +impl Effect { + pub fn prevents(&self, skill: Skill) -> bool { + match self { + Effect::Block => match skill { + Skill::Stun => true, + _ => false, + }, + _ => false, + } + } +} + #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub enum Skill { Attack, - Block, + + // ----------------- + // Nature + // ----------------- + Block, // reduce dmg + Parry, // avoid all dmg + Snare, + + Paralyse, + + // Strangle + Stun, Dodge, - Banish, + Evasion, // additional layer of dmg avoidance + + + // ----------------- + // Technology + // ----------------- + Replicate, + Swarm, + Orbit, + Repair, + Scout, // track? + + // ----------------- + // Nonviolence + // ----------------- Heal, - Accuracy, - Evasion, + Triage, // hot + Throw, // no dmg stun, adds vulnerable + Charm, + Calm, + Rez, + + // ------------------- + // Destruction + // ------------------- + Blast, Amplify, - HoT, - DoT, + Decay, // dot + Drain, + Curse, + Plague, // aoe dot + Ruin, // aoe + + // ----------------- + // Purity + // ----------------- + Accuracy, + Inspire, + Slay, + Shield, + Silence, + Inquiry, + Purify, + + // ----------------- + // Chaos + // ----------------- + Banish, + Hex, + Fear, + Taunt, + Pause, // speed slow // used by tests, no cd, no dmg TestTouch, @@ -37,19 +186,74 @@ impl Skill { match self { Skill::Attack => None, - Skill::Block => Some(1), - Skill::Dodge => Some(1), - Skill::Amplify => Some(1), - Skill::Evasion => Some(1), + // ----------------- + // Nature + // ----------------- + Skill::Block => Some(1), // reduce dmg + Skill::Parry => Some(1), // avoid all dmg + Skill::Snare => Some(2), + + Skill::Paralyse => Some(3), + + // Strangle + + Skill::Stun => Some(1), + Skill::Dodge => Some(2), + Skill::Evasion => Some(3), // additional layer of dmg avoidance + + // ----------------- + // Technology + // ----------------- + Skill::Replicate => Some(1), + Skill::Swarm => Some(3), + Skill::Orbit => Some(2), + Skill::Repair => Some(1), + Skill::Scout => Some(2), // track? + + // ----------------- + // Preservation + // ----------------- + Skill::Heal => Some(1), + Skill::Triage => Some(1), // hot + Skill::Throw => Some(2), // no dmg stun, adds vulnerable + Skill::Charm => Some(2), + Skill::Calm => Some(2), + Skill::Rez => Some(4), + + // ----------------- + // Destruction + // ----------------- + Skill::Blast => Some(1), + Skill::Amplify => Some(2), + Skill::Decay => Some(1), // dot + Skill::Drain => Some(2), + Skill::Curse => Some(2), + Skill::Plague => Some(2), // aoe dot + Skill::Ruin => Some(3), // aoe + + // ----------------- + // Purity + // ----------------- Skill::Accuracy => Some(1), + Skill::Inspire => Some(2), + Skill::Slay => Some(1), + Skill::Shield => Some(1), + Skill::Silence => Some(2), + Skill::Inquiry => Some(2), + Skill::Purify => Some(1), - Skill::Heal => Some(2), - Skill::Stun => Some(2), - Skill::DoT => Some(2), - Skill::HoT => Some(2), - - Skill::Banish => Some(3), + // ----------------- + // Chaos + // ----------------- + Skill::Banish => Some(2), + Skill::Hex => Some(1), + Skill::Fear => Some(1), + Skill::Taunt => Some(2), + Skill::Pause => Some(2), // speed slow + // ----------------- + // Test + // ----------------- Skill::TestTouch => None, Skill::TestStun => None, Skill::TestBlock => None, @@ -58,46 +262,175 @@ impl Skill { pub fn speed(&self) -> u8 { match self { - Skill::Dodge => 12, - Skill::Attack => 10, + Skill::Attack => 5, - Skill::Block => 5, - Skill::Amplify => 5, - Skill::Accuracy => 5, - Skill::Evasion => 5, - Skill::HoT => 5, - Skill::DoT => 5, + // ----------------- + // Nature + // ----------------- + Skill::Block => 10, // reduce dmg + Skill::Dodge => 10, + Skill::Parry => 10, // avoid all dmg + Skill::Snare => 10, - Skill::Heal => 2, - Skill::Stun => 2, + Skill::Paralyse => 5, + + // Strangle + + Skill::Stun => 5, + Skill::Evasion => 3, // additional layer of dmg avoidance + + // ----------------- + // Technology + // ----------------- + Skill::Replicate => 1, + Skill::Swarm => 3, + Skill::Orbit => 2, + Skill::Repair => 1, + Skill::Scout => 2, // track? + + // ----------------- + // Preservation + // ----------------- + Skill::Heal => 1, + Skill::Triage => 1, // hot + Skill::Throw => 2, // no dmg stun, adds vulnerable + Skill::Charm => 2, + Skill::Calm => 2, + Skill::Rez => 4, + + // ----------------- + // Destruction + // ----------------- + Skill::Blast => 1, + Skill::Amplify => 2, + Skill::Decay => 1, // dot + Skill::Drain => 2, + Skill::Curse => 2, + Skill::Plague => 2, // aoe dot + Skill::Ruin => 3, // aoe + + // ----------------- + // Purity + // ----------------- + Skill::Accuracy => 1, + Skill::Inspire => 2, + Skill::Slay => 1, + Skill::Shield => 1, + Skill::Silence => 2, + Skill::Inquiry => 2, + Skill::Purify => 1, + + // ----------------- + // Chaos + // ----------------- Skill::Banish => 2, + Skill::Hex => 1, + Skill::Fear => 1, + Skill::Taunt => 2, + Skill::Pause => 2, // speed slow - // test skills + // ----------------- + // Test + // ----------------- Skill::TestTouch => 10, - Skill::TestStun => 2, - Skill::TestBlock => 5, + Skill::TestStun => 5, + Skill::TestBlock => 10, } } - pub fn stat(&self, cryp: &Cryp) -> CrypStat { - match self { - Skill::Attack => cryp.str, - Skill::Block => cryp.str, - Skill::Stun => cryp.str, - Skill::Dodge => cryp.agi, - Skill::Heal => cryp.int, - Skill::Banish => cryp.str, - Skill::Evasion => cryp.str, - Skill::Accuracy => cryp.str, - Skill::Amplify => cryp.str, - Skill::HoT => cryp.str, - Skill::DoT => cryp.str, + pub fn resolve(&self, cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + let mut rng = thread_rng(); + let base: u64 = rng.gen(); - // test skills - Skill::TestTouch => cryp.int, - Skill::TestStun => cryp.str, - Skill::TestBlock => cryp.str, - } + let mut res = Resolution { base, result: None }; + + // println!("{:?}'s stats", self.name); + // println!("{:064b} <- finalised", roll.result); + // roll.result = roll.result & stat.value; + + // println!("{:064b} & <- attribute roll", stat.value); + // println!("{:064b} = {:?}", roll.result, roll.result); + // println!(""); + + // return Some(roll); + + match self { + Skill::Attack => { + target.hp.reduce(cryp.phys_dmg.value); + }, + // ----------------- + // Nature + // ----------------- + Skill::Block => block(cryp, target, log), + Skill::Dodge => panic!("nyi"), + Skill::Parry => panic!("nyi"), // avoid all dmg + Skill::Snare => panic!("nyi"), + + Skill::Paralyse => panic!("nyi"), + // Strangle + + Skill::Stun => stun(cryp, target, log), + Skill::Evasion => panic!("nyi"), // additional layer of dmg avoidance + + // ----------------- + // Technology + // ----------------- + Skill::Replicate => panic!("nyi"), + Skill::Swarm => panic!("nyi"), + Skill::Orbit => panic!("nyi"), + Skill::Repair => panic!("nyi"), + Skill::Scout => panic!("nyi"), // track? + + // ----------------- + // Preservation + // ----------------- + Skill::Heal => panic!("nyi"), + Skill::Triage => panic!("nyi"), // hot + Skill::Throw => panic!("nyi"), // no dmg stun, adds vulnerable + Skill::Charm => panic!("nyi"), + Skill::Calm => panic!("nyi"), + Skill::Rez => panic!("nyi"), + + // ----------------- + // Destruction + // ----------------- + Skill::Blast => panic!("nyi"), + Skill::Amplify => panic!("nyi"), + Skill::Decay => panic!("nyi"), // dot + Skill::Drain => panic!("nyi"), + Skill::Curse => panic!("nyi"), + Skill::Plague => panic!("nyi"), // aoe dot + Skill::Ruin => panic!("nyi"), // aoe + + // ----------------- + // Purity + // ----------------- + Skill::Accuracy => panic!("nyi"), + Skill::Inspire => panic!("nyi"), + Skill::Slay => panic!("nyi"), + Skill::Shield => panic!("nyi"), + Skill::Silence => panic!("nyi"), + Skill::Inquiry => panic!("nyi"), + Skill::Purify => panic!("nyi"), + + // ----------------- + // Chaos + // ----------------- + Skill::Banish => panic!("nyi"), + Skill::Hex => panic!("nyi"), + Skill::Fear => panic!("nyi"), + Skill::Taunt => panic!("nyi"), + Skill::Pause => panic!("nyi"), // speed slow + + // ----------------- + // Test + // ----------------- + Skill::TestTouch => (), + Skill::TestStun => stun(cryp, target, log), + Skill::TestBlock => block(cryp, target, log), + }; + + return res; } pub fn duration(&self) -> u8 { @@ -128,95 +461,20 @@ impl Skill { } } - -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -pub struct Cast { - pub id: Uuid, - pub skill: Skill, - pub source_team_id: Uuid, - pub source_cryp_id: Uuid, - pub target_cryp_id: Option, - pub target_team_id: Uuid, - pub roll: Option, -} - -impl Cast { - pub fn new(source_cryp_id: Uuid, source_team_id: Uuid, target_team_id: Option, skill: Skill) -> Cast { - - let (target_cryp_id, target_team_id) = match skill.self_targeting() { - true => (Some(source_cryp_id), source_team_id), - false => (None, target_team_id.unwrap()) - }; - - return Cast { - id: Uuid::new_v4(), - source_cryp_id, - source_team_id, - target_cryp_id, - target_team_id, - skill, - roll: None, - }; - } - - pub fn resolve(&mut self, cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> &mut Cast { - let roll = cryp.roll(self.skill); - - match self.skill { - // the real deal - Skill::Stun => target.stun(roll, log), - Skill::Attack => target.attack(roll, log), - Skill::Block => target.block(roll, log), - Skill::Heal => target, - Skill::Dodge => target, - Skill::Banish => target, - Skill::Accuracy => target, - Skill::Evasion => target, - Skill::Amplify => target, - Skill::HoT => target, - Skill::DoT => target, - - // Test Skills - Skill::TestStun => target.stun(roll, log), - Skill::TestBlock => target.block(roll, log), - Skill::TestTouch => target, - }; - - self.roll = Some(roll); - self - } - - pub fn set_target(&mut self, cryp_id: Uuid) -> &mut Cast { - self.target_cryp_id = Some(cryp_id); - self - } - - pub fn used_cooldown(self) -> bool { - let cs = CrypSkill::new(self.skill); - return cs.cd.is_some(); +fn stun(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + if !target.effects.iter().any(|e| e.effect.prevents(Skill::Stun)) { + let stun = CrypEffect { effect: Effect::Stun, duration: Skill::Stun.duration() }; + target.effects.push(stun); + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, stun.effect, stun.duration)); + } else { + log.push(format!("{:?} blocks.", target.name)); } } -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] -pub enum Effect { - // Skill Effects - Stun, - Silence, - Banish, - Block, - Haste, - Slow, - Regen, - Degen, - Bleed, - Leech, - Airborne, - Immune, - - // Passives / items - Ghost, - Stone, - Evasion, +fn block(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + let effect = CrypEffect { effect: Effect::Block, duration: Skill::Block.duration() }; + target.effects.push(effect); + log.push(format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); } // #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] From 6c7b6b741f6beb4490d27386a20123b0acec67c4 Mon Sep 17 00:00:00 2001 From: ntr Date: Thu, 8 Nov 2018 22:56:57 +1100 Subject: [PATCH 5/9] healing --- server/src/cryp.rs | 13 ++++-- server/src/game.rs | 16 +++---- server/src/skill.rs | 111 +++++++++++++++++++++++++++++++------------- 3 files changed, 94 insertions(+), 46 deletions(-) diff --git a/server/src/cryp.rs b/server/src/cryp.rs index 5cc5248e..1f7d47f9 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -9,7 +9,6 @@ use failure::err_msg; use account::Account; use rpc::{CrypSpawnParams}; -use game::{Log}; use skill::{Skill, Cooldown, Effect}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -53,15 +52,21 @@ pub struct CrypStat { } impl CrypStat { - fn set(&mut self, v: u64) -> &CrypStat { + pub fn set(&mut self, v: u64) -> &CrypStat { self.value = v; self } - pub fn reduce(&mut self, dmg: u64) -> &mut CrypStat { - self.value = self.value.saturating_sub(dmg); + pub fn reduce(&mut self, amt: u64) -> &mut CrypStat { + self.value = self.value.saturating_sub(amt); self } + + pub fn increase(&mut self, amt: u64) -> &mut CrypStat { + self.value = self.value.saturating_add(amt); + self + } + } #[derive(Debug,Clone,Serialize,Deserialize)] diff --git a/server/src/game.rs b/server/src/game.rs index d0dd47bd..a7f4a521 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -229,9 +229,10 @@ impl Game { } let skill = Cast::new(source_cryp_id, team_id, target_team_id, skill); + let skill_id = skill.id; self.stack.push(skill); - return Ok(skill.id); + return Ok(skill_id); } fn skill_phase_finished(&self) -> bool { @@ -355,15 +356,16 @@ impl Game { let mut source = self.cryp_by_id(skill.source_cryp_id).unwrap().clone(); let mut target = self.cryp_by_id(skill.target_cryp_id.unwrap()).unwrap().clone(); - self.log.push(format!("{:?} uses {:?} on {:?}", source.name, skill.skill, target.name)); - let resolution = skill.set_resolution(&mut source, &mut target, &mut self.log); - self.resolved.push(resolution.clone()); + // self.log.push(format!("{:?} uses {:?} on {:?}", source.name, skill.skill, target.name)); + skill.set_resolution(&mut source, &mut target); + self.log.push(skill.resolution.text.clone()); + self.resolved.push(skill.clone()); self.update_cryp(&mut source); self.update_cryp(&mut target); - return resolution.clone(); + return skill.clone(); }).collect::>(); // now Resolve has all been assigned @@ -836,9 +838,6 @@ mod tests { let _y_block_id = game.add_skill(y_team.id, y_cryp.id, None, Skill::Block).unwrap(); game.target_phase_start(); - // game.add_target(x_team.id, x_cryp.id, y_block_id).unwrap(); - // game.add_target(y_team.id, y_cryp.id, x_block_id).unwrap(); - // game.resolve_phase_start(); assert!(game.team_by_id(y_team.id).cryps[0].skill_on_cd(Skill::Block).is_some()); assert!(game.team_by_id(x_team.id).cryps[0].skill_on_cd(Skill::Block).is_some()); @@ -876,5 +875,4 @@ mod tests { assert!(game.team_by_id(x_team.id).cryps[0].is_stunned() == false); println!("{:#?}", game.log); } - } diff --git a/server/src/skill.rs b/server/src/skill.rs index 1bbfad12..b20ef700 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -1,10 +1,10 @@ -use rand::prelude::*; +use rand::{thread_rng, Rng}; use uuid::Uuid; -use game::{Log}; +// use game::{Log}; use cryp::{Cryp, CrypEffect}; -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub struct Cast { pub id: Uuid, pub skill: Skill, @@ -12,7 +12,7 @@ pub struct Cast { pub source_cryp_id: Uuid, pub target_cryp_id: Option, pub target_team_id: Uuid, - pub resolution: Option, + pub resolution: Resolution, } impl Cast { @@ -30,12 +30,12 @@ impl Cast { target_cryp_id, target_team_id, skill, - resolution: None, + resolution: Resolution { base: 0, result: None, text: String::new() }, }; } - pub fn set_resolution(&mut self, cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> &mut Cast { - self.resolution = Some(self.skill.resolve(cryp, target, log)); + pub fn set_resolution(&mut self, cryp: &mut Cryp, target: &mut Cryp) -> &mut Cast { + self.resolution = self.skill.resolve(cryp, target); self } @@ -44,15 +44,16 @@ impl Cast { self } - pub fn used_cooldown(self) -> bool { + pub fn used_cooldown(&self) -> bool { return self.skill.cd().is_some(); } } -#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +#[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] pub struct Resolution { pub base: u64, pub result: Option, + pub text: String, } pub type Cooldown = Option; @@ -118,11 +119,11 @@ pub enum Skill { Paralyse, - // Strangle + Strangle, // physical dot and disable Stun, - Dodge, - Evasion, // additional layer of dmg avoidance + Evade, // actively evade + Evasion, // adds evasion to cryp // ----------------- @@ -194,11 +195,12 @@ impl Skill { Skill::Snare => Some(2), Skill::Paralyse => Some(3), + Skill::Strangle => Some(3), // Strangle Skill::Stun => Some(1), - Skill::Dodge => Some(2), + Skill::Evade => Some(2), Skill::Evasion => Some(3), // additional layer of dmg avoidance // ----------------- @@ -268,11 +270,12 @@ impl Skill { // Nature // ----------------- Skill::Block => 10, // reduce dmg - Skill::Dodge => 10, + Skill::Evade => 10, Skill::Parry => 10, // avoid all dmg Skill::Snare => 10, Skill::Paralyse => 5, + Skill::Strangle => 5, // Strangle @@ -338,11 +341,11 @@ impl Skill { } } - pub fn resolve(&self, cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { + pub fn resolve(&self, cryp: &mut Cryp, target: &mut Cryp) -> Resolution { let mut rng = thread_rng(); let base: u64 = rng.gen(); - let mut res = Resolution { base, result: None }; + let mut res = Resolution { base, result: None, text: String::new() }; // println!("{:?}'s stats", self.name); // println!("{:064b} <- finalised", roll.result); @@ -355,21 +358,19 @@ impl Skill { // return Some(roll); match self { - Skill::Attack => { - target.hp.reduce(cryp.phys_dmg.value); - }, + Skill::Attack => attack(cryp, target, &mut res), // ----------------- // Nature // ----------------- - Skill::Block => block(cryp, target, log), - Skill::Dodge => panic!("nyi"), + Skill::Block => block(cryp, target, &mut res), + Skill::Evade => panic!("nyi"), // Skill::Parry => panic!("nyi"), // avoid all dmg Skill::Snare => panic!("nyi"), - Skill::Paralyse => panic!("nyi"), - // Strangle + Skill::Paralyse => panic!("nyi"), // no physical moves + Skill::Strangle => panic!("nyi"), // no physical moves - Skill::Stun => stun(cryp, target, log), + Skill::Stun => stun(cryp, target, &mut res), Skill::Evasion => panic!("nyi"), // additional layer of dmg avoidance // ----------------- @@ -384,7 +385,7 @@ impl Skill { // ----------------- // Preservation // ----------------- - Skill::Heal => panic!("nyi"), + Skill::Heal => heal(cryp, target, &mut res), Skill::Triage => panic!("nyi"), // hot Skill::Throw => panic!("nyi"), // no dmg stun, adds vulnerable Skill::Charm => panic!("nyi"), @@ -426,8 +427,8 @@ impl Skill { // Test // ----------------- Skill::TestTouch => (), - Skill::TestStun => stun(cryp, target, log), - Skill::TestBlock => block(cryp, target, log), + Skill::TestStun => stun(cryp, target, &mut res), + Skill::TestBlock => block(cryp, target, &mut res), }; return res; @@ -435,7 +436,7 @@ impl Skill { pub fn duration(&self) -> u8 { match self { - Skill::Dodge => 1, + Skill::Evade => 1, Skill::Stun => 2, Skill::Block => 1, @@ -461,20 +462,64 @@ impl Skill { } } -fn stun(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { +fn attack(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { + res.text = format!("{:?} -> {:?} | Attack for {:?}", cryp.name, target.name, cryp.phys_dmg); + target.hp.reduce(cryp.phys_dmg.value); +} + +fn stun(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { if !target.effects.iter().any(|e| e.effect.prevents(Skill::Stun)) { let stun = CrypEffect { effect: Effect::Stun, duration: Skill::Stun.duration() }; target.effects.push(stun); - log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, stun.effect, stun.duration)); + res.text = format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, stun.effect, stun.duration); } else { - log.push(format!("{:?} blocks.", target.name)); + res.text = format!("{:?} blocks.", target.name); } } -fn block(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { +fn block(_cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { let effect = CrypEffect { effect: Effect::Block, duration: Skill::Block.duration() }; target.effects.push(effect); - log.push(format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); + res.text = format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration); +} + +fn heal(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { + let new_hp = *[ + target.hp.value.saturating_add(cryp.spell_dmg.value), + target.stamina.value + ].iter().min().unwrap(); + + let healing = new_hp.saturating_sub(target.hp.value); + let overhealing = target.hp.value.saturating_add(cryp.phys_dmg.value).saturating_sub(target.stamina.value); + target.hp.value = new_hp; + res.text = format!("{:?} -> {:?} | Heal for {:?} ({:?}OH)", cryp.name, target.name, healing, overhealing); +} + +#[cfg(test)] +mod tests { + use skill::*; + + #[test] + fn heal_test() { + let mut x = Cryp::new() + .named(&"muji".to_string()) + .level(8) + .learn(Skill::Heal) + .create(); + + let mut y = Cryp::new() + .named(&"camel".to_string()) + .level(8) + .learn(Skill::Heal) + .create(); + + x.hp.reduce(5); + + let mut resolution = Resolution { base: 0, result: None, text: String::new() }; + heal(&mut y, &mut x, &mut resolution); + + println!("{:?}", resolution); + } } // #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] From 2908296410c041438122cf008d86116ef1c45681 Mon Sep 17 00:00:00 2001 From: ntr Date: Fri, 9 Nov 2018 14:00:51 +1100 Subject: [PATCH 6/9] triage --- server/src/skill.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/server/src/skill.rs b/server/src/skill.rs index b20ef700..9566dc1c 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -492,9 +492,20 @@ fn heal(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { let healing = new_hp.saturating_sub(target.hp.value); let overhealing = target.hp.value.saturating_add(cryp.phys_dmg.value).saturating_sub(target.stamina.value); target.hp.value = new_hp; - res.text = format!("{:?} -> {:?} | Heal for {:?} ({:?}OH)", cryp.name, target.name, healing, overhealing); + res.text = format!("{:?} -> {:?} | Heal for {:?} ({:?} OH)", cryp.name, target.name, healing, overhealing); } +fn triage(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { + target.hp.value.saturating_add(cryp.spell_dmg.value.wrapping_div(2)), + let healing = new_hp.saturating_sub(target.hp.value); + let overhealing = target.hp.value.saturating_add(cryp.phys_dmg.value).saturating_sub(target.stamina.value); + target.hp.value = new_hp; + res.text = format!("{:?} -> {:?} | Heal for {:?} ({:?} OH)", cryp.name, target.name, healing, overhealing); +} + + + + #[cfg(test)] mod tests { use skill::*; From 567b1b7ee921acb2e14daf8731718d49a47e2b1f Mon Sep 17 00:00:00 2001 From: ntr Date: Fri, 9 Nov 2018 15:17:49 +1100 Subject: [PATCH 7/9] effect ticks --- server/src/cryp.rs | 22 ++++++++++++++------ server/src/skill.rs | 50 ++++++++++++++++++++++++++++++++++----------- 2 files changed, 54 insertions(+), 18 deletions(-) diff --git a/server/src/cryp.rs b/server/src/cryp.rs index 1f7d47f9..62874609 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -9,7 +9,7 @@ use failure::err_msg; use account::Account; use rpc::{CrypSpawnParams}; -use skill::{Skill, Cooldown, Effect}; +use skill::{Skill, Cooldown, Effect, Tick}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct CrypSkill { @@ -32,6 +32,14 @@ impl CrypSkill { pub struct CrypEffect { pub effect: Effect, pub duration: u8, + pub tick: Option, +} + +impl CrypEffect { + pub fn tick(&self, cryp: &mut Cryp) -> &CrypEffect { + self.effect.tick(self, cryp); + self + } } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -211,15 +219,17 @@ impl Cryp { } pub fn reduce_effect_durations(&mut self) -> &mut Cryp { - self.effects = self.effects.clone().into_iter().filter_map(|mut s| { - s.duration = s.duration.saturating_sub(1); + self.effects = self.effects.clone().into_iter().filter_map(|mut effect| { - if s.duration == 0 { + effect.tick(self); + effect.duration = effect.duration.saturating_sub(1); + + if effect.duration == 0 { return None; } - println!("reduced effect {:?}", s); - return Some(s); + println!("reduced effect {:?}", effect); + return Some(effect); }).collect::>(); self diff --git a/server/src/skill.rs b/server/src/skill.rs index 9566dc1c..dd6ce388 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -30,7 +30,7 @@ impl Cast { target_cryp_id, target_team_id, skill, - resolution: Resolution { base: 0, result: None, text: String::new() }, + resolution: Resolution { base: 0, result: None, text: String::new(), }, }; } @@ -104,6 +104,22 @@ impl Effect { _ => false, } } + + pub fn tick(&self, cryp_effect: &CrypEffect, target: &mut Cryp) -> &Effect { + match self { + Effect::Decay => decay_tick(target, cryp_effect), + // Effect::Triage => triage_tick(target, res), + _ => (), + } + + self + } + +} + +#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] +pub struct Tick { + amount: u64 } #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] @@ -386,7 +402,7 @@ impl Skill { // Preservation // ----------------- Skill::Heal => heal(cryp, target, &mut res), - Skill::Triage => panic!("nyi"), // hot + Skill::Triage => triage(cryp, target, &mut res), // hot Skill::Throw => panic!("nyi"), // no dmg stun, adds vulnerable Skill::Charm => panic!("nyi"), Skill::Calm => panic!("nyi"), @@ -397,7 +413,7 @@ impl Skill { // ----------------- Skill::Blast => panic!("nyi"), Skill::Amplify => panic!("nyi"), - Skill::Decay => panic!("nyi"), // dot + Skill::Decay => decay(cryp, target, &mut res), // dot Skill::Drain => panic!("nyi"), Skill::Curse => panic!("nyi"), Skill::Plague => panic!("nyi"), // aoe dot @@ -469,7 +485,7 @@ fn attack(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { fn stun(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { if !target.effects.iter().any(|e| e.effect.prevents(Skill::Stun)) { - let stun = CrypEffect { effect: Effect::Stun, duration: Skill::Stun.duration() }; + let stun = CrypEffect { effect: Effect::Stun, duration: Skill::Stun.duration(), tick: None }; target.effects.push(stun); res.text = format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, stun.effect, stun.duration); } else { @@ -478,7 +494,7 @@ fn stun(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { } fn block(_cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { - let effect = CrypEffect { effect: Effect::Block, duration: Skill::Block.duration() }; + let effect = CrypEffect { effect: Effect::Block, duration: Skill::Block.duration(), tick: None }; target.effects.push(effect); res.text = format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration); } @@ -495,16 +511,26 @@ fn heal(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { res.text = format!("{:?} -> {:?} | Heal for {:?} ({:?} OH)", cryp.name, target.name, healing, overhealing); } -fn triage(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { - target.hp.value.saturating_add(cryp.spell_dmg.value.wrapping_div(2)), - let healing = new_hp.saturating_sub(target.hp.value); - let overhealing = target.hp.value.saturating_add(cryp.phys_dmg.value).saturating_sub(target.stamina.value); - target.hp.value = new_hp; - res.text = format!("{:?} -> {:?} | Heal for {:?} ({:?} OH)", cryp.name, target.name, healing, overhealing); +fn triage(_cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { + let effect = CrypEffect { effect: Effect::Triage, duration: Skill::Triage.duration(), tick: None }; + target.effects.push(effect); + res.text = format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration); } +fn decay(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { + let effect = CrypEffect { + effect: Effect::Decay, + duration: Skill::Decay.duration(), + tick: Some(Tick { amount: cryp.spell_dmg.value.wrapping_div(2) }) + }; + target.effects.push(effect); + res.text = format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration); +} - +fn decay_tick(target: &mut Cryp, effect: &CrypEffect) { + let tick = effect.tick.expect("no tick for decay"); + target.hp.reduce(tick.amount); +} #[cfg(test)] mod tests { From 2ab5009fe1134f42051d9107d58075b935b2ae92 Mon Sep 17 00:00:00 2001 From: ntr Date: Fri, 9 Nov 2018 15:33:17 +1100 Subject: [PATCH 8/9] dots and hots --- server/src/skill.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/server/src/skill.rs b/server/src/skill.rs index dd6ce388..45735472 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -456,6 +456,8 @@ impl Skill { Skill::Stun => 2, Skill::Block => 1, + Skill::Decay => 3, + Skill::TestBlock => 1, Skill::TestStun => 2, _ => panic!("{:?} does not have a duration", self), @@ -557,6 +559,30 @@ mod tests { println!("{:?}", resolution); } + + #[test] + fn decay_test() { + let mut x = Cryp::new() + .named(&"muji".to_string()) + .level(8) + .learn(Skill::Decay) + .create(); + + let mut y = Cryp::new() + .named(&"camel".to_string()) + .level(8) + .create(); + + let mut resolution = Resolution { base: 0, result: None, text: String::new() }; + decay(&mut x, &mut y, &mut resolution); + + assert!(y.effects.iter().any(|e| e.effect == Effect::Decay)); + + y.reduce_effect_durations(); + let decay = y.effects.iter().find(|e| e.effect == Effect::Decay); + assert!(y.hp.value == y.stamina.value.saturating_sub(decay.unwrap().tick.unwrap().amount)); + } + } // #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] From 8ce0fc4fbb5bfe57df666bafa4f7929809f88c34 Mon Sep 17 00:00:00 2001 From: ntr Date: Mon, 12 Nov 2018 18:15:58 +1100 Subject: [PATCH 9/9] add some skills, revert to log --- server/src/cryp.rs | 13 ++- server/src/game.rs | 5 +- server/src/skill.rs | 212 +++++++++++++++++++++++++++++++------------- 3 files changed, 163 insertions(+), 67 deletions(-) diff --git a/server/src/cryp.rs b/server/src/cryp.rs index 62874609..a966ca11 100755 --- a/server/src/cryp.rs +++ b/server/src/cryp.rs @@ -10,6 +10,7 @@ use failure::err_msg; use account::Account; use rpc::{CrypSpawnParams}; use skill::{Skill, Cooldown, Effect, Tick}; +use game::{Log}; #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] pub struct CrypSkill { @@ -36,8 +37,8 @@ pub struct CrypEffect { } impl CrypEffect { - pub fn tick(&self, cryp: &mut Cryp) -> &CrypEffect { - self.effect.tick(self, cryp); + pub fn tick(&self, cryp: &mut Cryp, log: &mut Log) -> &CrypEffect { + self.effect.tick(self, cryp, log); self } } @@ -175,6 +176,10 @@ impl Cryp { self.hp.value == 0 } + pub fn immune(&self, skill: Skill) -> bool { + self.effects.iter().any(|e| e.effect.immune(skill)) + } + pub fn is_stunned(&self) -> bool { self.effects.iter().any(|s| s.effect == Effect::Stun) } @@ -218,10 +223,10 @@ impl Cryp { self } - pub fn reduce_effect_durations(&mut self) -> &mut Cryp { + pub fn reduce_effect_durations(&mut self, log: &mut Log) -> &mut Cryp { self.effects = self.effects.clone().into_iter().filter_map(|mut effect| { - effect.tick(self); + effect.tick(self, log); effect.duration = effect.duration.saturating_sub(1); if effect.duration == 0 { diff --git a/server/src/game.rs b/server/src/game.rs index a7f4a521..75e1bcb3 100755 --- a/server/src/game.rs +++ b/server/src/game.rs @@ -357,8 +357,7 @@ impl Game { let mut target = self.cryp_by_id(skill.target_cryp_id.unwrap()).unwrap().clone(); // self.log.push(format!("{:?} uses {:?} on {:?}", source.name, skill.skill, target.name)); - skill.set_resolution(&mut source, &mut target); - self.log.push(skill.resolution.text.clone()); + skill.set_resolution(&mut source, &mut target, &mut self.log); self.resolved.push(skill.clone()); @@ -395,7 +394,7 @@ impl Game { } // always reduce durations - cryp.reduce_effect_durations(); + cryp.reduce_effect_durations(&mut self.log); self.update_cryp(&mut cryp); } diff --git a/server/src/skill.rs b/server/src/skill.rs index 45735472..a378be64 100755 --- a/server/src/skill.rs +++ b/server/src/skill.rs @@ -1,7 +1,7 @@ use rand::{thread_rng, Rng}; use uuid::Uuid; -// use game::{Log}; +use game::{Log}; use cryp::{Cryp, CrypEffect}; #[derive(Debug,Clone,PartialEq,Serialize,Deserialize)] @@ -30,12 +30,12 @@ impl Cast { target_cryp_id, target_team_id, skill, - resolution: Resolution { base: 0, result: None, text: String::new(), }, + resolution: Resolution { base: 0, result: None }, }; } - pub fn set_resolution(&mut self, cryp: &mut Cryp, target: &mut Cryp) -> &mut Cast { - self.resolution = self.skill.resolve(cryp, target); + pub fn set_resolution(&mut self, cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> &mut Cast { + self.resolution = self.skill.resolve(cryp, target, log); self } @@ -53,7 +53,6 @@ impl Cast { pub struct Resolution { pub base: u64, pub result: Option, - pub text: String, } pub type Cooldown = Option; @@ -72,14 +71,16 @@ pub enum Effect { Fury, Evasion, Blind, + Snare, // magic - Silence, + Hex, Banish, Slow, Haste, Enslave, Mesmerise, + Amplify, // magic immunity Immune, @@ -95,20 +96,21 @@ pub enum Effect { } impl Effect { - pub fn prevents(&self, skill: Skill) -> bool { + pub fn immune(&self, skill: Skill) -> bool { match self { Effect::Block => match skill { - Skill::Stun => true, + Skill::Stun | + Skill::Attack => true, _ => false, }, _ => false, } } - pub fn tick(&self, cryp_effect: &CrypEffect, target: &mut Cryp) -> &Effect { + pub fn tick(&self, cryp_effect: &CrypEffect, target: &mut Cryp, log: &mut Log) -> &Effect { match self { - Effect::Decay => decay_tick(target, cryp_effect), - // Effect::Triage => triage_tick(target, res), + Effect::Decay => decay_tick(target, cryp_effect, log), + Effect::Triage => triage_tick(target, cryp_effect, log), _ => (), } @@ -149,7 +151,7 @@ pub enum Skill { Swarm, Orbit, Repair, - Scout, // track? + Scan, // track? // ----------------- // Nonviolence @@ -175,7 +177,7 @@ pub enum Skill { // ----------------- // Purity // ----------------- - Accuracy, + Precision, Inspire, Slay, Shield, @@ -226,7 +228,7 @@ impl Skill { Skill::Swarm => Some(3), Skill::Orbit => Some(2), Skill::Repair => Some(1), - Skill::Scout => Some(2), // track? + Skill::Scan => Some(2), // track? // ----------------- // Preservation @@ -252,7 +254,7 @@ impl Skill { // ----------------- // Purity // ----------------- - Skill::Accuracy => Some(1), + Skill::Precision => Some(1), Skill::Inspire => Some(2), Skill::Slay => Some(1), Skill::Shield => Some(1), @@ -305,7 +307,7 @@ impl Skill { Skill::Swarm => 3, Skill::Orbit => 2, Skill::Repair => 1, - Skill::Scout => 2, // track? + Skill::Scan => 2, // track? // ----------------- // Preservation @@ -331,7 +333,7 @@ impl Skill { // ----------------- // Purity // ----------------- - Skill::Accuracy => 1, + Skill::Precision => 1, Skill::Inspire => 2, Skill::Slay => 1, Skill::Shield => 1, @@ -357,11 +359,11 @@ impl Skill { } } - pub fn resolve(&self, cryp: &mut Cryp, target: &mut Cryp) -> Resolution { + pub fn resolve(&self, cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) -> Resolution { let mut rng = thread_rng(); let base: u64 = rng.gen(); - let mut res = Resolution { base, result: None, text: String::new() }; + let res = Resolution { base, result: None }; // println!("{:?}'s stats", self.name); // println!("{:064b} <- finalised", roll.result); @@ -374,19 +376,19 @@ impl Skill { // return Some(roll); match self { - Skill::Attack => attack(cryp, target, &mut res), + Skill::Attack => attack(cryp, target, log), // ----------------- // Nature // ----------------- - Skill::Block => block(cryp, target, &mut res), + Skill::Block => block(cryp, target, log), Skill::Evade => panic!("nyi"), // Skill::Parry => panic!("nyi"), // avoid all dmg - Skill::Snare => panic!("nyi"), + Skill::Snare => snare(cryp, target, log), // TODO prevent physical moves Skill::Paralyse => panic!("nyi"), // no physical moves Skill::Strangle => panic!("nyi"), // no physical moves - Skill::Stun => stun(cryp, target, &mut res), + Skill::Stun => stun(cryp, target, log), Skill::Evasion => panic!("nyi"), // additional layer of dmg avoidance // ----------------- @@ -396,14 +398,14 @@ impl Skill { Skill::Swarm => panic!("nyi"), Skill::Orbit => panic!("nyi"), Skill::Repair => panic!("nyi"), - Skill::Scout => panic!("nyi"), // track? + Skill::Scan => panic!("nyi"), // track? // ----------------- // Preservation // ----------------- - Skill::Heal => heal(cryp, target, &mut res), - Skill::Triage => triage(cryp, target, &mut res), // hot - Skill::Throw => panic!("nyi"), // no dmg stun, adds vulnerable + Skill::Heal => heal(cryp, target, log), + Skill::Triage => triage(cryp, target, log), // hot + Skill::Throw => throw(cryp, target, log), // no dmg stun, adds vulnerable Skill::Charm => panic!("nyi"), Skill::Calm => panic!("nyi"), Skill::Rez => panic!("nyi"), @@ -411,9 +413,9 @@ impl Skill { // ----------------- // Destruction // ----------------- - Skill::Blast => panic!("nyi"), - Skill::Amplify => panic!("nyi"), - Skill::Decay => decay(cryp, target, &mut res), // dot + Skill::Blast => blast(cryp, target, log), + Skill::Amplify => amplify(cryp, target, log), // TODO increase magic dmg + Skill::Decay => decay(cryp, target, log), // dot Skill::Drain => panic!("nyi"), Skill::Curse => panic!("nyi"), Skill::Plague => panic!("nyi"), // aoe dot @@ -422,7 +424,7 @@ impl Skill { // ----------------- // Purity // ----------------- - Skill::Accuracy => panic!("nyi"), + Skill::Precision => panic!("nyi"), Skill::Inspire => panic!("nyi"), Skill::Slay => panic!("nyi"), Skill::Shield => panic!("nyi"), @@ -433,8 +435,8 @@ impl Skill { // ----------------- // Chaos // ----------------- - Skill::Banish => panic!("nyi"), - Skill::Hex => panic!("nyi"), + Skill::Banish => banish(cryp, target, log), // TODO prevent all actions + Skill::Hex => hex(cryp, target, log), // todo prevent casting Skill::Fear => panic!("nyi"), Skill::Taunt => panic!("nyi"), Skill::Pause => panic!("nyi"), // speed slow @@ -443,8 +445,8 @@ impl Skill { // Test // ----------------- Skill::TestTouch => (), - Skill::TestStun => stun(cryp, target, &mut res), - Skill::TestBlock => block(cryp, target, &mut res), + Skill::TestStun => stun(cryp, target, log), + Skill::TestBlock => block(cryp, target, log), }; return res; @@ -457,6 +459,7 @@ impl Skill { Skill::Block => 1, Skill::Decay => 3, + Skill::Triage => 3, Skill::TestBlock => 1, Skill::TestStun => 2, @@ -480,28 +483,48 @@ impl Skill { } } -fn attack(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { - res.text = format!("{:?} -> {:?} | Attack for {:?}", cryp.name, target.name, cryp.phys_dmg); +fn attack(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + log.push(format!("{:?} -> {:?} | Attack for {:?}", cryp.name, target.name, cryp.phys_dmg)); target.hp.reduce(cryp.phys_dmg.value); } -fn stun(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { - if !target.effects.iter().any(|e| e.effect.prevents(Skill::Stun)) { +fn stun(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + if !target.immune(Skill::Stun) { let stun = CrypEffect { effect: Effect::Stun, duration: Skill::Stun.duration(), tick: None }; target.effects.push(stun); - res.text = format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, stun.effect, stun.duration); + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, stun.effect, stun.duration)); } else { - res.text = format!("{:?} blocks.", target.name); + log.push(format!("{:?} -> {:?} | {:?} immune", cryp.name, target.name, target.name)); } } -fn block(_cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { - let effect = CrypEffect { effect: Effect::Block, duration: Skill::Block.duration(), tick: None }; - target.effects.push(effect); - res.text = format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration); +fn throw(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + if !target.immune(Skill::Throw) { + let stun = CrypEffect { effect: Effect::Stun, duration: Skill::Stun.duration(), tick: None }; + let vulnerable = CrypEffect { effect: Effect::Vulnerable, duration: Skill::Stun.duration(), tick: None }; + target.effects.push(stun); + target.effects.push(vulnerable); + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, stun.effect, stun.duration)); + log.push(format!("{:?} -> {:?} | {:?} for {:?}T", cryp.name, target.name, vulnerable.effect, vulnerable.duration)); + } else { + log.push(format!("{:?} -> {:?} | {:?} immune", cryp.name, target.name, target.name)); + } } -fn heal(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { + +fn block(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + let effect = CrypEffect { effect: Effect::Block, duration: Skill::Block.duration(), tick: None }; + target.effects.push(effect); + log.push(format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); +} + +fn snare(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + let effect = CrypEffect { effect: Effect::Snare, duration: Skill::Snare.duration(), tick: None }; + target.effects.push(effect); + log.push(format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); +} + +fn heal(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { let new_hp = *[ target.hp.value.saturating_add(cryp.spell_dmg.value), target.stamina.value @@ -510,30 +533,73 @@ fn heal(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { let healing = new_hp.saturating_sub(target.hp.value); let overhealing = target.hp.value.saturating_add(cryp.phys_dmg.value).saturating_sub(target.stamina.value); target.hp.value = new_hp; - res.text = format!("{:?} -> {:?} | Heal for {:?} ({:?} OH)", cryp.name, target.name, healing, overhealing); + log.push(format!("{:?} -> {:?} | Heal for {:?} ({:?} OH)", cryp.name, target.name, healing, overhealing)); } -fn triage(_cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { - let effect = CrypEffect { effect: Effect::Triage, duration: Skill::Triage.duration(), tick: None }; +fn triage(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + let effect = CrypEffect { + effect: Effect::Triage, + duration: Skill::Triage.duration(), + tick: Some(Tick { amount: cryp.spell_dmg.value.wrapping_div(2) }) + }; target.effects.push(effect); - res.text = format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration); + log.push(format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); } -fn decay(cryp: &mut Cryp, target: &mut Cryp, res: &mut Resolution) { +fn triage_tick(target: &mut Cryp, effect: &CrypEffect, log: &mut Log) { + let tick = effect.tick.expect("no tick for triage"); + let new_hp = *[ + target.hp.value.saturating_add(tick.amount), + target.stamina.value + ].iter().min().unwrap(); + + let healing = new_hp.saturating_sub(target.hp.value); + let overhealing = target.hp.value + tick.amount - target.stamina.value; + log.push(format!("{:?} | Triage healing for {:?} ({:?} OH)", target.name, healing, overhealing)); + target.hp.value = new_hp; +} + +fn blast(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + let amount = cryp.spell_dmg.value; + log.push(format!("{:?} -> {:?} | Blast for {:?}", cryp.name, target.name, amount)); + target.hp.reduce(amount); +} + +fn amplify(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + let effect = CrypEffect { effect: Effect::Amplify, duration: Skill::Amplify.duration(), tick: None }; + target.effects.push(effect); + log.push(format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); +} + +fn decay(cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { let effect = CrypEffect { effect: Effect::Decay, duration: Skill::Decay.duration(), tick: Some(Tick { amount: cryp.spell_dmg.value.wrapping_div(2) }) }; target.effects.push(effect); - res.text = format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration); + log.push(format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); } -fn decay_tick(target: &mut Cryp, effect: &CrypEffect) { +fn decay_tick(target: &mut Cryp, effect: &CrypEffect, log: &mut Log) { let tick = effect.tick.expect("no tick for decay"); target.hp.reduce(tick.amount); + log.push(format!("{:?} | Decay damage for {:?}", target.name, tick.amount)); } +fn hex(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + let effect = CrypEffect { effect: Effect::Hex, duration: Skill::Hex.duration(), tick: None }; + target.effects.push(effect); + log.push(format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); +} + +fn banish(_cryp: &mut Cryp, target: &mut Cryp, log: &mut Log) { + let effect = CrypEffect { effect: Effect::Banish, duration: Skill::Banish.duration(), tick: None }; + target.effects.push(effect); + log.push(format!("{:?} is {:?} for {:?}T", target.name, effect.effect, effect.duration)); +} + + #[cfg(test)] mod tests { use skill::*; @@ -554,10 +620,10 @@ mod tests { x.hp.reduce(5); - let mut resolution = Resolution { base: 0, result: None, text: String::new() }; - heal(&mut y, &mut x, &mut resolution); + let mut log = vec![]; + heal(&mut y, &mut x, &mut log); - println!("{:?}", resolution); + println!("{:?}", log); } #[test] @@ -565,7 +631,6 @@ mod tests { let mut x = Cryp::new() .named(&"muji".to_string()) .level(8) - .learn(Skill::Decay) .create(); let mut y = Cryp::new() @@ -573,16 +638,43 @@ mod tests { .level(8) .create(); - let mut resolution = Resolution { base: 0, result: None, text: String::new() }; - decay(&mut x, &mut y, &mut resolution); + let mut log = vec![]; + decay(&mut x, &mut y, &mut log); assert!(y.effects.iter().any(|e| e.effect == Effect::Decay)); - y.reduce_effect_durations(); + y.reduce_effect_durations(&mut log); let decay = y.effects.iter().find(|e| e.effect == Effect::Decay); assert!(y.hp.value == y.stamina.value.saturating_sub(decay.unwrap().tick.unwrap().amount)); } + #[test] + fn triage_test() { + let mut x = Cryp::new() + .named(&"muji".to_string()) + .level(8) + .create(); + + let mut y = Cryp::new() + .named(&"pretaliation".to_string()) + .level(8) + .create(); + + let mut log = vec![]; + + // ensure it doesn't have 0 sd + x.spell_dmg.value = 50; + y.hp.reduce(5); + + let prev_hp = y.hp.value; + + triage(&mut x, &mut y, &mut log); + + assert!(y.effects.iter().any(|e| e.effect == Effect::Triage)); + + y.reduce_effect_durations(&mut log); + assert!(y.hp.value > prev_hp); + } } // #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]