text_based_rpg 0.1.0

A text-based RPG game
Documentation
mod entity;
mod game;
mod items;
mod locations;
mod weapons;
use std::{io, io::Write};

use crate::entity::{Hero, Entity};
use crate::game::battle;
use items::Item;
use rand::{seq::SliceRandom, Rng};


pub fn create_hero() -> Hero {
    let name = get_user_input("Enter hero name: ");
    let weapon_name = "Flimsy Wooden Sword".to_string();
    let weapon_damage: i32 = 10;
    let mut items = Vec::new();

    let weapon = weapons::Weapon::new(&weapon_name, weapon_damage, |target| {
        println!("Flimsy Wooden Sword Splinters. 1 extra damage!");
        target.take_damage(1)
    });

    items.push(Item::new("Healing Potion", |target| {
        println!("Healing Potion applied. 50 health restored");
        target.heal_damage(50);
    }));

    Hero::new(name, 100, items, weapon)
}

///generate the encounter
pub fn generate_encounter(possible_enemies: Vec<entity::Enemy>) -> Vec<entity::Enemy> {
    let mut rng = rand::thread_rng();
    let num_enemies = rng.gen_range(1..4);
    let mut encounter_enemies: Vec<entity::Enemy> = Vec::new();

    for _ in 0..num_enemies {
        let enemy = possible_enemies.choose(&mut rng).unwrap();
        // &mut Vec<Box<&mut Hero>>
        encounter_enemies.push((*enemy).clone());
    }
    return encounter_enemies;
}

pub fn generate_loot(possible_loot: Vec<weapons::Weapon>) -> weapons::Weapon {
    let mut rng = rand::thread_rng();

    possible_loot.choose(&mut rng).unwrap().clone()
}

pub fn get_user_input(prompt: &str) -> String {
    // Display the prompt to the user
    print!("{}", prompt);
    io::stdout().flush().unwrap(); // Ensure the prompt is printed before user input

    // Create a new String to store the input
    let mut input = String::new();

    // Read the input from standard input (stdin) into the String
    io::stdin()
        .read_line(&mut input)
        .expect("Failed to read line");

    // Remove the newline character at the end of the input
    input.trim().to_string()
}

pub fn start_game(mut hero_party: Vec<Hero>) {

    println!("And so the adventure begins!");

    let locations = locations::load_locations();
    let mut hero_holder = Vec::new();

    for hero in hero_party.iter_mut() {
        hero_holder.push(Box::new(hero));
    }

    for location in locations.iter() {
        let possible_enemies = (location.load_entities)();
        println!("You are at the {}", location.name);

        //encounter & battle
        loop {
            let mut enemy_holder = Vec::new();

            let mut encounter_enemies = generate_encounter(possible_enemies.clone());

            for enemy in encounter_enemies.iter_mut() {
                enemy_holder.push(Box::new(enemy));
            }

            println!("You have encountered {} enemies!", enemy_holder.len());

            let battle_outcome = battle(&mut hero_holder, &mut enemy_holder);

            match battle_outcome {
                game::BattleOutcome::Win => {
                    let loot = generate_loot((location.load_loot)());

                    println!("You have defeated the enemies!");
                    println!("You have found a {}", loot.name);
                    println!("Would you like to equip the weapon? (y/n)");
                    let user_input = get_user_input("");

                    match &user_input as &str {
                        "y" => {
                            println!("Which hero would you like to equip the weapon to?");


                            for (index, hero) in hero_holder.iter().enumerate() {
                                
                                println!("{}-{}", index, hero.get_name());
                            }

                            let user_input = get_user_input("");
                            
                            match user_input.trim().parse::<usize>() {
                                Ok(index) => {
                                    if index >= hero_holder.len() {
                                        println!("Invalid input, weapon not equipped");
                                    } else {
                                    hero_holder[index].update_weapon(loot);}
                                }
                                Err(_) => {
                                    println!("Invalid input, weapon not equipped");
                                }
                            }
                        }
                        "n" => {
                            println!("{}", user_input);
                            println!("You have chosen not to equip the weapon");
                        }
                        _ => {
                            println!("Invalid input, weapon not equipped");
                        }
                    }

                    println!("Would you like to continue to the next location? (y/n)");
                    let user_input = get_user_input("");

                    match &user_input as &str {
                        "y" => {
                            //Break out of the encounter loop
                            break;
                        }
                        "n" => {
                            //Do nothing, let loop run again
                            continue;
                        }
                        _ => {
                            end_game(String::from("Invalid input, ending your adventure"));
                        }
                    }
                }
                game::BattleOutcome::Loss => {
                    end_game(String::from("You have won!"));
                }
            }
        }
    }

    println!("Congratulations! You have completed the adventure!");
}

fn end_game(reason: String) {
    println!("Your adventure has ended because:");
    panic!("{}", reason);
}
#[cfg(test)]
mod tests {
    use crate::weapons;

    #[test]
    fn overall_test() {
        let mut hero = super::Hero::default();
        hero.update_weapon(weapons::Weapon::new("Wendigo Claw", 30, |target| {
            println!("Wendigo Bleed Applied");
            target.take_damage(5)
        }));
        let hero_party = vec![hero];
        super::start_game(hero_party);

        assert!(true);
    }
}