Use comp::Stats to store Equipment, make char weapon selection work

This commit is contained in:
timokoesters 2019-08-27 21:56:46 +02:00
parent b53c882dc7
commit 155605841b
No known key found for this signature in database
GPG Key ID: CD80BE9AAEE78097
11 changed files with 138 additions and 54 deletions

View File

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

View File

@ -1,7 +1,7 @@
use specs::{Component, FlaggedStorage};
use specs_idvs::IDVStorage;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Tool {
Daggers,
SwordShield,
@ -36,7 +36,7 @@ pub const ALL_TOOLS: [Tool; 7] = [
Tool::Staff,
];
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[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 creepy thing to do.
Helmet,
@ -76,7 +76,7 @@ pub enum ConsumptionEffect {
Xp(i32),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Item {
Tool {
kind: Tool,

View File

@ -24,5 +24,5 @@ pub use inventory::{item, Inventory, InventoryUpdate, Item};
pub use last::Last;
pub use phys::{ForceUpdate, Ori, PhysicsState, Pos, Scale, Vel};
pub use player::Player;
pub use stats::{Exp, HealthSource, Level, Stats};
pub use stats::{Equipment, Exp, HealthSource, Level, Stats};
pub use visual::LightEmitter;

View File

@ -1,4 +1,4 @@
use crate::state::Uid;
use crate::{comp, state::Uid};
use specs::{Component, FlaggedStorage};
use specs_idvs::IDVStorage;
@ -44,6 +44,13 @@ pub struct Level {
amount: u32,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct Equipment {
pub main: Option<comp::Item>,
pub alt: Option<comp::Item>,
// TODO: Armor
}
impl Health {
pub fn current(&self) -> u32 {
self.current
@ -145,12 +152,12 @@ pub struct Stats {
pub energy: Energy,
pub level: Level,
pub exp: Exp,
pub equipment: Equipment,
pub is_dead: bool,
}
impl Stats {
pub fn should_die(&self) -> bool {
// TODO: Remove
self.health.current == 0
}
pub fn revive(&mut self) {
@ -161,7 +168,7 @@ impl Stats {
}
impl Stats {
pub fn new(name: String) -> Self {
pub fn new(name: String, main: Option<comp::Item>) -> Self {
Self {
name,
health: Health {
@ -179,6 +186,10 @@ impl Stats {
maximum: 200,
last_change: None,
},
equipment: Equipment {
main: main,
alt: None,
},
is_dead: false,
}
}

View File

@ -12,6 +12,7 @@ pub enum ClientMsg {
Character {
name: String,
body: comp::Body,
main: Option<comp::item::Tool>,
},
Controller(comp::Controller),
RequestState(ClientState),

View File

@ -411,7 +411,7 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C
let body = kind_to_body(id);
server
.create_npc(pos, comp::Stats::new(get_npc_name(id)), body)
.create_npc(pos, comp::Stats::new(get_npc_name(id), None), body)
.with(comp::Vel(vel))
.with(agent)
.build();

View File

@ -200,12 +200,13 @@ impl Server {
client: &mut Client,
name: String,
body: comp::Body,
main: Option<comp::Item>,
server_settings: &ServerSettings,
) {
let spawn_point = state.ecs().read_resource::<SpawnPoint>().0;
state.write_component(entity, body);
state.write_component(entity, comp::Stats::new(name));
state.write_component(entity, comp::Stats::new(name, main));
state.write_component(entity, comp::Controller::default());
state.write_component(entity, comp::Pos(spawn_point));
state.write_component(entity, comp::Vel(Vec3::zero()));
@ -433,11 +434,17 @@ impl Server {
// Handle chunk supplement
for npc in supplement.npcs {
let (mut stats, mut body) = if rand::random() {
let stats = comp::Stats::new("Humanoid".to_string());
let stats = comp::Stats::new(
"Humanoid".to_string(),
Some(comp::Item::Tool {
kind: comp::item::Tool::Sword,
power: 10,
}),
);
let body = comp::Body::Humanoid(comp::humanoid::Body::random());
(stats, body)
} else {
let stats = comp::Stats::new("Wolf".to_string());
let stats = comp::Stats::new("Wolf".to_string(), None);
let body = comp::Body::QuadrupedMedium(comp::quadruped_medium::Body::random());
(stats, body)
};
@ -445,7 +452,13 @@ impl Server {
if npc.boss {
if rand::random::<f32>() < 0.8 {
stats = comp::Stats::new("Humanoid".to_string());
stats = comp::Stats::new(
"Humanoid".to_string(),
Some(comp::Item::Tool {
kind: comp::item::Tool::Sword,
power: 10,
}),
);
body = comp::Body::Humanoid(comp::humanoid::Body::random());
}
stats = stats.with_max_health(500 + rand::random::<u32>() % 400);
@ -740,7 +753,7 @@ impl Server {
item_entity.and_then(|item_entity| {
ecs.write_storage::<comp::Item>()
.get_mut(item_entity)
.map(|item| (*item, item_entity))
.map(|item| (item.clone(), item_entity))
}),
ecs.write_storage::<comp::Inventory>().get_mut(entity),
) {
@ -759,7 +772,7 @@ impl Server {
state.write_component(entity, comp::InventoryUpdate);
}
ClientMsg::Character { name, body } => match client.client_state {
ClientMsg::Character { name, body, main } => match client.client_state {
// Become Registered first.
ClientState::Connected => {
client.error_state(RequestStateError::Impossible)
@ -773,6 +786,7 @@ impl Server {
client,
name,
body,
main.map(|t| comp::Item::Tool { kind: t, power: 10 }),
&server_settings,
);
if let Some(player) =

View File

@ -65,6 +65,7 @@ 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,
);
return PlayStateResult::Push(Box::new(SessionState::new(
global_state,
@ -89,6 +90,17 @@ impl PlayState for CharSelectionState {
global_state.window.renderer_mut(),
&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,
})
} else {
None
},
alt: None,
},
);
// Draw the UI to the screen.

View File

@ -15,7 +15,7 @@ use crate::{
};
use client::Client;
use common::{
comp::{humanoid, Body},
comp::{humanoid, Body, Equipment},
state::DeltaTime,
terrain::BlockKind,
};
@ -132,12 +132,24 @@ impl Scene {
);
}
pub fn render(&mut self, renderer: &mut Renderer, client: &Client, body: humanoid::Body) {
pub fn render(
&mut self,
renderer: &mut Renderer,
client: &Client,
body: humanoid::Body,
equipment: &Equipment,
) {
renderer.render_skybox(&self.skybox.model, &self.globals, &self.skybox.locals);
let model = &self
.figure_model_cache
.get_or_create_model(renderer, Body::Humanoid(body), client.get_tick())
.get_or_create_model(
renderer,
&client.entity(),
Body::Humanoid(body),
Some(equipment),
client.get_tick(),
)
.0;
renderer.render_figure(

View File

@ -209,7 +209,7 @@ pub struct CharSelectionUi {
character_creation: bool,
pub character_name: String,
pub character_body: humanoid::Body,
pub character_weapon: Tool, // TODO: Move into ecs inventory struct?
pub character_tool: Option<Tool>,
}
impl CharSelectionUi {
@ -235,7 +235,7 @@ impl CharSelectionUi {
character_creation: false,
character_name: "Character Name".to_string(),
character_body: humanoid::Body::random(),
character_weapon: Tool::Sword,
character_tool: Some(Tool::Sword),
}
}
@ -344,7 +344,7 @@ impl CharSelectionUi {
.was_clicked()
{
self.character_creation = true;
self.character_weapon = Tool::Sword;
self.character_tool = Some(Tool::Sword);
}
// Alpha Version
@ -710,7 +710,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 Tool::Hammer = self.character_weapon {
if Button::image(if let Some(Tool::Hammer) = self.character_tool {
self.imgs.icon_border_pressed
} else {
self.imgs.icon_border
@ -721,7 +721,7 @@ impl CharSelectionUi {
.set(self.ids.hammer_button, ui_widgets)
.was_clicked()
{
self.character_weapon = Tool::Hammer;
self.character_tool = Some(Tool::Hammer);
}
// REMOVE THIS AFTER IMPLEMENTATION
/*Rectangle::fill_with([67.0, 67.0], color::rgba(0.0, 0.0, 0.0, 0.8))
@ -734,7 +734,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 Tool::Bow = self.character_weapon {
if Button::image(if let Some(Tool::Bow) = self.character_tool {
self.imgs.icon_border_pressed
} else {
self.imgs.icon_border
@ -745,7 +745,7 @@ impl CharSelectionUi {
.set(self.ids.bow_button, ui_widgets)
.was_clicked()
{
//self.character_weapon = Tool::Bow;
//self.character_tool = Some(Tool::Bow);
}
// REMOVE THIS AFTER IMPLEMENTATION
Rectangle::fill_with([67.0, 67.0], color::rgba(0.0, 0.0, 0.0, 0.8))
@ -756,7 +756,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 Tool::Staff = self.character_weapon {
if Button::image(if let Some(Tool::Staff) = self.character_tool {
self.imgs.icon_border_pressed
} else {
self.imgs.icon_border
@ -767,7 +767,7 @@ impl CharSelectionUi {
.set(self.ids.staff_button, ui_widgets)
.was_clicked()
{
//self.character_weapon = Tool::Staff;
//self.character_tool = Some(Tool::Staff);
}
// REMOVE THIS AFTER IMPLEMENTATION
Rectangle::fill_with([67.0, 67.0], color::rgba(0.0, 0.0, 0.0, 0.8))
@ -778,7 +778,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 Tool::Sword = self.character_weapon {
if Button::image(if let Some(Tool::Sword) = self.character_tool {
self.imgs.icon_border_pressed
} else {
self.imgs.icon_border
@ -789,7 +789,7 @@ impl CharSelectionUi {
.set(self.ids.sword_button, ui_widgets)
.was_clicked()
{
self.character_weapon = Tool::Sword;
self.character_tool = Some(Tool::Sword);
}
// Daggers
@ -797,7 +797,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 Tool::Daggers = self.character_weapon {
if Button::image(if let Some(Tool::Daggers) = self.character_tool {
self.imgs.icon_border_pressed
} else {
self.imgs.icon_border
@ -808,7 +808,7 @@ impl CharSelectionUi {
.set(self.ids.daggers_button, ui_widgets)
.was_clicked()
{
// self.character_weapon = Tool::Daggers;
// self.character_tool = Some(Tool::Daggers);
} // 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)
@ -819,7 +819,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 Tool::Axe = self.character_weapon {
if Button::image(if let Some(Tool::Axe) = self.character_tool {
self.imgs.icon_border_pressed
} else {
self.imgs.icon_border
@ -830,7 +830,7 @@ impl CharSelectionUi {
.set(self.ids.axe_button, ui_widgets)
.was_clicked()
{
self.character_weapon = Tool::Axe;
self.character_tool = Some(Tool::Axe);
}
// REMOVE THIS AFTER IMPLEMENTATION
/*Rectangle::fill_with([67.0, 67.0], color::rgba(0.0, 0.0, 0.0, 0.8))

View File

@ -12,7 +12,9 @@ use crate::{
use client::Client;
use common::{
assets,
comp::{self, humanoid, item::Tool, object, quadruped, quadruped_medium, Body},
comp::{
self, humanoid, item::Tool, object, quadruped, quadruped_medium, Body, Equipment, Item,
},
figure::Segment,
terrain::TerrainChunkSize,
vol::VolSize,
@ -26,8 +28,14 @@ use vek::*;
const DAMAGE_FADE_COEFFICIENT: f64 = 5.0;
#[derive(PartialEq, Eq, Hash, Clone)]
enum FigureKey {
Simple(Body),
Complex(Body, Option<Equipment>),
}
pub struct FigureModelCache {
models: HashMap<Body, ((Model<FigurePipeline>, SkeletonAttr), u64)>,
models: HashMap<FigureKey, ((Model<FigurePipeline>, SkeletonAttr), u64)>,
}
impl FigureModelCache {
@ -40,16 +48,24 @@ impl FigureModelCache {
pub fn get_or_create_model(
&mut self,
renderer: &mut Renderer,
entity: &EcsEntity,
body: Body,
equipment: Option<&Equipment>,
tick: u64,
) -> &(Model<FigurePipeline>, SkeletonAttr) {
match self.models.get_mut(&body) {
let key = if equipment.is_some() {
FigureKey::Complex(body, equipment.cloned())
} else {
FigureKey::Simple(body)
};
match self.models.get_mut(&key) {
Some((_model, last_used)) => {
*last_used = tick;
}
None => {
self.models.insert(
body,
key.clone(),
(
{
let bone_meshes = match body {
@ -62,7 +78,7 @@ impl FigureModelCache {
Some(Self::load_right_hand(body.hand)),
Some(Self::load_left_foot(body.foot)),
Some(Self::load_right_foot(body.foot)),
Some(Self::load_weapon(Tool::Hammer)), // TODO: Inventory
Some(Self::load_main(equipment.and_then(|e| e.main.as_ref()))),
Some(Self::load_left_shoulder(body.shoulder)),
Some(Self::load_right_shoulder(body.shoulder)),
Some(Self::load_draw()),
@ -151,7 +167,7 @@ impl FigureModelCache {
}
}
&self.models[&body].0
&self.models[&key].0
}
pub fn clean(&mut self, tick: u64) {
@ -293,17 +309,24 @@ impl FigureModelCache {
)
}
fn load_weapon(weapon: Tool) -> Mesh<FigurePipeline> {
let (name, offset) = match weapon {
Tool::Sword => ("weapon.sword.rusty_2h", Vec3::new(-1.5, -6.5, -4.0)),
Tool::Axe => ("weapon.axe.rusty_2h", Vec3::new(-1.5, -6.5, -4.0)),
Tool::Hammer => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)),
Tool::Daggers => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)),
Tool::SwordShield => ("weapon.axe.rusty_2h", Vec3::new(-2.5, -6.5, -2.0)),
Tool::Bow => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)),
Tool::Staff => ("weapon.axe.rusty_2h", Vec3::new(-2.5, -6.5, -2.0)),
};
Self::load_mesh(name, offset)
fn load_main(item: Option<&Item>) -> Mesh<FigurePipeline> {
if let Some(item) = item {
let (name, offset) = match item {
Item::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, -6.5, -4.0)),
Tool::Hammer => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)),
Tool::Daggers => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)),
Tool::SwordShield => ("weapon.axe.rusty_2h", Vec3::new(-2.5, -6.5, -2.0)),
Tool::Bow => ("weapon.hammer.rusty_2h", Vec3::new(-2.5, -5.5, -4.0)),
Tool::Staff => ("weapon.axe.rusty_2h", Vec3::new(-2.5, -6.5, -2.0)),
},
_ => ("figure.empty", Vec3::default()),
};
Self::load_mesh(name, offset)
} else {
Self::load_mesh("figure.empty", Vec3::default())
}
}
fn load_left_shoulder(shoulder: humanoid::Shoulder) -> Mesh<FigurePipeline> {
@ -651,7 +674,7 @@ impl FigureMgr {
let skeleton_attr = &self
.model_cache
.get_or_create_model(renderer, *body, tick)
.get_or_create_model(renderer, &entity, *body, stats.map(|s| &s.equipment), tick)
.1;
match body {
@ -862,7 +885,7 @@ impl FigureMgr {
let frustum = camera.frustum(client);
for (entity, _, _, _, body, _, _) in (
for (entity, _, _, _, body, stats, _) in (
&ecs.entities(),
&ecs.read_storage::<comp::Pos>(),
&ecs.read_storage::<comp::Vel>(),
@ -904,7 +927,13 @@ impl FigureMgr {
} {
let model = &self
.model_cache
.get_or_create_model(renderer, *body, tick)
.get_or_create_model(
renderer,
&entity,
*body,
stats.map(|s| &s.equipment),
tick,
)
.0;
// Don't render the player's body while in first person mode