about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/display/color.rs9
-rw-r--r--src/entities/character.rs3
-rw-r--r--src/entities/entity_char.rs2
-rw-r--r--src/entities/item.rs2
-rw-r--r--src/entities/raw_types.rs4
-rw-r--r--src/game.rs42
-rw-r--r--src/messages.toml1
-rw-r--r--src/types/command.rs6
8 files changed, 63 insertions, 6 deletions
diff --git a/src/display/color.rs b/src/display/color.rs
index b1e799c5ed9f..afe0039998b8 100644
--- a/src/display/color.rs
+++ b/src/display/color.rs
@@ -15,6 +15,15 @@ impl Color {
     }
 }
 
+impl PartialEq for Color {
+    fn eq(&self, other: &Self) -> bool {
+        format!("{}{}", color::Fg(self), color::Bg(self))
+            == format!("{}{}", color::Fg(other), color::Bg(other))
+    }
+}
+
+impl Eq for Color {}
+
 impl color::Color for Color {
     fn write_fg(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.0.write_fg(f)
diff --git a/src/entities/character.rs b/src/entities/character.rs
index 360478e8be1c..3e8336b129ff 100644
--- a/src/entities/character.rs
+++ b/src/entities/character.rs
@@ -1,4 +1,5 @@
 use crate::display;
+use crate::entities::item::Item;
 use crate::types::{Position, Speed};
 use std::io::{self, Write};
 
@@ -7,6 +8,7 @@ const DEFAULT_SPEED: Speed = Speed(100);
 entity! {
     pub struct Character {
         pub o_name: Option<String>,
+        pub inventory: Vec<Box<Item>>,
     }
 }
 
@@ -18,6 +20,7 @@ impl Character {
             id: None,
             position: Position { x: 0, y: 0 },
             o_name: None,
+            inventory: Vec::new(),
         }
     }
 
diff --git a/src/entities/entity_char.rs b/src/entities/entity_char.rs
index 88ca8a55a823..70f26bfffdbd 100644
--- a/src/entities/entity_char.rs
+++ b/src/entities/entity_char.rs
@@ -2,7 +2,7 @@ use crate::display::color::Color;
 use std::fmt::{self, Display, Formatter};
 use termion::color;
 
-#[derive(Debug, Deserialize)]
+#[derive(Debug, Deserialize, PartialEq, Eq)]
 pub struct EntityChar {
     #[serde(default)]
     color: Color,
diff --git a/src/entities/item.rs b/src/entities/item.rs
index aa99fb42e2c1..5f08780d4fb2 100644
--- a/src/entities/item.rs
+++ b/src/entities/item.rs
@@ -4,7 +4,7 @@ use crate::entities::{Describe, EntityID};
 use crate::types::Position;
 use std::io::{self, Write};
 
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct Item {
     pub id: Option<EntityID>,
     pub typ: &'static ItemType<'static>,
diff --git a/src/entities/raw_types.rs b/src/entities/raw_types.rs
index 59dd19ed2fa2..4bc291b69580 100644
--- a/src/entities/raw_types.rs
+++ b/src/entities/raw_types.rs
@@ -17,7 +17,7 @@ pub struct CreatureType<'a> {
     pub friendly: bool,
 }
 
-#[derive(Debug, Deserialize)]
+#[derive(Debug, Deserialize, PartialEq, Eq)]
 pub struct EdibleItem<'a> {
     #[serde(borrow)]
     pub eat_message: Option<Message<'a>>,
@@ -26,7 +26,7 @@ pub struct EdibleItem<'a> {
     pub hitpoints_healed: u16,
 }
 
-#[derive(Debug, Deserialize)]
+#[derive(Debug, Deserialize, PartialEq, Eq)]
 pub struct ItemType<'a> {
     pub name: &'a str,
 
diff --git a/src/game.rs b/src/game.rs
index 2c69061516fd..c478e0d2f55b 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -1,5 +1,7 @@
 use crate::description::list_to_sentence;
 use crate::display::{self, Viewport};
+use crate::entities::entity::Describe;
+use crate::entities::entity::Entity;
 use crate::entities::{
     AnEntity, Character, Creature, EntityID, Identified, Item,
 };
@@ -187,8 +189,7 @@ impl<'a> Game<'a> {
         }
     }
 
