feat: store items as RON files

When a new item is created, a ron file will be used as a template
This commit is contained in:
timokoesters 2019-10-22 20:18:40 +02:00
parent edd56e2fc0
commit 20248a4818
No known key found for this signature in database
GPG Key ID: CD80BE9AAEE78097
41 changed files with 364 additions and 415 deletions

1
Cargo.lock generated
View File

@ -3609,6 +3609,7 @@ dependencies = [
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rayon 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ron 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -0,0 +1,11 @@
Item(
name: "An apple",
description: "Looks refreshing",
kind: Consumable(
kind: Apple,
effect: Health((
amount: 10,
cause: Item,
)),
),
)

View File

@ -0,0 +1,5 @@
Item(
name: "Boost rod",
description: "Your legs feel full of energy while holding this",
kind: Debug(Boost),
)

View File

@ -0,0 +1,5 @@
Item(
name: "Possession rod",
description: "Your body seems loose while holding this",
kind: Debug(Possess),
)

View File

@ -0,0 +1,5 @@
Item(
name: "A blue flower",
description: "Look beautiful",
kind: Ingredient(Flower),
)

View File

@ -0,0 +1,5 @@
Item(
name: "A pink flower",
description: "Look beautiful",
kind: Ingredient(Flower),
)

View File

@ -0,0 +1,5 @@
Item(
name: "A red flower",
description: "Look beautiful",
kind: Ingredient(Flower),
)

View File

@ -0,0 +1,5 @@
Item(
name: "A sunflower",
description: "Look beautiful",
kind: Ingredient(Flower),
)

View File

@ -0,0 +1,5 @@
Item(
name: "A white flower",
description: "Look beautiful",
kind: Ingredient(Flower),
)

View File

@ -0,0 +1,5 @@
Item(
name: "A yellow flower",
description: "Look beautiful",
kind: Ingredient(Flower),
)

View File

@ -0,0 +1,5 @@
Item(
name: "Long grass",
description: "Just some grass",
kind: Ingredient(Grass),
)

View File

@ -0,0 +1,5 @@
Item(
name: "Medium grass",
description: "Just some grass",
kind: Ingredient(Grass),
)

View File

@ -0,0 +1,5 @@
Item(
name: "Short grass",
description: "Just some grass",
kind: Ingredient(Grass),
)

View File

@ -0,0 +1,11 @@
Item(
name: "A mushroom",
description: "It's dirty, but looks edible",
kind: Consumable(
kind: Mushroom,
effect: Health((
amount: 10,
cause: Item,
)),
),
)

View File

@ -0,0 +1,8 @@
Item(
name: "Velorite",
description: "It's smooth and looks edible",
kind: Consumable(
kind: Velorite,
effect: Xp(50),
),
)

View File

@ -0,0 +1,8 @@
Item(
name: "Velorite Fragment",
description: "It's dirty, but looks edible",
kind: Consumable(
kind: VeloriteFrag,
effect: Xp(20),
),
)

View File

@ -0,0 +1,8 @@
Item(
name: "Your first axe",
description: "A dusty axe that looks surprisingly weak",
kind: Tool(
kind: Axe,
power: 10,
),
)

View File

@ -0,0 +1,8 @@
Item(
name: "Your first bow",
description: "A dusty bow that looks surprisingly weak",
kind: Tool(
kind: Bow,
power: 10,
),
)

View File

@ -0,0 +1,8 @@
Item(
name: "Your first dagger",
description: "A dusty dagger that looks surprisingly weak",
kind: Tool(
kind: Dagger,
power: 10,
),
)

View File

@ -0,0 +1,8 @@
Item(
name: "Your first hammer",
description: "A dusty hammer that looks surprisingly weak",
kind: Tool(
kind: Hammer,
power: 10,
),
)

View File

@ -0,0 +1,8 @@
Item(
name: "Your first staff",
description: "A dusty staff that looks surprisingly weak",
kind: Tool(
kind: Staff,
power: 10,
),
)

View File

@ -0,0 +1,8 @@
Item(
name: "Your first sword",
description: "A dusty sword that looks surprisingly weak",
kind: Tool(
kind: Sword,
power: 10,
),
)

View File

@ -169,12 +169,7 @@ impl Client {
}
/// Request a state transition to `ClientState::Character`.
pub fn request_character(
&mut self,
name: String,
body: comp::Body,
main: Option<comp::item::Tool>,
) {
pub fn request_character(&mut self, name: String, body: comp::Body, main: Option<String>) {
self.postbox
.send_message(ClientMsg::Character { name, body, main });
self.client_state = ClientState::Pending;

View File

@ -17,6 +17,7 @@ mio-extras = "2.0.5"
serde = "1.0.98"
serde_derive = "1.0.98"
serde_json = "1.0.40"
ron = "0.5.1"
bincode = "1.1.4"
log = "0.4.8"
rand = "0.7.0"

View File

@ -84,6 +84,12 @@ pub fn load<A: Asset + 'static>(specifier: &str) -> Result<Arc<A>, Error> {
load_map(specifier, |x| x)
}
/// Function used to load assets from the filesystem or the cache and return a clone.
pub fn load_cloned<A: Asset + Clone + 'static>(specifier: &str) -> Option<A> {
let asset: Option<Arc<A>> = load(specifier).ok();
asset.map(|asset| (*asset).clone())
}
/// Function used to load essential assets from the filesystem or the cache. It will panic if the asset is not found.
/// Example usage:
/// ```no_run
@ -96,6 +102,12 @@ pub fn load_expect<A: Asset + 'static>(specifier: &str) -> Arc<A> {
load(specifier).unwrap_or_else(|_| panic!("Failed loading essential asset: {}", specifier))
}
/// Function used to load essential assets from the filesystem or the cache and return a clone. It will panic if the asset is not found.
pub fn load_expect_cloned<A: Asset + Clone + 'static>(specifier: &str) -> A {
let asset: Arc<A> = load_expect(specifier);
(*asset).clone()
}
/// Load an asset while registering it to be watched and reloaded when it changes
pub fn load_watched<A: Asset + 'static>(
specifier: &str,

