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

View File

@ -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

View File

@ -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)

View File

@ -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<CrypSkill>,
pub statuses: Vec<CrypStatus>,
pub effects: Vec<CrypEffect>,
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::<Vec<CrypStatus>>();
}).collect::<Vec<CrypEffect>>();
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
}

View File

@ -12,6 +12,8 @@ use rpc::{GameStateParams, GameSkillParams, GamePveParams, GamePvpParams, GameTa
use cryp::{Cryp, cryp_get};
use skill::{Skill, Cast};
pub type Log = Vec<String>;
#[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::<Vec<String>>().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("<Skill Phase>".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<Uuid>, skill: Skill) -> Result<Uuid, Error> {
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::<Vec<&Cast>>()
.filter(|s| s.source_team_id == t.id).collect::<Vec<&Cast>>()
// 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("<Target Phase>".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("<Resolve Phase>".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::<Vec<Cast>>();
// 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);
}
}

View File

@ -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<u8>;
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,