From ff457701ff56072914acb8a7160cd02c2a07095a Mon Sep 17 00:00:00 2001 From: troido Date: Sun, 5 Apr 2020 23:22:36 +0200 Subject: trading now works --- content/encyclopediae/default_encyclopedia.json | 14 ++++- content/maps/room.json | 3 +- src/components/interactable.rs | 29 +++++++++- src/components/inventory.rs | 9 ++++ src/exchange.rs | 45 ++++++++++++++++ src/item.rs | 1 - src/main.rs | 1 + src/playerstate.rs | 2 +- src/systems/interact.rs | 70 +++++++++++++++++-------- src/systems/take.rs | 7 +-- 10 files changed, 147 insertions(+), 34 deletions(-) create mode 100644 src/exchange.rs diff --git a/content/encyclopediae/default_encyclopedia.json b/content/encyclopediae/default_encyclopedia.json index 1c48a6b..b7364d5 100644 --- a/content/encyclopediae/default_encyclopedia.json +++ b/content/encyclopediae/default_encyclopedia.json @@ -227,13 +227,23 @@ "components": [ ["Interactable", {"action": ["interaction", ["reply", "did you say '{}'?"]]}] ] + }, + "trader": { + "sprite": "human", + "height": 1.5, + "components": [ + ["Interactable", {"action": ["interaction", ["exchange", ["buy ", { + "pebble": [["radish", "radish"], ["pebble"]], + "radishseed": [["radish"], ["radishseed", "radishseed"]] + }]]]}] + ] } }, "items": { "pebble": {}, "stone": {"action": ["build", ["builtwall", ["Floor"], ["Blocking"]]]}, - "radishseed": {"action": ["build", ["plantedradishseed", ["Floor", "Soil"], ["Occupied", "Blocking"]]]}, - "radish": {"action": ["eat", 3]}, + "radishseed": {"sprite": "seed", "action": ["build", ["plantedradishseed", ["Floor", "Soil"], ["Occupied", "Blocking"]]]}, + "radish": {"sprite": "food", "action": ["eat", 3]}, "sword": {"action": ["equip", { "slot": "hand", "stats": {"strength": 50} diff --git a/content/maps/room.json b/content/maps/room.json index ca50eff..5d69b72 100644 --- a/content/maps/room.json +++ b/content/maps/room.json @@ -15,7 +15,7 @@ "X,,,,,.,,,,,,,,,,,,~~~,,,,,,,,,,,,,,,,,,,X", "X,^,,,.,,,,,,,,,,,,~~~,,,,,T,,,,######,,,X", "X,^,,,.,,,,,,,,,,,,bbb,,,,,,,,,,#++++#,,,X", - "X,,,,,.............bbb..........D++++#,,,X", + "X,,,t..............bbb..........D++++#,,,X", "X,**,,.,,,,,,,,,,,,bbb,,,,,,,,,,#++++#,,,X", "X,*,*,.,u,,,V,,V,,,~~~,,,T,,,T,,#++++#,,,X", "X,,*,,.,,,,,,,,,,,,~~~,,,,,,,,,,######,,,X", @@ -50,6 +50,7 @@ "D": ["ground", "closeddoor"], "s": ["ground", "sign"], "u": ["ground", "dude"], + "t": ["ground", "trader"], " ": [] } } diff --git a/src/components/interactable.rs b/src/components/interactable.rs index a59cc90..33e3a12 100644 --- a/src/components/interactable.rs +++ b/src/components/interactable.rs @@ -1,11 +1,14 @@ +use std::collections::HashMap; use serde_json::{Value}; use specs::{ Component, HashMapStorage }; use crate::{ - Template + Template, + exchange::Exchange, + ItemId }; #[derive(Component, Debug, Clone, PartialEq)] @@ -14,7 +17,8 @@ pub enum Interactable { Harvest, Change(Template), Say(String), - Reply(String) + Reply(String), + Exchange(String, HashMap) } use Interactable::*; @@ -28,6 +32,20 @@ impl Interactable { "change" => Change(Template::from_json(arg).ok()?), "say" => Say(arg.as_str()?.to_string()), "reply" => Reply(arg.as_str()?.to_string()), + "exchange" => Exchange( + arg.get(0)?.as_str()?.to_string(), + arg.get(1)? + .as_object()? + .iter() + .map(|(id, ex)| { + let exchange = Exchange { + cost: ex.get(0)?.as_array()?.iter().map(|i| Some(ItemId(i.as_str()?.to_string()))).collect::>>()?, + offer: ex.get(1)?.as_array()?.iter().map(|i| Some(ItemId(i.as_str()?.to_string()))).collect::>>()? + }; + Some((id.clone(), exchange)) + }) + .collect::>>()? + ), _ => None? }) } @@ -38,6 +56,13 @@ impl Interactable { Change(_) => arg.is_none(), Say(_) => arg.is_none(), Reply(_) => arg.is_some(), + Exchange(prefix, _exchanges) => { + if let Some(txt) = arg { + txt.starts_with(prefix) + } else { + true + } + } } } } diff --git a/src/components/inventory.rs b/src/components/inventory.rs index c3282e9..fa65b03 100644 --- a/src/components/inventory.rs +++ b/src/components/inventory.rs @@ -5,6 +5,7 @@ use crate::{ ItemId, item::{Item, ItemAction}, components::equipment::{Stat, Equippable}, + Encyclopedia }; #[derive(Debug, Clone)] @@ -25,6 +26,14 @@ impl Component for Inventory { impl Inventory { + pub fn add_item(&mut self, itemid: ItemId, enc: &Encyclopedia) { + self.items.insert(0, InventoryEntry{ + itemid: itemid.clone(), + item: enc.get_item(&itemid).unwrap(), + is_equipped: false + }); + } + fn equipped(&self) -> Vec { let mut equippables = Vec::new(); for entry in self.items.iter() { diff --git a/src/exchange.rs b/src/exchange.rs new file mode 100644 index 0000000..5e87ef2 --- /dev/null +++ b/src/exchange.rs @@ -0,0 +1,45 @@ + +use crate::{ + components::Inventory, + ItemId, + Encyclopedia +}; + +#[derive(Debug, Clone, PartialEq)] +pub struct Exchange { + pub cost: Vec, + pub offer: Vec +} + +impl Exchange { + pub fn show(&self) -> String { + format!( + "offer: [{}], price: [{}]", + self.offer.iter().map(|i| i.0.clone()).collect::>().join(", "), + self.cost.iter().map(|i| i.0.clone()).collect::>().join(", ") + ) + } + + pub fn can_trade(&self, inventory: &Inventory) -> bool { + if self.offer.len() as isize - self.cost.len() as isize > inventory.capacity as isize - inventory.items.len() as isize{ + return false; + } + let mut costs = self.cost.clone(); + for entry in inventory.items.iter() { + if let Some(pos) = costs.iter().position(|x| *x == entry.itemid){ + costs.remove(pos); + } + } + costs.is_empty() + } + + pub fn trade(&self, inventory: &mut Inventory, enc: &Encyclopedia) { + for item in self.cost.iter() { + let pos = inventory.items.iter().position(|entry| entry.itemid == item.clone()).unwrap(); + inventory.items.remove(pos); + } + for item in self.offer.iter() { + inventory.add_item(item.clone(), enc); + } + } +} diff --git a/src/item.rs b/src/item.rs index d5ffaa1..420f341 100644 --- a/src/item.rs +++ b/src/item.rs @@ -2,7 +2,6 @@ use std::collections::HashSet; use serde_json::{Value}; -use specs::{Component, DenseVecStorage}; use crate::{ Template, components::{ diff --git a/src/main.rs b/src/main.rs index 34819e3..21d3ccc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,6 +36,7 @@ mod timestamp; mod purgatory; mod config; mod item; +mod exchange; use self::{ pos::Pos, diff --git a/src/playerstate.rs b/src/playerstate.rs index 852c04f..9dec879 100644 --- a/src/playerstate.rs +++ b/src/playerstate.rs @@ -149,7 +149,7 @@ impl PlayerState { pub fn construct(&self, encyclopedia: &Encyclopedia) -> PreEntity { vec![ - ComponentWrapper::Visible(Visible{sprite: Sprite{name: "player".to_string()}, height: 1.2, name: self.id.name.clone()}), + ComponentWrapper::Visible(Visible{sprite: Sprite{name: "player".to_string()}, height: 1.75, name: self.id.name.clone()}), ComponentWrapper::Player(Player::new(self.id.clone())), ComponentWrapper::Inventory(Inventory{ items: self.inventory.iter().map( |(itemid, is_equipped)| { diff --git a/src/systems/interact.rs b/src/systems/interact.rs index 4bf2e4c..43c355d 100644 --- a/src/systems/interact.rs +++ b/src/systems/interact.rs @@ -10,22 +10,22 @@ use specs::{ Write }; -use crate::components::{ - Controller, - Position, - ControlCooldown, - Interactable, - Dead, - Removed, - Sound, - Ear +use crate::{ + components::{ + Controller, + Position, + ControlCooldown, + Interactable, + Dead, + Removed, + Sound, + Ear, + Inventory + }, + controls::{Control}, + resources::{Ground, NewEntities} }; -use crate::controls::{Control}; -use crate::resources::{Ground, NewEntities}; - - - pub struct Interact; impl <'a> System<'a> for Interact { type SystemData = ( @@ -38,12 +38,14 @@ impl <'a> System<'a> for Interact { WriteStorage<'a, Dead>, WriteStorage<'a, Removed>, Write<'a, NewEntities>, - WriteStorage<'a, Ear> + WriteStorage<'a, Ear>, + WriteStorage<'a, Inventory> ); - fn run(&mut self, (entities, controllers, positions, ground, mut cooldowns, interactables, mut deads, mut removeds, mut new, mut ears): Self::SystemData) { + fn run(&mut self, (entities, controllers, positions, ground, mut cooldowns, interactables, mut deads, mut removeds, mut new, mut ears, mut inventories): Self::SystemData) { for (entity, controller, position) in (&entities, &controllers, &positions).join(){ let mut target = None; + let ear = ears.get_mut(entity); match &controller.control { Control::Interact(directions, arg) => { 'targets: for direction in directions { @@ -70,13 +72,33 @@ impl <'a> System<'a> for Interact { removeds.insert(ent, Removed).unwrap(); } Interactable::Say(text) => { - if let Some(ear) = ears.get_mut(entity) { - ear.sounds.push(Sound{source: None, text: text.clone()}); - } + say(ear, text.clone()); } Interactable::Reply(text) => { - if let Some(ear) = ears.get_mut(entity) { - ear.sounds.push(Sound{source: None, text: text.replace("{}", &arg.unwrap())}); + say(ear, text.replace("{}", &arg.unwrap())); + } + Interactable::Exchange(prefix, exchanges) => { + if let Some(txt) = arg { + if let Some(inventory) = inventories.get_mut(entity) { + if txt.starts_with(prefix){ + let action = txt.split_at(prefix.len()).1; + if let Some(exchange) = exchanges.get(action) { + if exchange.can_trade(inventory){ + exchange.trade(inventory, &new.encyclopedia); + say(ear, format!("Success! '{}' ({})", txt, exchange.show())); + } else { + say(ear, format!("You do not have the required items or inventory space for '{}' ({})", txt, exchange.show())); + } + } else { + say(ear, format!("Invalid option: {}", action)); + } + } + } + } else { + say(ear, format!("options: {:?}", exchanges.iter().map(|(id, exchange)| + format!("{}{}: {}", prefix, id, exchange.show()) + ).collect::>()) + ); } } } @@ -85,3 +107,9 @@ impl <'a> System<'a> for Interact { } } } + +fn say(maybe_ear: Option<&mut Ear>, text: String){ + if let Some(ear) = maybe_ear { + ear.sounds.push(Sound{source: None, text}); + } +} diff --git a/src/systems/take.rs b/src/systems/take.rs index 5a86269..7781445 100644 --- a/src/systems/take.rs +++ b/src/systems/take.rs @@ -14,7 +14,6 @@ use crate::components::{ Position, Removed, Inventory, - inventory::InventoryEntry, Item, Visible }; @@ -51,11 +50,7 @@ impl <'a> System<'a> for Take { } for ent in ents { if let Some(item) = items.get(ent) { - inventory.items.insert(0, InventoryEntry{ - itemid: item.0.clone(), - item: new.encyclopedia.get_item(&item.0).unwrap(), - is_equipped: false - }); + inventory.add_item(item.0.clone(), &new.encyclopedia); if let Err(msg) = removed.insert(ent, Removed) { println!("{:?}", msg); } -- cgit