This commit is contained in:
ntr 2018-11-02 01:23:48 +11:00
parent 09b6f455d3
commit 63983536d7
6 changed files with 271 additions and 83 deletions

View File

@ -155,6 +155,9 @@ function GamePanel(props) {
return 'Game over'; return 'Game over';
} }
} }
const logs = game.log.reverse().map((l, i) => (<div key={i}>{l}</div>));
return ( return (
<section className="columns"> <section className="columns">
<div className="column is-2 title is-1"> <div className="column is-2 title is-1">
@ -172,7 +175,7 @@ function GamePanel(props) {
</div> </div>
</div> </div>
<div className="column is-2"> <div className="column is-2">
<div className="title is-4">log</div> <div className="title is-4">{logs}</div>
</div> </div>
</section> </section>
); );

View File

@ -1,6 +1,6 @@
# Cryps ("creeps") // Creeptography # Cryps ("creeps")
## Setup ## Combat
skill phase: skill phase:
1.1 -> block (sp 10) -> on self 1.1 -> block (sp 10) -> on self
@ -20,3 +20,127 @@ resolve phase:
1.1 <- attack (no effect because of block) 1.1 <- attack (no effect because of block)
2.2 <- attack (normal resolve) 2.2 <- attack (normal resolve)
1.1 <- hexed (no skills for the rest of this turn and next) 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

View File

@ -8,6 +8,8 @@
* move rpc functions out * move rpc functions out
* unwrap account for all functions except list * unwrap account for all functions except list
* handle unserializable cryps
* Global rolls * Global rolls
* Stats * Stats
@ -57,6 +59,11 @@
* run nginx as not root * run nginx as not root
# Art Styles
* Aztec
* Pixel
* Industrial
# Mechanic Ideas # Mechanic Ideas
teams teams
1v1 2v2 3v3 1v1 2v2 3v3
@ -89,6 +96,7 @@ gem td style attr combinations
techno artists for the soundtrack techno artists for the soundtrack
slimey slimey
ghostly ghostly
@ -114,24 +122,3 @@ gem td style attr combinations
* 18: Restrictions breed creativity * 18: Restrictions breed creativity
* 19: Your audience is good at recognizing problems and bad at solving them * 19: Your audience is good at recognizing problems and bad at solving them
* 20: All the lessons connect * 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)

View File

