1 Commits

Author SHA1 Message Date
Lauren Kaviak
a3f2b91119 refactor into multiple files
All checks were successful
Continuous Releases / build (windows) (push) Successful in 6s
2026-03-22 01:16:49 -05:00
5 changed files with 384 additions and 377 deletions

89
src/classes.rs Normal file
View File

@@ -0,0 +1,89 @@
use crate::dice::{DieRoll, RollResult, RollCalculation, Rollable};
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<RollResult>;
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 {
pub levels: u8,
// reset to 0 on long rest
pub expended_hit_die: u8,
// historical, each should be immutable.
pub hit_die_history: Vec<RollResult>
}
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<RollResult> {
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()
});
}
}

41
src/dice.rs Normal file
View File

@@ -0,0 +1,41 @@
use rand::prelude::*;
pub struct DieRoll {
pub sides: u8,
pub count: u8
}
pub struct RollCalculation {
pub rolls: Vec<DieRoll>,
pub modifier: i8
}
pub struct RollResult {
pub total: i32,
pub 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::<u8>() % 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
}
}
}

View File

@@ -1,381 +1,10 @@
use rand::prelude::*; mod dice;
mod stats_skills;
pub enum StandardSkills { mod player;
Acrobatics, mod classes;
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<DieRoll>,
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::<u8>() % 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<RollResult>;
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<RollResult>
}
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<RollResult> {
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;
}
}
use stats_skills::{StandardSkills, SkillProficiency, Skill, ProficiencySet, StatSet, Stat};
use player::Player;
fn main() { fn main() {
let demo : Player = Player { let demo : Player = Player {

146
src/player.rs Normal file
View File

@@ -0,0 +1,146 @@
use crate::stats_skills::{Stat, StatSet, ProficiencySet, SkillProficiency, StandardSkills};
pub struct Player {
pub strength_score: u8,
pub dexterity_score: u8,
pub constitution_score: u8,
pub intelligence_score: u8,
pub wisdom_score: u8,
pub charisma_score: u8,
pub level: u8,
// saves
pub dexterity_save_proficiency: SkillProficiency,
pub strength_save_proficiency: SkillProficiency,
pub constitution_save_proficiency: SkillProficiency,
pub intelligence_save_proficiency: SkillProficiency,
pub wisdom_save_proficiency: SkillProficiency,
pub charisma_save_proficiency: SkillProficiency,
// skills
// dexterity: Acrobatics, SleightOfHand, Stealth
pub acrobatics_proficiency: SkillProficiency,
pub sleight_of_hand_proficiency: SkillProficiency,
pub stealth_proficiency: SkillProficiency,
// strength: Athletics
pub athletics_proficiency: SkillProficiency,
// constitution: none
// intelligence: Arcana, History, Investigation, Nature, Religion
pub arcana_proficiency: SkillProficiency,
pub history_proficiency: SkillProficiency,
pub investigation_proficiency: SkillProficiency,
pub nature_proficiency: SkillProficiency,
pub religion_proficiency: SkillProficiency,
// wisdom: Animal Handling, Insight, Medicine, Perception, Survival
pub animal_handling_proficiency: SkillProficiency,
pub insight_proficiency: SkillProficiency,
pub medicine_proficiency: SkillProficiency,
pub perception_proficiency: SkillProficiency,
pub survival_proficiency: SkillProficiency,
// charisma: Deception, Intimidation, Performance, Persuasion
pub deception_proficiency: SkillProficiency,
pub intimidation_proficiency: SkillProficiency,
pub performance_proficiency: SkillProficiency,
pub 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;
}
}

102
src/stats_skills.rs Normal file
View File

@@ -0,0 +1,102 @@
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;
}
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);
}
}
impl Stat for u8 {
fn score(&self) -> u8 {
return *self;
}
}