mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'event-chat-messages' into 'master'
Event chat messages See merge request veloren/veloren!188 Former-commit-id: 5701dd41f52f76ee5733a3580da9fab59dc539e7
This commit is contained in:
commit
b5b61462ee
@ -294,14 +294,6 @@ impl Client {
|
||||
.ecs_mut()
|
||||
.write_storage::<comp::Jumping>()
|
||||
.remove(self.entity);
|
||||
self.state
|
||||
.ecs_mut()
|
||||
.write_storage::<comp::Dying>()
|
||||
.remove(self.entity);
|
||||
self.state
|
||||
.ecs_mut()
|
||||
.write_storage::<comp::Respawning>()
|
||||
.remove(self.entity);
|
||||
|
||||
self.tick += 1;
|
||||
Ok(frontend_events)
|
||||
|
@ -22,4 +22,5 @@ pub use inputs::Jumping;
|
||||
pub use inputs::Respawning;
|
||||
pub use player::Player;
|
||||
pub use stats::Dying;
|
||||
pub use stats::HealthSource;
|
||||
pub use stats::Stats;
|
||||
|
@ -1,17 +1,33 @@
|
||||
use crate::state::Time;
|
||||
use crate::state::{Time, Uid};
|
||||
use specs::{Component, FlaggedStorage, NullStorage, VecStorage};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub enum HealthSource {
|
||||
Attack { by: Uid }, // TODO: Implement weapon
|
||||
Suicide,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
pub struct Health {
|
||||
pub current: u32,
|
||||
pub maximum: u32,
|
||||
pub last_change: Option<(i32, f64)>,
|
||||
current: u32,
|
||||
maximum: u32,
|
||||
pub last_change: Option<(i32, f64, HealthSource)>,
|
||||
}
|
||||
|
||||
impl Health {
|
||||
pub fn change_by(&mut self, amount: i32) {
|
||||
pub fn get_current(&self) -> u32 {
|
||||
self.current
|
||||
}
|
||||
pub fn get_maximum(&self) -> u32 {
|
||||
self.maximum
|
||||
}
|
||||
pub fn set_to(&mut self, amount: u32, cause: HealthSource) {
|
||||
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.last_change = Some((amount, 0.0));
|
||||
self.last_change = Some((amount, 0.0, cause));
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,10 +35,12 @@ impl Health {
|
||||
pub struct Stats {
|
||||
pub hp: Health,
|
||||
pub xp: u32,
|
||||
pub is_dead: bool,
|
||||
}
|
||||
|
||||
impl Stats {
|
||||
pub fn is_dead(&self) -> bool {
|
||||
pub fn should_die(&self) -> bool {
|
||||
// TODO: Remove
|
||||
self.hp.current == 0
|
||||
}
|
||||
}
|
||||
@ -36,6 +54,7 @@ impl Default for Stats {
|
||||
last_change: None,
|
||||
},
|
||||
xp: 0,
|
||||
is_dead: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -44,9 +63,11 @@ impl Component for Stats {
|
||||
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct Dying;
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Dying {
|
||||
pub cause: HealthSource,
|
||||
}
|
||||
|
||||
impl Component for Dying {
|
||||
type Storage = NullStorage<Self>;
|
||||
type Storage = VecStorage<Self>;
|
||||
}
|
||||
|
@ -103,21 +103,24 @@ impl State {
|
||||
ecs.register_synced::<comp::Actor>();
|
||||
ecs.register_synced::<comp::Player>();
|
||||
ecs.register_synced::<comp::Stats>();
|
||||
ecs.register_synced::<comp::Attacking>();
|
||||
ecs.register_synced::<comp::Attacking>(); // TODO: Don't send this to the client?
|
||||
ecs.register::<comp::phys::ForceUpdate>();
|
||||
|
||||
// Register unsynced (or synced by other means) components.
|
||||
// Register components synced by other means
|
||||
ecs.register::<comp::phys::Pos>();
|
||||
ecs.register::<comp::phys::Vel>();
|
||||
ecs.register::<comp::phys::Dir>();
|
||||
ecs.register::<comp::AnimationInfo>();
|
||||
ecs.register::<comp::Attacking>();
|
||||
|
||||
// Register client-local components
|
||||
ecs.register::<comp::Control>();
|
||||
ecs.register::<comp::Jumping>();
|
||||
|
||||
// Register server-local components
|
||||
ecs.register::<comp::Agent>();
|
||||
ecs.register::<comp::Respawning>();
|
||||
ecs.register::<comp::Gliding>();
|
||||
ecs.register::<comp::Dying>();
|
||||
ecs.register::<comp::Agent>();
|
||||
ecs.register::<inventory::Inventory>();
|
||||
|
||||
// Register synced resources used by the ECS.
|
||||
|
@ -6,9 +6,10 @@ use vek::*;
|
||||
use crate::{
|
||||
comp::{
|
||||
phys::{Dir, ForceUpdate, Pos, Vel},
|
||||
Animation, AnimationInfo, Attacking, Control, Gliding, Jumping, Respawning, Stats,
|
||||
Animation, AnimationInfo, Attacking, Control, Gliding, HealthSource, Jumping, Respawning,
|
||||
Stats,
|
||||
},
|
||||
state::{DeltaTime, Time},
|
||||
state::{DeltaTime, Time, Uid},
|
||||
terrain::TerrainMap,
|
||||
vol::{ReadVol, Vox},
|
||||
};
|
||||
@ -19,6 +20,7 @@ pub struct Sys;
|
||||
impl<'a> System<'a> for Sys {
|
||||
type SystemData = (
|
||||
Entities<'a>,
|
||||
ReadStorage<'a, Uid>,
|
||||
Read<'a, Time>,
|
||||
Read<'a, DeltaTime>,
|
||||
ReadExpect<'a, TerrainMap>,
|
||||
@ -39,6 +41,7 @@ impl<'a> System<'a> for Sys {
|
||||
&mut self,
|
||||
(
|
||||
entities,
|
||||
uids,
|
||||
time,
|
||||
dt,
|
||||
terrain,
|
||||
@ -142,8 +145,8 @@ impl<'a> System<'a> for Sys {
|
||||
);
|
||||
}
|
||||
|
||||
for (entity, pos, dir, attacking) in
|
||||
(&entities, &positions, &directions, &mut attackings).join()
|
||||
for (entity, &uid, pos, dir, attacking) in
|
||||
(&entities, &uids, &positions, &directions, &mut attackings).join()
|
||||
{
|
||||
if !attacking.applied {
|
||||
for (b, pos_b, mut stat_b, mut vel_b) in
|
||||
@ -151,12 +154,12 @@ impl<'a> System<'a> for Sys {
|
||||
{
|
||||
// Check if it is a hit
|
||||
if entity != b
|
||||
&& !stat_b.is_dead()
|
||||
&& !stat_b.is_dead
|
||||
&& pos.0.distance_squared(pos_b.0) < 50.0
|
||||
&& dir.0.angle_between(pos_b.0 - pos.0).to_degrees() < 70.0
|
||||
{
|
||||
// Deal damage
|
||||
stat_b.hp.change_by(-10); // TODO: variable damage
|
||||
stat_b.hp.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;
|
||||
force_updates.insert(b, ForceUpdate);
|
||||
|
@ -21,8 +21,19 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
fn run(&mut self, (entities, dt, mut stats, mut dyings): Self::SystemData) {
|
||||
for (entity, mut stat) in (&entities, &mut stats).join() {
|
||||
if stat.hp.current == 0 {
|
||||
dyings.insert(entity, Dying);
|
||||
if stat.should_die() && !stat.is_dead {
|
||||
// TODO: Replace is_dead with client states
|
||||
dyings.insert(
|
||||
entity,
|
||||
Dying {
|
||||
cause: stat
|
||||
.hp
|
||||
.last_change
|
||||
.expect("Nothing caused the entity to die")
|
||||
.2, // Safe because damage is necessary for death
|
||||
},
|
||||
);
|
||||
stat.is_dead = true;
|
||||
}
|
||||
if let Some(change) = &mut stat.hp.last_change {
|
||||
change.1 += dt.0 as f64;
|
||||
|
@ -150,7 +150,7 @@ fn handle_kill(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
|
||||
.ecs_mut()
|
||||
.write_storage::<comp::Stats>()
|
||||
.get_mut(entity)
|
||||
.map(|s| s.hp.current = 0);
|
||||
.map(|s| s.hp.set_to(0, comp::HealthSource::Suicide));
|
||||
}
|
||||
|
||||
fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &ChatCommand) {
|
||||
|
@ -39,9 +39,16 @@ const CLIENT_TIMEOUT: f64 = 20.0; // Seconds
|
||||
const DEFAULT_WORLD_SEED: u32 = 1337;
|
||||
|
||||
pub enum Event {
|
||||
ClientConnected { entity: EcsEntity },
|
||||
ClientDisconnected { entity: EcsEntity },
|
||||
Chat { entity: EcsEntity, msg: String },
|
||||
ClientConnected {
|
||||
entity: EcsEntity,
|
||||
},
|
||||
ClientDisconnected {
|
||||
entity: EcsEntity,
|
||||
},
|
||||
Chat {
|
||||
entity: Option<EcsEntity>,
|
||||
msg: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@ -204,14 +211,37 @@ impl Server {
|
||||
self.world.tick(dt);
|
||||
|
||||
// Sync deaths.
|
||||
let todo_kill = (
|
||||
&self.state.ecs().entities(),
|
||||
&self.state.ecs().read_storage::<comp::Dying>(),
|
||||
)
|
||||
let ecs = &self.state.ecs();
|
||||
let clients = &mut self.clients;
|
||||
let todo_kill = (&ecs.entities(), &ecs.read_storage::<comp::Dying>())
|
||||
.join()
|
||||
.map(|(entity, _)| entity)
|
||||
.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::Chat(msg));
|
||||
}
|
||||
|
||||
entity
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Actually kill them
|
||||
for entity in todo_kill {
|
||||
if let Some(client) = self.clients.get_mut(&entity) {
|
||||
self.state
|
||||
@ -220,6 +250,7 @@ impl Server {
|
||||
client.force_state(ClientState::Dead);
|
||||
} else {
|
||||
self.state.ecs_mut().delete_entity_synced(entity);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@ -316,8 +347,6 @@ impl Server {
|
||||
// Cleanup
|
||||
let ecs = self.state.ecs_mut();
|
||||
for entity in ecs.entities().join() {
|
||||
ecs.write_storage::<comp::Jumping>().remove(entity);
|
||||
ecs.write_storage::<comp::Gliding>().remove(entity);
|
||||
ecs.write_storage::<comp::Dying>().remove(entity);
|
||||
ecs.write_storage::<comp::Respawning>().remove(entity);
|
||||
}
|
||||
@ -415,7 +444,13 @@ impl Server {
|
||||
},
|
||||
ClientMsg::Register { player } => match client.client_state {
|
||||
ClientState::Connected => {
|
||||
Self::initialize_player(state, entity, client, player)
|
||||
Self::initialize_player(state, entity, client, player);
|
||||
if let Some(player) =
|
||||
state.ecs().read_storage::<comp::Player>().get(entity)
|
||||
{
|
||||
new_chat_msgs
|
||||
.push((None, format!("{} logged in", &player.alias)));
|
||||
}
|
||||
}
|
||||
// Use RequestState instead (No need to send `player` again).
|
||||
_ => client.error_state(RequestStateError::Impossible),
|
||||
@ -464,7 +499,7 @@ impl Server {
|
||||
ClientState::Registered
|
||||
| ClientState::Spectator
|
||||
| ClientState::Dead
|
||||
| ClientState::Character => new_chat_msgs.push((entity, msg)),
|
||||
| ClientState::Character => new_chat_msgs.push((Some(entity), msg)),
|
||||
ClientState::Pending => {}
|
||||
},
|
||||
ClientMsg::PlayerAnimation(animation_info) => {
|
||||
@ -507,7 +542,9 @@ impl Server {
|
||||
// Always possible.
|
||||
ClientMsg::Ping => client.postbox.send_message(ServerMsg::Pong),
|
||||
ClientMsg::Pong => {}
|
||||
ClientMsg::Disconnect => disconnect = true,
|
||||
ClientMsg::Disconnect => {
|
||||
disconnect = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if state.get_time() - client.last_ping > CLIENT_TIMEOUT || // Timeout
|
||||
@ -521,6 +558,9 @@ impl Server {
|
||||
}
|
||||
|
||||
if disconnect {
|
||||
if let Some(player) = state.ecs().read_storage::<comp::Player>().get(entity) {
|
||||
new_chat_msgs.push((None, format!("{} disconnected", &player.alias)));
|
||||
}
|
||||
disconnected_clients.push(entity);
|
||||
client.postbox.send_message(ServerMsg::Disconnect);
|
||||
true
|
||||
@ -531,20 +571,23 @@ impl Server {
|
||||
|
||||
// Handle new chat messages.
|
||||
for (entity, msg) in new_chat_msgs {
|
||||
// Handle chat commands.
|
||||
if msg.starts_with("/") && msg.len() > 1 {
|
||||
let argv = String::from(&msg[1..]);
|
||||
self.process_chat_cmd(entity, argv);
|
||||
if let Some(entity) = entity {
|
||||
// Handle chat commands.
|
||||
if msg.starts_with("/") && msg.len() > 1 {
|
||||
let argv = String::from(&msg[1..]);
|
||||
self.process_chat_cmd(entity, argv);
|
||||
} else {
|
||||
self.clients.notify_registered(ServerMsg::Chat(
|
||||
match self.state.ecs().read_storage::<comp::Player>().get(entity) {
|
||||
Some(player) => format!("[{}] {}", &player.alias, msg),
|
||||
None => format!("[<anon>] {}", msg),
|
||||
},
|
||||
));
|
||||
}
|
||||
} else {
|
||||
self.clients.notify_registered(ServerMsg::Chat(
|
||||
match self.state.ecs().read_storage::<comp::Player>().get(entity) {
|
||||
Some(player) => format!("[{}] {}", &player.alias, msg),
|
||||
None => format!("[<anon>] {}", msg),
|
||||
},
|
||||
));
|
||||
|
||||
frontend_events.push(Event::Chat { entity, msg });
|
||||
self.clients.notify_registered(ServerMsg::Chat(msg.clone()));
|
||||
}
|
||||
frontend_events.push(Event::Chat { entity, msg });
|
||||
}
|
||||
|
||||
// Handle client disconnects.
|
||||
|
@ -307,7 +307,7 @@ impl Hud {
|
||||
let mut health_back_id_walker = self.ids.health_bar_backs.walk();
|
||||
for (pos, name) in (&entities, &pos, &actor, &stats, player.maybe())
|
||||
.join()
|
||||
.filter(|(entity, _, _, stats, _)| *entity != me && !stats.is_dead())
|
||||
.filter(|(entity, _, _, stats, _)| *entity != me && !stats.is_dead)
|
||||
.map(|(entity, pos, actor, _, player)| match actor {
|
||||
comp::Actor::Character {
|
||||
name: char_name, ..
|
||||
@ -338,7 +338,7 @@ impl Hud {
|
||||
|
||||
for (entity, pos, stats) in (&entities, &pos, &stats)
|
||||
.join()
|
||||
.filter(|(entity, _, stats)| *entity != me && !stats.is_dead())
|
||||
.filter(|(entity, _, stats)| *entity != me && !stats.is_dead)
|
||||
{
|
||||
let back_id = health_back_id_walker.next(
|
||||
&mut self.ids.health_bar_backs,
|
||||
@ -358,7 +358,7 @@ impl Hud {
|
||||
// Filling
|
||||
Rectangle::fill_with(
|
||||
[
|
||||
120.0 * (stats.hp.current as f64 / stats.hp.maximum as f64),
|
||||
120.0 * (stats.hp.get_current() as f64 / stats.hp.get_maximum() as f64),
|
||||
8.0,
|
||||
],
|
||||
HP_COLOR,
|
||||
|
@ -78,7 +78,7 @@ 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.current as f64 / self.stats.hp.maximum as f64;
|
||||
let hp_percentage = self.stats.hp.get_current() as f64 / self.stats.hp.get_maximum() as f64;
|
||||
let mana_percentage = 1.0;
|
||||
|
||||
// TODO: Only show while aiming with a bow or when casting a spell.
|
||||
|
@ -475,7 +475,7 @@ impl FigureMgr {
|
||||
// Change in health as color!
|
||||
let col = stats
|
||||
.and_then(|stats| stats.hp.last_change)
|
||||
.map(|(change_by, time)| {
|
||||
.map(|(change_by, time, _)| {
|
||||
Rgba::broadcast(1.0)
|
||||
+ Rgba::new(0.0, -1.0, -1.0, 0.0)
|
||||
.map(|c| (c / (1.0 + DAMAGE_FADE_COEFFICIENT * time)) as f32)
|
||||
@ -617,7 +617,7 @@ impl FigureMgr {
|
||||
)
|
||||
.join()
|
||||
{
|
||||
if stat.is_dead() {
|
||||
if stat.is_dead {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user