-    /// Returns a list of all creature entities at the given position
-    fn creatures_at<'b>(&'b self, pos: Position) -> Vec<&'b Creature> {
+    fn downcast_entities_at<A: Entity>(&self, pos: Position) -> Vec<&A> {
         self.entities
             .at(pos)
             .iter()
@@ -196,6 +197,16 @@ impl<'a> Game<'a> {
             .collect()
     }
 
+    /// Returns a list of all creature entities at the given position
+    fn creatures_at(&self, pos: Position) -> Vec<&Creature> {
+        self.downcast_entities_at(pos)
+    }
+
+    /// Returns a list of all item entities at the given position
+    fn items_at(&self, pos: Position) -> Vec<&Item> {
+        self.downcast_entities_at(pos)
+    }
+
     /// Returns a collision, if any, at the given Position in the game
     fn collision_at(&self, pos: Position) -> Option<Collision> {
         if !pos.within(self.viewport.inner) {
@@ -436,6 +447,31 @@ impl<'a> Game<'a> {
         }
     }
 
+    fn pick_up(&mut self) -> io::Result<()> {
+        let pos = self.character().position;
+        let items = self.items_at(pos);
+        match items.len() {
+            0 => Ok(()),
+            1 => {
+                let item_id = items.get(0).unwrap().id();
+                let item: Box<Item> =
+                    self.entities.remove(item_id).unwrap().downcast().unwrap();
+                let desc = item.description();
+                self.mut_character().inventory.push(item);
+                self.say(
+                    "global.pick_up",
+                    &template_params!({
+                        "item" => { "name" => &desc, },
+                    }),
+                )
+            }
+            _ => {
+                // TODO prompt with a menu of items to pick up
+                unimplemented!()
+            }
+        }
+    }
+
     fn flush_promises(&mut self) {
         unsafe {
             let game = self as *mut Self;
@@ -498,6 +534,8 @@ impl<'a> Game<'a> {
 
                         Some(PreviousMessage) => self.previous_message()?,
 
+                        Some(PickUp) => self.pick_up()?,
+
                         None => (),
                     }
 
diff --git a/src/messages.toml b/src/messages.toml
index 7c6255142dd2..a9a6b2e009a6 100644
--- a/src/messages.toml
+++ b/src/messages.toml
@@ -2,6 +2,7 @@
 welcome = "Welcome to Xanthous, {{character.name}}! It's dangerous out there, why not stay inside?"
 describe_entities = "You see here {{descriptions}}"
 describe_no_entities = "You see nothing here."
+pick_up = "You pick up the {{item.name}}."
 
 [combat]
 attack = "You attack the {{creature.name}}."
diff --git a/src/types/command.rs b/src/types/command.rs
index 15017cde9904..17ca4d280fd8 100644
--- a/src/types/command.rs
+++ b/src/types/command.rs
@@ -10,6 +10,9 @@ pub enum Command {
     /// Move the character in a direction
     Move(Direction),
 
+    /// Pick up any item(s) at the current position
+    PickUp,
+
     /// Display the previous message
     PreviousMessage,
 }
@@ -19,6 +22,7 @@ impl Command {
         use Command::*;
         match k {
             Char('q') => Some(Quit),
+
             Char('h') | Char('a') | Key::Left => Some(Move(Left)),
             Char('k') | Char('w') | Key::Up => Some(Move(Up)),
             Char('j') | Char('s') | Key::Down => Some(Move(Down)),
@@ -29,6 +33,8 @@ impl Command {
             Char('n') => Some(Move(DownRight)),
 
             Ctrl('p') => Some(PreviousMessage),
+            Char(',') => Some(PickUp),
+
             _ => None,
         }
     }