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 active_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 {

View File

@ -1,6 +1,6 @@
use crate::{
assets::{self, Asset},
comp::CharacterAbility,
comp::{body::humanoid, CharacterAbility},
effect::Effect,
terrain::{Block, BlockKind},
};
@ -80,22 +80,17 @@ pub enum DebugKind {
#[derive(Clone, Copy, 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.
Helmet,
Shoulders,
Chestplate,
Belt,
Gloves,
Pants,
Boots,
Back,
Tabard,
Gem,
Necklace,
Shoulder(humanoid::Shoulder),
Chest(humanoid::Chest),
Belt(humanoid::Belt),
Hand(humanoid::Hand),
Pants(humanoid::Pants),
Foot(humanoid::Foot),
}
#[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 {
Apple,
Cheese,
@ -124,13 +119,13 @@ pub struct ToolData {
// TODO: item specific abilities
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ItemKind {
/// Something wieldable
Tool(ToolData),
Armor {
kind: Armor,
power: u32,
stats: ArmorStats,
},
Consumable {
kind: Consumable,

View File

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

View File

@ -1,15 +1,12 @@
use super::utils::*;
use crate::{
comp::{CharacterState, StateUpdate, ToolData},
states::wielding,
comp::{CharacterState, StateUpdate},
sys::character_behavior::{CharacterBehavior, JoinData},
};
use std::{collections::VecDeque, time::Duration};
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct Data {
/// The weapon being equipped
pub tool: ToolData,
/// Time left before next state
pub time_left: Duration,
}
@ -40,7 +37,6 @@ impl CharacterBehavior for Data {
.time_left
.checked_sub(Duration::from_secs_f32(data.dt.0))
.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
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 {
tool,
time_left: tool.equip_time(),
});
} else {
@ -253,8 +252,8 @@ pub fn attempt_dodge_ability(data: &JoinData, update: &mut StateUpdate) {
}
}
pub fn unwrap_tool_data(data: &JoinData) -> Option<ToolData> {
if let Some(Tool(tool)) = data.loadout.active_item.as_ref().map(|i| i.item.kind) {
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) {
Some(tool)
} else {
None

View File

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

View File

@ -1,7 +1,7 @@
use crate::{sys, Server, StateExt};
use common::comp::{
self, Agent, Alignment, Body, Gravity, LightEmitter, Pos, Projectile, Scale, Stats, Vel,
WaypointArea,
self, Agent, Alignment, Body, Gravity, LightEmitter, Loadout, Pos, Projectile, Scale, Stats,
Vel, WaypointArea,
};
use specs::{Builder, Entity as EcsEntity, WorldExt};
use vek::{Rgb, Vec3};
@ -24,6 +24,7 @@ pub fn handle_create_npc(
server: &mut Server,
pos: Pos,
stats: Stats,
loadout: Loadout,
body: Body,
agent: Agent,
alignment: Alignment,
@ -31,7 +32,7 @@ pub fn handle_create_npc(
) {
server
.state
.create_npc(pos, stats, body)
.create_npc(pos, stats, loadout, body)
.with(agent)
.with(scale)
.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
.ecs()
.write_storage::<comp::Inventory>()
.get_mut(entity)
.and_then(|inv| inv.remove(slot));
.and_then(|inv| inv.remove(slot_idx));
let mut event = comp::InventoryUpdateEvent::Used;
if let Some(item) = item_opt {
match item.kind {
comp::ItemKind::Consumable { kind, effect } => {
event = comp::InventoryUpdateEvent::Consumed(kind);
state.apply_effect(entity, effect);
match &item.kind {
comp::ItemKind::Tool(tool) => {
if let Some(loadout) =
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::item::Utility::Collar => {
let reinsert = if let Some(pos) =
@ -155,7 +231,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
.ecs()
.write_storage::<comp::Inventory>()
.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()
.write_storage::<comp::Inventory>()
.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 {
pos,
stats,
loadout,
body,
agent,
alignment,
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::ClientDisconnect(entity) => {
frontend_events.push(handle_client_disconnect(self, entity))

View File

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

View File

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

View File

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

View File

@ -58,7 +58,7 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
&mut self,
renderer: &mut Renderer,
body: Body,
item_kind: Option<ItemKind>,
item_kind: Option<&ItemKind>,
tick: u64,
camera_mode: CameraMode,
character_state: Option<&CharacterState>,
@ -70,7 +70,7 @@ impl<Skel: Skeleton> FigureModelCache<Skel> {
let key = if item_kind.is_some() {
FigureKey::Complex(
body,
item_kind,
item_kind.cloned(),
camera_mode,
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 {
let (name, offset) = match item_kind {
ItemKind::Tool(ToolData { kind, .. }) => match kind {

View File

@ -386,7 +386,7 @@ impl FigureMgr {
let active_item_kind = loadout
.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 {
Some(tool.kind)
} else {
@ -1348,7 +1348,7 @@ impl FigureMgr {
};
let active_item_kind = loadout
.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 FigureMgr {

View File

@ -208,7 +208,7 @@ impl Scene {
renderer: &mut Renderer,
tick: u64,
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);