View File

@ -1,11 +1,12 @@
use crate::{
comp,
assets::{self, Asset},
effect::Effect,
terrain::{Block, BlockKind},
};
use rand::prelude::*;
use specs::{Component, FlaggedStorage};
use specs_idvs::IDVStorage;
use std::fs::File;
use std::io::BufReader;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Tool {
@ -18,52 +19,6 @@ pub enum Tool {
Staff,
}
impl Tool {
pub fn name(&self) -> &'static str {
match self {
Tool::Dagger => "Dagger",
Tool::Shield => "Shield",
Tool::Sword => "Sword",
Tool::Axe => "Axe",
Tool::Hammer => "Hammer",
Tool::Bow => "Bow",
Tool::Staff => "Staff",
}
}
pub fn description(&self) -> &'static str {
match self {
Tool::Dagger => {
"A basic kitchen knife.\n\
NOT YET AVAILABLE."
}
Tool::Shield => {
"This shield belonged to many adventurers.\n\
Now it's yours.\n\
NOT YET AVAILABLE."
}
Tool::Sword => "When closing one eye it's nearly like it wasn't rusty at all!",
Tool::Axe => {
"It has a name written on it.\n\
Sounds dwarvish."
}
Tool::Hammer => "Use with caution around nails.",
Tool::Bow => "An old but sturdy hunting bow.",
Tool::Staff => "The wood smells like magic.",
}
}
}
pub const ALL_TOOLS: [Tool; 7] = [
Tool::Dagger,
Tool::Shield,
Tool::Sword,
Tool::Axe,
Tool::Hammer,
Tool::Bow,
Tool::Staff,
];
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Armor {
// TODO: Don't make armor be a body part. Wearing enemy's head is funny but also a creepy thing to do.
@ -80,28 +35,6 @@ pub enum Armor {
Necklace,
}
impl Armor {
pub fn name(&self) -> &'static str {
match self {
Armor::Helmet => "Helmet",
Armor::Shoulders => "Shoulder Pads",
Armor::Chestplate => "Chestplate",
Armor::Belt => "Belt",
Armor::Gloves => "Gloves",
Armor::Pants => "Pants",
Armor::Boots => "Boots",
Armor::Back => "Back",
Armor::Tabard => "Tabard",
Armor::Gem => "Gem",
Armor::Necklace => "Necklace",
}
}
pub fn description(&self) -> &'static str {
self.name()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Consumable {
Apple,
@ -111,50 +44,12 @@ pub enum Consumable {
VeloriteFrag,
}
impl Consumable {
pub fn name(&self) -> &'static str {
match self {
Consumable::Apple => "Apple",
Consumable::Potion => "Potion",
Consumable::Mushroom => "Mushroom",
Consumable::Velorite => "Velorite",
Consumable::VeloriteFrag => "Glowing Fragment",
}
}
pub fn description(&self) -> &'static str {
match self {
Consumable::Apple => "A tasty Apple.",
Consumable::Potion => "This Potion contains the essence of Life.",
Consumable::Mushroom => "A common Mushroom.",
Consumable::Velorite => "Has a subtle turqoise glow.",
Consumable::VeloriteFrag => "Seems to be the fragment of a bigger piece...",
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Ingredient {
Flower,
Grass,
}
impl Ingredient {
pub fn name(&self) -> &'static str {
match self {
Ingredient::Flower => "Flower",
Ingredient::Grass => "Grass",
}
}
pub fn description(&self) -> &'static str {
match self {
Ingredient::Flower => "It smells great.",
Ingredient::Grass => "Greener than an orc's snout.",
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Debug {
Boost,
@ -162,167 +57,75 @@ pub enum Debug {
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Item {
Tool {
kind: Tool,
power: u32,
stamina: i32,
strength: i32,
dexterity: i32,
intelligence: i32,
},
Armor {
kind: Armor,
stamina: i32,
strength: i32,
dexterity: i32,
intelligence: i32,
},
Consumable {
kind: Consumable,
effect: Effect,
},
Ingredient {
kind: Ingredient,
},
pub enum ItemKind {
Tool { kind: Tool, power: u32 },
Armor { kind: Armor, power: u32 },
Consumable { kind: Consumable, effect: Effect },
Ingredient(Ingredient),
Debug(Debug),
}
impl Item {
pub fn name(&self) -> &'static str {
match self {
Item::Tool { kind, .. } => kind.name(),
Item::Armor { kind, .. } => kind.name(),
Item::Consumable { kind, .. } => kind.name(),
Item::Ingredient { kind, .. } => kind.name(),
Item::Debug(_) => "Debugging item",
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Item {
name: String,
description: String,
pub kind: ItemKind,
}
pub fn title(&self) -> String {
format!("{} ({})", self.name(), self.category())
}
pub fn info(&self) -> String {
match self {
Item::Tool { power, .. } => format!("{:+} attack", power),
Item::Armor { .. } => String::new(),
Item::Consumable { effect, .. } => format!("{}", effect.info()),
Item::Ingredient { .. } => String::new(),
Item::Debug(_) => format!("+99999 insanity"),
}
}
pub fn category(&self) -> &'static str {
match self {
Item::Tool { .. } => "Tool",
Item::Armor { .. } => "Armor",
Item::Consumable { .. } => "Consumable",
Item::Ingredient { .. } => "Ingredient",
Item::Debug(_) => "Debug",
}
}
pub fn description(&self) -> String {
match self {
Item::Tool { kind, .. } => format!("{}", kind.description()),
Item::Armor { kind, .. } => format!("{}", kind.description()),
Item::Consumable { kind, .. } => format!("{}", kind.description()),
Item::Ingredient { kind, .. } => format!("{}", kind.description()),
Item::Debug(_) => format!("Debugging item"),
}
}
pub fn try_reclaim_from_block(block: Block) -> Option<Self> {
match block.kind() {
BlockKind::Apple => Some(Self::apple()),
BlockKind::Mushroom => Some(Self::mushroom()),
BlockKind::Velorite => Some(Self::velorite()),
BlockKind::BlueFlower => Some(Self::flower()),
BlockKind::PinkFlower => Some(Self::flower()),
BlockKind::PurpleFlower => Some(Self::flower()),
BlockKind::RedFlower => Some(Self::flower()),
BlockKind::WhiteFlower => Some(Self::flower()),
BlockKind::YellowFlower => Some(Self::flower()),
BlockKind::Sunflower => Some(Self::flower()),
BlockKind::LongGrass => Some(Self::grass()),
BlockKind::MediumGrass => Some(Self::grass()),
BlockKind::ShortGrass => Some(Self::grass()),
BlockKind::Chest => Some(match rand::random::<usize>() % 4 {
0 => Self::apple(),
1 => Self::velorite(),
2 => Item::Tool {
kind: *(&ALL_TOOLS).choose(&mut rand::thread_rng()).unwrap(),
power: 8 + rand::random::<u32>() % (rand::random::<u32>() % 29 + 1),
stamina: 0,
strength: 0,
dexterity: 0,
intelligence: 0,
},
3 => Self::veloritefrag(),
_ => unreachable!(),
}),
_ => None,
}
}
// General item constructors
pub fn apple() -> Self {
Item::Consumable {
kind: Consumable::Apple,
effect: Effect::Health(comp::HealthChange {
amount: 50,
cause: comp::HealthSource::Item,
}),
}
}
pub fn mushroom() -> Self {
Item::Consumable {
kind: Consumable::Mushroom,
effect: Effect::Health(comp::HealthChange {
amount: 10,
cause: comp::HealthSource::Item,
}),
}
}
pub fn velorite() -> Self {
Item::Consumable {
kind: Consumable::Velorite,
effect: Effect::Xp(50),
}
}
pub fn veloritefrag() -> Self {
Item::Consumable {
kind: Consumable::VeloriteFrag,
effect: Effect::Xp(20),
}
}
pub fn flower() -> Self {
Item::Ingredient {
kind: Ingredient::Flower,
}
}
pub fn grass() -> Self {
Item::Ingredient {
kind: Ingredient::Grass,
}
impl Asset for Item {
const ENDINGS: &'static [&'static str] = &["ron"];
fn parse(buf_reader: BufReader<File>) -> Result<Self, assets::Error> {
Ok(ron::de::from_reader(buf_reader).unwrap())
}
}
impl Default for Item {
fn default() -> Self {
Item::Tool {
kind: Tool::Hammer,
power: 0,
stamina: 0,
strength: 0,
dexterity: 0,
intelligence: 0,
impl Item {
pub fn name(&self) -> &str {
&self.name
}
pub fn description(&self) -> &str {
&self.description
}
pub fn try_reclaim_from_block(block: Block) -> Option<Self> {
match block.kind() {
BlockKind::Apple => Some(assets::load_expect_cloned("common.items.apple")),
BlockKind::Mushroom => Some(assets::load_expect_cloned("common.items.mushroom")),
BlockKind::Velorite => Some(assets::load_expect_cloned("common.items.velorite")),
BlockKind::BlueFlower => Some(assets::load_expect_cloned("common.items.flowers.blue")),
BlockKind::PinkFlower => Some(assets::load_expect_cloned("common.items.flowers.pink")),
BlockKind::PurpleFlower => {
Some(assets::load_expect_cloned("common.items.flowers.purple"))
}
BlockKind::RedFlower => Some(assets::load_expect_cloned("common.items.flowers.red")),
BlockKind::WhiteFlower => {
Some(assets::load_expect_cloned("common.items.flowers.white"))
}
BlockKind::YellowFlower => {
Some(assets::load_expect_cloned("common.items.flowers.yellow"))
}
BlockKind::Sunflower => Some(assets::load_expect_cloned("common.items.flowers.sun")),
BlockKind::LongGrass => Some(assets::load_expect_cloned("common.items.grasses.long")),
BlockKind::MediumGrass => {
Some(assets::load_expect_cloned("common.items.grasses.medium"))
}
BlockKind::ShortGrass => Some(assets::load_expect_cloned("common.items.grasses.short")),
BlockKind::Chest => Some(match rand::random::<usize>() % 4 {
0 => assets::load_expect_cloned("common.items.apple"),
1 => assets::load_expect_cloned("common.items.velorite"),
// TODO: Implement random asset loading
//2 => Item::Tool {
// kind: *(&ALL_TOOLS).choose(&mut rand::thread_rng()),
// power: 8 + rand::random::<u32>() % (rand::random::<u32>() % 29 + 1),
// stamina: 0,
// strength: 0,
// dexterity: 0,
// intelligence: 0,
//},
2 => assets::load_expect_cloned("common.items.apple"),
3 => assets::load_expect_cloned("common.items.veloritefrag"),
_ => unreachable!(),
}),
_ => None,
}
}
}

View File

@ -1,8 +1,9 @@
pub mod item;
// Reexports
pub use item::{Debug, Item, Tool};
pub use item::{Debug, Item, ItemKind, Tool};
use crate::assets;
use specs::{Component, HashMapStorage, NullStorage};
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
@ -72,8 +73,8 @@ impl Default for Inventory {
slots: vec![None; 25],
};
inventory.push(Item::Debug(Debug::Boost));
inventory.push(Item::Debug(Debug::Possess));
inventory.push(assets::load_expect_cloned("common.items.debug.boost"));
inventory.push(assets::load_expect_cloned("common.items.debug.possess"));
inventory
}
}

View File

@ -22,7 +22,7 @@ pub use controller::{
ControlEvent, Controller, ControllerInputs, InventoryManip, MountState, Mounting,
};
pub use inputs::CanBuild;
pub use inventory::{item, Inventory, InventoryUpdate, Item};
pub use inventory::{item, Inventory, InventoryUpdate, Item, ItemKind};
pub use last::Last;
pub use location::Waypoint;
pub use phys::{ForceUpdate, Gravity, Mass, Ori, PhysicsState, Pos, Scale, Sticky, Vel};

View File

@ -51,7 +51,7 @@ pub enum ServerEvent {
entity: EcsEntity,
name: String,
body: comp::Body,
main: Option<comp::Item>,
main: Option<String>,
},
CreateNpc {
pos: comp::Pos,

View File

@ -12,7 +12,7 @@ pub enum ClientMsg {
Character {
name: String,
body: comp::Body,
main: Option<comp::item::Tool>,
main: Option<String>, // Specifier for the weapon
},
ControllerInputs(comp::ControllerInputs),
ControlEvent(comp::ControlEvent),

View File

@ -1,7 +1,7 @@
use crate::{
comp::{
item::Item, ActionState::*, CharacterState, Controller, HealthChange, HealthSource, Ori,
Pos, Stats,
ActionState::*, CharacterState, Controller, HealthChange, HealthSource, ItemKind, Ori, Pos,
Stats,
},
event::{EventBus, LocalEvent, ServerEvent},
state::{DeltaTime, Uid},
@ -110,12 +110,13 @@ impl<'a> System<'a> for Sys {
&& ori2.angle_between(pos_b2 - pos2) < (1.0 / pos2.distance(pos_b2)).atan()
{
// Weapon gives base damage
let mut dmg =
if let Some(Item::Tool { power, .. }) = stat.equipment.main {
power as i32
} else {
1
};
let mut dmg = if let Some(ItemKind::Tool { power, .. }) =
stat.equipment.main.as_ref().map(|i| &i.kind)
{
*power as i32
} else {
1
};
// Block
if character_b.action.is_block()

View File

@ -5,7 +5,8 @@ use super::{
use crate::{
comp::{
self, item, projectile, ActionState::*, Body, CharacterState, ControlEvent, Controller,
HealthChange, HealthSource, Item, MovementState::*, PhysicsState, Projectile, Stats, Vel,
HealthChange, HealthSource, ItemKind, MovementState::*, PhysicsState, Projectile, Stats,
Vel,
},
event::{EventBus, LocalEvent, ServerEvent},
};
@ -131,8 +132,8 @@ impl<'a> System<'a> for Sys {
};
}
match stats.equipment.main {
Some(Item::Tool {
match stats.equipment.main.as_ref().map(|i| &i.kind) {
Some(ItemKind::Tool {
kind: item::Tool::Bow,
power,
..
@ -158,7 +159,7 @@ impl<'a> System<'a> for Sys {
hit_wall: vec![projectile::Effect::Stick],
hit_entity: vec![
projectile::Effect::Damage(HealthChange {
amount: -(power as i32),
amount: -(*power as i32),
cause: HealthSource::Attack { by: *uid },
}),
projectile::Effect::Vanish,
@ -170,7 +171,7 @@ impl<'a> System<'a> for Sys {
}
}
}
Some(Item::Tool {
Some(ItemKind::Tool {
kind: item::Tool::Staff,
power,
..
@ -220,7 +221,7 @@ impl<'a> System<'a> for Sys {
hit_wall: vec![projectile::Effect::Vanish],
hit_entity: vec![
projectile::Effect::Damage(HealthChange {
amount: -(power as i32),
amount: -(*power as i32),
cause: HealthSource::Attack { by: *uid },
}),
projectile::Effect::Vanish,
@ -232,7 +233,7 @@ impl<'a> System<'a> for Sys {
}
}
}
Some(Item::Tool { .. }) => {
Some(ItemKind::Tool { .. }) => {
// Melee Attack
if inputs.primary
&& (character.movement == Stand
@ -263,7 +264,7 @@ impl<'a> System<'a> for Sys {
};
}
}
Some(Item::Debug(item::Debug::Boost)) => {
Some(ItemKind::Debug(item::Debug::Boost)) => {
if inputs.primary {
local_emitter.emit(LocalEvent::Boost {
entity,
@ -278,7 +279,7 @@ impl<'a> System<'a> for Sys {
});
}
}
Some(Item::Debug(item::Debug::Possess)) => {
Some(ItemKind::Debug(item::Debug::Possess)) => {
if inputs.primary
&& (character.movement == Stand
|| character.movement == Run

View File

@ -21,7 +21,7 @@ use crate::{
cmd::CHAT_COMMANDS,
};
use common::{
comp,
assets, comp,
effect::Effect,
event::{EventBus, ServerEvent},
msg::{ClientMsg, ClientState, ServerError, ServerInfo, ServerMsg},
@ -201,9 +201,12 @@ impl Server {
entity: EcsEntity,
name: String,
body: comp::Body,
main: Option<comp::Item>,
main: Option<String>,
server_settings: &ServerSettings,
) {
// Give no item when an invalid specifier is given
let main = main.and_then(|specifier| assets::load_cloned(&specifier));
let spawn_point = state.ecs().read_resource::<SpawnPoint>().0;
state.write_component(entity, body);
@ -428,40 +431,44 @@ impl Server {
}
comp::InventoryManip::Use(slot) => {
let item = state
let item_opt = state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.and_then(|inv| inv.remove(slot));
match item {
Some(comp::Item::Tool { .. }) | Some(comp::Item::Debug(_)) => {
if let Some(stats) =
state.ecs().write_storage::<comp::Stats>().get_mut(entity)
{
// Insert old item into inventory
if let Some(old_item) = stats.equipment.main.take() {
state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.map(|inv| inv.insert(slot, old_item));
}
match item_opt {
Some(item) => match item.kind {
comp::ItemKind::Tool { .. } => {
if let Some(stats) = state
.ecs()
.write_storage::<comp::Stats>()
.get_mut(entity)
{
// Insert old item into inventory
if let Some(old_item) = stats.equipment.main.take() {
state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.map(|inv| inv.insert(slot, old_item));
}
stats.equipment.main = item;
stats.equipment.main = Some(item);
}
}
}
Some(comp::Item::Consumable { effect, .. }) => {
state.apply_effect(entity, effect);
}
Some(item) => {
// Re-insert it if unused
let _ = state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.map(|inv| inv.insert(slot, item));
}
comp::ItemKind::Consumable { effect, .. } => {
state.apply_effect(entity, effect);
}
_ => {
// Re-insert it if unused
let _ = state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.map(|inv| inv.insert(slot, item));
}
},
_ => {}
}
@ -610,14 +617,15 @@ impl Server {
{
let mut inventories = ecs.write_storage::<comp::Inventory>();
if let Some(inventory) = inventories.get_mut(possesse) {
inventory
.push(comp::Item::Debug(comp::item::Debug::Possess));
inventory.push(assets::load_expect_cloned(
"common.items.debug.possess",
));
} else {
let _ = inventories.insert(
possesse,
comp::Inventory {
slots: vec![Some(comp::Item::Debug(
comp::item::Debug::Possess,
slots: vec![Some(assets::load_expect_cloned(
"common.items.debug.possess",
))],
},
);

View File

@ -1,7 +1,8 @@
use super::SysTimer;
use crate::{auth_provider::AuthProvider, client::Client, CLIENT_TIMEOUT};
use common::{
comp::{Admin, Body, CanBuild, Controller, Item, Ori, Player, Pos, Vel},
assets,
comp::{Admin, Body, CanBuild, Controller, Ori, Player, Pos, Vel},
event::{EventBus, ServerEvent},
msg::{validate_chat_msg, ChatMsgValidationError, MAX_BYTES_CHAT_MSG},
msg::{ClientMsg, ClientState, RequestStateError, ServerMsg},
@ -168,14 +169,7 @@ impl<'a> System<'a> for Sys {
entity,
name,
body,
main: main.map(|t| Item::Tool {
kind: t,
power: 10,
stamina: 0,
strength: 0,
dexterity: 0,
intelligence: 0,
}),
main: main.and_then(|specifier| assets::load_cloned(&specifier)),
});
}
ClientState::Character => client.error_state(RequestStateError::Already),

View File

@ -1,6 +1,7 @@
use super::SysTimer;
use crate::{chunk_generator::ChunkGenerator, client::Client, Tick};
use common::{
assets,
comp::{self, Player, Pos},
event::{EventBus, ServerEvent},
msg::ServerMsg,
@ -95,14 +96,9 @@ impl<'a> System<'a> for Sys {
let (mut stats, mut body) = if rand::random() {
let stats = comp::Stats::new(
"Humanoid".to_string(),
Some(comp::Item::Tool {
kind: comp::item::Tool::Sword,
power: 5,
stamina: 0,
strength: 0,
dexterity: 0,
intelligence: 0,
}),
Some(assets::load_expect_cloned(
"common.items.weapons.starter_sword",
)),
);
let body = comp::Body::Humanoid(comp::humanoid::Body::random());
(stats, body)
@ -120,14 +116,9 @@ impl<'a> System<'a> for Sys {
if rand::random::<f32>() < 0.8 {
stats = comp::Stats::new(
"Humanoid".to_string(),
Some(comp::Item::Tool {
kind: comp::item::Tool::Sword,
power: 10,
stamina: 0,
strength: 0,
dexterity: 0,
intelligence: 0,
}),
Some(assets::load_expect_cloned(
"common.items.weapons.starter_sword",
)),
);
body = comp::Body::Humanoid(comp::humanoid::Body::random());
}

View File

@ -1,6 +1,6 @@
use super::{
img_ids::{Imgs, ImgsRot},
item_imgs::{ItemImgs, ItemKind},
item_imgs::{ItemImgs, ItemKey},
Event as HudEvent, Fonts, TEXT_COLOR,
};
use crate::ui::{ImageFrame, Tooltip, TooltipManager, Tooltipable};
@ -65,7 +65,7 @@ impl<'a> Bag<'a> {
pub struct State {
ids: Ids,
img_id_cache: Vec<Option<(ItemKind, image::Id)>>,
img_id_cache: Vec<Option<(ItemKey, image::Id)>>,
selected_slot: Option<usize>,
}
@ -195,8 +195,8 @@ impl<'a> Widget for Bag<'a> {
slot_widget
.with_tooltip(
self.tooltip_manager,
&item.title(),
&format!("{}\n{}", item.info(), item.description()),
&item.name(),
&format!("{}\n{}", item.name(), item.description()),
&item_tooltip,
)
.set(state.ids.inv_slots[i], ui)
@ -222,7 +222,7 @@ impl<'a> Widget for Bag<'a> {
state.update(|s| s.selected_slot = selected_slot);
}
// Item
if let Some(kind) = item.as_ref().map(|i| ItemKind::from(i)) {
if let Some(kind) = item.as_ref().map(|i| ItemKey::from(i)) {
Button::image(match &state.img_id_cache[i] {
Some((cached_kind, id)) if cached_kind == &kind => *id,
_ => {

View File

@ -1,7 +1,7 @@
use crate::ui::{Graphic, Transform, Ui};
use common::{
assets::{self, watch::ReloadIndicator, Asset},
comp::item::{Armor, Consumable, Debug, Ingredient, Item, Tool},
comp::item::{Armor, Consumable, Debug, Ingredient, Item, ItemKind, Tool},
};
use conrod_core::image::Id;
use dot_vox::DotVoxData;
@ -13,21 +13,21 @@ use std::{fs::File, io::BufReader, sync::Arc};
use vek::*;
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ItemKind {
pub enum ItemKey {
Tool(Tool),
Armor(Armor),
Consumable(Consumable),
Ingredient(Ingredient),
Debug(Debug),
}
impl From<&Item> for ItemKind {
impl From<&Item> for ItemKey {
fn from(item: &Item) -> Self {
match item {
Item::Tool { kind, .. } => ItemKind::Tool(kind.clone()),
Item::Armor { kind, .. } => ItemKind::Armor(kind.clone()),
Item::Consumable { kind, .. } => ItemKind::Consumable(kind.clone()),
Item::Ingredient { kind, .. } => ItemKind::Ingredient(kind.clone()),
Item::Debug(kind) => ItemKind::Debug(kind.clone()),
match &item.kind {
ItemKind::Tool { kind, .. } => ItemKey::Tool(kind.clone()),
ItemKind::Armor { kind, .. } => ItemKey::Armor(kind.clone()),
ItemKind::Consumable { kind, .. } => ItemKey::Consumable(kind.clone()),
ItemKind::Ingredient(kind) => ItemKey::Ingredient(kind.clone()),
ItemKind::Debug(kind) => ItemKey::Debug(kind.clone()),
}
}
}
@ -68,7 +68,7 @@ impl ImageSpec {
}
}
#[derive(Serialize, Deserialize)]
struct ItemImagesSpec(HashMap<ItemKind, ImageSpec>);
struct ItemImagesSpec(HashMap<ItemKey, ImageSpec>);
impl Asset for ItemImagesSpec {
const ENDINGS: &'static [&'static str] = &["ron"];
fn parse(buf_reader: BufReader<File>) -> Result<Self, assets::Error> {
@ -77,7 +77,7 @@ impl Asset for ItemImagesSpec {
}
pub struct ItemImgs {
map: HashMap<ItemKind, Id>,
map: HashMap<ItemKey, Id>,
indicator: ReloadIndicator,
}
impl ItemImgs {
@ -110,7 +110,7 @@ impl ItemImgs {
// See if we already have an id we can use
match self.map.get(&kind) {
Some(id) => ui.replace_graphic(*id, graphic),
// Otherwise, generate new id and insert it into our Id -> ItemKind map
// Otherwise, generate new id and insert it into our Id -> ItemKey map
None => {
self.map.insert(kind.clone(), ui.add_graphic(graphic));
}
@ -118,7 +118,7 @@ impl ItemImgs {
}
}
}
pub fn img_id(&self, item_kind: ItemKind) -> Option<Id> {
pub fn img_id(&self, item_kind: ItemKey) -> Option<Id> {
match self.map.get(&item_kind) {
Some(id) => Some(*id),
// There was no specification in the ron

View File

@ -3,8 +3,7 @@ use super::{
/*FOCUS_COLOR, RAGE_COLOR,*/ HP_COLOR, LOW_HP_COLOR, MANA_COLOR, TEXT_COLOR, XP_COLOR,
};
use crate::GlobalState;
use common::comp::{item::Debug, item::Tool, Item, Stats};
use common::comp::{item::Debug, item::Tool, Item, ItemKind, Stats};
use conrod_core::{
color,
widget::{self, Button, Image, Rectangle, Text},
@ -506,8 +505,8 @@ impl<'a> Widget for Skillbar<'a> {
// M1 Slot
Image::new(self.imgs.skillbar_slot_big_bg)
.w_h(36.0 * scale, 36.0 * scale)
.color(match self.stats.equipment.main {
Some(Item::Tool { kind, .. }) => match kind {
.color(match self.stats.equipment.main.as_ref().map(|i| &i.kind) {
Some(ItemKind::Tool { kind, .. }) => match kind {
Tool::Bow => Some(BG_COLOR_2),
Tool::Staff => Some(BG_COLOR_2),
_ => Some(BG_COLOR_2),
@ -516,20 +515,20 @@ impl<'a> Widget for Skillbar<'a> {
})
.middle_of(state.ids.m1_slot)
.set(state.ids.m1_slot_bg, ui);
Button::image(match self.stats.equipment.main {
Some(Item::Tool { kind, .. }) => match kind {
Button::image(match self.stats.equipment.main.as_ref().map(|i| &i.kind) {
Some(ItemKind::Tool { kind, .. }) => match kind {
Tool::Sword => self.imgs.twohsword_m1,
Tool::Hammer => self.imgs.twohhammer_m1,
Tool::Axe => self.imgs.twohaxe_m1,
Tool::Bow => self.imgs.bow_m1,
Tool::Staff => self.imgs.staff_m1,
Tool::Debug(Debug::Boost) => self.imgs.flyingrod_m1,
_ => self.imgs.twohaxe_m1,
},
Some(Item::Debug(Debug::Boost)) => self.imgs.flyingrod_m1,
_ => self.imgs.twohaxe_m1,
}) // Insert Icon here
.w(match self.stats.equipment.main {
Some(Item::Tool { kind, .. }) => match kind {
.w(match self.stats.equipment.main.as_ref().map(|i| &i.kind) {
Some(ItemKind::Tool { kind, .. }) => match kind {
Tool::Bow => 30.0 * scale,
Tool::Staff => 30.0 * scale,
_ => 38.0 * scale,
@ -537,7 +536,7 @@ impl<'a> Widget for Skillbar<'a> {
_ => 38.0 * scale,
})
.h(match self.stats.equipment.main {
Some(Item::Tool { kind, .. }) => match kind {
Some(ItemKind::Tool { kind, .. }) => match kind {
Tool::Bow => 30.0 * scale,
Tool::Staff => 36.0 * scale,
_ => 38.0 * scale,
@ -563,20 +562,20 @@ impl<'a> Widget for Skillbar<'a> {
})
.middle_of(state.ids.m2_slot)
.set(state.ids.m2_slot_bg, ui);
Button::image(match self.stats.equipment.main {
Some(Item::Tool { kind, .. }) => match kind {
Button::image(match self.stats.equipment.main.as_ref().map(|i| &i.kind) {
Some(ItemKind::Tool { kind, .. }) => match kind {
Tool::Sword => self.imgs.twohsword_m2,
Tool::Hammer => self.imgs.twohhammer_m2,
Tool::Axe => self.imgs.twohaxe_m2,
Tool::Bow => self.imgs.bow_m2,
Tool::Staff => self.imgs.staff_m2,
Tool::Debug(Debug::Boost) => self.imgs.flyingrod_m2,
_ => self.imgs.twohaxe_m2,
},
Some(Item::Debug(Debug::Boost)) => self.imgs.flyingrod_m2,
_ => self.imgs.twohaxe_m2,
}) // Insert Icon here
.w(match self.stats.equipment.main {
Some(Item::Tool { kind, .. }) => match kind {
Some(ItemKind::Tool { kind, .. }) => match kind {
Tool::Bow => 30.0 * scale,
Tool::Staff => 30.0 * scale,
_ => 38.0 * scale,
@ -584,7 +583,7 @@ impl<'a> Widget for Skillbar<'a> {
_ => 38.0 * scale,
})
.h(match self.stats.equipment.main {
Some(Item::Tool { kind, .. }) => match kind {
Some(ItemKind::Tool { kind, .. }) => match kind {
Tool::Bow => 30.0 * scale,
Tool::Staff => 30.0 * scale,
_ => 38.0 * scale,

View File

@ -6,7 +6,7 @@ use crate::{
PlayStateResult,
};
use client::{self, Client};
use common::{clock::Clock, comp, msg::ClientState};
use common::{assets, clock::Clock, comp, msg::ClientState};
use log::error;
use scene::Scene;
use std::{cell::RefCell, rc::Rc, time::Duration};
@ -67,7 +67,9 @@ impl PlayState for CharSelectionState {
self.client.borrow_mut().request_character(
self.char_selection_ui.character_name.clone(),
comp::Body::Humanoid(self.char_selection_ui.character_body),
self.char_selection_ui.character_tool,
self.char_selection_ui
.character_tool
.map(|specifier| specifier.to_owned()),
);
return PlayStateResult::Push(Box::new(SessionState::new(
global_state,
@ -93,18 +95,10 @@ impl PlayState for CharSelectionState {
&self.client.borrow(),
self.char_selection_ui.character_body,
&comp::Equipment {
main: if let Some(kind) = self.char_selection_ui.character_tool {
Some(comp::Item::Tool {
kind: kind,
power: 10,
stamina: 0,
strength: 0,
dexterity: 0,
intelligence: 0,
})
} else {
None
},
main: self
.char_selection_ui
.character_tool
.and_then(|specifier| assets::load_cloned(&specifier)),
alt: None,
},
);

View File

@ -17,6 +17,13 @@ use conrod_core::{
widget_ids, Borderable, Color, Colorable, Labelable, Positionable, Sizeable, UiCell, Widget,
};
const STARTER_HAMMER: &str = "common.items.weapons.starter_hammer";
const STARTER_BOW: &str = "common.items.weapons.starter_bow";
const STARTER_AXE: &str = "common.items.weapons.starter_axe";
const STARTER_STAFF: &str = "common.items.weapons.starter_staff";
const STARTER_SWORD: &str = "common.items.weapons.starter_sword";
const STARTER_DAGGER: &str = "common.items.weapons.starter_dagger";
widget_ids! {
struct Ids {
// Background and logo
@ -249,7 +256,7 @@ pub struct CharSelectionUi {
//deletion_confirmation: bool,
pub character_name: String,
pub character_body: humanoid::Body,
pub character_tool: Option<Tool>,
pub character_tool: Option<&'static str>,
}
impl CharSelectionUi {
@ -280,7 +287,7 @@ impl CharSelectionUi {
character_creation: false,
character_name: "Character Name".to_string(),
character_body: humanoid::Body::random(),
character_tool: Some(Tool::Sword),
character_tool: Some(STARTER_SWORD),
}
}
@ -469,7 +476,7 @@ impl CharSelectionUi {
.was_clicked()
{
self.character_creation = true;
self.character_tool = Some(Tool::Sword);
self.character_tool = Some(STARTER_SWORD);
}
// Alpha Version
@ -852,7 +859,7 @@ impl CharSelectionUi {
.w_h(70.0, 70.0)
.bottom_left_with_margins_on(self.ids.creation_buttons_alignment_2, 0.0, 0.0)
.set(self.ids.hammer, ui_widgets);
if Button::image(if let Some(Tool::Hammer) = self.character_tool {
if Button::image(if let Some(STARTER_HAMMER) = self.character_tool {
self.imgs.icon_border_pressed
} else {
self.imgs.icon_border
@ -864,7 +871,7 @@ impl CharSelectionUi {
.set(self.ids.hammer_button, ui_widgets)
.was_clicked()
{
self.character_tool = Some(Tool::Hammer);
self.character_tool = Some(STARTER_HAMMER);
}
// REMOVE THIS AFTER IMPLEMENTATION
/*Rectangle::fill_with([67.0, 67.0], color::rgba(0.0, 0.0, 0.0, 0.8))
@ -877,7 +884,7 @@ impl CharSelectionUi {
.w_h(70.0, 70.0)
.right_from(self.ids.hammer, 2.0)
.set(self.ids.bow, ui_widgets);
if Button::image(if let Some(Tool::Bow) = self.character_tool {
if Button::image(if let Some(STARTER_BOW) = self.character_tool {
self.imgs.icon_border_pressed
} else {
self.imgs.icon_border
@ -889,7 +896,7 @@ impl CharSelectionUi {
.set(self.ids.bow_button, ui_widgets)
.was_clicked()
{
self.character_tool = Some(Tool::Bow);
self.character_tool = Some(STARTER_BOW);
}
// REMOVE THIS AFTER IMPLEMENTATION
/*Rectangle::fill_with([67.0, 67.0], color::rgba(0.0, 0.0, 0.0, 0.8))
@ -900,7 +907,7 @@ impl CharSelectionUi {
.w_h(70.0, 70.0)
.right_from(self.ids.bow, 2.0)
.set(self.ids.staff, ui_widgets);
if Button::image(if let Some(Tool::Staff) = self.character_tool {
if Button::image(if let Some(STARTER_STAFF) = self.character_tool {
self.imgs.icon_border_pressed
} else {
self.imgs.icon_border
@ -912,7 +919,7 @@ impl CharSelectionUi {
.set(self.ids.staff_button, ui_widgets)
.was_clicked()
{
self.character_tool = Some(Tool::Staff);
self.character_tool = Some(STARTER_STAFF);
}
// REMOVE THIS AFTER IMPLEMENTATION
/*Rectangle::fill_with([67.0, 67.0], color::rgba(0.0, 0.0, 0.0, 0.8))
@ -923,7 +930,7 @@ impl CharSelectionUi {
.w_h(70.0, 70.0)
.up_from(self.ids.hammer, 2.0)
.set(self.ids.sword, ui_widgets);
if Button::image(if let Some(Tool::Sword) = self.character_tool {
if Button::image(if let Some(STARTER_SWORD) = self.character_tool {
self.imgs.icon_border_pressed
} else {
self.imgs.icon_border
@ -935,7 +942,7 @@ impl CharSelectionUi {
.set(self.ids.sword_button, ui_widgets)
.was_clicked()
{
self.character_tool = Some(Tool::Sword);
self.character_tool = Some(STARTER_SWORD);
}
// Daggers
@ -943,7 +950,7 @@ impl CharSelectionUi {
.w_h(70.0, 70.0)
.right_from(self.ids.sword, 2.0)
.set(self.ids.daggers, ui_widgets);
if Button::image(if let Some(Tool::Dagger) = self.character_tool {
if Button::image(if let Some(STARTER_DAGGER) = self.character_tool {
self.imgs.icon_border_pressed
} else {
self.imgs.icon_border
@ -955,7 +962,7 @@ impl CharSelectionUi {
.set(self.ids.daggers_button, ui_widgets)
.was_clicked()
{
// self.character_tool = Some(Tool::Daggers);
// self.character_tool = Some(STARTER_DAGGER);
} // REMOVE THIS AFTER IMPLEMENTATION
Rectangle::fill_with([67.0, 67.0], color::rgba(0.0, 0.0, 0.0, 0.8))
.middle_of(self.ids.daggers)
@ -966,7 +973,7 @@ impl CharSelectionUi {
.w_h(70.0, 70.0)
.right_from(self.ids.daggers, 2.0)
.set(self.ids.axe, ui_widgets);
if Button::image(if let Some(Tool::Axe) = self.character_tool {
if Button::image(if let Some(STARTER_AXE) = self.character_tool {
self.imgs.icon_border_pressed
} else {
self.imgs.icon_border
@ -978,7 +985,7 @@ impl CharSelectionUi {
.set(self.ids.axe_button, ui_widgets)
.was_clicked()
{
self.character_tool = Some(Tool::Axe);
self.character_tool = Some(STARTER_AXE);
}
// REMOVE THIS AFTER IMPLEMENTATION
/*Rectangle::fill_with([67.0, 67.0], color::rgba(0.0, 0.0, 0.0, 0.8))

View File

@ -10,7 +10,7 @@ use common::{
Belt, BodyType, Chest, EyeColor, Eyebrows, Foot, Hand, Pants, Race, Shoulder, Skin,
},
item::Tool,
object, quadruped, quadruped_medium, Item,
object, quadruped, quadruped_medium, Item, ItemKind,
},
figure::{DynaUnionizer, MatSegment, Material, Segment},
};
@ -511,8 +511,8 @@ impl HumArmorFootSpec {
pub fn mesh_main(item: Option<&Item>) -> Mesh<FigurePipeline> {
if let Some(item) = item {
let (name, offset) = match item {
Item::Tool { kind, .. } => match kind {
let (name, offset) = match item.kind {
ItemKind::Tool { kind, .. } => match kind {
Tool::Sword => ("weapon.sword.rusty_2h", Vec3::new(-1.5, -6.5, -4.0)),
Tool::Axe => ("weapon.axe.rusty_2h", Vec3::new(-1.5, -5.0, -4.0)),
Tool::Hammer => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)),
@ -521,7 +521,7 @@ pub fn mesh_main(item: Option<&Item>) -> Mesh<FigurePipeline> {
Tool::Bow => ("weapon.bow.simple-bow", Vec3::new(-1.0, -6.0, -2.0)),
Tool::Staff => ("weapon.staff.wood-fire", Vec3::new(-1.0, -6.0, -3.0)),
},
Item::Debug(_) => ("weapon.debug_wand", Vec3::new(-1.5, -9.5, -4.0)),
ItemKind::Debug(_) => ("weapon.debug_wand", Vec3::new(-1.5, -9.5, -4.0)),
_ => return Mesh::new(),
};
load_mesh(name, offset)