Added armour items and equipping of armour

This commit is contained in:
Joshua Barretto 2020-02-26 17:04:43 +00:00 committed by timokoesters
parent e80750bc53
commit e20feeeb0c
19 changed files with 176 additions and 52 deletions

View File

@ -0,0 +1,8 @@
Item(
name: "Iron Chestplate",
description: "Arrows to the stomach are soooo last update.",
kind: Armor(
kind: Chest(Plate),
stats: 20,
),
)

View File

@ -0,0 +1,8 @@
Item(
name: "Assassin Pants",
description: "Only the best for a member of the creed.",
kind: Armor(
kind: Pants(Assassin),
stats: 20,
),
)

View File

@ -0,0 +1,8 @@
Item(
name: "Green Camo Pants",
description: "Perfect for hunting.",
kind: Armor(
kind: Pants(Green),
stats: 20,
),
)

View File

@ -39,7 +39,13 @@ pub struct ItemConfig {
pub struct Loadout { pub struct Loadout {
pub active_item: Option<ItemConfig>, pub active_item: Option<ItemConfig>,
pub second_item: Option<ItemConfig>, pub second_item: Option<ItemConfig>,
// armor
pub shoulder: Option<Item>,
pub chest: Option<Item>,
pub belt: Option<Item>,
pub hand: Option<Item>,
pub pants: Option<Item>,
pub foot: Option<Item>,
} }
impl From<CharacterAbility> for CharacterState { impl From<CharacterAbility> for CharacterState {

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
assets::{self, Asset}, assets::{self, Asset},
comp::CharacterAbility, comp::{body::humanoid, CharacterAbility},
effect::Effect, effect::Effect,
terrain::{Block, BlockKind}, terrain::{Block, BlockKind},
}; };
@ -80,22 +80,17 @@ pub enum DebugKind {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Armor { pub enum Armor {
// TODO: Don't make armor be a body part. Wearing enemy's head is funny but also a creepy Shoulder(humanoid::Shoulder),
// thing to do. Chest(humanoid::Chest),
Helmet, Belt(humanoid::Belt),
Shoulders, Hand(humanoid::Hand),
Chestplate, Pants(humanoid::Pants),
Belt, Foot(humanoid::Foot),
Gloves,
Pants,
Boots,
Back,
Tabard,
Gem,
Necklace,
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] pub type ArmorStats = u32;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Consumable { pub enum Consumable {
Apple, Apple,
Cheese, Cheese,
@ -124,13 +119,13 @@ pub struct ToolData {
// TODO: item specific abilities // TODO: item specific abilities
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ItemKind { pub enum ItemKind {
/// Something wieldable /// Something wieldable
Tool(ToolData), Tool(ToolData),
Armor { Armor {
kind: Armor, kind: Armor,
power: u32, stats: ArmorStats,
}, },
Consumable { Consumable {
kind: Consumable, kind: Consumable,

View File

@ -100,6 +100,7 @@ pub enum ServerEvent {
CreateNpc { CreateNpc {
pos: comp::Pos, pos: comp::Pos,
stats: comp::Stats, stats: comp::Stats,
loadout: comp::Loadout,
body: comp::Body, body: comp::Body,
agent: comp::Agent, agent: comp::Agent,
alignment: comp::Alignment, alignment: comp::Alignment,

View File

@ -1,15 +1,12 @@
use super::utils::*; use super::utils::*;
use crate::{ use crate::{
comp::{CharacterState, StateUpdate, ToolData}, comp::{CharacterState, StateUpdate},
states::wielding,
sys::character_behavior::{CharacterBehavior, JoinData}, sys::character_behavior::{CharacterBehavior, JoinData},
}; };
use std::{collections::VecDeque, time::Duration}; use std::{collections::VecDeque, time::Duration};
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct Data { pub struct Data {
/// The weapon being equipped
pub tool: ToolData,
/// Time left before next state /// Time left before next state
pub time_left: Duration, pub time_left: Duration,
} }
@ -40,7 +37,6 @@ impl CharacterBehavior for Data {
.time_left .time_left
.checked_sub(Duration::from_secs_f32(data.dt.0)) .checked_sub(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(), .unwrap_or_default(),
tool: self.tool,
}); });
} }

View File

@ -119,9 +119,8 @@ pub fn handle_wield(data: &JoinData, update: &mut StateUpdate) {
/// If a tool is equipped, goes into Equipping state, otherwise goes to Idle /// If a tool is equipped, goes into Equipping state, otherwise goes to Idle
pub fn attempt_wield(data: &JoinData, update: &mut StateUpdate) { pub fn attempt_wield(data: &JoinData, update: &mut StateUpdate) {
if let Some(Tool(tool)) = data.loadout.active_item.as_ref().map(|i| i.item.kind) { if let Some(Tool(tool)) = data.loadout.active_item.as_ref().map(|i| &i.item.kind) {
update.character = CharacterState::Equipping(equipping::Data { update.character = CharacterState::Equipping(equipping::Data {
tool,
time_left: tool.equip_time(), time_left: tool.equip_time(),
}); });
} else { } else {
@ -253,8 +252,8 @@ pub fn attempt_dodge_ability(data: &JoinData, update: &mut StateUpdate) {
} }
} }
pub fn unwrap_tool_data(data: &JoinData) -> Option<ToolData> { pub fn unwrap_tool_data<'a>(data: &'a JoinData) -> Option<&'a ToolData> {
if let Some(Tool(tool)) = data.loadout.active_item.as_ref().map(|i| i.item.kind) { if let Some(Tool(tool)) = data.loadout.active_item.as_ref().map(|i| &i.item.kind) {
Some(tool) Some(tool)
} else { } else {
None None

View File

@ -505,6 +505,7 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C
.create_npc( .create_npc(
pos, pos,
comp::Stats::new(get_npc_name(id).into(), body), comp::Stats::new(get_npc_name(id).into(), body),
comp::Loadout::default(),
body, body,
) )
.with(comp::Vel(vel)) .with(comp::Vel(vel))

View File

@ -1,7 +1,7 @@
use crate::{sys, Server, StateExt}; use crate::{sys, Server, StateExt};
use common::comp::{ use common::comp::{
self, Agent, Alignment, Body, Gravity, LightEmitter, Pos, Projectile, Scale, Stats, Vel, self, Agent, Alignment, Body, Gravity, LightEmitter, Loadout, Pos, Projectile, Scale, Stats,
WaypointArea, Vel, WaypointArea,
}; };
use specs::{Builder, Entity as EcsEntity, WorldExt}; use specs::{Builder, Entity as EcsEntity, WorldExt};
use vek::{Rgb, Vec3}; use vek::{Rgb, Vec3};
@ -24,6 +24,7 @@ pub fn handle_create_npc(
server: &mut Server, server: &mut Server,
pos: Pos, pos: Pos,
stats: Stats, stats: Stats,
loadout: Loadout,
body: Body, body: Body,
agent: Agent, agent: Agent,
alignment: Alignment, alignment: Alignment,
@ -31,7 +32,7 @@ pub fn handle_create_npc(
) { ) {
server server
.state .state
.create_npc(pos, stats, body) .create_npc(pos, stats, loadout, body)
.with(agent) .with(agent)
.with(scale) .with(scale)
.with(alignment) .with(alignment)

View File

@ -83,21 +83,97 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
} }
}, },
comp::InventoryManip::Use(slot) => { comp::InventoryManip::Use(slot_idx) => {
let item_opt = state let item_opt = state
.ecs() .ecs()
.write_storage::<comp::Inventory>() .write_storage::<comp::Inventory>()
.get_mut(entity) .get_mut(entity)
.and_then(|inv| inv.remove(slot)); .and_then(|inv| inv.remove(slot_idx));
let mut event = comp::InventoryUpdateEvent::Used; let mut event = comp::InventoryUpdateEvent::Used;
if let Some(item) = item_opt { if let Some(item) = item_opt {
match item.kind { match &item.kind {
comp::ItemKind::Consumable { kind, effect } => { comp::ItemKind::Tool(tool) => {
event = comp::InventoryUpdateEvent::Consumed(kind); if let Some(loadout) =
state.apply_effect(entity, effect); state.ecs().write_storage::<comp::Loadout>().get_mut(entity)
{
// Insert old item into inventory
if let Some(old_item) = loadout.active_item.take() {
state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.map(|inv| inv.insert(slot_idx, old_item.item));
}
let mut abilities = tool.get_abilities();
let mut ability_drain = abilities.drain(..);
let active_item = comp::ItemConfig {
item,
primary_ability: ability_drain.next(),
secondary_ability: ability_drain.next(),
block_ability: Some(comp::CharacterAbility::BasicBlock),
dodge_ability: Some(comp::CharacterAbility::Roll),
};
loadout.active_item = Some(active_item);
}
}, },
comp::ItemKind::Consumable { kind, effect } => {
event = comp::InventoryUpdateEvent::Consumed(*kind);
state.apply_effect(entity, *effect);
},
comp::ItemKind::Armor { kind, .. } => {
if let Some(loadout) =
state.ecs().write_storage::<comp::Loadout>().get_mut(entity)
{
if let Some(comp::Body::Humanoid(body)) =
state.ecs().write_storage::<comp::Body>().get_mut(entity)
{
use comp::item::Armor::*;
let slot = match kind.clone() {
Shoulder(shoulder) => {
body.shoulder = shoulder;
&mut loadout.shoulder
},
Chest(chest) => {
body.chest = chest;
&mut loadout.chest
},
Belt(belt) => {
body.belt = belt;
&mut loadout.belt
},
Hand(hand) => {
body.hand = hand;
&mut loadout.hand
},
Pants(pants) => {
body.pants = pants;
&mut loadout.pants
},
Foot(foot) => {
body.foot = foot;
&mut loadout.foot
},
};
// Insert old item into inventory
if let Some(old_item) = slot.take() {
state
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.map(|inv| inv.insert(slot_idx, old_item));
}
*slot = Some(item);
}
}
},
comp::ItemKind::Utility { kind } => match kind { comp::ItemKind::Utility { kind } => match kind {
comp::item::Utility::Collar => { comp::item::Utility::Collar => {
let reinsert = if let Some(pos) = let reinsert = if let Some(pos) =
@ -155,7 +231,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
.ecs() .ecs()
.write_storage::<comp::Inventory>() .write_storage::<comp::Inventory>()
.get_mut(entity) .get_mut(entity)
.map(|inv| inv.insert(slot, item)); .map(|inv| inv.insert(slot_idx, item));
} }
}, },
}, },
@ -164,7 +240,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
.ecs() .ecs()
.write_storage::<comp::Inventory>() .write_storage::<comp::Inventory>()
.get_mut(entity) .get_mut(entity)
.map(|inv| inv.insert(slot, item)); .map(|inv| inv.insert(slot_idx, item));
}, },
} }
} }

View File

@ -76,11 +76,12 @@ impl Server {
ServerEvent::CreateNpc { ServerEvent::CreateNpc {
pos, pos,
stats, stats,
loadout,
body, body,
agent, agent,
alignment, alignment,
scale, scale,
} => handle_create_npc(self, pos, stats, body, agent, alignment, scale), } => handle_create_npc(self, pos, stats, loadout, body, agent, alignment, scale),
ServerEvent::CreateWaypoint(pos) => handle_create_waypoint(self, pos), ServerEvent::CreateWaypoint(pos) => handle_create_waypoint(self, pos),
ServerEvent::ClientDisconnect(entity) => { ServerEvent::ClientDisconnect(entity) => {
frontend_events.push(handle_client_disconnect(self, entity)) frontend_events.push(handle_client_disconnect(self, entity))

View File

@ -17,6 +17,7 @@ pub trait StateExt {
&mut self, &mut self,
pos: comp::Pos, pos: comp::Pos,
stats: comp::Stats, stats: comp::Stats,
loadout: comp::Loadout,
body: comp::Body, body: comp::Body,
) -> EcsEntityBuilder; ) -> EcsEntityBuilder;
fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder; fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder;
@ -81,6 +82,7 @@ impl StateExt for State {
&mut self, &mut self,
pos: comp::Pos, pos: comp::Pos,
stats: comp::Stats, stats: comp::Stats,
loadout: comp::Loadout,
body: comp::Body, body: comp::Body,
) -> EcsEntityBuilder { ) -> EcsEntityBuilder {
self.ecs_mut() self.ecs_mut()
@ -95,7 +97,7 @@ impl StateExt for State {
.with(comp::Energy::new(500)) .with(comp::Energy::new(500))
.with(comp::Gravity(1.0)) .with(comp::Gravity(1.0))
.with(comp::CharacterState::default()) .with(comp::CharacterState::default())
.with(comp::Loadout::default()) // TODO Give the poor npc something to do .with(loadout)
} }
/// Build a static object entity /// Build a static object entity
@ -161,7 +163,7 @@ impl StateExt for State {
self.write_component( self.write_component(
entity, entity,
if let Some(comp::ItemKind::Tool(tool)) = main.as_ref().map(|i| i.kind) { if let Some(comp::ItemKind::Tool(tool)) = main.as_ref().map(|i| &i.kind) {
let mut abilities = tool.get_abilities(); let mut abilities = tool.get_abilities();
let mut ability_drain = abilities.drain(..); let mut ability_drain = abilities.drain(..);
comp::Loadout { comp::Loadout {
@ -173,6 +175,12 @@ impl StateExt for State {
dodge_ability: Some(comp::CharacterAbility::Roll), dodge_ability: Some(comp::CharacterAbility::Roll),
}), }),
second_item: None, second_item: None,
shoulder: None,
chest: None,
belt: None,
hand: None,
pants: None,
foot: None,
} }
} else { } else {
comp::Loadout::default() comp::Loadout::default()

View File

@ -190,6 +190,12 @@ impl<'a> System<'a> for Sys {
dodge_ability: None, dodge_ability: None,
}), }),
second_item: None, second_item: None,
shoulder: None,
chest: None,
belt: None,
hand: None,
pants: None,
foot: None,
}; };
let mut scale = 1.0; let mut scale = 1.0;
@ -222,6 +228,12 @@ impl<'a> System<'a> for Sys {
dodge_ability: None, dodge_ability: None,
}), }),
second_item: None, second_item: None,
shoulder: None,
chest: None,
belt: None,
hand: None,
pants: None,
foot: None,
}; };
stats.level.set_level(rand::thread_rng().gen_range(8, 15)); stats.level.set_level(rand::thread_rng().gen_range(8, 15));
@ -246,6 +258,7 @@ impl<'a> System<'a> for Sys {
server_emitter.emit(ServerEvent::CreateNpc { server_emitter.emit(ServerEvent::CreateNpc {
pos: Pos(entity.pos), pos: Pos(entity.pos),
stats, stats,
loadout,
body, body,
alignment, alignment,
agent: comp::Agent::default().with_patrol_origin(entity.pos), agent: comp::Agent::default().with_patrol_origin(entity.pos),

View File

@ -112,15 +112,18 @@ impl PlayState for CharSelectionState {
} }
// Render the scene. // Render the scene.
let item = self
.char_selection_ui
.get_character_data()
.and_then(|data| data.tool)
.and_then(|tool| assets::load_cloned::<comp::Item>(&tool).ok());
let item_kind = item.map(|i| i.kind);
self.scene.render( self.scene.render(
global_state.window.renderer_mut(), global_state.window.renderer_mut(),
self.client.borrow().get_tick(), self.client.borrow().get_tick(),
humanoid_body.clone(), humanoid_body.clone(),
self.char_selection_ui item_kind.as_ref(),
.get_character_data()
.and_then(|data| data.tool)
.and_then(|tool| assets::load_cloned::<comp::Item>(&tool).ok())
.map(|i| i.kind),
); );
// Draw the UI to the screen. // Draw the UI to the screen.

View File

@ -58,7 +58,7 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
&mut self, &mut self,
renderer: &mut Renderer, renderer: &mut Renderer,
body: Body, body: Body,
item_kind: Option<ItemKind>, item_kind: Option<&ItemKind>,
tick: u64, tick: u64,
camera_mode: CameraMode, camera_mode: CameraMode,
character_state: Option<&CharacterState>, character_state: Option<&CharacterState>,
@ -70,7 +70,7 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
let key = if item_kind.is_some() { let key = if item_kind.is_some() {
FigureKey::Complex( FigureKey::Complex(
body, body,
item_kind, item_kind.cloned(),
camera_mode, camera_mode,
character_state.map(|cs| CharacterStateCacheKey::from(cs)), character_state.map(|cs| CharacterStateCacheKey::from(cs)),
) )

View File

@ -534,7 +534,7 @@ impl HumArmorFootSpec {
} }
} }
pub fn mesh_main(item_kind: Option<ItemKind>) -> Mesh<FigurePipeline> { pub fn mesh_main(item_kind: Option<&ItemKind>) -> Mesh<FigurePipeline> {
if let Some(item_kind) = item_kind { if let Some(item_kind) = item_kind {
let (name, offset) = match item_kind { let (name, offset) = match item_kind {
ItemKind::Tool(ToolData { kind, .. }) => match kind { ItemKind::Tool(ToolData { kind, .. }) => match kind {

View File

@ -386,7 +386,7 @@ impl FigureMgr {
let active_item_kind = loadout let active_item_kind = loadout
.and_then(|l| l.active_item.as_ref()) .and_then(|l| l.active_item.as_ref())
.map(|i| i.item.kind); .map(|i| &i.item.kind);
let active_tool_kind = if let Some(ItemKind::Tool(tool)) = active_item_kind { let active_tool_kind = if let Some(ItemKind::Tool(tool)) = active_item_kind {
Some(tool.kind) Some(tool.kind)
} else { } else {
@ -1348,7 +1348,7 @@ impl FigureMgr {
}; };
let active_item_kind = loadout let active_item_kind = loadout
.and_then(|l| l.active_item.as_ref()) .and_then(|l| l.active_item.as_ref())
.map(|i| i.item.kind); .map(|i| &i.item.kind);
let character_state = if is_player { character_state } else { None }; let character_state = if is_player { character_state } else { None };
let FigureMgr { let FigureMgr {

View File

@ -208,7 +208,7 @@ impl Scene {
renderer: &mut Renderer, renderer: &mut Renderer,
tick: u64, tick: u64,
body: Option<humanoid::Body>, body: Option<humanoid::Body>,
active_item_kind: Option<ItemKind>, active_item_kind: Option<&ItemKind>,
) { ) {
renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals); renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals);