Remove Actor and use Body instead

This commit is contained in:
timokoesters 2019-06-30 13:48:28 +02:00
parent b1a7a61d77
commit 14400f6380
No known key found for this signature in database
GPG Key ID: CD80BE9AAEE78097
12 changed files with 235 additions and 258 deletions

View File

@ -4,18 +4,13 @@ pub mod quadruped_medium;
use specs::{Component, FlaggedStorage, VecStorage};
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Actor {
Character { name: String, body: Body },
}
impl Component for Actor {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Body {
Humanoid(humanoid::Body),
Quadruped(quadruped::Body),
QuadrupedMedium(quadruped_medium::Body),
}
impl Component for Body {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}

View File

@ -13,7 +13,7 @@ mod stats;
pub use action_state::ActionState;
pub use agent::Agent;
pub use animation::{Animation, AnimationInfo};
pub use body::{humanoid, quadruped, quadruped_medium, Actor, Body};
pub use body::{humanoid, quadruped, quadruped_medium, Body};
pub use controller::Controller;
pub use inputs::{Attacking, Gliding, Jumping, MoveDir, OnGround, Respawning, Rolling};
pub use inventory::{item, Inventory};

View File

@ -5,6 +5,7 @@ use specs::{Component, FlaggedStorage, VecStorage};
pub enum HealthSource {
Attack { by: Uid }, // TODO: Implement weapon
Suicide,
Revive,
Unknown,
}
@ -23,18 +24,20 @@ impl Health {
self.maximum
}
pub fn set_to(&mut self, amount: u32, cause: HealthSource) {
let amount = amount.min(self.maximum);
self.last_change = Some((amount as i32 - self.current as i32, 0.0, cause));
self.current = amount;
}
pub fn change_by(&mut self, amount: i32, cause: HealthSource) {
self.current = (self.current as i32 + amount).max(0) as u32;
self.current = ((self.current as i32 + amount).max(0) as u32).min(self.maximum);
self.last_change = Some((amount, 0.0, cause));
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Stats {
pub hp: Health,
pub name: String,
pub health: Health,
pub xp: u32,
pub is_dead: bool,
}
@ -42,14 +45,15 @@ pub struct Stats {
impl Stats {
pub fn should_die(&self) -> bool {
// TODO: Remove
self.hp.current == 0
self.health.current == 0
}
}
impl Default for Stats {
fn default() -> Self {
impl Stats {
pub fn new(name: String) -> Self {
Self {
hp: Health {
name,
health: Health {
current: 100,
maximum: 100,
last_change: None,

View File

@ -20,7 +20,7 @@ sphynx::sum_type! {
Pos(comp::Pos),
Vel(comp::Vel),
Ori(comp::Ori),
Actor(comp::Actor),
Body(comp::Body),
Player(comp::Player),
Stats(comp::Stats),
}
@ -33,7 +33,7 @@ sphynx::sum_type! {
Pos(PhantomData<comp::Pos>),
Vel(PhantomData<comp::Vel>),
Ori(PhantomData<comp::Ori>),
Actor(PhantomData<comp::Actor>),
Body(PhantomData<comp::Body>),
Player(PhantomData<comp::Player>),
Stats(PhantomData<comp::Stats>),
}

View File

@ -99,7 +99,7 @@ impl State {
// Create a new Sphynx ECS world.
fn setup_sphynx_world(ecs: &mut sphynx::World<EcsCompPacket, EcsResPacket>) {
// Register server->client synced components.
ecs.register_synced::<comp::Actor>();
ecs.register_synced::<comp::Body>();
ecs.register_synced::<comp::Player>();
ecs.register_synced::<comp::Stats>();

View File

@ -52,7 +52,9 @@ impl<'a> System<'a> for Sys {
&& ori.0.angle_between(pos_b.0 - pos.0).to_degrees() < 70.0
{
// Deal damage
stat_b.hp.change_by(-10, HealthSource::Attack { by: *uid }); // TODO: variable damage and weapon
stat_b
.health
.change_by(-10, HealthSource::Attack { by: *uid }); // TODO: variable damage and weapon
vel_b.0 += (pos_b.0 - pos.0).normalized() * 10.0;
vel_b.0.z = 15.0;
let _ = force_updates.insert(b, ForceUpdate);

View File

@ -22,7 +22,7 @@ impl<'a> System<'a> for Sys {
if let Err(err) = dyings.insert(
entity,
Dying {
cause: match stat.hp.last_change {
cause: match stat.health.last_change {
Some(change) => change.2,
None => {
warn!("Nothing caused an entity to die!");
@ -35,7 +35,7 @@ impl<'a> System<'a> for Sys {
}
stat.is_dead = true;
}
if let Some(change) = &mut stat.hp.last_change {
if let Some(change) = &mut stat.health.last_change {
change.1 += dt.0 as f64;
}
}

View File

@ -161,7 +161,7 @@ fn handle_kill(server: &mut Server, entity: EcsEntity, _args: String, _action: &
.ecs_mut()
.write_storage::<comp::Stats>()
.get_mut(entity)
.map(|s| s.hp.set_to(0, comp::HealthSource::Suicide));
.map(|s| s.health.set_to(0, comp::HealthSource::Suicide));
}
fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {

View File

@ -146,8 +146,8 @@ impl Server {
.with(comp::Vel(Vec3::zero()))
.with(comp::Ori(Vec3::unit_y()))
.with(comp::Controller::default())
.with(comp::Actor::Character { name, body })
.with(comp::Stats::default())
.with(body)
.with(comp::Stats::new(name))
.with(comp::ActionState::default())
.with(comp::ForceUpdate)
}
@ -161,8 +161,8 @@ impl Server {
) {
let spawn_point = state.ecs().read_resource::<SpawnPoint>().0;
state.write_component(entity, comp::Actor::Character { name, body });
state.write_component(entity, comp::Stats::default());
state.write_component(entity, body);
state.write_component(entity, comp::Stats::new(name));
state.write_component(entity, comp::Controller::default());
state.write_component(entity, comp::Pos(spawn_point));
state.write_component(entity, comp::Vel(Vec3::zero()));
@ -677,9 +677,7 @@ impl Server {
self.state.write_component(entity, comp::ForceUpdate);
client.force_state(ClientState::Dead);
} else {
if let Err(err) = self.state.ecs_mut().delete_entity_synced(entity) {
warn!("Failed to delete client not found in kill list: {:?}", err);
}
let _ = self.state.ecs_mut().delete_entity_synced(entity);
continue;
}
}
@ -696,14 +694,20 @@ impl Server {
for entity in todo_respawn {
if let Some(client) = self.clients.get_mut(&entity) {
client.allow_state(ClientState::Character);
self.state.write_component(entity, comp::Stats::default());
self.state
.ecs_mut()
.write_storage::<comp::Stats>()
.get_mut(entity)
.map(|stats| {
stats
.health
.set_to(stats.health.get_maximum(), comp::HealthSource::Revive)
});
self.state
.ecs_mut()
.write_storage::<comp::Pos>()
.get_mut(entity)
.map(|pos| pos.0.z += 100.0);
self.state.write_component(entity, comp::Vel(Vec3::zero()));
self.state.write_component(entity, comp::ForceUpdate);
}
}

View File

@ -314,7 +314,6 @@ impl Hud {
// Nametags and healthbars
if self.show.ingame {
let ecs = client.state().ecs();
let actor = ecs.read_storage::<comp::Actor>();
let pos = ecs.read_storage::<comp::Pos>();
let stats = ecs.read_storage::<comp::Stats>();
let player = ecs.read_storage::<comp::Player>();
@ -333,29 +332,25 @@ impl Hud {
let mut health_back_id_walker = self.ids.health_bar_backs.walk();
// Render Name Tags
for (pos, name) in (&entities, &pos, &actor, &stats, player.maybe())
for (pos, name) in (&entities, &pos, &stats, player.maybe())
.join()
.filter(|(entity, _, _, stats, _)| *entity != me && !stats.is_dead)
.filter(|(entity, _, stats, _)| *entity != me && !stats.is_dead)
// Don't process nametags outside the vd (visibility further limited by ui backend)
.filter(|(_, pos, _, _, _)| {
.filter(|(_, pos, _, _)| {
(pos.0 - player_pos)
.map2(TerrainChunkSize::SIZE, |d, sz| d.abs() as f32 / sz as f32)
.magnitude()
< view_distance as f32
})
.map(|(_, pos, actor, _, player)| match actor {
comp::Actor::Character {
name: char_name, ..
} => {
// Temporary
// If the player used the default character name display their name instead
let name = if char_name == "Character Name" {
player.map_or(char_name, |p| &p.alias)
} else {
char_name
};
(pos.0, name)
}
.map(|(_, pos, stats, player)| {
// TODO: This is temporary
// If the player used the default character name display their name instead
let name = if stats.name == "Character Name" {
player.map_or(&stats.name, |p| &p.alias)
} else {
&stats.name
};
(pos.0, name)
})
{
let id = name_id_walker.next(
@ -377,7 +372,7 @@ impl Hud {
.filter(|(entity, _, stats)| {
*entity != me
&& !stats.is_dead
&& stats.hp.get_current() != stats.hp.get_maximum()
&& stats.health.get_current() != stats.health.get_maximum()
})
// Don't process health bars outside the vd (visibility further limited by ui backend)
.filter(|(_, pos, _)| {
@ -405,7 +400,9 @@ impl Hud {
// % HP Filling
Rectangle::fill_with(
[
120.0 * (stats.hp.get_current() as f64 / stats.hp.get_maximum() as f64),
120.0
* (stats.health.get_current() as f64
/ stats.health.get_maximum() as f64),
8.0,
],
HP_COLOR,
@ -545,14 +542,14 @@ impl Hud {
// Skillbar
// Get player stats
let stats = client
if let Some(stats) = client
.state()
.ecs()
.read_storage::<comp::Stats>()
.get(client.entity())
.map(|&s| s)
.unwrap_or_default();
Skillbar::new(&self.imgs, &self.fonts, stats).set(self.ids.skillbar, ui_widgets);
{
Skillbar::new(&self.imgs, &self.fonts, stats).set(self.ids.skillbar, ui_widgets);
}
// Chat box
match Chat::new(&mut self.new_messages, &self.imgs, &self.fonts)

View File

@ -31,14 +31,14 @@ pub struct Skillbar<'a> {
imgs: &'a Imgs,
fonts: &'a Fonts,
stats: Stats,
stats: &'a Stats,
#[conrod(common_builder)]
common: widget::CommonBuilder,
}
impl<'a> Skillbar<'a> {
pub fn new(imgs: &'a Imgs, fonts: &'a Fonts, stats: Stats) -> Self {
pub fn new(imgs: &'a Imgs, fonts: &'a Fonts, stats: &'a Stats) -> Self {
Self {
imgs,
fonts,
@ -78,7 +78,8 @@ impl<'a> Widget for Skillbar<'a> {
let next_level_xp = (level as f64).powi(4) - start_level_xp;
// TODO: We need a max xp value
let xp_percentage = (self.stats.xp as f64 - start_level_xp) / next_level_xp;
let hp_percentage = self.stats.hp.get_current() as f64 / self.stats.hp.get_maximum() as f64;
let hp_percentage =
self.stats.health.get_current() as f64 / self.stats.health.get_maximum() as f64;
let mana_percentage = 1.0;
// TODO: Only show while aiming with a bow or when casting a spell.

View File

@ -551,12 +551,12 @@ impl FigureMgr {
.get(client.entity())
.map_or(Vec3::zero(), |pos| pos.0);
for (entity, pos, vel, ori, actor, animation_info, stats) in (
for (entity, pos, vel, ori, body, animation_info, stats) in (
&ecs.entities(),
&ecs.read_storage::<comp::Pos>(),
&ecs.read_storage::<comp::Vel>(),
&ecs.read_storage::<comp::Ori>(),
&ecs.read_storage::<comp::Actor>(),
&ecs.read_storage::<comp::Body>(),
&ecs.read_storage::<comp::AnimationInfo>(),
ecs.read_storage::<comp::Stats>().maybe(),
)
@ -569,18 +569,16 @@ impl FigureMgr {
/ view_distance as f32;
// Keep from re-adding/removing entities on the border of the vd
if vd_frac > 1.2 {
match actor {
comp::Actor::Character { body, .. } => match body {
Body::Humanoid(_) => {
self.character_states.remove(&entity);
}
Body::Quadruped(_) => {
self.quadruped_states.remove(&entity);
}
Body::QuadrupedMedium(_) => {
self.quadruped_medium_states.remove(&entity);
}
},
match body {
Body::Humanoid(_) => {
self.character_states.remove(&entity);
}
Body::Quadruped(_) => {
self.quadruped_states.remove(&entity);
}
Body::QuadrupedMedium(_) => {
self.quadruped_medium_states.remove(&entity);
}
}
continue;
} else if vd_frac > 1.0 {
@ -589,7 +587,7 @@ impl FigureMgr {
// Change in health as color!
let col = stats
.and_then(|stats| stats.hp.last_change)
.and_then(|stats| stats.health.last_change)
.map(|(_, time, _)| {
Rgba::broadcast(1.0)
+ Rgba::new(0.0, -1.0, -1.0, 0.0)
@ -597,170 +595,150 @@ impl FigureMgr {
})
.unwrap_or(Rgba::broadcast(1.0));
match actor {
comp::Actor::Character { body, .. } => {
let skeleton_attr = &self
.model_cache
.get_or_create_model(renderer, *body, tick)
.1;
let skeleton_attr = &self
.model_cache
.get_or_create_model(renderer, *body, tick)
.1;
match body {
Body::Humanoid(_) => {
let state = self.character_states.entry(entity).or_insert_with(|| {
FigureState::new(renderer, CharacterSkeleton::new())
});
match body {
Body::Humanoid(_) => {
let state = self
.character_states
.entry(entity)
.or_insert_with(|| FigureState::new(renderer, CharacterSkeleton::new()));
let target_skeleton = match animation_info.animation {
comp::Animation::Idle => {
anim::character::IdleAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
skeleton_attr,
)
}
comp::Animation::Run => {
anim::character::RunAnimation::update_skeleton(
state.skeleton_mut(),
(vel.0.magnitude(), time),
animation_info.time,
skeleton_attr,
)
}
comp::Animation::Jump => {
anim::character::JumpAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
skeleton_attr,
)
}
comp::Animation::Attack => {
anim::character::AttackAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
skeleton_attr,
)
}
comp::Animation::Roll => {
anim::character::RollAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
skeleton_attr,
)
}
comp::Animation::Crun => {
anim::character::CrunAnimation::update_skeleton(
state.skeleton_mut(),
(vel.0.magnitude(), time),
animation_info.time,
skeleton_attr,
)
}
comp::Animation::Cidle => {
anim::character::CidleAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
skeleton_attr,
)
}
comp::Animation::Gliding => {
anim::character::GlidingAnimation::update_skeleton(
state.skeleton_mut(),
(vel.0.magnitude(), time),
animation_info.time,
skeleton_attr,
)
}
};
state.skeleton.interpolate(&target_skeleton, dt);
state.update(renderer, pos.0, ori.0, col, dt);
let target_skeleton = match animation_info.animation {
comp::Animation::Idle => anim::character::IdleAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
skeleton_attr,
),
comp::Animation::Run => anim::character::RunAnimation::update_skeleton(
state.skeleton_mut(),
(vel.0.magnitude(), time),
animation_info.time,
skeleton_attr,
),
comp::Animation::Jump => anim::character::JumpAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
skeleton_attr,
),
comp::Animation::Attack => {
anim::character::AttackAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
skeleton_attr,
)
}
Body::Quadruped(_) => {
let state = self.quadruped_states.entry(entity).or_insert_with(|| {
FigureState::new(renderer, QuadrupedSkeleton::new())
});
let target_skeleton = match animation_info.animation {
comp::Animation::Run => {
anim::quadruped::RunAnimation::update_skeleton(
state.skeleton_mut(),
(vel.0.magnitude(), time),
animation_info.time,
skeleton_attr,
)
}
comp::Animation::Idle => {
anim::quadruped::IdleAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
skeleton_attr,
)
}
comp::Animation::Jump => {
anim::quadruped::JumpAnimation::update_skeleton(
state.skeleton_mut(),
(vel.0.magnitude(), time),
animation_info.time,
skeleton_attr,
)
}
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_skeleton, dt);
state.update(renderer, pos.0, ori.0, col, dt);
comp::Animation::Roll => anim::character::RollAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
skeleton_attr,
),
comp::Animation::Crun => anim::character::CrunAnimation::update_skeleton(
state.skeleton_mut(),
(vel.0.magnitude(), time),
animation_info.time,
skeleton_attr,
),
comp::Animation::Cidle => anim::character::CidleAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
skeleton_attr,
),
comp::Animation::Gliding => {
anim::character::GlidingAnimation::update_skeleton(
state.skeleton_mut(),
(vel.0.magnitude(), time),
animation_info.time,
skeleton_attr,
)
}
Body::QuadrupedMedium(_) => {
let state =
self.quadruped_medium_states
.entry(entity)
.or_insert_with(|| {
FigureState::new(renderer, QuadrupedMediumSkeleton::new())
});
};
let target_skeleton = match animation_info.animation {
comp::Animation::Run => {
anim::quadrupedmedium::RunAnimation::update_skeleton(
state.skeleton_mut(),
(vel.0.magnitude(), time),
animation_info.time,
skeleton_attr,
)
}
comp::Animation::Idle => {
anim::quadrupedmedium::IdleAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
skeleton_attr,
)
}
comp::Animation::Jump => {
anim::quadrupedmedium::JumpAnimation::update_skeleton(
state.skeleton_mut(),
(vel.0.magnitude(), time),
animation_info.time,
skeleton_attr,
)
}
state.skeleton.interpolate(&target_skeleton, dt);
state.update(renderer, pos.0, ori.0, col, dt);
}
Body::Quadruped(_) => {
let state = self
.quadruped_states
.entry(entity)
.or_insert_with(|| FigureState::new(renderer, QuadrupedSkeleton::new()));
// TODO!
_ => state.skeleton_mut().clone(),
};
let target_skeleton = match animation_info.animation {
comp::Animation::Run => anim::quadruped::RunAnimation::update_skeleton(
state.skeleton_mut(),
(vel.0.magnitude(), time),
animation_info.time,
skeleton_attr,
),
comp::Animation::Idle => anim::quadruped::IdleAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
skeleton_attr,
),
comp::Animation::Jump => anim::quadruped::JumpAnimation::update_skeleton(
state.skeleton_mut(),
(vel.0.magnitude(), time),
animation_info.time,
skeleton_attr,
),
state.skeleton.interpolate(&target_skeleton, dt);
state.update(renderer, pos.0, ori.0, col, dt);
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_skeleton, dt);
state.update(renderer, pos.0, ori.0, col, dt);
}
Body::QuadrupedMedium(_) => {
let state = self
.quadruped_medium_states
.entry(entity)
.or_insert_with(|| {
FigureState::new(renderer, QuadrupedMediumSkeleton::new())
});
let target_skeleton = match animation_info.animation {
comp::Animation::Run => {
anim::quadrupedmedium::RunAnimation::update_skeleton(
state.skeleton_mut(),
(vel.0.magnitude(), time),
animation_info.time,
skeleton_attr,
)
}
}
} // TODO: Non-character actors
comp::Animation::Idle => {
anim::quadrupedmedium::IdleAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
skeleton_attr,
)
}
comp::Animation::Jump => {
anim::quadrupedmedium::JumpAnimation::update_skeleton(
state.skeleton_mut(),
(vel.0.magnitude(), time),
animation_info.time,
skeleton_attr,
)
}
// TODO!
_ => state.skeleton_mut().clone(),
};
state.skeleton.interpolate(&target_skeleton, dt);
state.update(renderer, pos.0, ori.0, col, dt);
}
}
}
@ -791,12 +769,12 @@ impl FigureMgr {
.get(client.entity())
.map_or(Vec3::zero(), |pos| pos.0);
for (entity, _, _, _, actor, _, _) in (
for (entity, _, _, _, body, _, _) in (
&ecs.entities(),
&ecs.read_storage::<comp::Pos>(),
&ecs.read_storage::<comp::Vel>(),
&ecs.read_storage::<comp::Ori>(),
&ecs.read_storage::<comp::Actor>(),
&ecs.read_storage::<comp::Body>(),
&ecs.read_storage::<comp::AnimationInfo>(),
ecs.read_storage::<comp::Stats>().maybe(),
)
@ -811,32 +789,28 @@ impl FigureMgr {
// Don't render dead entities
.filter(|(_, _, _, _, _, _, stats)| stats.map_or(true, |s| !s.is_dead))
{
match actor {
comp::Actor::Character { body, .. } => {
if let Some((locals, bone_consts)) = match body {
Body::Humanoid(_) => self
.character_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
Body::Quadruped(_) => self
.quadruped_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
Body::QuadrupedMedium(_) => self
.quadruped_medium_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
} {
let model = &self
.model_cache
.get_or_create_model(renderer, *body, tick)
.0;
if let Some((locals, bone_consts)) = match body {
Body::Humanoid(_) => self
.character_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
Body::Quadruped(_) => self
.quadruped_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
Body::QuadrupedMedium(_) => self
.quadruped_medium_states
.get(&entity)
.map(|state| (state.locals(), state.bone_consts())),
} {
let model = &self
.model_cache
.get_or_create_model(renderer, *body, tick)
.0;
renderer.render_figure(model, globals, locals, bone_consts);
} else {
warn!("Body has no saved figure");
}
}
renderer.render_figure(model, globals, locals, bone_consts);
} else {
warn!("Body has no saved figure");
}
}
}