Modify character sheets to be label-based
This commit is contained in:
parent
460b24d96e
commit
b685057346
|
@ -2,11 +2,3 @@
|
||||||
extern crate serde_derive;
|
extern crate serde_derive;
|
||||||
|
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
assert_eq!(2 + 2, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,201 +0,0 @@
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Error)]
|
|
||||||
pub enum Error {
|
|
||||||
#[error("Tier out of range {0}")]
|
|
||||||
TierOutOfRange(u8),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum Type {
|
|
||||||
Arkus,
|
|
||||||
Delve,
|
|
||||||
Glaive,
|
|
||||||
Jack,
|
|
||||||
Nano,
|
|
||||||
Wright,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum StatType {
|
|
||||||
Might,
|
|
||||||
Speed,
|
|
||||||
Intellect,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum Cost {
|
|
||||||
Nothing,
|
|
||||||
Constant { stat: StatType, cost: u8 },
|
|
||||||
Variable(StatType),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Tier(u8);
|
|
||||||
|
|
||||||
impl Tier {
|
|
||||||
pub fn new(val: u8) -> Result<Tier, Error> {
|
|
||||||
if val < 1 || val > 6 {
|
|
||||||
Err(Error::TierOutOfRange(val))
|
|
||||||
} else {
|
|
||||||
Ok(Tier(val))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Stat {
|
|
||||||
current: u8,
|
|
||||||
max: u8,
|
|
||||||
edge: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Stats {
|
|
||||||
might: Stat,
|
|
||||||
speed: Stat,
|
|
||||||
intellect: Stat,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct SpecialAbility {
|
|
||||||
name: String,
|
|
||||||
cost: Cost,
|
|
||||||
description: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum SkillRank {
|
|
||||||
Inability,
|
|
||||||
Trained,
|
|
||||||
Specialized,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Skill {
|
|
||||||
skill: String,
|
|
||||||
rank: SkillRank,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub enum Range {
|
|
||||||
Immediate,
|
|
||||||
Short,
|
|
||||||
Long,
|
|
||||||
VeryLong,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Attack {
|
|
||||||
weapon: String,
|
|
||||||
damage: u8,
|
|
||||||
range: Range,
|
|
||||||
ammo: Option<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
pub struct Defense {
|
|
||||||
armor: String,
|
|
||||||
defense: u8,
|
|
||||||
speed_cost: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
struct CharacterSheet {
|
|
||||||
name: String,
|
|
||||||
descriptor: String,
|
|
||||||
type_: Type,
|
|
||||||
focus: String,
|
|
||||||
tier: Tier,
|
|
||||||
pools: Stats,
|
|
||||||
effort: u8,
|
|
||||||
cypher_limit: u8,
|
|
||||||
special_abilities: Vec<SpecialAbility>,
|
|
||||||
skills: Vec<Skill>,
|
|
||||||
attacks: Vec<Attack>,
|
|
||||||
defenses: Vec<Defense>,
|
|
||||||
equipment: Vec<String>,
|
|
||||||
cyphers: Vec<Cypher>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
struct Cypher {
|
|
||||||
name: String,
|
|
||||||
level: u8,
|
|
||||||
form: String,
|
|
||||||
description: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(test, feature = "test_data"))]
|
|
||||||
mod test_data {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn opal() -> CharacterSheet {
|
|
||||||
CharacterSheet {
|
|
||||||
name: "Opal".to_owned(),
|
|
||||||
descriptor: "Adaptable".to_owned(),
|
|
||||||
type_: Type::Jack,
|
|
||||||
focus: "Speaks with a Silver Tongue".to_owned(),
|
|
||||||
tier: Tier::new(1).unwrap(),
|
|
||||||
pools: Stats {
|
|
||||||
might: Stat {
|
|
||||||
current: 11,
|
|
||||||
max: 11,
|
|
||||||
edge: 0,
|
|
||||||
},
|
|
||||||
speed: Stat {
|
|
||||||
current: 12,
|
|
||||||
max: 12,
|
|
||||||
edge: 0,
|
|
||||||
},
|
|
||||||
intellect: Stat {
|
|
||||||
current: 13,
|
|
||||||
max: 13,
|
|
||||||
edge: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
effort: 1,
|
|
||||||
cypher_limit: 3,
|
|
||||||
special_abilities: vec![
|
|
||||||
SpecialAbility { name: "Versatile".to_owned(),
|
|
||||||
cost: Cost::Nothing,
|
|
||||||
description: "+2 to any pool. It can be reassigned after each ten-hour recovery roll.".to_owned() },
|
|
||||||
SpecialAbility { name: "Flex Skill".to_owned(),
|
|
||||||
cost: Cost::Nothing,
|
|
||||||
description: "at the beginning of each day, choose one skill (other than attack or defense). For the rest of the day, you are trained in that skill.".to_owned() },
|
|
||||||
SpecialAbility{ name: "Face Morph".to_owned(),
|
|
||||||
cost: Cost::Constant { stat: StatType::Intellect, cost: 2 },
|
|
||||||
description: "You alter your features and coloration for one hour.".to_owned() },
|
|
||||||
],
|
|
||||||
skills: vec![
|
|
||||||
Skill{ skill: "Perception".to_owned(), rank: SkillRank::Trained },
|
|
||||||
Skill{ skill: "Resilient".to_owned(), rank: SkillRank::Trained },
|
|
||||||
Skill{ skill: "Social interactions".to_owned(), rank: SkillRank::Trained },
|
|
||||||
Skill{ skill: "Pleasant social interactiosn".to_owned(), rank: SkillRank::Specialized },
|
|
||||||
],
|
|
||||||
attacks: vec![
|
|
||||||
Attack { weapon: "Crank Crossbow".to_owned(), damage: 4, range: Range::Long, ammo: Some(25), },
|
|
||||||
Attack { weapon: "Rapier".to_owned(), damage: 2, range: Range::Immediate, ammo: None, },
|
|
||||||
],
|
|
||||||
defenses: vec![
|
|
||||||
Defense {
|
|
||||||
armor: "Brigandine".to_owned(),
|
|
||||||
defense: 2,
|
|
||||||
speed_cost: 2,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
equipment: vec![
|
|
||||||
"A book for recording favorite words, inspiration stories, and speech anecdotes.".to_owned(),
|
|
||||||
"Explorer's pack".to_owned(),
|
|
||||||
],
|
|
||||||
cyphers: vec![
|
|
||||||
Cypher {
|
|
||||||
name: "Flying Cap".to_owned(),
|
|
||||||
level: 3,
|
|
||||||
form: "Hat".to_owned(),
|
|
||||||
description: "Whatever".to_owned(),
|
|
||||||
}
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,394 @@
|
||||||
|
use crate::types::Roll;
|
||||||
|
use std::{collections::HashMap, iter::FromIterator};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Error)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Tier out of range {0}")]
|
||||||
|
TierOutOfRange(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum CharacterType {
|
||||||
|
Arkus,
|
||||||
|
Delve,
|
||||||
|
Glaive,
|
||||||
|
Jack { edge: PoolType },
|
||||||
|
Nano,
|
||||||
|
Wright,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&CharacterType> for String {
|
||||||
|
fn from(type_: &CharacterType) -> String {
|
||||||
|
match type_ {
|
||||||
|
CharacterType::Arkus => "Arkus",
|
||||||
|
CharacterType::Delve => "Delve",
|
||||||
|
CharacterType::Glaive => "Glaive",
|
||||||
|
CharacterType::Jack { .. } => "Jack",
|
||||||
|
CharacterType::Nano => "Nano",
|
||||||
|
CharacterType::Wright => "Wright",
|
||||||
|
}
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CharacterType> for String {
|
||||||
|
fn from(type_: CharacterType) -> String {
|
||||||
|
String::from(&type_)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||||
|
pub enum PoolType {
|
||||||
|
Might,
|
||||||
|
Speed,
|
||||||
|
Intellect,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum Cost {
|
||||||
|
Nothing,
|
||||||
|
Constant { stat: PoolType, cost: u8 },
|
||||||
|
Variable(PoolType),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Tier(u8);
|
||||||
|
|
||||||
|
impl Tier {
|
||||||
|
pub fn new(val: u8) -> Result<Tier, Error> {
|
||||||
|
if val < 1 || val > 6 {
|
||||||
|
Err(Error::TierOutOfRange(val))
|
||||||
|
} else {
|
||||||
|
Ok(Tier(val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct Pool {
|
||||||
|
pub current: u8,
|
||||||
|
pub max: u8,
|
||||||
|
pub edge: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct Pools {
|
||||||
|
pools: HashMap<PoolType, Pool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pools {
|
||||||
|
pub fn might(&self) -> &Pool {
|
||||||
|
self.pools.get(&PoolType::Might).unwrap()
|
||||||
|
}
|
||||||
|
pub fn speed(&self) -> &Pool {
|
||||||
|
self.pools.get(&PoolType::Speed).unwrap()
|
||||||
|
}
|
||||||
|
pub fn intellect(&self) -> &Pool {
|
||||||
|
self.pools.get(&PoolType::Intellect).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
|
pub struct PointAllocation {
|
||||||
|
pub might: u8,
|
||||||
|
pub speed: u8,
|
||||||
|
pub intellect: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct SpecialAbility {
|
||||||
|
name: String,
|
||||||
|
cost: Cost,
|
||||||
|
description: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum SkillRank {
|
||||||
|
Inability,
|
||||||
|
Trained,
|
||||||
|
Specialized,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Skill {
|
||||||
|
skill: String,
|
||||||
|
rank: SkillRank,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub enum Range {
|
||||||
|
Immediate,
|
||||||
|
Short,
|
||||||
|
Long,
|
||||||
|
VeryLong,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Attack {
|
||||||
|
weapon: String,
|
||||||
|
damage: u8,
|
||||||
|
range: Range,
|
||||||
|
ammo: Option<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Defense {
|
||||||
|
armor: String,
|
||||||
|
defense: u8,
|
||||||
|
speed_cost: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct CharacterSheet {
|
||||||
|
name: String,
|
||||||
|
descriptor: String,
|
||||||
|
character_type: CharacterType,
|
||||||
|
focus: String,
|
||||||
|
initial_points: PointAllocation,
|
||||||
|
/*
|
||||||
|
tier: Tier,
|
||||||
|
effort: u8,
|
||||||
|
cypher_limit: u8,
|
||||||
|
special_abilities: Vec<SpecialAbility>,
|
||||||
|
skills: Vec<Skill>,
|
||||||
|
attacks: Vec<Attack>,
|
||||||
|
defenses: Vec<Defense>,
|
||||||
|
equipment: Vec<String>,
|
||||||
|
cyphers: Vec<Cypher>,
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CharacterSheet {
|
||||||
|
pub fn name(&self) -> String {
|
||||||
|
self.name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn description(&self) -> String {
|
||||||
|
let (first, _) = self.descriptor.split_at(1);
|
||||||
|
let first = first.to_lowercase();
|
||||||
|
let article =
|
||||||
|
if first == "a" || first == "e" || first == "i" || first == "o" || first == "u" {
|
||||||
|
"an"
|
||||||
|
} else {
|
||||||
|
"a"
|
||||||
|
};
|
||||||
|
format!(
|
||||||
|
"{} {} {} who {}",
|
||||||
|
article,
|
||||||
|
self.descriptor,
|
||||||
|
String::from(&self.character_type),
|
||||||
|
self.focus
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pools(&self) -> Pools {
|
||||||
|
Pools {
|
||||||
|
pools: HashMap::from_iter(
|
||||||
|
vec![
|
||||||
|
(PoolType::Might, self.might_pool()),
|
||||||
|
(PoolType::Speed, self.speed_pool()),
|
||||||
|
(PoolType::Intellect, self.intellect_pool()),
|
||||||
|
]
|
||||||
|
.into_iter(),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn might_pool(&self) -> Pool {
|
||||||
|
let (base, edge) = match self.character_type {
|
||||||
|
CharacterType::Glaive => (11, 1),
|
||||||
|
CharacterType::Jack {
|
||||||
|
edge: PoolType::Might,
|
||||||
|
} => (10, 1),
|
||||||
|
CharacterType::Jack { edge: _ } => (10, 0),
|
||||||
|
CharacterType::Nano => (7, 0),
|
||||||
|
CharacterType::Arkus => (8, 0),
|
||||||
|
CharacterType::Delve => (9, 0),
|
||||||
|
CharacterType::Wright => (9, 0),
|
||||||
|
};
|
||||||
|
let max = base + self.initial_points.might;
|
||||||
|
let current = max;
|
||||||
|
|
||||||
|
Pool { max, current, edge }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn speed_pool(&self) -> Pool {
|
||||||
|
let (base, edge) = match self.character_type {
|
||||||
|
CharacterType::Glaive => (10, 1),
|
||||||
|
CharacterType::Jack {
|
||||||
|
edge: PoolType::Speed,
|
||||||
|
} => (10, 1),
|
||||||
|
CharacterType::Jack { edge: _ } => (10, 0),
|
||||||
|
CharacterType::Nano => (9, 0),
|
||||||
|
CharacterType::Arkus => (9, 0),
|
||||||
|
CharacterType::Delve => (9, 1),
|
||||||
|
CharacterType::Wright => (7, 0),
|
||||||
|
};
|
||||||
|
let max = base + self.initial_points.speed;
|
||||||
|
let current = max;
|
||||||
|
Pool { max, current, edge }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intellect_pool(&self) -> Pool {
|
||||||
|
let (base, edge) = match self.character_type {
|
||||||
|
CharacterType::Glaive => (7, 0),
|
||||||
|
CharacterType::Jack {
|
||||||
|
edge: PoolType::Intellect,
|
||||||
|
} => (10, 1),
|
||||||
|
CharacterType::Jack { edge: _ } => (10, 0),
|
||||||
|
CharacterType::Nano => (12, 1),
|
||||||
|
CharacterType::Arkus => (11, 1),
|
||||||
|
CharacterType::Delve => (10, 1),
|
||||||
|
CharacterType::Wright => (12, 0),
|
||||||
|
};
|
||||||
|
let max = base + self.initial_points.intellect;
|
||||||
|
let current = max;
|
||||||
|
|
||||||
|
Pool { max, current, edge }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct CypherTemplate {
|
||||||
|
name: String,
|
||||||
|
level: Roll,
|
||||||
|
form: String,
|
||||||
|
description: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct Cypher {
|
||||||
|
name: String,
|
||||||
|
level: u8,
|
||||||
|
form: String,
|
||||||
|
description: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "test_data"))]
|
||||||
|
mod test_data {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn opal() -> CharacterSheet {
|
||||||
|
CharacterSheet {
|
||||||
|
name: "Opal".to_owned(),
|
||||||
|
descriptor: "Adaptable".to_owned(),
|
||||||
|
character_type: CharacterType::Jack {
|
||||||
|
edge: PoolType::Intellect,
|
||||||
|
},
|
||||||
|
focus: "Speaks with a Silver Tongue".to_owned(),
|
||||||
|
initial_points: PointAllocation {
|
||||||
|
might: 1,
|
||||||
|
speed: 2,
|
||||||
|
intellect: 3,
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
pools: Pools {
|
||||||
|
might: Pool {
|
||||||
|
current: 11,
|
||||||
|
max: 11,
|
||||||
|
edge: 0,
|
||||||
|
},
|
||||||
|
speed: Pool {
|
||||||
|
current: 12,
|
||||||
|
max: 12,
|
||||||
|
edge: 0,
|
||||||
|
},
|
||||||
|
intellect: Pool {
|
||||||
|
current: 13,
|
||||||
|
max: 13,
|
||||||
|
edge: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
tier: Tier::new(1).unwrap(),
|
||||||
|
effort: 1,
|
||||||
|
cypher_limit: 3,
|
||||||
|
special_abilities: vec![
|
||||||
|
SpecialAbility { name: "Versatile".to_owned(),
|
||||||
|
cost: Cost::Nothing,
|
||||||
|
description: "+2 to any pool. It can be reassigned after each ten-hour recovery roll.".to_owned() },
|
||||||
|
SpecialAbility { name: "Flex Skill".to_owned(),
|
||||||
|
cost: Cost::Nothing,
|
||||||
|
description: "at the beginning of each day, choose one skill (other than attack or defense). For the rest of the day, you are trained in that skill.".to_owned() },
|
||||||
|
SpecialAbility{ name: "Face Morph".to_owned(),
|
||||||
|
cost: Cost::Constant { stat: PoolType::Intellect, cost: 2 },
|
||||||
|
description: "You alter your features and coloration for one hour.".to_owned() },
|
||||||
|
],
|
||||||
|
skills: vec![
|
||||||
|
Skill{ skill: "Perception".to_owned(), rank: SkillRank::Trained },
|
||||||
|
Skill{ skill: "Resilient".to_owned(), rank: SkillRank::Trained },
|
||||||
|
Skill{ skill: "Social interactions".to_owned(), rank: SkillRank::Trained },
|
||||||
|
Skill{ skill: "Pleasant social interactiosn".to_owned(), rank: SkillRank::Specialized },
|
||||||
|
],
|
||||||
|
attacks: vec![
|
||||||
|
Attack { weapon: "Crank Crossbow".to_owned(), damage: 4, range: Range::Long, ammo: Some(25), },
|
||||||
|
Attack { weapon: "Rapier".to_owned(), damage: 2, range: Range::Immediate, ammo: None, },
|
||||||
|
],
|
||||||
|
defenses: vec![
|
||||||
|
Defense {
|
||||||
|
armor: "Brigandine".to_owned(),
|
||||||
|
defense: 2,
|
||||||
|
speed_cost: 2,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
equipment: vec![
|
||||||
|
"A book for recording favorite words, inspiration stories, and speech anecdotes.".to_owned(),
|
||||||
|
"Explorer's pack".to_owned(),
|
||||||
|
],
|
||||||
|
cyphers: vec![
|
||||||
|
Cypher {
|
||||||
|
name: "Flying Cap".to_owned(),
|
||||||
|
level: 3,
|
||||||
|
form: "Hat".to_owned(),
|
||||||
|
description: "Whatever".to_owned(),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn opals_character_sheet() {
|
||||||
|
let opal = super::test_data::opal();
|
||||||
|
assert_eq!(opal.name(), "Opal");
|
||||||
|
assert_eq!(
|
||||||
|
opal.description(),
|
||||||
|
"an Adaptable Jack who Speaks with a Silver Tongue"
|
||||||
|
);
|
||||||
|
let pools = opal.pools();
|
||||||
|
assert_eq!(
|
||||||
|
pools.might(),
|
||||||
|
&Pool {
|
||||||
|
current: 11,
|
||||||
|
max: 11,
|
||||||
|
edge: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
pools.speed(),
|
||||||
|
&Pool {
|
||||||
|
current: 12,
|
||||||
|
max: 12,
|
||||||
|
edge: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
pools.intellect(),
|
||||||
|
&Pool {
|
||||||
|
current: 13,
|
||||||
|
max: 13,
|
||||||
|
edge: 1
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
mod character;
|
||||||
|
|
||||||
|
pub use character::CharacterSheet;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Die {
|
||||||
|
sides: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Roll {
|
||||||
|
dice: Vec<Die>,
|
||||||
|
modifier: i8,
|
||||||
|
}
|
Loading…
Reference in New Issue