underworld_core 0.6.3

Small library to either play a game or assist in generating rooms
Documentation
use std::ops::RangeInclusive;

use rand::Rng;
use strum::IntoEnumIterator;
use uuid::Uuid;

use crate::{
    components::{
        fixtures::FixtureType,
        rooms::{Descriptor, Dimensions, ExitType, Flavour, Room, RoomType},
        Ghost, LifeModifier, Species,
    },
    generators::generator::Generator,
};

use super::{BuildExitArgs, BuildFixturesArgs, BuildNpcsArgs, RoomPrototype};

#[derive(Default, Clone)]
pub struct ExitGenerationArgs {
    pub num_exits: Option<RangeInclusive<u16>>,
    pub possible_exit_types: Option<Vec<ExitType>>,
}

#[derive(Default, Clone)]
pub struct RoomNpcGenerationArgs {
    pub num_groups: Option<RangeInclusive<u16>>,
    pub possible_species: Option<Vec<Species>>,
    pub possible_life_modifiers: Option<Vec<LifeModifier>>,
    pub allow_npcs_to_spawn_dead: Option<bool>,
    pub ghosts: Option<Vec<Ghost>>,
}

#[derive(Default, Clone)]
pub struct RoomFixtureGenerationArgs {
    pub num_groups: Option<RangeInclusive<u16>>,
    pub possible_types: Option<Vec<FixtureType>>,
}

#[derive(Default)]
pub struct RoomGeneratorBuilder {
    num_descriptors: Option<RangeInclusive<u16>>,
    room_type: Option<RoomType>,
    possible_descriptors: Option<Vec<Descriptor>>,
    entrance_id: Option<Uuid>,
    danger_level: Option<u32>,
    include_flavour_text: Option<bool>,
    possible_flavour_texts: Option<Vec<Flavour>>,
    name: Option<String>,
    dimensions: Option<Dimensions>,
    exit_generation_args: Option<ExitGenerationArgs>,
    room_npc_generation_args: Option<RoomNpcGenerationArgs>,
    room_fixture_generation_args: Option<RoomFixtureGenerationArgs>,
}

impl RoomGeneratorBuilder {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn num_descriptors(&mut self, num_descriptors: RangeInclusive<u16>) -> &mut Self {
        self.num_descriptors = Some(num_descriptors);

        self
    }

    pub fn room_type(&mut self, room_type: RoomType) -> &mut Self {
        self.room_type = Some(room_type);

        self
    }

    pub fn possible_descriptors(&mut self, possible_descriptors: Vec<Descriptor>) -> &mut Self {
        self.possible_descriptors = Some(possible_descriptors);

        self
    }

    pub fn entrance_id(&mut self, entrance_id: Uuid) -> &mut Self {
        self.entrance_id = Some(entrance_id);

        self
    }

    pub fn danger_level(&mut self, danger_level: u32) -> &mut Self {
        self.danger_level = Some(danger_level);

        self
    }

    pub fn include_flavour_text(&mut self, include_flavour: bool) -> &mut Self {
        self.include_flavour_text = Some(include_flavour);

        self
    }

    pub fn possible_flavour_texts(&mut self, possible_flavour_texts: Vec<Flavour>) -> &mut Self {
        self.possible_flavour_texts = Some(possible_flavour_texts);

        self
    }

    pub fn name(&mut self, name: &str) -> &mut Self {
        self.name = Some(name.to_string());

        self
    }

    pub fn dimensions(&mut self, dimensions: Dimensions) -> &mut Self {
        self.dimensions = Some(dimensions);

        self
    }

    pub fn exit_generation_args(&mut self, exit_generation_args: ExitGenerationArgs) -> &mut Self {
        self.exit_generation_args = Some(exit_generation_args);

        self
    }

    pub fn room_fixture_generation_args(
        &mut self,
        room_fixture_generation_args: RoomFixtureGenerationArgs,
    ) -> &mut Self {
        self.room_fixture_generation_args = Some(room_fixture_generation_args);

        self
    }

    pub fn room_npc_generation_args(
        &mut self,
        room_npc_generation_args: RoomNpcGenerationArgs,
    ) -> &mut Self {
        self.room_npc_generation_args = Some(room_npc_generation_args);

        self
    }

