Compare commits

..

No commits in common. "6f4c37728f0f26c89519903ae8e12f48a4c15ae2" and "a0635de65a991316caf547f6f566bd3158261a83" have entirely different histories.

6 changed files with 34 additions and 111 deletions

View File

@ -6,7 +6,6 @@ use crate::position::Position;
pub trait Artifact { pub trait Artifact {
//! An artifact that can be collected by the player //! An artifact that can be collected by the player
/// get the character and color used to draw the artifact into the level
fn get_representation(&self) -> (&str, Color); fn get_representation(&self) -> (&str, Color);
/// get the position of the artifact in the level /// get the position of the artifact in the level
fn get_immutable_position(&self) -> &Position; fn get_immutable_position(&self) -> &Position;
@ -16,17 +15,13 @@ pub trait Artifact {
fn was_collected(&self) -> bool; fn was_collected(&self) -> bool;
} }
/// An artifact that contains a random amount of gold pieces.
pub struct Chest { pub struct Chest {
/// the chests position /// a chest that contains some gold
position: Position, position: Position,
/// the chests value
gold: usize, gold: usize,
} }
impl Chest { impl Chest {
/// create a chest at the given position with a random amount of gold.
/// The gold amount depends on the level, the deeper you go, the more you get.
pub fn new(position: Position) -> Self { pub fn new(position: Position) -> Self {
let min_gold = 10 * (position.get_level() + 1); let min_gold = 10 * (position.get_level() + 1);
let max_gold = min_gold + 10 * position.get_level(); let max_gold = min_gold + 10 * position.get_level();
@ -59,13 +54,10 @@ impl Artifact for Chest {
} }
} }
#[derive(Clone, Copy)]
/// An artifact that gives the player some health on consumption.
pub struct Potion { pub struct Potion {
/// a potion that restores some health /// a potion that restores some health
position: Position, position: Position,
health: usize, health: usize,
was_collected: bool,
} }
impl Potion { impl Potion {
@ -75,29 +67,19 @@ impl Potion {
Self { Self {
position, position,
health: rand::thread_rng().gen_range(min_health_gain..=max_health_gain), health: rand::thread_rng().gen_range(min_health_gain..=max_health_gain),
was_collected: false,
} }
} }
pub fn get_health(&self) -> usize {
self.health
}
} }
impl Artifact for Potion { impl Artifact for Potion {
fn get_representation(&self) -> (&str, Color) { fn get_representation(&self) -> (&str, Color) {
("P", Color::Green) ("P", Color::Green)
} }
fn get_immutable_position(&self) -> &Position { fn get_immutable_position(&self) -> &Position {
&self.position &self.position
} }
fn collect(&mut self, player: &mut Player, messages: &mut Vec<String>) { fn collect(&mut self, player: &mut Player, messages: &mut Vec<String>) {
//! called when the player walked on to a potion. // only consume potion of the player can gain at least one health point
//!
//! Depending on health status and inventory usage the potion will
//! be consumed directly or moved to inventory.
if !player.is_healthy() { if !player.is_healthy() {
let old = player.get_life(); let old = player.get_life();
player.change_life(self.health.try_into().unwrap()); player.change_life(self.health.try_into().unwrap());
@ -107,16 +89,15 @@ impl Artifact for Potion {
format!("picked up potion and gained {} health.", new - old).to_string(), format!("picked up potion and gained {} health.", new - old).to_string(),
); );
self.health = 0; self.health = 0;
self.was_collected = true;
} else if player.add_to_inventory(self) {
messages.insert(0, "move potion to inventory.".to_string());
self.was_collected = true;
} else { } else {
messages.insert(0, "inventory is full.".to_string()); messages.insert(
0,
"not using the potion because you're healthy.".to_string(),
);
} }
} }
fn was_collected(&self) -> bool { fn was_collected(&self) -> bool {
self.was_collected self.health == 0
} }
} }

View File

