diff --git a/src/worker/events.rs b/src/worker/events.rs index 8abf30d..20d1e52 100644 --- a/src/worker/events.rs +++ b/src/worker/events.rs @@ -595,7 +595,7 @@ impl EventsWorker { max_change, } => { if effect_roll < *probability - && let Ok((character_id, health_change, died)) = Self::apply_character_health_change( + && let Ok((character_id, health_change, died, first_name, last_name)) = Self::apply_character_health_change( &mut conn, pool, broker, @@ -608,23 +608,30 @@ impl EventsWorker { effect_results.push(json!({ "type": "character_health_change", "character_id": character_id, - "change": health_change + "change": health_change, + "character_first_name": first_name, + "character_last_name": last_name })); if died { effect_results.push(json!({ "type": "character_death", - "character_id": character_id + "character_id": character_id, + "character_first_name": first_name, + "character_last_name": last_name })); } } } EventEffect::CharacterDeath { probability } => { if effect_roll < *probability - && let Ok(character_id) = Self::apply_character_death(&mut conn, user_id, pool, broker) + && let Ok((character_id, first_name, last_name)) = + Self::apply_character_death(&mut conn, user_id, pool, broker) { effect_results.push(json!({ "type": "character_death", - "character_id": character_id + "character_id": character_id, + "character_first_name": first_name, + "character_last_name": last_name })); } } @@ -675,18 +682,56 @@ impl EventsWorker { eff.get("character_id").and_then(|v| v.as_i64()).map(|n| n as i32) }); + // Optional: Namen-Snapshot aus Effekten extrahieren (auch wenn der Charakter danach stirbt/gelöscht wird) + let (top_first_name, top_last_name) = if let Some(cid) = top_character_id { + let eff = effect_results.iter().find(|e| { + e.get("character_id") + .and_then(|v| v.as_i64()) + .map(|n| n as i32) + == Some(cid) + }); + let fn_ = eff + .and_then(|e| e.get("character_first_name")) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()); + let ln_ = eff + .and_then(|e| e.get("character_last_name")) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()); + (fn_, ln_) + } else { + (None, None) + }; + + // Frontend: `tr` ist der i18n-Key, Details liegen in `value` (Interpolation/Detailansicht) let mut notification_json = serde_json::json!({ "tr": format!("random_event.{}", event.id), - "event_id": event.id, - "event_type": "personal", - "effects": effect_results + "value": { + "event_id": event.id, + "event_type": "personal", + "title": event.title, + "description": event.description, + "effects": effect_results + } }); if let Some(cid) = top_character_id { - notification_json["character_id"] = serde_json::json!(cid); + notification_json["value"]["character_id"] = serde_json::json!(cid); + } + if let Some(fn_) = top_first_name { + notification_json["value"]["character_first_name"] = serde_json::json!(fn_); + } + if let Some(ln_) = top_last_name { + notification_json["value"]["character_last_name"] = serde_json::json!(ln_); } - Self::notify_user(pool, broker, user_id, ¬ification_json.to_string())?; + Self::notify_user( + pool, + broker, + user_id, + ¬ification_json.to_string(), + top_character_id, + )?; // Sende Benachrichtigung über WebSocket let mut notification = json!({ @@ -753,6 +798,9 @@ impl EventsWorker { } }; + let first_name = rows.first().and_then(|r| r.get("first_name")).cloned(); + let last_name = rows.first().and_then(|r| r.get("last_name")).cloned(); + // Wende Effekte an (in diesem Fall nur CharacterDeath) let mut effect_results = Vec::new(); for effect in &event.effects { @@ -764,7 +812,9 @@ impl EventsWorker { { effect_results.push(json!({ "type": "character_death", - "character_id": character_id + "character_id": character_id, + "character_first_name": first_name, + "character_last_name": last_name })); } } @@ -780,12 +830,25 @@ impl EventsWorker { // Schreibe Benachrichtigung in die Datenbank mit Event-Details let notification_json = serde_json::json!({ "tr": format!("random_event.{}", event.id), - "event_id": event.id, - "event_type": "personal", - "character_id": character_id, - "effects": effect_results + "value": { + "event_id": event.id, + "event_type": "personal", + "title": event.title, + "description": event.description, + "character_id": character_id, + "character_first_name": first_name, + "character_last_name": last_name, + "effects": effect_results + } }); - Self::notify_user(pool, broker, user_id, ¬ification_json.to_string())?; + // Falls das Kind inzwischen gelöscht wurde, halten wir Name + ID im Payload fest. + Self::notify_user( + pool, + broker, + user_id, + ¬ification_json.to_string(), + Some(character_id), + )?; // Sende Benachrichtigung über WebSocket let notification = json!({ @@ -1041,12 +1104,18 @@ impl EventsWorker { // Schreibe Benachrichtigung in die Datenbank mit Event-Details let notification_json = serde_json::json!({ "tr": format!("random_event.{}", event.id), - "event_id": event.id, - "event_type": "regional", - "region_id": region_id, - "effects": effect_results + "value": { + "event_id": event.id, + "event_type": "regional", + "title": event.title, + "description": event.description, + "region_id": region_id, + "effects": effect_results + } }); - if let Err(err) = Self::notify_user(pool, broker, uid, ¬ification_json.to_string()) { + if let Err(err) = + Self::notify_user(pool, broker, uid, ¬ification_json.to_string(), None) + { eprintln!("[EventsWorker] Fehler beim Schreiben der Benachrichtigung für User {}: {}", uid, err); } @@ -1350,7 +1419,7 @@ impl EventsWorker { min_change: i32, max_change: i32, rng: &mut impl Rng, - ) -> Result<(i32, i32, bool), DbError> { + ) -> Result<(i32, i32, bool, Option, Option), DbError> { // Hole einen zufälligen Charakter des Spielers conn.prepare("get_random_character", QUERY_GET_RANDOM_CHARACTER)?; let rows = conn.execute("get_random_character", &[&user_id])?; @@ -1374,6 +1443,10 @@ impl EventsWorker { .and_then(|v| v.parse::().ok()) .unwrap_or(100); + // Namen-Snapshot (wichtig, falls der Charakter durch das Event stirbt und gelöscht wird) + let first_name = rows.first().and_then(|r| r.get("first_name")).cloned(); + let last_name = rows.first().and_then(|r| r.get("last_name")).cloned(); + let health_change = rng.gen_range(min_change..=max_change); let new_health = (current_health + health_change).clamp(0, 100); @@ -1389,7 +1462,7 @@ impl EventsWorker { let _ = Self::handle_character_death(pool, broker, character_id); } - Ok((character_id, health_change, died)) + Ok((character_id, health_change, died, first_name, last_name)) } fn apply_character_death( @@ -1397,7 +1470,7 @@ impl EventsWorker { user_id: i32, pool: &ConnectionPool, broker: &MessageBroker, - ) -> Result { + ) -> Result<(i32, Option, Option), DbError> { // Hole einen zufälligen Charakter des Spielers conn.prepare("get_random_character_death", QUERY_GET_RANDOM_CHARACTER)?; let rows = conn.execute("get_random_character_death", &[&user_id])?; @@ -1415,11 +1488,14 @@ impl EventsWorker { } }; + let first_name = rows.first().and_then(|r| r.get("first_name")).cloned(); + let last_name = rows.first().and_then(|r| r.get("last_name")).cloned(); + // Verwende die existierende Logik zum Löschen von Charakteren // (ähnlich wie in CharacterCreationWorker) Self::handle_character_death(pool, broker, character_id)?; - Ok(character_id) + Ok((character_id, first_name, last_name)) } fn apply_regional_character_health_change( @@ -1535,7 +1611,7 @@ impl EventsWorker { .get("employer_user_id") .and_then(|v| v.parse::().ok()) { - Self::notify_user(pool, broker, user_id, "director_death")?; + Self::notify_user(pool, broker, user_id, "director_death", None)?; } } @@ -1547,7 +1623,7 @@ impl EventsWorker { .get("related_user_id") .and_then(|v| v.parse::().ok()) { - Self::notify_user(pool, broker, related_user_id, "relationship_death")?; + Self::notify_user(pool, broker, related_user_id, "relationship_death", None)?; } } @@ -1575,13 +1651,13 @@ impl EventsWorker { .get("father_user_id") .and_then(|v| v.parse::().ok()) { - Self::notify_user(pool, broker, father_user_id, "child_death")?; + Self::notify_user(pool, broker, father_user_id, "child_death", None)?; } if let Some(mother_user_id) = row .get("mother_user_id") .and_then(|v| v.parse::().ok()) { - Self::notify_user(pool, broker, mother_user_id, "child_death")?; + Self::notify_user(pool, broker, mother_user_id, "child_death", None)?; } } @@ -1744,9 +1820,10 @@ impl EventsWorker { broker: &MessageBroker, user_id: i32, event_type: &str, + character_id: Option, ) -> Result<(), DbError> { // DB-Notification (zentralisiert). Historisch wird hier als `tr` der event_type-String gespeichert. - insert_notification(pool, user_id, event_type, None)?; + insert_notification(pool, user_id, event_type, character_id)?; // Frontend-Update (zentralisiert) publish_update_status(broker, user_id); diff --git a/src/worker/sql.rs b/src/worker/sql.rs index 8f6b2ba..f36a7ae 100644 --- a/src/worker/sql.rs +++ b/src/worker/sql.rs @@ -14,7 +14,12 @@ SELECT id FROM falukant_data.falukant_user ORDER BY RANDOM() LIMIT 1; "#; pub const QUERY_GET_RANDOM_INFANT: &str = r#" -SELECT c.id AS character_id, c.user_id, CURRENT_DATE - c.birthdate::date AS age_days +SELECT + c.id AS character_id, + c.user_id, + c.first_name, + c.last_name, + CURRENT_DATE - c.birthdate::date AS age_days FROM falukant_data."character" c WHERE c.user_id IS NOT NULL AND c.health > 0 AND CURRENT_DATE - c.birthdate::date <= 730 ORDER BY RANDOM() LIMIT 1; @@ -693,7 +698,7 @@ WHERE region_id = $1; "#; pub const QUERY_GET_RANDOM_CHARACTER: &str = r#" -SELECT id, health +SELECT id, health, first_name, last_name FROM falukant_data."character" WHERE user_id = $1 AND health > 0 ORDER BY RANDOM() LIMIT 1;