From 4297622aa978070f24a36e6444f3d34cca9db6a0 Mon Sep 17 00:00:00 2001 From: "NG (Graham)" Date: Sat, 20 Dec 2025 21:52:38 -0500 Subject: [PATCH] Implement healing events; fix #63 --- .../src/events/heal_assist_bonus.rs | 32 +++++++++++++++++++ rc_multiplayer/src/events/heal_bonus.rs | 2 +- rc_multiplayer/src/events/mod.rs | 18 +++++++++++ rc_multiplayer/src/matches/generic.rs | 27 +++++++++++++++- rc_multiplayer/src/matches/messages.rs | 6 ++++ 5 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 rc_multiplayer/src/events/heal_assist_bonus.rs diff --git a/rc_multiplayer/src/events/heal_assist_bonus.rs b/rc_multiplayer/src/events/heal_assist_bonus.rs new file mode 100644 index 0000000..f007084 --- /dev/null +++ b/rc_multiplayer/src/events/heal_assist_bonus.rs @@ -0,0 +1,32 @@ +pub struct HealBonus { + msg_router: tokio::sync::mpsc::Sender, +} + +pub(super) fn handler(init_ctx: &crate::InitConfig) -> crate::handlers::SimpleRlnl { + crate::handlers::SimpleRlnl::new(HealBonus::new(init_ctx)) +} + +impl HealBonus { + fn new(init_ctx: &crate::InitConfig) -> Self { + Self { + msg_router: init_ctx.matches_chann.clone(), + } + } +} + +#[async_trait::async_trait] +impl crate::handlers::RlnlEventCodeHandler for HealBonus { + type In = rlnl::events::ingame::HealAssistBonus; + const CODE: rlnl::event_code::NetworkEvent = rlnl::event_code::NetworkEvent::HeallingAssistBonusRequest; + + async fn handle(&self, data: Self::In, _peer: &std::sync::Arc>, user: &crate::UserData, _sender: &std::sync::Arc>) { + if let Some(user_info) = user.user().await { + log::info!("Heal assist for player {}", data.healing_player_id); + super::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::HealAssistBonus { + user_id: user_info.user_id(), + healer: data.healing_player_id, + healee: data.healed_player_id, + }).await); + } + } +} diff --git a/rc_multiplayer/src/events/heal_bonus.rs b/rc_multiplayer/src/events/heal_bonus.rs index 89b5a50..9309004 100644 --- a/rc_multiplayer/src/events/heal_bonus.rs +++ b/rc_multiplayer/src/events/heal_bonus.rs @@ -21,7 +21,7 @@ impl crate::handlers::RlnlEventCodeHandler for HealBonus { async fn handle(&self, data: Self::In, _peer: &std::sync::Arc>, user: &crate::UserData, _sender: &std::sync::Arc>) { if let Some(user_info) = user.user().await { - super::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::DestroyCubesBonus { + super::log_channel_send_failure(self.msg_router.send(crate::matches::GameMessage::HealCubesBonus { user_id: user_info.user_id(), info: data, }).await); diff --git a/rc_multiplayer/src/events/mod.rs b/rc_multiplayer/src/events/mod.rs index 2ae93a4..be3be32 100644 --- a/rc_multiplayer/src/events/mod.rs +++ b/rc_multiplayer/src/events/mod.rs @@ -16,6 +16,7 @@ mod assist_bonus; mod damage_bonus; mod heal_bonus; mod player_leave; +mod heal_assist_bonus; pub async fn handler(init_ctx: &crate::InitConfig) -> crate::handler::LnlEventHandler { crate::handler::LnlEventHandler::new(init_ctx.users.clone(), crate::vehicle_motion::handler(init_ctx)) @@ -202,6 +203,21 @@ pub async fn handler(init_ctx: &crate::InitConfig) -> crate::handler::LnlEventHa rlnl::events::ingame::DestroyedHealedCubesBonus, >::handler(init_ctx)) .add(player_leave::handler(init_ctx)) + .add(crate::handlers::Broadcaster::< + false, + {rlnl::event_code::NetworkEvent::LockOnNotification as i16}, + {rlnl::event_code::NetworkEvent::LockOnNotificationBroadcast as i16}, + {literustlib::packet::Property::ReliableOrdered as u8}, + rlnl::events::ingame::LockOnNotifier, + >::handler(init_ctx)) + .add(crate::handlers::Broadcaster::< + false, + {rlnl::event_code::NetworkEvent::HealAlly as i16}, + {rlnl::event_code::NetworkEvent::HealAllyResponse as i16}, + {literustlib::packet::Property::ReliableOrdered as u8}, + rlnl::events::ingame::HealAllyCubes, + >::handler(init_ctx)) + .add(heal_assist_bonus::handler(init_ctx)) } #[inline] @@ -242,6 +258,8 @@ mod _broadcast_impls { impl Broadcastable for rlnl::events::ingame::InitiateSurrender {} impl Broadcastable for rlnl::events::ingame::SurrenderVoteCast {} impl Broadcastable for rlnl::events::ingame::AwardProtoniumDestroyedCubes {} + impl Broadcastable for rlnl::events::ingame::LockOnNotifier {} + impl Broadcastable for rlnl::events::ingame::HealAllyCubes {} impl Broadcastable for rlnl::events::sync::UpdateGameModeSettings {} impl Broadcastable for rlnl::events::sync::GetTeamBase {} diff --git a/rc_multiplayer/src/matches/generic.rs b/rc_multiplayer/src/matches/generic.rs index b6b42d4..8ca81c7 100644 --- a/rc_multiplayer/src/matches/generic.rs +++ b/rc_multiplayer/src/matches/generic.rs @@ -118,6 +118,7 @@ pub(super) struct UserData { pub kills: std::sync::atomic::AtomicU32, pub deaths: std::sync::atomic::AtomicU32, pub assists: std::sync::atomic::AtomicU32, + pub heal_assists: std::sync::atomic::AtomicU32, pub healed: std::sync::atomic::AtomicU32, pub received_healed: std::sync::atomic::AtomicU32, pub cubes: std::sync::atomic::AtomicU32, @@ -132,6 +133,7 @@ impl UserData { kills: std::sync::atomic::AtomicU32::new(0), deaths: std::sync::atomic::AtomicU32::new(0), assists: std::sync::atomic::AtomicU32::new(0), + heal_assists: std::sync::atomic::AtomicU32::new(0), healed: std::sync::atomic::AtomicU32::new(0), received_healed: std::sync::atomic::AtomicU32::new(0), cubes: std::sync::atomic::AtomicU32::new(0), @@ -144,6 +146,7 @@ impl UserData { pub(super) fn generic_score(&self) -> u32 { self.kills.load(std::sync::atomic::Ordering::Relaxed) * 1_000 + self.assists.load(std::sync::atomic::Ordering::Relaxed) * 100 + //+ self.heal_assists.load(std::sync::atomic::Ordering::Relaxed) * 200 + self.healed.load(std::sync::atomic::Ordering::Relaxed) + self.cubes.load(std::sync::atomic::Ordering::Relaxed) + self.crystals.load(std::sync::atomic::Ordering::Relaxed) * 25 @@ -156,7 +159,8 @@ impl UserData { | rlnl::types::IngameStatId::DestroyedCubesDefendingTheBase => (self.cubes.load(std::sync::atomic::Ordering::SeqCst), 1), rlnl::types::IngameStatId::Kill => (self.kills.load(std::sync::atomic::Ordering::Relaxed), 1_000), rlnl::types::IngameStatId::KillAssist => (self.assists.load(std::sync::atomic::Ordering::Relaxed), 100), - rlnl::types::IngameStatId::HealCubes => (self.assists.load(std::sync::atomic::Ordering::SeqCst), 1), + rlnl::types::IngameStatId::HealCubes => (self.healed.load(std::sync::atomic::Ordering::SeqCst), 1), + rlnl::types::IngameStatId::HealAssist => (self.heal_assists.load(std::sync::atomic::Ordering::SeqCst), 200), rlnl::types::IngameStatId::RobotDestroyed => (self.deaths.load(std::sync::atomic::Ordering::Relaxed), 0), rlnl::types::IngameStatId::DestroyedProtoniumCubes => (self.deaths.load(std::sync::atomic::Ordering::Relaxed), 25), s => panic!("Cannot generate game stat {:?}", s) @@ -446,6 +450,9 @@ impl GenericGamemodeEngine { super::GameMessage::AssistBonus { user_id, shootee, shooters } => { self.on_assist_bonus(user_id, shootee, shooters).await; }, + super::GameMessage::HealAssistBonus { user_id, healer, healee } => { + self.on_heal_assist_bonus(user_id, healer, healee).await; + }, super::GameMessage::DestroyCubesBonus { user_id, info } => { self.on_destroy_cubes_bonus(user_id, info).await; }, @@ -946,6 +953,24 @@ impl GenericGamemodeEngine { } } + async fn on_heal_assist_bonus(&self, + _user_id: i32, + healer: u8, + _healee: u8, + ) { + if let Some(to_reward_desc) = self.user_descriptor(healer) { + //let to_reward_desc = self.user_descriptor(healee).unwrap(); + to_reward_desc.counters.heal_assists.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let data = to_reward_desc.counters.get_generic_packet(healer, rlnl::types::IngameStatId::HealAssist, None); + self.broadcast( + rlnl::event_code::NetworkEvent::UpdateGameStats, + literustlib::packet::Property::ReliableOrdered, + &data, + true, + ).await; + } + } + async fn on_destroy_cubes_bonus(&self, _user_id: i32, info: rlnl::events::ingame::DestroyedHealedCubesBonus diff --git a/rc_multiplayer/src/matches/messages.rs b/rc_multiplayer/src/matches/messages.rs index 1cae394..b07d969 100644 --- a/rc_multiplayer/src/matches/messages.rs +++ b/rc_multiplayer/src/matches/messages.rs @@ -62,6 +62,11 @@ pub enum GameMessage { shootee: u8, shooters: Vec, }, + HealAssistBonus { + user_id: i32, + healer: u8, + healee: u8, + }, DestroyCubesBonus { user_id: i32, info: rlnl::events::ingame::DestroyedHealedCubesBonus, @@ -121,6 +126,7 @@ impl GameMessage { Self::MapPing { user_id, .. } => *user_id, Self::KillBonus { user_id, .. } => *user_id, Self::AssistBonus { user_id, .. } => *user_id, + Self::HealAssistBonus { user_id, .. } => *user_id, Self::DestroyCubesBonus { user_id, .. } => *user_id, Self::HealCubesBonus { user_id, .. } => *user_id, Self::BroadcastRlnl { user_id, .. } => *user_id,