@ -22,7 +22,6 @@ pub struct Game {
player: Player, player: Player,
/// the levels of the game /// the levels of the game
levels: Vec<Level>, levels: Vec<Level>,
/// messages that are displayed in the ui
pub messages: Vec<String>, pub messages: Vec<String>,
} }
@ -131,7 +130,7 @@ impl Game {
let (next_level, x, y) = self.next_start(); let (next_level, x, y) = self.next_start();
player_level = next_level; player_level = next_level;
self.messages self.messages
.insert(0, format!("you climb down to level {}.", next_level + 1)); .insert(0, format!("you climb down to level {}.", next_level));
self.get_mutable_player() self.get_mutable_player()
.get_position() .get_position()
.set(next_level, x, y); .set(next_level, x, y);
@ -141,7 +140,7 @@ impl Game {
let (next_level, x, y) = self.prev_end(); let (next_level, x, y) = self.prev_end();
player_level = next_level; player_level = next_level;
self.messages self.messages
.insert(0, format!("you climb up to level {}.", next_level + 1)); .insert(0, format!("you climb up to level {}.", next_level));
self.get_mutable_player() self.get_mutable_player()
.get_position() .get_position()
.set(next_level, x, y); .set(next_level, x, y);

View File

@ -6,10 +6,6 @@ use rand::Rng;
use crate::artifacts::Artifact; use crate::artifacts::Artifact;
#[cfg(test)] #[cfg(test)]
use crate::artifacts::{Chest, Potion}; use crate::artifacts::{Chest, Potion};
use crate::level_generator::ROOMS_HORIZONTAL;
use crate::level_generator::ROOMS_VERTICAL;
use crate::level_generator::ROOM_HEIGHT;
use crate::level_generator::ROOM_WIDTH;
use crate::monster::Monster; use crate::monster::Monster;
#[cfg(test)] #[cfg(test)]
use crate::monster::{Orc, Rat}; use crate::monster::{Orc, Rat};
@ -17,8 +13,8 @@ use crate::player::Player;
use crate::position::HasPosition; use crate::position::HasPosition;
use crate::position::Position; use crate::position::Position;
pub const LEVEL_WIDTH: usize = 1 + ROOMS_VERTICAL * ROOM_WIDTH; pub const LEVEL_WIDTH: usize = 50;
pub const LEVEL_HEIGHT: usize = 1 + ROOMS_HORIZONTAL * ROOM_HEIGHT; pub const LEVEL_HEIGHT: usize = 25;
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum StructureElement { pub enum StructureElement {

View File

@ -14,11 +14,11 @@ use crate::level::{Level, StructureElement};
use crate::monster::{Monster, Orc, Rat, Snake}; use crate::monster::{Monster, Orc, Rat, Snake};
use crate::position::Position; use crate::position::Position;
pub const ROOMS_VERTICAL: usize = 9; const ROOMS_VERTICAL: usize = 7;
pub const ROOMS_HORIZONTAL: usize = 7; const ROOMS_HORIZONTAL: usize = 4;
pub const ROOM_WIDTH: usize = 7; const ROOM_WIDTH: usize = 7;
pub const ROOM_HEIGHT: usize = 6; const ROOM_HEIGHT: usize = 6;
#[derive(PartialEq, Copy, Clone)] #[derive(PartialEq, Copy, Clone)]
enum RoomType { enum RoomType {

View File

@ -33,9 +33,6 @@ mod position;
/// length of a game frame in ms /// length of a game frame in ms
pub const FRAME_LENGTH: u64 = 100; pub const FRAME_LENGTH: u64 = 100;
pub const MIN_WIDTH: u16 = 120;
pub const MIN_HEIGHT: u16 = 30;
// //
fn main() -> Result<()> { fn main() -> Result<()> {
let mut game = Game::new(Player::new(realname().as_str(), 30)); let mut game = Game::new(Player::new(realname().as_str(), 30));
@ -57,30 +54,27 @@ fn main() -> Result<()> {
// don't draw stuff except an info box if the terminal is too small (less than 80x25) // don't draw stuff except an info box if the terminal is too small (less than 80x25)
// to prevent the read drawing code from crashing the game. // to prevent the read drawing code from crashing the game.
if area.width < MIN_WIDTH || area.height < MIN_HEIGHT { if area.width < 80 || area.height < 25 {
let block = Block::default() let block = Block::default()
.title("Info") .title("Info")
.borders(Borders::ALL) .borders(Borders::ALL)
.border_style(Style::default().fg(Color::White)) .border_style(Style::default().fg(Color::White))
.border_type(BorderType::Rounded) .border_type(BorderType::Rounded)
.style(Style::default().bg(Color::Black)); .style(Style::default().bg(Color::Black));
let paragraph = Paragraph::new(format!( let paragraph = Paragraph::new("Terminal needs to be at leas 80x25!")
"Terminal needs to be at leas {}x{}!",
MIN_WIDTH, MIN_HEIGHT
))
.block(block) .block(block)
.wrap(Wrap { trim: true }); .wrap(Wrap { trim: true });
frame.render_widget(paragraph, area); frame.render_widget(paragraph, area);
return; return;
} }
if area.width > MIN_WIDTH { if area.width > 80 {
area.x = (area.width - MIN_WIDTH) / 2; area.x = (area.width - 80) / 2;
area.width = MIN_WIDTH; area.width = 80;
} }
if area.height > MIN_HEIGHT { if area.height > 25 {
area.y = (area.height - level::LEVEL_HEIGHT as u16) / 2; area.y = (area.height - 25) / 2;
area.height = level::LEVEL_HEIGHT as u16; area.height = 25;
} }
let map_area = Rect { let map_area = Rect {
@ -92,10 +86,10 @@ fn main() -> Result<()> {
frame.render_stateful_widget(LevelWidget {}, map_area, &mut game); frame.render_stateful_widget(LevelWidget {}, map_area, &mut game);
let stats_area = Rect { let stats_area = Rect {
x: area.x + map_area.width, x: area.x + 50,
y: area.y, y: area.y,
width: MIN_WIDTH - map_area.width, width: 30,
height: map_area.height / 2 + 1, height: 15,
}; };
let block = Block::default() let block = Block::default()
.title( .title(
@ -109,24 +103,22 @@ fn main() -> Result<()> {
.style(Style::default().bg(Color::Blue)); .style(Style::default().bg(Color::Blue));
frame.render_widget( frame.render_widget(
Paragraph::new(format!( Paragraph::new(format!(
"Health: {}/{}\nExp: {}\nGold: {}\nLevel: {}\nInventory: {}/{}", "Health: {}/{}\nExp: {}\nGold: {}\nLevel: {}",
game.get_player().get_life(), game.get_player().get_life(),
game.get_player().get_max_life(), game.get_player().get_max_life(),
game.get_player().get_experience(), game.get_player().get_experience(),
game.get_player().get_gold(), game.get_player().get_gold(),
game.get_player().get_immutable_position().get_level() + 1, game.get_player().get_immutable_position().get_level() + 1
game.get_player().inventory_size().0,
game.get_player().inventory_size().1,
)) ))
.block(block) .block(block)
.wrap(Wrap { trim: true }), .wrap(Wrap { trim: true }),
stats_area, stats_area,
); );
let messages_area = Rect { let messages_area = Rect {
x: area.x + map_area.width, x: area.x + 50,
y: area.y + map_area.height / 2 + 1, y: area.y + 15,
width: MIN_WIDTH - map_area.width, width: 30,
height: map_area.height / 2, height: 10,
}; };
// Display the latest messages from the game to the user // Display the latest messages from the game to the user
let block = Block::default() let block = Block::default()
@ -163,19 +155,6 @@ fn main() -> Result<()> {
.to_string(), .to_string(),
); );
} }
KeyCode::Char('p') => {
let gained_health = game.get_mutable_player().consume_inventory();
if gained_health > 0 {
game.messages.insert(
0,
format!(
"used a potion from inventory and gained {} health.",
gained_health
)
.to_string(),
);
}
}
KeyCode::Char('q') => { KeyCode::Char('q') => {
break; break;
} }

View File

@ -1,7 +1,6 @@
use rand::Rng; use rand::Rng;
use std::cmp::{max, min}; use std::cmp::{max, min};
use crate::artifacts::Potion;
use crate::position::{HasPosition, Position}; use crate::position::{HasPosition, Position};
pub struct Player { pub struct Player {
@ -11,8 +10,6 @@ pub struct Player {
max_life: i16, max_life: i16,
gold: usize, gold: usize,
experience: usize, experience: usize,
inventory: Vec<Potion>,
inventory_slots: usize,
} }
impl Player { impl Player {
@ -24,8 +21,6 @@ impl Player {
max_life, max_life,
gold: 0, gold: 0,
experience: 0, experience: 0,
inventory: vec![],
inventory_slots: 2,
} }
} }
pub fn get_name(&self) -> String { pub fn get_name(&self) -> String {
@ -70,30 +65,6 @@ impl Player {
pub fn damage(&self) -> usize { pub fn damage(&self) -> usize {
rand::thread_rng().gen_range(1..4) rand::thread_rng().gen_range(1..4)
} }
pub fn add_to_inventory(&mut self, potion: &Potion) -> bool {
if self.inventory.len() < self.inventory_slots {
self.inventory.push(*potion);
true
} else {
false
}
}
pub fn inventory_size(&self) -> (usize, usize) {
(self.inventory.len(), self.inventory_slots)
}
pub fn consume_inventory(&mut self) -> usize {
if self.is_healthy() {
return 0;
}
if let Some(potion) = self.inventory.pop() {
self.change_life(potion.get_health() as i16);
return potion.get_health();
}
0
}
} }
impl HasPosition for Player { impl HasPosition for Player {
@ -114,7 +85,6 @@ fn test_get_name() {
max_life: 10, max_life: 10,
gold: 0, gold: 0,
experience: 0, experience: 0,
inventory: vec![],
}; };
assert_eq!(p.get_name(), "Teddy Tester"); assert_eq!(p.get_name(), "Teddy Tester");
} }
@ -138,7 +108,6 @@ fn test_change_life() {
max_life: 10, max_life: 10,
gold: 0, gold: 0,
experience: 0, experience: 0,
inventory: vec![],
}; };
assert_eq!(p.get_life(), 5); assert_eq!(p.get_life(), 5);
p.change_life(-2); p.change_life(-2);
@ -172,7 +141,6 @@ fn test_max_life() {
max_life: 10, max_life: 10,
gold: 0, gold: 0,
experience: 0, experience: 0,
inventory: vec![],
}; };
assert_eq!(p.get_max_life(), 10); assert_eq!(p.get_max_life(), 10);
} }