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.
This commit is contained in:
WiseDev 2026-08-23 13:53:41 +03:00
parent 498f2cb62d
commit dbbd162bed
2 changed files with 37 additions and 3 deletions

View file

@ -163,7 +163,7 @@ impl BattleSession {
while self.tick < tick { while self.tick < tick {
self.tick += 1; self.tick += 1;
self.release_queued(self.tick); self.release_queued(self.tick);
self.mode.battle.tick(); self.mode.battle.tick(self.tick);
if self.pushes_snapshots() && self.tick >= self.next_snapshot { if self.pushes_snapshots() && self.tick >= self.next_snapshot {
self.next_snapshot = self.tick + SNAPSHOT_INTERVAL_TICKS; self.next_snapshot = self.tick + SNAPSHOT_INTERVAL_TICKS;
if let Some(message) = self.snapshot_message() { if let Some(message) = self.snapshot_message() {

View file

@ -1,8 +1,15 @@
use crate::battle::logic_battle::LogicBattle; use crate::battle::logic_battle::LogicBattle;
use crate::battle::logic_component::{LogicComponent, COMPONENT_COMBAT, COMPONENT_HITPOINT}; 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; use crate::data::LogicDataRef;
pub const TICK_MILLISECONDS: i32 = 50; 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_SPEED: &str = "Speed";
pub const COLUMN_SIGHT_RANGE: &str = "SightRange"; pub const COLUMN_SIGHT_RANGE: &str = "SightRange";
pub const COLUMN_RANGE: &str = "Range"; pub const COLUMN_RANGE: &str = "Range";
@ -131,7 +138,34 @@ impl LogicGameObjectEntry {
} }
} }
impl LogicBattle { 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.retarget();
self.move_objects(); self.move_objects();
self.resolve_attacks(); self.resolve_attacks();