underworld_core 0.6.3

Small library to either play a game or assist in generating rooms
Documentation
use chrono::Utc;
use rand::{prelude::ThreadRng, Rng};
use strum::IntoEnumIterator;
use uuid::Uuid;

use crate::components::{
    items::{CharacterItem, Descriptor, Item, ItemType, LocationTag},
    spells::{LearnedSpell, Spell, SpellMemory, SpellName},
    Character, Effects, Inventory, Material, PlayerCharacter, Size, Species, Stats, Tag,
    {Attack, Defense},
};

use super::{generator::Generator, stats::build_specific_health};

struct PlayerCharacterPrototype {
    pub character_name: Option<String>,
    pub species: Option<Species>,
    pub size: Option<Size>,
}

pub fn player_generator(
    character_name: Option<String>,
    species: Option<Species>,
    size: Option<Size>,
) -> impl Generator<PlayerCharacter> {
    PlayerCharacterPrototype {
        character_name,
        species,
        size,
    }
}

impl Generator<PlayerCharacter> for PlayerCharacterPrototype {
    fn generate(&self) -> PlayerCharacter {
        let mut rng = rand::thread_rng();

        let species = match &self.species {
            Some(it) => *it,
            None => {
                let options: Vec<Species> = Species::iter().collect();
                let index = rng.gen_range(0..options.len());
                *options.get(index).unwrap()
            }
        };

        let stats_generator = build_specific_health(25, &species, false);
        let mut stats: Stats = stats_generator.generate();

        if let Some(size) = &self.size {
            stats.height = *size;
        }

        let starter_weapon = starter_weapon(&mut rng);
        let starter_wearables = starter_wearables();

        PlayerCharacter {
            character: Character {
                stats,
                species,
                life_modifier: None,
                inventory: Inventory {
                    equipment: starter_wearables
                        .into_iter()
                        .chain(vec![starter_weapon].into_iter())
                        .collect(),
                },
                current_effects: Effects::default(),
                spell_memory: SpellMemory {
                    spells: vec![LearnedSpell {
                        id: Uuid::new_v4(),
                        spell: Spell {
                            name: SpellName::Phoenix,
                            attack: None,
                            defense: None,
                            uses: 1,
                        },
                        learned_at: Utc::now(),
                    }],
                },
            },
            id: Uuid::new_v4(),
            name: self.character_name.clone(),
            gold: 0,
        }
    }
}

fn starter_wearables() -> Vec<CharacterItem> {
    let trousers = Item {
        id: Uuid::new_v4(),
        name: None,
        item_type: ItemType::Trousers,
        tags: vec![Tag::Clothing, Tag::Cloth],
        descriptors: vec![Descriptor::Dirty, Descriptor::Stained],
        material: Some(Material::Linen),
        attack: None,
        defense: Some(Defense {
            damage_resistance: 1,
        }),
        consumable: None,
        throwable: None,
    };

    let shirt = Item {
        id: Uuid::new_v4(),
        name: None,
        item_type: ItemType::Shirt,
        tags: vec![Tag::Clothing, Tag::Cloth],
        descriptors: vec![Descriptor::Colourful],
        material: Some(Material::Linen),
        attack: None,
        defense: Some(Defense {
            damage_resistance: 1,
        }),
        consumable: None,
        throwable: None,
    };

    let boots = Item {
        id: Uuid::new_v4(),
        name: None,
        item_type: ItemType::Boots,
        tags: vec![Tag::Clothing, Tag::Leather],
        descriptors: vec![Descriptor::Dirty],
        material: Some(Material::Leather),
        attack: None,
        defense: Some(Defense {
            damage_resistance: 1,
        }),
        consumable: None,
        throwable: None,
    };

    vec![
        CharacterItem {
            item: trousers,
            at_the_ready: true,
            equipped_location: LocationTag::Leg,
        },
        CharacterItem {
            item: shirt,
            at_the_ready: true,
            equipped_location: LocationTag::Body,
        },
        CharacterItem {
            item: boots,
            at_the_ready: true,
            equipped_location: LocationTag::Feet,
        },
    ]
}

fn starter_weapon(rng: &mut ThreadRng) -> CharacterItem {
    let weapon_types: Vec<ItemType> = vec![
        ItemType::Dagger,
        ItemType::Dirk,
        ItemType::ShortSword,
        ItemType::Club,
    ];

    let index = rng.gen_range(0..weapon_types.len());
    let item_type = *weapon_types.get(index).unwrap_or(&ItemType::Dagger);

    let tags = if item_type == ItemType::Club {
        vec![Tag::Blunt]
    } else {
        vec![Tag::Blade]
    };

    let material = if item_type == ItemType::Club {
        Some(Material::Wooden)
    } else {
        Some(Material::Iron)
    };

    let descriptors = if item_type == ItemType::Club {
        vec![Descriptor::Beaten]
    } else {
        vec![Descriptor::Rusty]
    };

    let item = Item {
        id: Uuid::new_v4(),
        name: None,
        item_type,
        tags,
        material,
        descriptors,
        attack: Some(Attack {
            num_rolls: 1,
            modifier: -1,
            effects: Vec::new(),
        }),
        defense: None,
        consumable: None,
        throwable: None,
    };

    CharacterItem {
        item,
        equipped_location: LocationTag::Hand,
        at_the_ready: true,
    }
}