Construct a basic character with some descriptors and focuses

This commit is contained in:
Savanni D'Gerinel 2022-03-05 16:14:29 -05:00
parent 9a3676d696
commit 0588a803cf
2 changed files with 313 additions and 65 deletions

View File

@ -25,31 +25,38 @@ pub enum CharacterTypeEdge {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct CharacterType {
text: String,
name: String,
starting_pools: HashMap<PoolType, u8>,
edge: CharacterTypeEdge,
}
impl CharacterType {
fn pool(&self, type_: PoolType) -> u8 {
self.starting_pools.get(&type_).unwrap().clone()
fn pool(&self, type_: &PoolType) -> u8 {
self.starting_pools.get(type_).unwrap().clone()
}
fn edge(&self, type_: PoolType) -> u8 {
fn edge(&self, type_: &PoolType) -> u8 {
match self.edge {
CharacterTypeEdge::Flexible => 0,
CharacterTypeEdge::Static(ref edge) => edge.get(&type_).unwrap_or(&0).clone(),
CharacterTypeEdge::Static(ref edge) => edge.get(type_).unwrap_or(&0).clone(),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum Cost {
Nothing,
Constant { stat: PoolType, cost: u8 },
Variable(PoolType),
}
impl Default for Cost {
fn default() -> Cost {
Cost::Nothing
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Tier(u8);
@ -88,33 +95,32 @@ impl Pools {
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct PointAllocation {
pub might: u8,
pub speed: u8,
pub intellect: u8,
}
pub struct PointAllocation(HashMap<PoolType, u8>);
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct SpecialAbility {
name: String,
#[serde(default)]
cost: Cost,
description: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum SkillRank {
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum SkillLevel {
Inability,
Trained,
Specialized,
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Skill {
skill: String,
rank: SkillRank,
name: String,
level: SkillLevel,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Range {
Immediate,
Short,
@ -137,12 +143,47 @@ pub struct Defense {
speed_cost: u8,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Enabler {
name: String,
descriptor: String,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum DescriptorAbility {
Skill(Skill),
Enabler(Enabler),
SpecialAbility(SpecialAbility),
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Descriptor {
name: String,
effects: Vec<DescriptorAbility>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub struct FocusAbility {
name: String,
effect: DescriptorAbility,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Focus {
name: String,
tier1: Vec<FocusAbility>,
tier2: Vec<FocusAbility>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CharacterSheet {
name: String,
descriptor: String,
descriptor: Descriptor,
character_type: CharacterType,
focus: String,
focus: Focus,
flex_edge: Option<PoolType>,
initial_points: PointAllocation,
/*
tier: Tier,
@ -163,7 +204,7 @@ impl CharacterSheet {
}
pub fn description(&self) -> String {
let (first, _) = self.descriptor.split_at(1);
let (first, _) = self.descriptor.name.split_at(1);
let first = first.to_lowercase();
let article =
if first == "a" || first == "e" || first == "i" || first == "o" || first == "u" {
@ -172,11 +213,12 @@ impl CharacterSheet {
"a"
};
format!(
"{} {} {:?} who {}",
article, self.descriptor, self.character_type, self.focus
"{} {} {} who {}",
article, self.descriptor.name, self.character_type.name, self.focus.name
)
}
/*
pub fn pools(&self) -> Pools {
Pools {
pools: HashMap::from_iter(
@ -189,30 +231,59 @@ impl CharacterSheet {
),
}
}
*/
fn might_pool(&self) -> Pool {
fn pool(&self, pool: &PoolType) -> Pool {
let max =
self.character_type.pool(pool) + self.initial_points.0.get(pool).cloned().unwrap_or(0);
Pool {
current: 0,
max: 0,
edge: 0,
current: max,
max,
edge: self.edge(pool),
}
}
fn edge(&self, pool: &PoolType) -> u8 {
match self.character_type.edge {
CharacterTypeEdge::Static(ref edges) => edges.get(pool).cloned().unwrap_or(0).clone(),
CharacterTypeEdge::Flexible => {
if self.flex_edge == Some(pool.clone()) {
1
} else {
0
}
}
}
}
/*
fn might_pool(&self) -> Pool {
Pool {
current: max,
max,
edge: self.edge(PoolType::Might),
}
}
fn speed_pool(&self) -> Pool {
let max = self.character_type.pool(PoolType::Speed) + self.initial_points.speed;
Pool {
current: 0,
max: 0,
edge: 0,
current: max,
max,
edge: self.edge(PoolType::Speed),
}
}
fn intellect_pool(&self) -> Pool {
let max = self.character_type.pool(PoolType::Intellect) + self.initial_points.intellect;
Pool {
current: 0,
max: 0,
edge: 0,
current: max,
max,
edge: self.edge(PoolType::Intellect),
}
}
*/
}
#[derive(Debug, Serialize, Deserialize)]
@ -236,7 +307,7 @@ mod test_data {
use super::*;
#[allow(dead_code)]
pub const GLAIVE_DESCRIPTOR: &str = "text: Glaive
pub const GLAIVE: &str = "name: Glaive
starting_pools:
might: 11
speed: 10
@ -245,7 +316,7 @@ edge:
static:
might: 1
speed: 1";
pub const JACK_DESCRIPTOR: &str = "text: Jack
pub const JACK: &str = "name: Jack
starting_pools:
might: 10
speed: 10
@ -253,19 +324,152 @@ starting_pools:
edge: flexible
";
pub fn jack() -> CharacterType {
let mut starting_pools: HashMap<PoolType, u8> = HashMap::new();
starting_pools.insert(PoolType::Might, 10);
starting_pools.insert(PoolType::Speed, 10);
starting_pools.insert(PoolType::Intellect, 10);
CharacterType {
name: "Jack".to_owned(),
starting_pools,
edge: CharacterTypeEdge::Flexible,
}
}
pub const ADAPTABLE: &str = "
name: Adaptable
effects:
- specialability:
name: Versatile
description: +2 to any pool, changeable every ten hours
- skill:
name: Pleasant Social Interactions
level: trained
- skill:
name: Overcoming deprivation, sorrow, or pain
level: trained
";
pub fn adaptable() -> Descriptor {
Descriptor {
name: "Adaptable".to_owned(),
effects: vec![
DescriptorAbility::SpecialAbility(versatile()),
DescriptorAbility::Skill(pleasant_social()),
DescriptorAbility::Skill(overcoming_adversity()),
],
}
}
pub fn overcoming_adversity() -> Skill {
Skill {
name: "Overcoming deprivation, sorrow, or pain".to_owned(),
level: SkillLevel::Trained,
}
}
pub const VERSATILE: &str = "
name: Versatile
description: +2 to any pool, changeable every ten hours
";
pub fn versatile() -> SpecialAbility {
SpecialAbility {
name: "Versatile".to_owned(),
cost: Cost::Nothing,
description: "+2 to any pool, changeable every ten hours".to_owned(),
}
}
pub const ENTERTAINS: &str = "name: Entertains
tier1:
- name: Levity
text: Some text which describes what the player gets
action: enabler
";
pub const EXPLORES_DARK_PLACES: &str = "name: Explores Dark Places
tier1:
- name: Superb Explorer
skills:
- skill: searching
level: trained
- skill: listening
level: trained
";
pub const FOCUSES_MIND_OVER_MATTER: &str = "name: Focuses Mind Over Matter
tier1:
- name: Deflect Attacks
cost: { type: \"Constant\", stat: \"Intellect\", cost: 1 }
text: Trained in speed defense for ten minutes
";
pub const SILVER_TONGUE: &str = "
name: Speaks with a Silver Tongue
equipment:
- Book of favorite stories
tier1:
- name: Poetic License
skills:
- name: all social interactions
level: trained
tier2:
- name: A Smile and a Word
text: free level of effort on interaction tasks
";
pub fn silver_tongue() -> Focus {
Focus {
name: "Speaks with a Silver Tongue".to_owned(),
tier1: vec![FocusAbility {
name: "Poetic License".to_owned(),
effect: DescriptorAbility::Skill(Skill {
name: "all social interactions".to_owned(),
level: SkillLevel::Trained,
}),
}],
tier2: vec![FocusAbility {
name: "A Smile and a Word".to_owned(),
effect: DescriptorAbility::Enabler(Enabler {
name: "".to_owned(),
descriptor: "free level of effort on any interaction task".to_owned(),
}),
}],
}
}
pub const DEFLECT_ATTACKS: &str = "
name: Deflect Attacks
cost: { constant: { stat: \"intellect\", cost: 1 } }
description: Trained in speed defense for ten minutes
";
pub const PLEASANT_SOCIAL: &str = "
name: Pleasant social interactions
level: trained
";
pub fn pleasant_social() -> Skill {
Skill {
name: "Pleasant social interactions".to_owned(),
level: SkillLevel::Trained,
}
}
pub fn opal() -> CharacterSheet {
let mut initial_points: HashMap<PoolType, u8> = HashMap::new();
initial_points.insert(PoolType::Might, 1);
initial_points.insert(PoolType::Speed, 2);
initial_points.insert(PoolType::Intellect, 3);
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,
},
descriptor: adaptable(),
character_type: jack(),
focus: silver_tongue(),
flex_edge: Some(PoolType::Intellect),
initial_points: PointAllocation(initial_points),
}
}
}
@ -273,26 +477,75 @@ edge: flexible
#[cfg(test)]
mod test {
use super::test_data::*;
use super::*;
use serde_yaml;
#[test]
fn parses_character_type() {
let glaive: CharacterType =
serde_yaml::from_str(GLAIVE_DESCRIPTOR).expect("should deserialize");
assert_eq!(glaive.text, "Glaive");
assert_eq!(glaive.pool(PoolType::Might), 11);
assert_eq!(glaive.edge(PoolType::Might), 1);
assert_eq!(glaive.edge(PoolType::Intellect), 0);
let glaive: CharacterType = serde_yaml::from_str(GLAIVE).expect("should deserialize");
assert_eq!(glaive.name, "Glaive");
assert_eq!(glaive.pool(&PoolType::Might), 11);
assert_eq!(glaive.edge(&PoolType::Might), 1);
assert_eq!(glaive.edge(&PoolType::Intellect), 0);
}
#[test]
fn parses_character_type_with_flexible_edge() {
let jack: CharacterType =
serde_yaml::from_str(JACK_DESCRIPTOR).expect("should deserialize");
assert_eq!(jack.text, "Jack");
let jack: CharacterType = serde_yaml::from_str(JACK).expect("should deserialize");
assert_eq!(jack.name, "Jack");
}
use super::*;
#[test]
fn parses_skill() {
let skill: Skill = serde_yaml::from_str(PLEASANT_SOCIAL).expect("should deserialize");
assert_eq!(skill, pleasant_social());
}
#[test]
fn parses_special_ability() {
let special: SpecialAbility = serde_yaml::from_str(VERSATILE).expect("should deserialize");
assert_eq!(special, versatile());
}
#[test]
fn parses_special_ability_with_cost() {
let deflect = SpecialAbility {
name: "Deflect Attacks".to_owned(),
cost: Cost::Constant {
stat: PoolType::Intellect,
cost: 1,
},
description: "Trained in speed defense for ten minutes".to_owned(),
};
println!("SPECIAL: {}", serde_yaml::to_string(&deflect).unwrap());
let ability: SpecialAbility =
serde_yaml::from_str(DEFLECT_ATTACKS).expect("should deserialize");
assert_eq!(ability, deflect);
}
#[test]
fn parses_adaptable_descriptor() {
let descriptor: Descriptor = serde_yaml::from_str(ADAPTABLE).expect("should deserialize");
assert_eq!(descriptor.effects.len(), 3);
assert_eq!(
descriptor.effects[0],
DescriptorAbility::SpecialAbility(versatile())
);
}
/*
#[test]
fn parses_enabler_focus() {
let entertains: Focus = serde_yaml::from_str(ENTERTAINS).expect("should deserialize");
assert_eq!(
entertains.tier1,
DescriptorAbility::Enabler(Enabler {
name: "Levity".to_owned(),
text: "Some text which describes what the player gets".to_owned(),
})
);
}
*/
#[test]
fn opals_character_sheet() {
@ -302,10 +555,9 @@ mod test {
opal.description(),
"an Adaptable Jack who Speaks with a Silver Tongue"
);
let pools = opal.pools();
assert_eq!(
pools.might(),
&Pool {
opal.pool(&PoolType::Might),
Pool {
current: 11,
max: 11,
edge: 0
@ -313,8 +565,8 @@ mod test {
);
assert_eq!(
pools.speed(),
&Pool {
opal.pool(&PoolType::Speed),
Pool {
current: 12,
max: 12,
edge: 0
@ -322,8 +574,8 @@ mod test {
);
assert_eq!(
pools.intellect(),
&Pool {
opal.pool(&PoolType::Intellect),
Pool {
current: 13,
max: 13,
edge: 1

View File

@ -21,8 +21,4 @@ in pkgs.mkShell {
rust
unstable.rust-analyzer
];
shellHook = ''
if [ -e ~/.nixpkgs/shellhook.sh ]; then . ~/.nixpkgs/shellhook.sh; fi
'';
}