diff --git a/src/worker/events.rs b/src/worker/events.rs index 1329e8d..96cc4e2 100644 --- a/src/worker/events.rs +++ b/src/worker/events.rs @@ -548,6 +548,8 @@ impl EventsWorker { *min_change, *max_change, rng, + pool, + broker, ) { effect_results.push(json!({ @@ -842,6 +844,8 @@ impl EventsWorker { *min_change, *max_change, rng, + pool, + broker, ) { effect_results.push(json!({ "type": "character_health_change", @@ -1273,6 +1277,8 @@ impl EventsWorker { min_change: i32, max_change: i32, rng: &mut impl Rng, + pool: &ConnectionPool, + broker: &MessageBroker, ) -> Result<(i32, i32), DbError> { // Hole einen zufälligen Charakter des Spielers conn.prepare("get_random_character", QUERY_GET_RANDOM_CHARACTER)?; @@ -1304,6 +1310,13 @@ impl EventsWorker { conn.prepare("update_health", QUERY_UPDATE_HEALTH)?; conn.execute("update_health", &[&new_health, &character_id])?; + // Bei Health <= 0 sofort Tod prüfen und verarbeiten + if new_health <= 0 { + if let Err(e) = Self::handle_character_death(pool, broker, character_id) { + eprintln!("[EventsWorker] handle_character_death nach Health-Update: {e}"); + } + } + Ok((character_id, health_change)) } @@ -1343,12 +1356,15 @@ impl EventsWorker { min_change: i32, max_change: i32, rng: &mut impl Rng, + pool: &ConnectionPool, + broker: &MessageBroker, ) -> Result, DbError> { // Hole alle lebenden Charaktere in der Region conn.prepare("get_region_characters", QUERY_GET_REGION_CHARACTERS)?; let rows = conn.execute("get_region_characters", &[®ion_id])?; let mut affected_characters = Vec::new(); + let mut dead_character_ids = Vec::new(); for row in rows { let character_id: Option = row @@ -1373,6 +1389,16 @@ impl EventsWorker { conn.execute("update_health_regional", &[&new_health, &character_id])?; affected_characters.push((character_id, health_change)); + if new_health <= 0 { + dead_character_ids.push(character_id); + } + } + + // Bei Health <= 0 Tod verarbeiten + for character_id in dead_character_ids { + if let Err(e) = Self::handle_character_death(pool, broker, character_id) { + eprintln!("[EventsWorker] handle_character_death nach regionalem Health-Update: {e}"); + } } Ok(affected_characters) diff --git a/src/worker/sql.rs b/src/worker/sql.rs index 1d80545..e7a90b5 100644 --- a/src/worker/sql.rs +++ b/src/worker/sql.rs @@ -587,6 +587,11 @@ pub const QUERY_GET_USERS_TO_UPDATE: &str = r#" WHERE user_id IS NOT NULL; "#; +pub const QUERY_GET_CHARACTERS_ZERO_HEALTH: &str = r#" + SELECT id FROM falukant_data."character" + WHERE user_id IS NOT NULL AND health <= 0; +"#; + // politics worker queries pub const QUERY_COUNT_OFFICES_PER_REGION: &str = r#" WITH diff --git a/src/worker/user_character.rs b/src/worker/user_character.rs index cf423f4..df9bf6b 100644 --- a/src/worker/user_character.rs +++ b/src/worker/user_character.rs @@ -9,6 +9,7 @@ use std::time::{Duration, Instant}; use super::base::{BaseWorker, Worker, WorkerState}; use crate::worker::sql::{ + QUERY_GET_CHARACTERS_ZERO_HEALTH, QUERY_GET_USERS_TO_UPDATE, QUERY_UPDATE_CHARACTERS_HEALTH, QUERY_UPDATE_MOOD, @@ -59,6 +60,7 @@ pub struct UserCharacterWorker { last_hourly_run: Option, last_pregnancy_run: Option, last_mood_run: Option, + last_death_check_run: Option, } // SQL moved to `src/worker/sql.rs` @@ -76,6 +78,7 @@ impl UserCharacterWorker { last_hourly_run: None, last_pregnancy_run: None, last_mood_run: None, + last_death_check_run: None, } } @@ -83,6 +86,7 @@ impl UserCharacterWorker { self.base.set_current_step("UserCharacterWorker iteration"); self.maybe_run_hourly_tasks(); + self.maybe_run_death_check(); self.maybe_run_mood_updates(); self.maybe_run_daily_pregnancies(); @@ -116,6 +120,51 @@ impl UserCharacterWorker { self.last_hourly_run = Some(now); } + /// Alle 12 Stunden: Charaktere mit health <= 0 finden und Tod verarbeiten + fn maybe_run_death_check(&mut self) { + const DEATH_CHECK_INTERVAL_SECS: u64 = 12 * 3600; + let now = Instant::now(); + let should_run = match self.last_death_check_run { + None => true, + Some(last) => now.saturating_duration_since(last) >= Duration::from_secs(DEATH_CHECK_INTERVAL_SECS), + }; + + if !should_run { + return; + } + + if let Err(err) = self.process_zero_health_deaths() { + eprintln!("[UserCharacterWorker] Fehler bei Tod-Prüfung (health <= 0): {err}"); + } + + self.last_death_check_run = Some(now); + } + + fn process_zero_health_deaths(&mut self) -> Result<(), DbError> { + let ids: Vec = { + let mut conn = self + .base + .pool + .get() + .map_err(|e| DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?; + + conn.prepare("get_characters_zero_health", QUERY_GET_CHARACTERS_ZERO_HEALTH)?; + let rows = conn.execute("get_characters_zero_health", &[])?; + + rows.into_iter() + .filter_map(|r| r.get("id").and_then(|v| v.parse::().ok())) + .collect() + }; + + for character_id in ids { + if let Err(e) = self.handle_character_death(character_id) { + eprintln!("[UserCharacterWorker] handle_character_death für Charakter {}: {e}", character_id); + } + } + + Ok(()) + } + fn run_hourly_tasks(&mut self) -> Result<(), DbError> { self.process_character_events()?; self.handle_credits()?;