@ -9,7 +9,8 @@ use failure::err_msg;
use account::Account; use account::Account;
use rpc::{CrypSpawnParams}; use rpc::{CrypSpawnParams};
use skill::{Skill, Cooldown, Roll}; use game::{Log};
use skill::{Skill, Cooldown, Roll, Effect};
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub struct CrypSkill { pub struct CrypSkill {
@ -29,15 +30,8 @@ impl CrypSkill {
} }
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub enum Status { pub struct CrypEffect {
Stunned, effect: Effect,
Silenced,
Blocking,
}
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub struct CrypStatus {
status: Status,
duration: u8, duration: u8,
} }
@ -80,7 +74,7 @@ pub struct Cryp {
pub xp: u64, pub xp: u64,
pub lvl: u8, pub lvl: u8,
pub skills: Vec<CrypSkill>, pub skills: Vec<CrypSkill>,
pub statuses: Vec<CrypStatus>, pub effects: Vec<CrypEffect>,
pub name: String, pub name: String,
} }
@ -103,7 +97,7 @@ impl Cryp {
lvl: 0, lvl: 0,
xp: 0, xp: 0,
skills: vec![CrypSkill::new(Skill::Attack)], skills: vec![CrypSkill::new(Skill::Attack)],
statuses: vec![], effects: vec![],
name: String::new() name: String::new()
}; };
} }
@ -165,7 +159,7 @@ impl Cryp {
} }
pub fn is_stunned(&self) -> bool { 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> { pub fn available_skills(&self) -> Vec<&CrypSkill> {
@ -207,17 +201,17 @@ impl Cryp {
self self
} }
pub fn reduce_statuses(&mut self) -> &mut Cryp { pub fn reduce_effect_durations(&mut self) -> &mut Cryp {
self.statuses = self.statuses.clone().into_iter().filter_map(|mut s| { self.effects = self.effects.clone().into_iter().filter_map(|mut s| {
s.duration = s.duration.saturating_sub(1); s.duration = s.duration.saturating_sub(1);
if s.duration == 0 { if s.duration == 0 {
return None; return None;
} }
println!("reduced status {:?}", s); println!("reduced effect {:?}", s);
return Some(s); return Some(s);
}).collect::<Vec<CrypStatus>>(); }).collect::<Vec<CrypEffect>>();
self self
} }
@ -246,20 +240,26 @@ impl Cryp {
return roll; return roll;
} }
pub fn stun(&mut self, _roll: Roll) -> &mut Cryp { pub fn stun(&mut self, _roll: Roll, log: &mut Log) -> &mut Cryp {
if !self.statuses.iter().any(|s| s.status == Status::Blocking) { if !self.effects.iter().any(|s| s.effect == Effect::Block) {
self.statuses.push(CrypStatus { status: Status::Stunned, duration: Skill::Stun.duration() }); 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 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.hp.reduce(roll.result);
self self
} }
pub fn block(&mut self, _roll: Roll) -> &mut Cryp { pub fn block(&mut self, _roll: Roll, log: &mut Log) -> &mut Cryp {
self.statuses.push(CrypStatus { status: Status::Blocking, duration: Skill::Block.duration() }); 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 self
} }

View File

@ -12,6 +12,8 @@ use rpc::{GameStateParams, GameSkillParams, GamePveParams, GamePvpParams, GameTa
use cryp::{Cryp, cryp_get}; use cryp::{Cryp, cryp_get};
use skill::{Skill, Cast}; use skill::{Skill, Cast};
pub type Log = Vec<String>;
#[derive(Debug,Clone,Serialize,Deserialize)] #[derive(Debug,Clone,Serialize,Deserialize)]
pub struct Team { pub struct Team {
id: Uuid, id: Uuid,
@ -47,7 +49,7 @@ pub enum Phase {
Start, Start,
Skill, Skill,
Target, Target,
Damage, Resolve,
Finish, Finish,
} }
@ -94,11 +96,18 @@ impl Game {
self self
} }
// check team not already in
fn add_team(&mut self, team: Team) -> Result<&mut Game, Error> { fn add_team(&mut self, team: Team) -> Result<&mut Game, Error> {
if self.teams.len() == self.team_num { if self.teams.len() == self.team_num {
return Err(err_msg("maximum number of teams")); 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::<Vec<String>>().join(", ");
self.log.push(format!("{:?} has joined the game.", team_description));
self.teams.push(team); self.teams.push(team);
Ok(self) Ok(self)
@ -139,13 +148,17 @@ impl Game {
} }
fn start(&mut self) -> &mut Game { fn start(&mut self) -> &mut Game {
self.log.push("Game starting...".to_string());
self.skill_phase_start(); self.skill_phase_start();
self self
} }
fn skill_phase_start(&mut self) -> &mut Game { fn skill_phase_start(&mut self) -> &mut Game {
if ![Phase::Start, Phase::Damage].contains(&self.phase) { self.log.push("<Skill Phase>".to_string());
panic!("game not in damage or start phase");
if ![Phase::Start, Phase::Resolve].contains(&self.phase) {
panic!("game not in Resolve or start phase");
} }
self.phase = Phase::Skill; self.phase = Phase::Skill;
@ -175,8 +188,6 @@ impl Game {
self 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<Uuid>, skill: Skill) -> Result<Uuid, Error> { fn add_skill(&mut self, team_id: Uuid, source_cryp_id: Uuid, target_team_id: Option<Uuid>, skill: Skill) -> Result<Uuid, Error> {
if self.phase != Phase::Skill { if self.phase != Phase::Skill {
return Err(err_msg("game not in skill phase")); 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 // check here as well so uncastable spells don't go on the stack
if !skill.castable(&cryp) { 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 // for every team
.all(|t| self.stack.iter() .all(|t| self.stack.iter()
// the number of skills they have cast // the number of skills they have cast
.filter(|s| s.source_team_id == t.id) .filter(|s| s.source_team_id == t.id).collect::<Vec<&Cast>>()
.collect::<Vec<&Cast>>()
// should equal the number required this turn // should equal the number required this turn
.len() == t.skills_required() .len() == t.skills_required()
) )
} }
// move all skills into their target team's targets list
fn target_phase_start(&mut self) -> &mut Game { fn target_phase_start(&mut self) -> &mut Game {
assert!(self.skill_phase_finished()); assert!(self.skill_phase_finished());
self.log.push("<Target Phase>".to_string());
if self.phase != Phase::Skill { if self.phase != Phase::Skill {
panic!("game not in skill phase"); panic!("game not in skill phase");
} }
@ -250,7 +261,7 @@ impl Game {
// all cryps are stunned or otherwise inactive // all cryps are stunned or otherwise inactive
if self.target_phase_finished() { if self.target_phase_finished() {
self.damage_phase_start(); self.resolve_phase_start();
} }
self self
@ -311,12 +322,14 @@ impl Game {
// requires no input // requires no input
// just do it // just do it
fn damage_phase_start(&mut self) -> &mut Game { fn resolve_phase_start(&mut self) -> &mut Game {
if self.phase != Phase::Target { if self.phase != Phase::Target {
panic!("game not in target phase"); panic!("game not in target phase");
} }
assert!(self.target_phase_finished());
self.phase = Phase::Damage; self.phase = Phase::Resolve;
self.log.push("<Resolve Phase>".to_string());
self.resolve_skills(); self.resolve_skills();
@ -330,8 +343,8 @@ impl Game {
} }
fn resolve_skills(&mut self) -> &mut Game { fn resolve_skills(&mut self) -> &mut Game {
if self.phase != Phase::Damage { if self.phase != Phase::Resolve {
panic!("game not in damage phase"); panic!("game not in Resolve phase");
} }
self.stack.sort_unstable_by_key(|s| s.skill.speed()); 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 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 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.resolved.push(*resolution);
self.update_cryp(&mut source); self.update_cryp(&mut source);
self.update_cryp(&mut target); self.update_cryp(&mut target);
return *resolution; return *resolution;
}).collect::<Vec<Cast>>(); }).collect::<Vec<Cast>>();
// now damage has all been assigned // now Resolve has all been assigned
// handle cooldowns and statuses // handle cooldowns and statuses
self.progress_durations(); self.progress_durations();
@ -378,7 +393,7 @@ impl Game {
} }
// always reduce durations // always reduce durations
cryp.reduce_statuses(); cryp.reduce_effect_durations();
self.update_cryp(&mut cryp); 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)?; game.add_target(account.id, params.cryp_id, params.skill_id)?;
if game.target_phase_finished() { if game.target_phase_finished() {
game.damage_phase_start(); game.resolve_phase_start();
} }
game_update(&game, tx)?; game_update(&game, tx)?;
@ -753,7 +768,7 @@ mod tests {
assert!(game.target_phase_finished()); assert!(game.target_phase_finished());
game.damage_phase_start(); game.resolve_phase_start();
assert!([Phase::Skill, Phase::Finish].contains(&game.phase)); 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(); game.add_target(y_team.id, y_cryp.id, x_stun_id).unwrap();
assert!(game.target_phase_finished()); assert!(game.target_phase_finished());
game.damage_phase_start(); game.resolve_phase_start();
// should auto progress back to skill phase // should auto progress back to skill phase
assert!(game.phase == Phase::Skill); 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(x_team.id, x_cryp.id, y_attack_id).unwrap();
game.add_target(y_team.id, y_cryp.id, x_stun_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 // should auto progress back to skill phase
assert!(game.phase == Phase::Skill); assert!(game.phase == Phase::Skill);
@ -823,7 +838,7 @@ mod tests {
game.target_phase_start(); game.target_phase_start();
// game.add_target(x_team.id, x_cryp.id, y_block_id).unwrap(); // 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.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(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()); 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.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 // should not be stunned because of block
assert!(game.team_by_id(x_team.id).cryps[0].is_stunned() == false); assert!(game.team_by_id(x_team.id).cryps[0].is_stunned() == false);
println!("{:#?}", game.log);
} }
} }

View File

@ -2,6 +2,7 @@
use uuid::Uuid; use uuid::Uuid;
use cryp::{Cryp, CrypSkill, CrypStat}; use cryp::{Cryp, CrypSkill, CrypStat};
use game::{Log};
#[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)] #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
pub struct Roll { pub struct Roll {
@ -15,9 +16,16 @@ pub type Cooldown = Option<u8>;
pub enum Skill { pub enum Skill {
Attack, Attack,
Block, Block,
Heal,
Stun, Stun,
Dodge, Dodge,
Banish,
Heal,
Accuracy,
Evasion,
Amplify,
HoT,
DoT,
// used by tests, no cd, no dmg // used by tests, no cd, no dmg
TestTouch, TestTouch,
TestStun, TestStun,
@ -28,10 +36,20 @@ impl Skill {
pub fn cd(&self) -> Cooldown { pub fn cd(&self) -> Cooldown {
match self { match self {
Skill::Attack => None, Skill::Attack => None,
Skill::Block => Some(1), Skill::Block => Some(1),
Skill::Dodge => Some(1), Skill::Dodge => Some(1),
Skill::Amplify => Some(1),
Skill::Evasion => Some(1),
Skill::Accuracy => Some(1),
Skill::Heal => Some(2), Skill::Heal => Some(2),
Skill::Stun => Some(2), Skill::Stun => Some(2),
Skill::DoT => Some(2),
Skill::HoT => Some(2),
Skill::Banish => Some(3),
Skill::TestTouch => None, Skill::TestTouch => None,
Skill::TestStun => None, Skill::TestStun => None,
Skill::TestBlock => None, Skill::TestBlock => None,
@ -40,11 +58,21 @@ impl Skill {
pub fn speed(&self) -> u8 { pub fn speed(&self) -> u8 {
match self { match self {
Skill::Dodge => 12,
Skill::Attack => 10, Skill::Attack => 10,
Skill::Block => 5, 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::Heal => 2,
Skill::Stun => 2, Skill::Stun => 2,
Skill::Banish => 2,
// test skills
Skill::TestTouch => 10, Skill::TestTouch => 10,
Skill::TestStun => 2, Skill::TestStun => 2,
Skill::TestBlock => 5, Skill::TestBlock => 5,
@ -58,6 +86,12 @@ impl Skill {
Skill::Stun => cryp.str, Skill::Stun => cryp.str,
Skill::Dodge => cryp.agi, Skill::Dodge => cryp.agi,
Skill::Heal => cryp.int, 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 // test skills
Skill::TestTouch => cryp.int, 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); let roll = cryp.roll(self.skill);
println!("{:?} -> {:?} -> {:?}", cryp.name, self.skill, target.name);
match self.skill { match self.skill {
// the real deal // the real deal
Skill::Stun => target.stun(roll), Skill::Stun => target.stun(roll, log),
Skill::Attack => target.attack(roll), Skill::Attack => target.attack(roll, log),
Skill::Block => target.block(roll), Skill::Block => target.block(roll, log),
Skill::Heal => target, Skill::Heal => target,
Skill::Dodge => target, Skill::Dodge => target,
Skill::Banish => target,
Skill::Accuracy => target,
Skill::Evasion => target,
Skill::Amplify => target,
Skill::HoT => target,
Skill::DoT => target,
// Test Skills // Test Skills
Skill::TestStun => target.stun(roll), Skill::TestStun => target.stun(roll, log),
Skill::TestBlock => target.block(roll), Skill::TestBlock => target.block(roll, log),
Skill::TestTouch => target, Skill::TestTouch => target,
}; };
// println!("{:?} gettin clapped for {:?}", target.name, roll.result);
self.roll = Some(roll); self.roll = Some(roll);
self 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)] // #[derive(Debug,Clone,Copy,PartialEq,Serialize,Deserialize)]
// pub enum Skill { // pub enum Skill {
// Stoney, // Stoney,