mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Move from state components to single CharaterState struct
This makes split animations easy and improves overall code quality
This commit is contained in:
@ -37,6 +37,7 @@ use vek::*;
|
||||
use world::{ChunkSupplement, World};
|
||||
|
||||
const CLIENT_TIMEOUT: f64 = 20.0; // Seconds
|
||||
const HUMANOID_JUMP_ACCEL: f32 = 18.0;
|
||||
|
||||
pub enum Event {
|
||||
ClientConnected {
|
||||
@ -153,8 +154,7 @@ impl Server {
|
||||
.with(comp::Controller::default())
|
||||
.with(body)
|
||||
.with(comp::Stats::new(name))
|
||||
.with(comp::ActionState::default())
|
||||
.with(comp::ForceUpdate)
|
||||
.with(comp::CharacterState::default())
|
||||
}
|
||||
|
||||
/// Build a static object entity
|
||||
@ -175,8 +175,7 @@ impl Server {
|
||||
..comp::LightEmitter::default()
|
||||
})
|
||||
//.with(comp::LightEmitter::default())
|
||||
.with(comp::ActionState::default())
|
||||
.with(comp::ForceUpdate)
|
||||
.with(comp::CharacterState::default())
|
||||
}
|
||||
|
||||
pub fn create_player_character(
|
||||
@ -195,7 +194,7 @@ impl Server {
|
||||
state.write_component(entity, comp::Pos(spawn_point));
|
||||
state.write_component(entity, comp::Vel(Vec3::zero()));
|
||||
state.write_component(entity, comp::Ori(Vec3::unit_y()));
|
||||
state.write_component(entity, comp::ActionState::default());
|
||||
state.write_component(entity, comp::CharacterState::default());
|
||||
state.write_component(entity, comp::Inventory::default());
|
||||
state.write_component(entity, comp::InventoryUpdate);
|
||||
// Make sure physics are accepted.
|
||||
@ -221,6 +220,9 @@ impl Server {
|
||||
let terrain = self.state.ecs().read_resource::<TerrainMap>();
|
||||
let mut block_change = self.state.ecs().write_resource::<BlockChange>();
|
||||
let mut stats = self.state.ecs().write_storage::<comp::Stats>();
|
||||
let mut positions = self.state.ecs().write_storage::<comp::Pos>();
|
||||
let mut velocities = self.state.ecs().write_storage::<comp::Vel>();
|
||||
let mut force_updates = self.state.ecs().write_storage::<comp::ForceUpdate>();
|
||||
|
||||
for event in self.state.ecs().read_resource::<EventBus>().recv_all() {
|
||||
match event {
|
||||
@ -250,6 +252,74 @@ impl Server {
|
||||
.cast();
|
||||
}
|
||||
}
|
||||
GameEvent::Jump(entity) => {
|
||||
if let Some(vel) = velocities.get_mut(entity) {
|
||||
vel.0.z = HUMANOID_JUMP_ACCEL;
|
||||
let _ = force_updates.insert(entity, comp::ForceUpdate);
|
||||
}
|
||||
}
|
||||
GameEvent::Die { entity, cause } => {
|
||||
// Chat message
|
||||
if let Some(player) =
|
||||
self.state.ecs().read_storage::<comp::Player>().get(entity)
|
||||
{
|
||||
let msg = if let comp::HealthSource::Attack { by } = cause {
|
||||
self.state()
|
||||
.ecs()
|
||||
.entity_from_uid(by.into())
|
||||
.and_then(|attacker| {
|
||||
self.state
|
||||
.ecs()
|
||||
.read_storage::<comp::Player>()
|
||||
.get(attacker)
|
||||
.map(|attacker_alias| {
|
||||
format!(
|
||||
"{} was killed by {}",
|
||||
&player.alias, &attacker_alias.alias
|
||||
)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.unwrap_or(format!("{} died", &player.alias));
|
||||
|
||||
self.clients.notify_registered(ServerMsg::kill(msg));
|
||||
}
|
||||
|
||||
// Give EXP to the client
|
||||
if let Some(entity_stats) = stats.get(entity).cloned() {
|
||||
if let comp::HealthSource::Attack { by } = cause {
|
||||
self.state.ecs().entity_from_uid(by.into()).map(|attacker| {
|
||||
if let Some(attacker_stats) = stats.get_mut(attacker) {
|
||||
// TODO: Discuss whether we should give EXP by Player Killing or not.
|
||||
attacker_stats.exp.change_by(
|
||||
entity_stats.health.maximum() as f64 / 10.0
|
||||
+ entity_stats.level.level() as f64 * 10.0,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(client) = self.clients.get_mut(&entity) {
|
||||
let _ = velocities.insert(entity, comp::Vel(Vec3::zero()));
|
||||
let _ = force_updates.insert(entity, comp::ForceUpdate);
|
||||
client.force_state(ClientState::Dead);
|
||||
} else {
|
||||
//let _ = self.state.ecs_mut().delete_entity_synced(entity);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
GameEvent::Respawn(entity) => {
|
||||
// Only clients can respawn
|
||||
if let Some(client) = self.clients.get_mut(&entity) {
|
||||
client.allow_state(ClientState::Character);
|
||||
stats.get_mut(entity).map(|stats| stats.revive());
|
||||
positions.get_mut(entity).map(|pos| pos.0.z += 20.0);
|
||||
force_updates.insert(entity, comp::ForceUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -357,7 +427,7 @@ impl Server {
|
||||
.with(comp::Controller::default())
|
||||
.with(body)
|
||||
.with(stats)
|
||||
.with(comp::ActionState::default())
|
||||
.with(comp::CharacterState::default())
|
||||
.with(comp::Agent::enemy())
|
||||
.with(comp::Scale(scale))
|
||||
.build();
|
||||
@ -475,13 +545,6 @@ impl Server {
|
||||
|
||||
// 7) Finish the tick, pass control back to the frontend.
|
||||
|
||||
// Cleanup
|
||||
let ecs = self.state.ecs_mut();
|
||||
for entity in ecs.entities().join() {
|
||||
ecs.write_storage::<comp::Dying>().remove(entity);
|
||||
ecs.write_storage::<comp::Respawning>().remove(entity);
|
||||
}
|
||||
|
||||
Ok(frontend_events)
|
||||
}
|
||||
|
||||
@ -885,12 +948,12 @@ impl Server {
|
||||
state.write_component(entity, player);
|
||||
|
||||
// Sync physics of all entities
|
||||
for (&uid, &pos, vel, ori, action_state) in (
|
||||
for (&uid, &pos, vel, ori, character_state) in (
|
||||
&state.ecs().read_storage::<Uid>(),
|
||||
&state.ecs().read_storage::<comp::Pos>(), // We assume all these entities have a position
|
||||
state.ecs().read_storage::<comp::Vel>().maybe(),
|
||||
state.ecs().read_storage::<comp::Ori>().maybe(),
|
||||
state.ecs().read_storage::<comp::ActionState>().maybe(),
|
||||
state.ecs().read_storage::<comp::CharacterState>().maybe(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
@ -910,10 +973,10 @@ impl Server {
|
||||
ori,
|
||||
});
|
||||
}
|
||||
if let Some(action_state) = action_state.copied() {
|
||||
client.notify(ServerMsg::EntityActionState {
|
||||
if let Some(character_state) = character_state.copied() {
|
||||
client.notify(ServerMsg::EntityCharacterState {
|
||||
entity: uid.into(),
|
||||
action_state,
|
||||
character_state,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -928,84 +991,7 @@ impl Server {
|
||||
self.clients
|
||||
.notify_registered(ServerMsg::EcsSync(self.state.ecs_mut().next_sync_package()));
|
||||
|
||||
// TODO: Move this into some new method like `handle_sys_outputs` right after ticking the world
|
||||
// Handle deaths.
|
||||
let ecs = self.state.ecs_mut();
|
||||
let clients = &mut self.clients;
|
||||
let todo_kill = (&ecs.entities(), &ecs.read_storage::<comp::Dying>())
|
||||
.join()
|
||||
.map(|(entity, dying)| {
|
||||
// Chat message
|
||||
if let Some(player) = ecs.read_storage::<comp::Player>().get(entity) {
|
||||
let msg = if let comp::HealthSource::Attack { by } = dying.cause {
|
||||
ecs.entity_from_uid(by.into()).and_then(|attacker| {
|
||||
ecs.read_storage::<comp::Player>()
|
||||
.get(attacker)
|
||||
.map(|attacker_alias| {
|
||||
format!(
|
||||
"{} was killed by {}",
|
||||
&player.alias, &attacker_alias.alias
|
||||
)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.unwrap_or(format!("{} died", &player.alias));
|
||||
|
||||
clients.notify_registered(ServerMsg::kill(msg));
|
||||
}
|
||||
|
||||
// Give EXP to the client
|
||||
let mut stats = ecs.write_storage::<comp::Stats>();
|
||||
if let Some(entity_stats) = stats.get(entity).cloned() {
|
||||
if let comp::HealthSource::Attack { by } = dying.cause {
|
||||
ecs.entity_from_uid(by.into()).map(|attacker| {
|
||||
if let Some(attacker_stats) = stats.get_mut(attacker) {
|
||||
// TODO: Discuss whether we should give EXP by Player Killing or not.
|
||||
attacker_stats.exp.change_by(
|
||||
entity_stats.health.maximum() as f64 / 10.0
|
||||
+ entity_stats.level.level() as f64 * 10.0,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
entity
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Actually kill them
|
||||
for entity in todo_kill {
|
||||
if let Some(client) = self.clients.get_mut(&entity) {
|
||||
let _ = ecs.write_storage().insert(entity, comp::Vel(Vec3::zero()));
|
||||
let _ = ecs.write_storage().insert(entity, comp::ForceUpdate);
|
||||
client.force_state(ClientState::Dead);
|
||||
} else {
|
||||
let _ = ecs.delete_entity_synced(entity);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle respawns
|
||||
let todo_respawn = (&ecs.entities(), &ecs.read_storage::<comp::Respawning>())
|
||||
.join()
|
||||
.map(|(entity, _)| entity)
|
||||
.collect::<Vec<EcsEntity>>();
|
||||
|
||||
for entity in todo_respawn {
|
||||
if let Some(client) = self.clients.get_mut(&entity) {
|
||||
client.allow_state(ClientState::Character);
|
||||
ecs.write_storage::<comp::Stats>()
|
||||
.get_mut(entity)
|
||||
.map(|stats| stats.revive());
|
||||
ecs.write_storage::<comp::Pos>()
|
||||
.get_mut(entity)
|
||||
.map(|pos| pos.0.z += 20.0);
|
||||
let _ = ecs.write_storage().insert(entity, comp::ForceUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
// Sync physics
|
||||
for (entity, &uid, &pos, force_update) in (
|
||||
@ -1043,7 +1029,7 @@ impl Server {
|
||||
let mut last_pos = ecs.write_storage::<comp::Last<comp::Pos>>();
|
||||
let mut last_vel = ecs.write_storage::<comp::Last<comp::Vel>>();
|
||||
let mut last_ori = ecs.write_storage::<comp::Last<comp::Ori>>();
|
||||
let mut last_action_state = ecs.write_storage::<comp::Last<comp::ActionState>>();
|
||||
let mut last_character_state = ecs.write_storage::<comp::Last<comp::CharacterState>>();
|
||||
|
||||
if let Some(client_pos) = ecs.read_storage::<comp::Pos>().get(entity) {
|
||||
if last_pos
|
||||
@ -1099,16 +1085,19 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(client_action_state) = ecs.read_storage::<comp::ActionState>().get(entity) {
|
||||
if last_action_state
|
||||
if let Some(client_character_state) =
|
||||
ecs.read_storage::<comp::CharacterState>().get(entity)
|
||||
{
|
||||
if last_character_state
|
||||
.get(entity)
|
||||
.map(|&l| l != *client_action_state)
|
||||
.map(|&l| l != *client_character_state)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
let _ = last_action_state.insert(entity, comp::Last(*client_action_state));
|
||||
let msg = ServerMsg::EntityActionState {
|
||||
let _ =
|
||||
last_character_state.insert(entity, comp::Last(*client_character_state));
|
||||
let msg = ServerMsg::EntityCharacterState {
|
||||
entity: uid.into(),
|
||||
action_state: *client_action_state,
|
||||
character_state: *client_character_state,
|
||||
};
|
||||
match force_update {
|
||||
Some(_) => clients.notify_ingame_if(msg, in_vd),
|
||||
|
Reference in New Issue
Block a user