use rand::prelude::*; pub enum StandardSkills { Acrobatics, AnimalHandling, Arcana, Athletics, Deception, History, Insight, Intimidation, Investigation, Medicine, Nature, Perception, Performance, Persuasion, Religion, SleightOfHand, Stealth, Survival, StrengthSave, DexteritySave, ConstitutionSave, IntelligenceSave, WisdomSave, CharismaSave } #[derive(PartialEq, Clone, Copy)] pub enum SkillProficiency { None, Proficient, Expert, } impl Default for SkillProficiency { fn default() -> Self { SkillProficiency::None } } pub trait Stat { fn score(&self) -> u8; fn modifier(&self) -> i8 { return (self.score() as i8 / 2) - 5; } fn skill_modifier(&self, proficiency_modifier: u8, proficiency: SkillProficiency) -> i8 { let modifier : i8 = self.modifier(); let expert = proficiency == SkillProficiency::Expert; // are they an expert? let proficient = expert || proficiency == SkillProficiency::Proficient; // are they at least proficient? let proficiency_boost : u8 = proficiency_modifier * proficient as u8; let expertise_boost : u8 = proficiency_modifier * expert as u8; return modifier + (proficiency_boost as i8) + (expertise_boost as i8); } } pub trait StatSet { fn strength(&self) -> &impl Stat; fn dexterity(&self) -> &impl Stat; fn constitution(&self) -> &impl Stat; fn intelligence(&self) -> &impl Stat; fn wisdom(&self) -> &impl Stat; fn charisma(&self) -> &impl Stat; } pub trait ProficiencySet { fn get_proficiency(&self, skill: StandardSkills) -> SkillProficiency; fn get_proficiency_modifier(&self) -> u8; } pub trait Skill { fn select_stat<'a>(&self, set: &'a impl StatSet) -> &'a dyn Stat; fn get_modifier(&self, set: &impl StatSet, proficiency: SkillProficiency, proficiency_modifier: u8) -> i8 { return self.select_stat(set).skill_modifier(proficiency_modifier, proficiency); } fn is_save(&self) -> bool; } pub struct DieRoll { sides: u8, count: u8 } pub struct RollCalculation { rolls: Vec, modifier: i8 } pub struct RollResult { total: i32, calculation: RollCalculation } pub trait Rollable { fn calculation(&self) -> RollCalculation; fn roll(&self) -> i32 { let calculation = self.calculation(); let mut total : i32 = calculation.modifier as i32; let mut _rng = rand::rng(); for die in calculation.rolls { for _ in 0..die.count { total += (rand::random::() % die.sides + 1) as i32; } } return total; } } impl Rollable for DieRoll { fn calculation(&self) -> RollCalculation { return RollCalculation { rolls: vec![DieRoll { sides: self.sides, count: self.count }], modifier: 0 } } } pub trait ShortRestRecovery { fn on_short_rest(&mut self); } pub trait LongRestRecovery { fn on_long_rest(&mut self); } pub trait LevelAndHitDieManagement { fn get_hit_die() -> u8; fn max_hit_die(&self) -> DieRoll { return DieRoll { sides: Self::get_hit_die(), count: self.get_total_levels() } } fn expended_hit_die(&self) -> u8; fn get_total_levels(&self) -> u8; fn single_hit_die(&self) -> DieRoll { return DieRoll { sides: Self::get_hit_die(), count: 1 } } fn available_hit_die(&self) -> DieRoll { let max = self.max_hit_die(); let expended = self.expended_hit_die(); return DieRoll { sides: max.sides, count: max.count - expended } } fn get_historical_hit_die(&self) -> &Vec; fn get_total_hp(&self) -> i32 { let mut max_hp = 0; let historical_hp_rolls = self.get_historical_hit_die(); for roll in historical_hp_rolls { max_hp += roll.total; } return max_hp; } fn level_up(&mut self); } pub struct UniversalClass { levels: u8, // reset to 0 on long rest expended_hit_die: u8, // historical, each should be immutable. hit_die_history: Vec } impl LongRestRecovery for UniversalClass { fn on_long_rest(&mut self) { self.expended_hit_die = 0; } } impl LevelAndHitDieManagement for UniversalClass { fn get_hit_die() -> u8 { return 6; } fn get_total_levels(&self) -> u8 { return self.levels; } fn expended_hit_die(&self) -> u8 { return self.expended_hit_die; } fn get_historical_hit_die(&self) -> &Vec { return &self.hit_die_history; } fn level_up(&mut self) { self.levels += 1; let new_hit_die = self.single_hit_die(); self.hit_die_history.push(RollResult { total: new_hit_die.roll(), calculation: new_hit_die.calculation() }); } } impl Stat for u8 { fn score(&self) -> u8 { return *self; } } impl Skill for StandardSkills { fn select_stat<'a>(&self, set: &'a impl StatSet) -> &'a dyn Stat { match self { StandardSkills::DexteritySave | StandardSkills::Acrobatics | StandardSkills::SleightOfHand | StandardSkills::Stealth => set.dexterity(), StandardSkills::StrengthSave | StandardSkills::Athletics => set.strength(), StandardSkills::ConstitutionSave => set.constitution(), StandardSkills::IntelligenceSave | StandardSkills::Arcana | StandardSkills::History | StandardSkills::Investigation | StandardSkills::Nature | StandardSkills::Religion => set.intelligence(), StandardSkills::WisdomSave | StandardSkills::AnimalHandling | StandardSkills::Insight | StandardSkills::Medicine | StandardSkills::Perception | StandardSkills::Survival => set.wisdom(), StandardSkills::CharismaSave | StandardSkills::Deception | StandardSkills::Intimidation | StandardSkills::Performance | StandardSkills::Persuasion => set.charisma(), } } fn is_save(&self) -> bool { return matches!(self, StandardSkills::StrengthSave | StandardSkills::DexteritySave | StandardSkills::ConstitutionSave | StandardSkills::IntelligenceSave | StandardSkills::WisdomSave | StandardSkills::CharismaSave); } } pub struct Player { strength_score: u8, dexterity_score: u8, constitution_score: u8, intelligence_score: u8, wisdom_score: u8, charisma_score: u8, level: u8, // saves dexterity_save_proficiency: SkillProficiency, strength_save_proficiency: SkillProficiency, constitution_save_proficiency: SkillProficiency, intelligence_save_proficiency: SkillProficiency, wisdom_save_proficiency: SkillProficiency, charisma_save_proficiency: SkillProficiency, // skills // dexterity: Acrobatics, SleightOfHand, Stealth acrobatics_proficiency: SkillProficiency, sleight_of_hand_proficiency: SkillProficiency, stealth_proficiency: SkillProficiency, // strength: Athletics athletics_proficiency: SkillProficiency, // constitution: none // intelligence: Arcana, History, Investigation, Nature, Religion arcana_proficiency: SkillProficiency, history_proficiency: SkillProficiency, investigation_proficiency: SkillProficiency, nature_proficiency: SkillProficiency, religion_proficiency: SkillProficiency, // wisdom: Animal Handling, Insight, Medicine, Perception, Survival animal_handling_proficiency: SkillProficiency, insight_proficiency: SkillProficiency, medicine_proficiency: SkillProficiency, perception_proficiency: SkillProficiency, survival_proficiency: SkillProficiency, // charisma: Deception, Intimidation, Performance, Persuasion deception_proficiency: SkillProficiency, intimidation_proficiency: SkillProficiency, performance_proficiency: SkillProficiency, persuasion_proficiency: SkillProficiency } impl Default for Player { fn default() -> Self { return Player { strength_score: 10, dexterity_score: 10, constitution_score: 10, intelligence_score: 10, wisdom_score: 10, charisma_score: 10, level: 1, dexterity_save_proficiency: SkillProficiency::None, strength_save_proficiency: SkillProficiency::None, constitution_save_proficiency: SkillProficiency::None, intelligence_save_proficiency: SkillProficiency::None, wisdom_save_proficiency: SkillProficiency::None, charisma_save_proficiency: SkillProficiency::None, acrobatics_proficiency: SkillProficiency::None, sleight_of_hand_proficiency: SkillProficiency::None, stealth_proficiency: SkillProficiency::None, athletics_proficiency: SkillProficiency::None, arcana_proficiency: SkillProficiency::None, history_proficiency: SkillProficiency::None, investigation_proficiency: SkillProficiency::None, nature_proficiency: SkillProficiency::None, religion_proficiency: SkillProficiency::None, animal_handling_proficiency: SkillProficiency::None, insight_proficiency: SkillProficiency::None, medicine_proficiency: SkillProficiency::None, perception_proficiency: SkillProficiency::None, survival_proficiency: SkillProficiency::None, deception_proficiency: SkillProficiency::None, intimidation_proficiency: SkillProficiency::None, performance_proficiency: SkillProficiency::None, persuasion_proficiency: SkillProficiency::None } } } impl StatSet for Player { fn strength(&self) -> &impl Stat { return &self.strength_score; } fn dexterity(&self) -> &impl Stat { return &self.dexterity_score; } fn constitution(&self) -> &impl Stat { return &self.constitution_score; } fn intelligence(&self) -> &impl Stat { return &self.intelligence_score; } fn wisdom(&self) -> &impl Stat { return &self.wisdom_score; } fn charisma(&self) -> &impl Stat { return &self.charisma_score; } } impl ProficiencySet for Player { fn get_proficiency(&self, skill: StandardSkills) -> SkillProficiency { match skill { StandardSkills::DexteritySave => self.dexterity_save_proficiency, StandardSkills::StrengthSave => self.strength_save_proficiency, StandardSkills::ConstitutionSave => self.constitution_save_proficiency, StandardSkills::IntelligenceSave => self.intelligence_save_proficiency, StandardSkills::WisdomSave => self.wisdom_save_proficiency, StandardSkills::CharismaSave => self.charisma_save_proficiency, StandardSkills::Acrobatics => self.acrobatics_proficiency, StandardSkills::SleightOfHand => self.sleight_of_hand_proficiency, StandardSkills::Stealth => self.stealth_proficiency, StandardSkills::Athletics => self.athletics_proficiency, StandardSkills::Arcana => self.arcana_proficiency, StandardSkills::History => self.history_proficiency, StandardSkills::Investigation => self.investigation_proficiency, StandardSkills::Nature => self.nature_proficiency, StandardSkills::Religion => self.religion_proficiency, StandardSkills::AnimalHandling => self.animal_handling_proficiency, StandardSkills::Insight => self.insight_proficiency, StandardSkills::Medicine => self.medicine_proficiency, StandardSkills::Perception => self.perception_proficiency, StandardSkills::Survival => self.survival_proficiency, StandardSkills::Deception => self.deception_proficiency, StandardSkills::Intimidation => self.intimidation_proficiency, StandardSkills::Performance => self.performance_proficiency, StandardSkills::Persuasion => self.persuasion_proficiency } } fn get_proficiency_modifier(&self) -> u8 { return ((self.level - 1) / 4) + 2; } } fn main() { let demo : Player = Player { strength_score: 12, dexterity_score: 20, constitution_score: 15, intelligence_score: 10, wisdom_score: 5, charisma_score: 1, stealth_proficiency: SkillProficiency::Expert, ..Default::default() }; println!("STR Modifier: {}", demo.strength().modifier()); println!("DEX Modifier: {}", demo.dexterity().modifier()); println!("CON Modifier: {}", demo.constitution().modifier()); println!("INT Modifier: {}", demo.intelligence().modifier()); println!("WIS Modifier: {}", demo.wisdom().modifier()); println!("CHA Modifier: {}", demo.charisma().modifier()); println!("Stealth Modifier: {}", StandardSkills::Stealth.get_modifier(&demo, demo.get_proficiency(StandardSkills::Stealth), demo.get_proficiency_modifier())); }