diff options
author | Alexander Foremny <aforemny@posteo.de> | 2024-03-17 16:32:22 +0100 |
---|---|---|
committer | Alexander Foremny <aforemny@posteo.de> | 2024-03-17 16:32:22 +0100 |
commit | 312aa627052c24e0aa1948e584c046bc15f18c96 (patch) | |
tree | ff74e110ae4481979352962985e75fb5ae99dfe9 /src | |
parent | e228208de62f8abf1744f389271168d996a10b25 (diff) |
feat: add damage indicators
Diffstat (limited to 'src')
-rw-r--r-- | src/client.rs | 108 | ||||
-rw-r--r-- | src/protocol.rs | 5 | ||||
-rw-r--r-- | src/server.rs | 12 | ||||
-rw-r--r-- | src/shared.rs | 1 | ||||
-rw-r--r-- | src/shared/health_event.rs | 7 |
5 files changed, 132 insertions, 1 deletions
diff --git a/src/client.rs b/src/client.rs index 1805cae..8220047 100644 --- a/src/client.rs +++ b/src/client.rs @@ -4,6 +4,7 @@ use crate::shared::ability::*; use crate::shared::champion::*; use crate::shared::cooldown::*; use crate::shared::health::*; +use crate::shared::health_event::*; use crate::shared::imperative::*; use crate::shared::projectile::*; use crate::shared::*; @@ -11,6 +12,7 @@ use bevy::input::keyboard::*; use bevy::input::mouse::MouseButton; use bevy::prelude::*; use bevy::sprite::{MaterialMesh2dBundle, Mesh2dHandle}; +use lightyear::client::events::*; use lightyear::client::input::InputSystemSet; use lightyear::prelude::*; use std::net::SocketAddr; @@ -79,6 +81,13 @@ impl Plugin for ClientPlugin { ( (hotbar_hotbar_display, hotbar_hotbar_highlight), hotbar_cooldown, + ( + health_indicator_despawn, + health_indicator_tick, + health_indicator_move_up, + health_indicator_spawn, + ) + .chain(), ), ) .add_systems( @@ -355,6 +364,11 @@ fn cursor_world_position( camera.viewport_to_world_2d(camera_transform, cursor_position) } +fn world_to_viewport(cameras: &Query<(&Camera, &GlobalTransform)>, position: Vec2) -> Option<Vec2> { + let (camera, camera_transform) = cameras.single(); + camera.world_to_viewport(camera_transform, Vec3::new(position.x, position.y, 0.)) +} + fn hovered_other_player( cameras: &Query<(&Camera, &GlobalTransform)>, client_id: &Res<ClientId>, @@ -550,3 +564,97 @@ fn hotbar_hotbar_highlight(attack: Res<Attack>, mut hotbars: Query<(&Hotbar, &mu } } } + +#[derive(Component)] +pub struct HealthIndicator { + position: Vec2, + timer: Timer, +} + +fn health_indicator_spawn( + cameras: Query<(&Camera, &GlobalTransform)>, + mut commands: Commands, + mut event_reader: EventReader<MessageEvent<HealthChanged>>, + players: Query<(&PlayerId, &PlayerPosition)>, +) { + for event in event_reader.read() { + let HealthChanged(HealthEvent { + target_player, + health_gained, + }) = event.message(); + let Some(position) = any_player_position(*target_player, &players) else { + continue; + }; + let health_gained_or_lost = health_gained.abs(); + let Some(mut screen_position) = world_to_viewport(&cameras, position.0) else { + continue; + }; + screen_position += Vec2::new(12.5, -12.5); + commands.spawn(( + TextBundle::from_section( + format!("{health_gained_or_lost}"), + TextStyle { + font_size: 6., + color: Color::RED, + ..Default::default() + }, + ) + .with_style(Style { + position_type: PositionType::Absolute, + top: Val::Px(screen_position.y), + left: Val::Px(screen_position.x), + ..Default::default() + }), + HealthIndicator { + position: position.0, + timer: Timer::from_seconds(1., TimerMode::Once), + }, + )); + } +} + +fn any_player_position( + player_id: PlayerId, + players: &Query<(&PlayerId, &PlayerPosition)>, +) -> Option<PlayerPosition> { + for (id, position) in players.iter() { + if *id == player_id { + return Some(*position); + } + } + None +} + +fn health_indicator_move_up( + cameras: Query<(&Camera, &GlobalTransform)>, + mut health_indicators: Query<(&mut Style, &HealthIndicator)>, +) { + for (mut style, health_indicator) in health_indicators.iter_mut() { + let Some(mut screen_position) = world_to_viewport(&cameras, health_indicator.position) + else { + continue; + }; + screen_position += Vec2::new(12.5, -12.5); + let s = health_indicator.timer.fraction(); + style.top = Val::Px((1. - s) * screen_position.y + s * (screen_position.y - 20.)); + style.left = Val::Px(screen_position.x); + } +} + +fn health_indicator_tick(mut health_indicators: Query<&mut HealthIndicator>, time: Res<Time>) { + for mut health_indicator in health_indicators.iter_mut() { + health_indicator.timer.tick(time.delta()); + } +} + +fn health_indicator_despawn( + health_indicators: Query<(Entity, &HealthIndicator)>, + mut commands: Commands, +) { + for (entity, health_indicator) in health_indicators.iter() { + if !health_indicator.timer.finished() { + continue; + } + commands.entity(entity).despawn(); + } +} diff --git a/src/protocol.rs b/src/protocol.rs index 70f4f64..c1c18e4 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -1,6 +1,7 @@ use crate::shared::champion::*; use crate::shared::cooldown::*; use crate::shared::health::*; +use crate::shared::health_event::*; use crate::shared::imperative::*; use crate::shared::projectile::*; use crate::shared::*; @@ -19,9 +20,13 @@ impl UserAction for Inputs {} #[derive(Message, Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct SelectChampion(pub Champion); +#[derive(Message, Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct HealthChanged(pub HealthEvent); + #[message_protocol(protocol = "MyProtocol")] pub enum Messages { SelectChampion(SelectChampion), + HealthChanged(HealthChanged), } #[component_protocol(protocol = "MyProtocol")] diff --git a/src/server.rs b/src/server.rs index f6cbac3..7d50d9f 100644 --- a/src/server.rs +++ b/src/server.rs @@ -3,6 +3,7 @@ use crate::server::network::*; use crate::shared::champion::*; use crate::shared::cooldown::*; use crate::shared::health::*; +use crate::shared::health_event::*; use crate::shared::imperative::*; use crate::shared::projectile::*; use crate::shared::*; @@ -350,10 +351,11 @@ fn projectile_move( fn projectile_despawn( entity_map: Res<EntityMap>, mut commands: Commands, + mut connection_manager: ResMut<ServerConnectionManager>, mut healths: Query<&mut Health>, player_positions: Query<&PlayerPosition>, - projectile_targets: Query<(&PlayerId, &PlayerPosition)>, projectiles: Query<(Entity, &mut Projectile)>, + projectile_targets: Query<(&PlayerId, &PlayerPosition)>, ) { for (entity, projectile) in projectiles.iter() { let (despawn, maybe_target_player): (bool, Option<PlayerId>) = (|| match &projectile.type_ { @@ -414,6 +416,14 @@ fn projectile_despawn( if let Some(target_player) = maybe_target_player { if let Some(target_entity) = entity_map.0.get(&target_player.0) { if let Ok(mut health) = healths.get_mut(*target_entity) { + let _ = connection_manager + .send_message_to_target::<Channel1, HealthChanged>( + HealthChanged(HealthEvent { + target_player, + health_gained: -projectile.damage, + }), + NetworkTarget::All, + ); health.0 = (health.0 - projectile.damage).max(0.); } } diff --git a/src/shared.rs b/src/shared.rs index 6f01f85..f5a813e 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -13,6 +13,7 @@ pub mod ability; pub mod champion; pub mod cooldown; pub mod health; +pub mod health_event; pub mod imperative; pub mod projectile; diff --git a/src/shared/health_event.rs b/src/shared/health_event.rs new file mode 100644 index 0000000..9932599 --- /dev/null +++ b/src/shared/health_event.rs @@ -0,0 +1,7 @@ +use crate::shared::*; + +#[derive(Message, Serialize, Deserialize, PartialEq, Clone, Debug)] +pub struct HealthEvent { + pub target_player: PlayerId, + pub health_gained: f32, +} |