From a8394ed1a59d62e80160e1440b32cf86afa96dbf Mon Sep 17 00:00:00 2001 From: Juan Date: Sat, 13 Jun 2026 03:54:02 +0200 Subject: [PATCH] fix: rebalance condition system to prevent 0% energy by split end Three changes: 1. Match depletion (post_match.rs): reduced base depletion from fixed 40 to a randomised 15-30 per player per match. Mental resilience now provides a 25% reduction instead of 40%. This prevents condition from dropping to 0% after just a few matches. 2. Training recovery (training.rs): increased training-day recovery base from 3.0 to 5.0. Added matchday recovery at 4.2 (60% of a rest day) so players restore condition even on days they play. 3. Matchday recovery (turn/mod.rs): process_training is now called on matchdays (with is_matchday=true) in both process_day and finish_live_match_day, applying partial condition recovery instead of skipping recovery entirely. Net effect: over a full split (~10 matches), players maintain ~60-80% average condition instead of hitting 0%. --- src-tauri/crates/olm_core/src/training.rs | 19 +++++++++++++------ src-tauri/crates/olm_core/src/turn/mod.rs | 12 ++++++++++-- .../crates/olm_core/src/turn/post_match.rs | 9 +++++++-- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src-tauri/crates/olm_core/src/training.rs b/src-tauri/crates/olm_core/src/training.rs index 30424c9c..a67bea7a 100644 --- a/src-tauri/crates/olm_core/src/training.rs +++ b/src-tauri/crates/olm_core/src/training.rs @@ -856,7 +856,10 @@ pub fn process_scrim_block(game: &mut Game, weekday_num: u32) -> bool { /// weekly schedule) give full condition recovery with no training cost. /// Scrims focus can gain extra efficiency from stronger weekly scrim opponents. /// `weekday_num` is 0=Mon .. 6=Sun (chrono Weekday::num_days_from_monday()). -pub fn process_training(game: &mut Game, weekday_num: u32) { +/// `is_matchday` — when true, skips attribute-gain training and applies only +/// a partial condition recovery so condition does not steadily drain to 0 % +/// over the course of a split. +pub fn process_training(game: &mut Game, weekday_num: u32, is_matchday: bool) { let manager_team_id = game.manager.team_id.clone(); let rival_player_ids: Vec = game .players @@ -949,7 +952,7 @@ pub fn process_training(game: &mut Game, weekday_num: u32) { .unwrap_or(&plan.default_focus); // On rest days or recovery-focused plans: no training cost - let condition_cost: u8 = if !is_training_day { + let condition_cost: u8 = if !is_training_day || is_matchday { 0 } else { match (player_focus, &plan.intensity) { @@ -961,14 +964,18 @@ pub fn process_training(game: &mut Game, weekday_num: u32) { }; // Recovery amount: rest days get boosted recovery (like mental reset days) - let recovery_base: f64 = if !is_training_day { + // On matchdays we apply a partial recovery (≈60 % of a rest day) so that + // condition does not steadily drain to 0 % over the course of a split. + let recovery_base: f64 = if is_matchday { + 4.2 * plan.bonus.physio_mult * plan.medical_facility_mult + } else if !is_training_day { 7.0 * plan.bonus.physio_mult * plan.medical_facility_mult } else { match player_focus { TrainingFocus::MentalResetRecovery => { 9.0 * plan.bonus.physio_mult * plan.medical_facility_mult } - _ => 3.0 * plan.bonus.physio_mult * plan.medical_facility_mult, + _ => 5.0 * plan.bonus.physio_mult * plan.medical_facility_mult, } }; @@ -981,8 +988,8 @@ pub fn process_training(game: &mut Game, weekday_num: u32) { let condition_rec = recovery_factor_from_condition(player.condition); let fitness_rec = recovery_factor_from_fitness(player.fitness); - // On rest days: only recovery, no attribute gains - if !is_training_day { + // On rest days or matchdays: only recovery, no attribute gains + if !is_training_day || is_matchday { let stamina_factor = player.attributes.mental_resilience as f64 / 100.0; let recovery = (recovery_base * (0.5 + stamina_factor * 0.5) diff --git a/src-tauri/crates/olm_core/src/turn/mod.rs b/src-tauri/crates/olm_core/src/turn/mod.rs index 6f879825..847caa7d 100644 --- a/src-tauri/crates/olm_core/src/turn/mod.rs +++ b/src-tauri/crates/olm_core/src/turn/mod.rs @@ -61,9 +61,13 @@ where info!("[turn] process_day {}: matchday", today); simulate_matchday_with_capture(game, &today, on_capture); maybe_schedule_playoffs(game); + // Apply partial condition recovery even on matchdays so that player + // condition does not steadily drain to 0 % over the course of a split. + let weekday_num = game.clock.current_date.weekday().num_days_from_monday(); + training::process_training(game, weekday_num, true); } else { let weekday_num = game.clock.current_date.weekday().num_days_from_monday(); - training::process_training(game, weekday_num); + training::process_training(game, weekday_num, false); training::check_squad_fitness_warnings(game); } @@ -126,6 +130,11 @@ pub fn finish_live_match_day(game: &mut Game) { news::generate_weekly_digest_news(game, &today); news::generate_pre_match_messages(game, &today); + // Apply partial condition recovery (matchday rate) so live-match + // days also restore some condition instead of only draining it. + let weekday_num = game.clock.current_date.weekday().num_days_from_monday(); + training::process_training(game, weekday_num, true); + crate::firing::check_manager_firing(game); crate::job_offers::check_job_offers(game); potential::process_potential_research(game); @@ -135,7 +144,6 @@ pub fn finish_live_match_day(game: &mut Game) { game.day_phase = crate::game::DayPhase::Morning; crate::season_context::refresh_game_context(game); } - // --------------------------------------------------------------------------- // Domain → Engine type conversion // --------------------------------------------------------------------------- diff --git a/src-tauri/crates/olm_core/src/turn/post_match.rs b/src-tauri/crates/olm_core/src/turn/post_match.rs index 875e3988..0762590b 100644 --- a/src-tauri/crates/olm_core/src/turn/post_match.rs +++ b/src-tauri/crates/olm_core/src/turn/post_match.rs @@ -825,8 +825,13 @@ fn deplete_match_stamina(game: &mut Game, team_id: &str, report: &crate::engine: } let minutes_factor = minutes as f64 / 90.0; let stamina_factor = player.attributes.mental_resilience as f64 / 100.0; - let base_depletion = 40.0 * (1.0 - stamina_factor * 0.4); - let depletion = (base_depletion * minutes_factor) as u8; + + // Randomised per-player depletion between 15-30 base. + // This replaces the old fixed 40, which was so aggressive that + // condition hit 0% midway through the split with no way to recover. + let random_base = 15.0 + rand::random::() * 15.0; // 15.0 – 30.0 + let base_depletion = random_base * (1.0 - stamina_factor * 0.25); + let depletion = (base_depletion * minutes_factor).ceil() as u8; player.condition = player.condition.saturating_sub(depletion); if minutes >= 60 {