    pub fn build(&self) -> impl Generator<Room> {
        let num_descriptors = match &self.num_descriptors {
            Some(it) => it.clone(),
            None => 0..=2,
        };

        let room_type = match &self.room_type {
            Some(it) => *it,
            None => {
                let room_types: Vec<RoomType> = RoomType::iter().collect();
                let mut rng = rand::thread_rng();
                let index = rng.gen_range(0..room_types.len());
                *room_types.get(index).unwrap()
            }
        };

        let possible_descriptors = match &self.possible_descriptors {
            Some(it) => it.clone(),
            None => room_type.possible_descriptors(),
        };

        let possible_flavour_texts = match &self.possible_flavour_texts {
            Some(it) => it.clone(),
            None => room_type.possible_flavours(),
        };

        let build_exit_args = match &self.exit_generation_args {
            Some(exit_generation_args) => {
                let num_exits = match &exit_generation_args.num_exits {
                    Some(it) => it.clone(),
                    None => num_exits(&room_type),
                };

                let exit_types = match &exit_generation_args.possible_exit_types {
                    Some(it) => it.clone(),
                    None => exit_types(&room_type),
                };

                BuildExitArgs {
                    num_exits,
                    exit_types,
                }
            }
            None => BuildExitArgs {
                num_exits: num_exits(&room_type),
                exit_types: exit_types(&room_type),
            },
        };

        let build_npc_args = match &self.room_npc_generation_args {
            Some(room_npc_generation_args) => {
                let num_groups = match &room_npc_generation_args.num_groups {
                    Some(it) => it.clone(),
                    None => num_groups(&room_type),
                };

                let possible_species = match &room_npc_generation_args.possible_species {
                    Some(it) => it.clone(),
                    None => Species::iter().collect(),
                };

                let possible_life_modifiers =
                    match &room_npc_generation_args.possible_life_modifiers {
                        Some(it) => it.clone(),
                        None => LifeModifier::iter().collect(),
                    };

                let allow_npcs_to_spawn_dead =
                    match &room_npc_generation_args.allow_npcs_to_spawn_dead {
                        Some(it) => *it,
                        None => true,
                    };

                let ghosts = match &room_npc_generation_args.ghosts {
                    Some(it) => it.to_vec(),
                    None => Vec::new(),
                };

                BuildNpcsArgs {
                    num_groups,
                    possible_species,
                    possible_life_modifiers,
                    allow_npcs_to_spawn_dead,
                    ghosts,
                }
            }
            None => BuildNpcsArgs {
                num_groups: num_groups(&room_type),
                possible_species: Species::iter().collect(),
                possible_life_modifiers: LifeModifier::iter().collect(),
                allow_npcs_to_spawn_dead: true,
                ghosts: Vec::new(),
            },
        };

        let build_fixtures_args = match &self.room_fixture_generation_args {
            Some(room_fixture_gen_args) => {
                let num_groups = match &room_fixture_gen_args.num_groups {
                    Some(it) => it.to_owned(),
                    None => num_fixture_groups(&room_type),
                };

                let possible_types = match &room_fixture_gen_args.possible_types {
                    Some(it) => it.to_owned(),
                    None => possible_fixtures(&room_type),
                };

                BuildFixturesArgs {
                    num_groups,
                    possible_types,
                }
            }
            None => BuildFixturesArgs {
                num_groups: num_fixture_groups(&room_type),
                possible_types: possible_fixtures(&room_type),
            },
        };

        RoomPrototype {
            num_descriptors,
            room_type,
            possible_descriptors,
            entrance_id: self.entrance_id,
            danger_level: self.danger_level.unwrap_or(1),
            possible_flavour_texts,
            include_flavour_text: self.include_flavour_text.unwrap_or(true),
            name: self.name.clone(),
            dimensions: self.dimensions.clone(),
            build_exit_args,
            build_npc_args,
            build_fixtures_args,
        }
    }
}

fn possible_fixtures(room_type: &RoomType) -> Vec<FixtureType> {
    match *room_type {
        RoomType::PrisonCell => vec![
            FixtureType::Bucket,
            FixtureType::Cot,
            FixtureType::SleepingRoll,
        ],
        RoomType::EntryWay => vec![
            FixtureType::Barrel,
            FixtureType::Bucket,
            FixtureType::Chair,
            FixtureType::Chest,
            FixtureType::WeaponRack,
        ],
        RoomType::TavernHall => vec![
            FixtureType::Chair,
            FixtureType::Table,
            FixtureType::Barrel,
            FixtureType::Crate,
            FixtureType::Bucket,
        ],
        _ => FixtureType::iter().collect(),
    }
}

fn num_exits(room_type: &RoomType) -> RangeInclusive<u16> {
    match *room_type {
        RoomType::PrisonCell => 1..=2,
        RoomType::Cavern
        | RoomType::TavernHall
        | RoomType::Mausoleum
        | RoomType::Cemetery
        | RoomType::Crypt
        | RoomType::TempleHall
        | RoomType::Cave
        | RoomType::Room => 3..=5,
        RoomType::EntryWay => 2..=2,
    }
}

fn num_fixture_groups(room_type: &RoomType) -> RangeInclusive<u16> {
    match *room_type {
        RoomType::PrisonCell => 0..=1,
        RoomType::Room => 0..=1,
        RoomType::EntryWay => 0..=1,
        _ => 0..=2,
    }
}

fn num_groups(room_type: &RoomType) -> RangeInclusive<u16> {
    match *room_type {
        RoomType::PrisonCell | RoomType::EntryWay | RoomType::Mausoleum => 0..=1,
        RoomType::Cave | RoomType::Crypt | RoomType::Room | RoomType::TempleHall => 1..=2,
        RoomType::Cemetery | RoomType::Cavern | RoomType::TavernHall => 1..=3,
    }
}

fn exit_types(room_type: &RoomType) -> Vec<ExitType> {
    match *room_type {
        RoomType::PrisonCell => vec![
            ExitType::DugOutTunnelEntrance,
            ExitType::Door,
            ExitType::OpeningToTheVoid,
            ExitType::HoleInTheFloor,
            ExitType::HoleInTheWall,
        ],
        _ => ExitType::iter().collect(),
    }
}