From f96e474f3250781680d5438cc92d628f7d9c49af Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Wed, 7 Jan 2026 10:14:24 +0100 Subject: [PATCH] Add random heir fallback logic in EventsWorker: Implement a new SQL query to select a random NPC heir from the same region when no children are available. Enhance error handling to log cases where no heir is found, ensuring better user feedback and maintaining character associations. Update UserCharacterWorker to utilize this new logic for heir assignment. --- src/worker/events.rs | 23 ++++++++++++++++------ src/worker/sql.rs | 37 ++++++++++++++++++++++++++++++++++-- src/worker/user_character.rs | 24 +++++++++++++++++++++++ 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/src/worker/events.rs b/src/worker/events.rs index 19afb6d..c0bddc9 100644 --- a/src/worker/events.rs +++ b/src/worker/events.rs @@ -45,6 +45,7 @@ use crate::worker::sql::{ QUERY_DELETE_CHILD_RELATION, QUERY_DELETE_CHARACTER, QUERY_GET_HEIR, + QUERY_GET_RANDOM_HEIR_FROM_REGION, QUERY_SET_CHARACTER_USER, QUERY_CLEAR_CHARACTER_USER, QUERY_GET_CURRENT_MONEY, @@ -1589,18 +1590,28 @@ impl EventsWorker { .and_then(|r| r.get("child_character_id")) .and_then(|v| v.parse::().ok()); + // Kein Kind als Erbe vorhanden? Dann fallback: zufälliger NPC-Character aus der Region, + // Alter 10–14 Tage. let heir_id = match heir_id { Some(id) if id > 0 => id, _ => { - // Kein Erbe gefunden - Vermögen geht verloren - eprintln!( - "[EventsWorker] Kein Erbe für Charakter {} gefunden, Vermögen geht verloren", - deceased_character_id - ); - return Ok(()); + conn.prepare("random_heir_region", QUERY_GET_RANDOM_HEIR_FROM_REGION)?; + let rows = conn.execute("random_heir_region", &[&deceased_character_id])?; + rows.first() + .and_then(|r| r.get("child_character_id")) + .and_then(|v| v.parse::().ok()) + .unwrap_or(0) } }; + if heir_id <= 0 { + eprintln!( + "[EventsWorker] Kein Erbe für Charakter {} gefunden (weder Kind noch Fallback in Region). User {} hat danach keinen Character.", + deceased_character_id, falukant_user_id + ); + return Ok(()); + } + // 2) Wichtig: erst die alte User-Zuordnung am verstorbenen Charakter lösen. // Falls es einen Unique-Constraint auf `character.user_id` gibt, würde das // direkte Setzen am Erben sonst fehlschlagen. diff --git a/src/worker/sql.rs b/src/worker/sql.rs index d2f1aab..8f6b2ba 100644 --- a/src/worker/sql.rs +++ b/src/worker/sql.rs @@ -1351,6 +1351,31 @@ pub const QUERY_RANDOM_HEIR: &str = r#" chosen.child_character_id; "#; +// Fallback-Erbe: Wenn ein Spieler-Character ohne Kinder stirbt, suchen wir einen zufälligen +// NPC-Character in derselben Region (Alter 10–14 Tage) und übergeben ihm die user_id. +pub const QUERY_GET_RANDOM_HEIR_FROM_REGION: &str = r#" + SELECT ch.id AS child_character_id + FROM falukant_data.character ch + WHERE ch.user_id IS NULL + AND ch.health > 0 + AND ch.id <> $1 + AND ch.region_id = ( + SELECT region_id + FROM falukant_data.character + WHERE id = $1 + ) + -- Alter zwischen 10 und 14 Tagen: birthdate in [now-14d, now-10d] + AND ch.birthdate <= NOW() - INTERVAL '10 days' + AND ch.birthdate >= NOW() - INTERVAL '14 days' + AND ch.title_of_nobility = ( + SELECT id + FROM falukant_type.title + WHERE label_tr = 'noncivil' + ) + ORDER BY RANDOM() + LIMIT 1; +"#; + pub const QUERY_UPDATE_USER_MONEY: &str = r#" UPDATE falukant_data.falukant_user SET money = $1, @@ -1600,8 +1625,16 @@ pub const QUERY_GET_FINISHED_PRODUCTIONS: &str = r#" ON p.product_id = pr.id JOIN falukant_data.branch br ON p.branch_id = br.id - JOIN falukant_data.character c - ON c.user_id = br.falukant_user_id + -- Es kann vorkommen, dass ein User temporär keinen zugeordneten Character hat + -- (z.B. nach Tod/Erbe). Produktionen sollen trotzdem abschließen. + -- LATERAL verhindert Duplikate, falls ein User mehrere Characters hat. + LEFT JOIN LATERAL ( + SELECT c.id, c.user_id + FROM falukant_data.character c + WHERE c.user_id = br.falukant_user_id + ORDER BY c.updated_at DESC NULLS LAST, c.id DESC + LIMIT 1 + ) c ON TRUE LEFT JOIN falukant_data.knowledge k ON p.product_id = k.product_id AND k.character_id = c.id diff --git a/src/worker/user_character.rs b/src/worker/user_character.rs index 201bf05..3d8026c 100644 --- a/src/worker/user_character.rs +++ b/src/worker/user_character.rs @@ -28,6 +28,7 @@ use crate::worker::sql::{ QUERY_COUNT_CHILDREN, QUERY_GET_HEIR, QUERY_RANDOM_HEIR, + QUERY_GET_RANDOM_HEIR_FROM_REGION, QUERY_SET_CHARACTER_USER, QUERY_CLEAR_CHARACTER_USER, QUERY_UPDATE_USER_MONEY, @@ -608,6 +609,12 @@ impl UserCharacterWorker { new_money = self.calculate_new_money(falukant_user_id, heir_id > 0)?; } + // Wenn es gar keine Kinder gibt, nimm einen zufälligen NPC in der Region (Alter 10–14 Tage). + if heir_id < 1 { + heir_id = self.get_random_heir_from_region(character_id)?; + new_money = self.calculate_new_money(falukant_user_id, heir_id > 0)?; + } + if heir_id > 0 { // Erst die alte Zuordnung lösen (Unique-Constraint safety), dann den Erben zuweisen. self.clear_character_user(character_id)?; @@ -669,6 +676,23 @@ impl UserCharacterWorker { .unwrap_or(-1)) } + fn get_random_heir_from_region(&mut self, deceased_character_id: i32) -> Result { + let mut conn = self + .base + .pool + .get() + .map_err(|e| DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?; + + conn.prepare("random_heir_region", QUERY_GET_RANDOM_HEIR_FROM_REGION)?; + let rows = conn.execute("random_heir_region", &[&deceased_character_id])?; + + Ok(rows + .first() + .and_then(|r| r.get("child_character_id")) + .and_then(|v| v.parse::().ok()) + .unwrap_or(-1)) + } + fn set_new_character( &mut self, falukant_user_id: i32,