From dbbd162bed5cfaccfb3b6f6329fd5080c436e382 Mon Sep 17 00:00:00 2001 From: WiseDev <83840010+wisedevik@users.noreply.github.com> Date: Sun, 23 Aug 2026 13:53:41 +0300 Subject: [PATCH] regenerate mana on the server at type 0 the client stopped working the elixir bar out for itself and started reading it from the summoner in our snapshot - which never moved, so the bar sat where the opening state left it and no card could be afforded. the rule from LogicSummoner::tick: an accumulator gains five thousand a tick and one mana is granted for every MANA_REGEN_MS * 100 / MAX_MANA it holds, the remainder carried rather than dropped. that works out at 2.8 seconds a mana with the shipped globals, and halves in the last sixty seconds through MANA_REGEN_MS_END, which is the speed-up the game has always had. --- crates/game-service/src/battle_session.rs | 2 +- crates/logic/src/battle/logic_simulation.rs | 38 +++++++++++++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/crates/game-service/src/battle_session.rs b/crates/game-service/src/battle_session.rs index e1fe9a9..030bdff 100644 --- a/crates/game-service/src/battle_session.rs +++ b/crates/game-service/src/battle_session.rs @@ -163,7 +163,7 @@ impl BattleSession { while self.tick < tick { self.tick += 1; self.release_queued(self.tick); - self.mode.battle.tick(); + self.mode.battle.tick(self.tick); if self.pushes_snapshots() && self.tick >= self.next_snapshot { self.next_snapshot = self.tick + SNAPSHOT_INTERVAL_TICKS; if let Some(message) = self.snapshot_message() { diff --git a/crates/logic/src/battle/logic_simulation.rs b/crates/logic/src/battle/logic_simulation.rs index 00d494a..298f5db 100644 --- a/crates/logic/src/battle/logic_simulation.rs +++ b/crates/logic/src/battle/logic_simulation.rs @@ -1,8 +1,15 @@ use crate::battle::logic_battle::LogicBattle; use crate::battle::logic_component::{LogicComponent, COMPONENT_COMBAT, COMPONENT_HITPOINT}; -use crate::battle::logic_game_object::LogicGameObjectEntry; +use crate::battle::logic_game_object::{LogicGameObjectEntry, LogicObjectBody}; +use crate::battle::logic_battle::BATTLE_TICKS_PER_SECOND; use crate::data::LogicDataRef; pub const TICK_MILLISECONDS: i32 = 50; +pub const MANA_ACCUMULATOR_STEP: i32 = 5000; +pub const MANA_RATE_SCALE: i32 = 100; +pub const GLOBAL_MAX_MANA: &str = "MAX_MANA"; +pub const GLOBAL_MANA_REGEN: &str = "MANA_REGEN_MS"; +pub const GLOBAL_MANA_REGEN_END: &str = "MANA_REGEN_MS_END"; +pub const GLOBAL_MANA_SPEED_UP_SECONDS: &str = "MANA_SPEED_UP_WHEN_REMAINING_SECONDS"; pub const COLUMN_SPEED: &str = "Speed"; pub const COLUMN_SIGHT_RANGE: &str = "SightRange"; pub const COLUMN_RANGE: &str = "Range"; @@ -131,7 +138,34 @@ impl LogicGameObjectEntry { } } impl LogicBattle { - pub fn tick(&mut self) { + fn regenerate_mana(&mut self, tick: i32) { + let max_mana = crate::LogicGlobals::number(GLOBAL_MAX_MANA).max(1); + let seconds_left = (self.match_length_seconds() - tick / BATTLE_TICKS_PER_SECOND).max(0); + let speed_up = crate::LogicGlobals::number(GLOBAL_MANA_SPEED_UP_SECONDS); + let regen = if seconds_left <= speed_up { + crate::LogicGlobals::number(GLOBAL_MANA_REGEN_END) + } else { + crate::LogicGlobals::number(GLOBAL_MANA_REGEN) + }; + let step = regen.saturating_mul(MANA_RATE_SCALE) / max_mana; + if step < 1 { + return; + } + for entry in self.objects.objects.iter_mut() { + let LogicObjectBody::Summoner(summoner) = &mut entry.body else { + continue; + }; + summoner.mana_regen_timer += MANA_ACCUMULATOR_STEP; + let gained = summoner.mana_regen_timer / step; + if gained < 1 { + continue; + } + summoner.mana_regen_timer -= gained * step; + summoner.mana = (summoner.mana + gained).clamp(0, max_mana); + } + } + pub fn tick(&mut self, tick: i32) { + self.regenerate_mana(tick); self.retarget(); self.move_objects(); self.resolve_attacks();