Aktualisiere Logik zur Schwangerschaftsplanung: Ändere die Intervalle für die Schwangerschaftsplanung in den SQL-Abfragen auf 18 Stunden und entferne nicht benötigte Abfragen zur Geburt. Dies verbessert die Einhaltung der Regel, dass eine Mutter pro Kalendertag nur ein Kind gebären kann.
All checks were successful
Deploy yourpart (blue-green) / deploy (push) Successful in 1m44s

This commit is contained in:
Torsten Schulz (local)
2026-05-21 09:31:02 +02:00
parent cb22b111a3
commit 87f6e7315d
3 changed files with 50 additions and 95 deletions

View File

@@ -23,10 +23,9 @@ use super::sql::{
QUERY_GET_ACTIVE_LOVER_ROWS_FOR_DAILY, QUERY_GET_ACTIVE_LOVER_ROWS_FOR_MONTHLY, QUERY_GET_ACTIVE_LOVER_ROWS_FOR_DAILY, QUERY_GET_ACTIVE_LOVER_ROWS_FOR_MONTHLY,
QUERY_GET_ACTIVE_LOVER_ROWS_FOR_INSTALLMENT, QUERY_GET_LOVER_PREGNANCY_CANDIDATES, QUERY_GET_ACTIVE_LOVER_ROWS_FOR_INSTALLMENT, QUERY_GET_LOVER_PREGNANCY_CANDIDATES,
QUERY_HAS_MOTHER_BIRTH_TODAY, QUERY_HAS_MOTHER_BIRTH_TODAY,
QUERY_GET_MARRIAGE_ROWS, QUERY_GET_USER_HOUSE_ROW_BY_USER, QUERY_INSERT_CHILD, QUERY_GET_MARRIAGE_ROWS, QUERY_GET_USER_HOUSE_ROW_BY_USER, QUERY_LOVER_INSTALLMENT_SCHEMA_READY,
QUERY_INSERT_CHILD_RELATION_LOVER, QUERY_LOVER_BIRTH_PENALTY_MARRIAGE,
QUERY_LOVER_BIRTH_PENALTY_REPUTATION, QUERY_LOVER_INSTALLMENT_SCHEMA_READY,
QUERY_MARK_LOVER_DAILY_DONE, QUERY_MARK_LOVER_INSTALLMENT_AT, QUERY_MARK_LOVER_MONTHLY_DONE, QUERY_MARK_LOVER_DAILY_DONE, QUERY_MARK_LOVER_INSTALLMENT_AT, QUERY_MARK_LOVER_MONTHLY_DONE,
QUERY_SET_CHARACTER_PREGNANCY_FROM_LOVER,
QUERY_MARRIAGE_SUBTRACT_SATISFACTION, QUERY_RESET_LOVER_UNDERPAY_COUNTERS, QUERY_MARRIAGE_SUBTRACT_SATISFACTION, QUERY_RESET_LOVER_UNDERPAY_COUNTERS,
QUERY_UPDATE_CHARACTER_REPUTATION, QUERY_UPDATE_LOVER_UNDERPAY_STATE, QUERY_UPDATE_CHARACTER_REPUTATION, QUERY_UPDATE_LOVER_UNDERPAY_STATE,
QUERY_UPDATE_LOVER_VISIBILITY_DISCRETION, QUERY_UPDATE_MARRIAGE_STATE_AND_BUFFS, QUERY_UPDATE_LOVER_VISIBILITY_DISCRETION, QUERY_UPDATE_MARRIAGE_STATE_AND_BUFFS,
@@ -1010,12 +1009,9 @@ impl FalukantFamilyWorker {
.map_err(|e| DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?; .map_err(|e| DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?;
conn.prepare("lover_preg", QUERY_GET_LOVER_PREGNANCY_CANDIDATES)?; conn.prepare("lover_preg", QUERY_GET_LOVER_PREGNANCY_CANDIDATES)?;
conn.prepare("set_lover_preg", QUERY_SET_CHARACTER_PREGNANCY_FROM_LOVER)?;
let rows = conn.execute("lover_preg", &[])?; let rows = conn.execute("lover_preg", &[])?;
conn.prepare("insert_child", QUERY_INSERT_CHILD)?;
conn.prepare("insert_child_rel_lover", QUERY_INSERT_CHILD_RELATION_LOVER)?;
conn.prepare("pen_mar", QUERY_LOVER_BIRTH_PENALTY_MARRIAGE)?;
conn.prepare("pen_rep", QUERY_LOVER_BIRTH_PENALTY_REPUTATION)?;
conn.prepare("has_mother_birth_today", QUERY_HAS_MOTHER_BIRTH_TODAY)?; conn.prepare("has_mother_birth_today", QUERY_HAS_MOTHER_BIRTH_TODAY)?;
for row in rows { for row in rows {
@@ -1024,60 +1020,28 @@ impl FalukantFamilyWorker {
if father_cid < 0 || mother_cid < 0 { if father_cid < 0 || mother_cid < 0 {
continue; continue;
} }
let title_of_nobility = parse_i32(&row, "title_of_nobility", 0);
let last_name = parse_i32(&row, "last_name", 0);
let region_id = parse_i32(&row, "region_id", 0);
let father_uid = parse_opt_i32(&row, "father_uid"); let father_uid = parse_opt_i32(&row, "father_uid");
let mother_uid = parse_opt_i32(&row, "mother_uid"); let mother_uid = parse_opt_i32(&row, "mother_uid");
if mother_uid.is_some() && Self::mother_already_had_birth_today(&mut conn, mother_cid)? { if Self::mother_already_had_birth_today(&mut conn, mother_cid)? {
continue; continue;
} }
let gender = if self.dist.sample(&mut self.rng) < 0.5 { let set_rows = conn.execute("set_lover_preg", &[&mother_cid, &father_cid])?;
"male" if set_rows.is_empty() {
} else {
"female"
};
let inserted =
conn.execute("insert_child", &[&region_id, &gender, &last_name, &title_of_nobility])?;
let child_cid = inserted
.first()
.and_then(|r| r.get("child_cid"))
.and_then(|v| v.parse::<i32>().ok())
.unwrap_or(-1);
if child_cid < 0 {
continue; continue;
} }
conn.execute(
"insert_child_rel_lover",
&[&father_cid, &mother_cid, &child_cid],
)?;
conn.execute("pen_mar", &[&father_cid])?;
conn.execute("pen_mar", &[&mother_cid])?;
conn.execute("pen_rep", &[&father_cid])?;
conn.execute("pen_rep", &[&mother_cid])?;
if let Some(uid) = father_uid { if let Some(uid) = father_uid {
self.publish_children(uid); self.publish_falukant_update_family_and_status(uid, "lover_pregnancy");
} }
if let Some(uid) = mother_uid { if let Some(uid) = mother_uid {
self.publish_children(uid); self.publish_falukant_update_family_and_status(uid, "lover_pregnancy");
} }
} }
Ok(()) Ok(())
} }
fn publish_children(&self, user_id: i32) {
let children_update =
format!(r#"{{"event":"children_update","user_id":{}}}"#, user_id);
self.base.broker.publish(children_update);
self.publish_falukant_update_family_and_status(user_id, "lover_birth");
}
fn mother_already_had_birth_today( fn mother_already_had_birth_today(
conn: &mut crate::db::DbConnection, conn: &mut crate::db::DbConnection,
mother_character_id: i32, mother_character_id: i32,

View File

@@ -2179,7 +2179,7 @@ pub const QUERY_AUTOBATISM: &str = r#"
// im Alter 18~44 (657016000 Tage), damit kinderlose Ehen nicht über viele Jahre ohne Nachwuchs bleiben. // im Alter 18~44 (657016000 Tage), damit kinderlose Ehen nicht über viele Jahre ohne Nachwuchs bleiben.
// WICHTIG: Vater/Mutter und Alter immer über gender ableiten — nicht character1/2 fest als Mutter! // WICHTIG: Vater/Mutter und Alter immer über gender ableiten — nicht character1/2 fest als Mutter!
// //
// Ablauf Ehe: (1) Konzeption setzt `marriage_pregnancy_due_at` (+5 Tage), (2) Geburt wenn fällig. // Ablauf Ehe: (1) Konzeption setzt `marriage_pregnancy_due_at` (+18 Stunden), (2) Geburt wenn fällig.
// Migration: `008_falukant_marriage_pregnancy_due.sql`. // Migration: `008_falukant_marriage_pregnancy_due.sql`.
/// Fällige Geburten (Ehe): `marriage_pregnancy_due_at <= NOW()`. /// Fällige Geburten (Ehe): `marriage_pregnancy_due_at <= NOW()`.
@@ -2234,7 +2234,7 @@ pub const QUERY_CLEAR_STALE_MARRIAGE_PREGNANCY_DUE: &str = r#"
AND marriage_pregnancy_due_at < NOW() - INTERVAL '30 days'; AND marriage_pregnancy_due_at < NOW() - INTERVAL '30 days';
"#; "#;
/// Täglicher Konzeptionswurf (Ehe): bei Treffer wird `marriage_pregnancy_due_at` auf +5 Tage gesetzt. /// Täglicher Konzeptionswurf (Ehe): bei Treffer wird `marriage_pregnancy_due_at` auf +18 Stunden gesetzt.
pub const QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE: &str = r#" pub const QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE: &str = r#"
WITH paired AS ( WITH paired AS (
SELECT SELECT
@@ -2333,8 +2333,8 @@ pub const QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE: &str = r#"
AND ma.mother_age_days < 18993 AND ma.mother_age_days < 18993
AND random() < ma.prob_year AND random() < ma.prob_year
) )
UPDATE falukant_data.relationship r UPDATE falukant_data.relationship r
SET marriage_pregnancy_due_at = NOW() + INTERVAL '5 days' SET marriage_pregnancy_due_at = NOW() + INTERVAL '18 hours'
FROM conceivable c FROM conceivable c
WHERE r.id = c.relationship_id; WHERE r.id = c.relationship_id;
"#; "#;
@@ -3962,14 +3962,25 @@ pub const QUERY_GET_LOVER_PREGNANCY_CANDIDATES: &str = r#"
) )
LEFT JOIN falukant_data.falukant_user fu1 ON fu1.id = c1.user_id LEFT JOIN falukant_data.falukant_user fu1 ON fu1.id = c1.user_id
LEFT JOIN falukant_data.falukant_user fu2 ON fu2.id = c2.user_id LEFT JOIN falukant_data.falukant_user fu2 ON fu2.id = c2.user_id
WHERE (c1.gender = 'female' AND c2.gender = 'male') WHERE ((c1.gender = 'female' AND c2.gender = 'male')
OR (c1.gender = 'male' AND c2.gender = 'female') OR (c1.gender = 'male' AND c2.gender = 'female'))
AND rs.affection >= 45 AND rs.affection >= 45
AND rs.maintenance_level >= 30 AND rs.maintenance_level >= 30
-- `last_monthly_processed_at` wird im Familien-Tageslauf mitgeführt (1 Spieljahr = 1 Kalendertag). -- `last_monthly_processed_at` wird im Familien-Tageslauf mitgeführt (1 Spieljahr = 1 Kalendertag).
AND rs.last_monthly_processed_at IS NOT NULL AND rs.last_monthly_processed_at IS NOT NULL
AND rs.last_monthly_processed_at >= NOW() - INTERVAL '50 days' AND rs.last_monthly_processed_at >= NOW() - INTERVAL '50 days'
AND c_female.pregnancy_due_at IS NULL AND c_female.pregnancy_due_at IS NULL
AND NOT EXISTS (
SELECT 1
FROM falukant_data.relationship rm
JOIN falukant_type.relationship rtm
ON rtm.id = rm.relationship_type_id AND rtm.tr = 'married'
JOIN falukant_data.character cm1 ON cm1.id = rm.character1_id
JOIN falukant_data.character cm2 ON cm2.id = rm.character2_id
WHERE rm.marriage_pregnancy_due_at IS NOT NULL
AND ((cm1.gender = 'female' AND cm1.id = c_female.id)
OR (cm2.gender = 'female' AND cm2.id = c_female.id))
)
AND NOT EXISTS ( AND NOT EXISTS (
SELECT 1 SELECT 1
FROM falukant_data.child_relation cr FROM falukant_data.child_relation cr
@@ -3997,48 +4008,28 @@ pub const QUERY_GET_LOVER_PREGNANCY_CANDIDATES: &str = r#"
); );
"#; "#;
pub const QUERY_LOVER_BIRTH_PENALTY_MARRIAGE: &str = r#" /// Liebschafts-Konzeption setzt eine geplante Schwangerschaft am Mutter-Charakter
UPDATE falukant_data.relationship_state rs /// (gleicher Geburtsweg wie Admin/Spiel): Geburt erfolgt, wenn `pregnancy_due_at <= NOW()`.
SET marriage_satisfaction = GREATEST(0, COALESCE(rs.marriage_satisfaction, 55) - 8), pub const QUERY_SET_CHARACTER_PREGNANCY_FROM_LOVER: &str = r#"
updated_at = NOW() UPDATE falukant_data.character c_m
FROM falukant_data.relationship r SET pregnancy_due_at = NOW() + INTERVAL '18 hours',
JOIN falukant_type.relationship rt ON rt.id = r.relationship_type_id pregnancy_father_character_id = $2::int,
WHERE rt.id = r.relationship_type_id updated_at = NOW()
AND rs.relationship_id = r.id WHERE c_m.id = $1::int
AND rt.tr IN ('married', 'engaged', 'wooing') AND c_m.health > 0
AND (r.character1_id = $1::int OR r.character2_id = $1::int); AND c_m.pregnancy_due_at IS NULL
"#; AND NOT EXISTS (
SELECT 1
pub const QUERY_LOVER_BIRTH_PENALTY_REPUTATION: &str = r#" FROM falukant_data.relationship rm
UPDATE falukant_data.character JOIN falukant_type.relationship rtm
SET reputation = GREATEST(0::numeric, COALESCE(reputation, 50::numeric) - 4::numeric), ON rtm.id = rm.relationship_type_id AND rtm.tr = 'married'
updated_at = NOW() JOIN falukant_data.character cm1 ON cm1.id = rm.character1_id
WHERE id = $1::int; JOIN falukant_data.character cm2 ON cm2.id = rm.character2_id
"#; WHERE rm.marriage_pregnancy_due_at IS NOT NULL
AND ((cm1.gender = 'female' AND cm1.id = c_m.id)
pub const QUERY_INSERT_CHILD_RELATION_LOVER: &str = r#" OR (cm2.gender = 'female' AND cm2.id = c_m.id))
INSERT INTO falukant_data.child_relation ( )
father_character_id, RETURNING c_m.id AS mother_cid;
mother_character_id,
child_character_id,
name_set,
legitimacy,
birth_context,
public_known,
created_at,
updated_at
)
VALUES (
$1::int,
$2::int,
$3::int,
FALSE,
'hidden_bastard',
'lover',
FALSE,
NOW(),
NOW()
);
"#; "#;
/// Pro Mutter maximal eine Geburt pro Kalendertag (jeder Vater); Rust prüft zusätzlich vor Insert. /// Pro Mutter maximal eine Geburt pro Kalendertag (jeder Vater); Rust prüft zusätzlich vor Insert.

View File

@@ -193,7 +193,7 @@ impl UserCharacterWorker {
} }
/// Ehe: Geburten/stale stündlich; Konzeption höchstens einmal pro Kalendertag (ein Jahreswurf, `prob_year`). /// Ehe: Geburten/stale stündlich; Konzeption höchstens einmal pro Kalendertag (ein Jahreswurf, `prob_year`).
/// Konzeption setzt `marriage_pregnancy_due_at` (+5 Tage); Geburt über `QUERY_GET_MARRIAGE_BIRTH_DELIVERIES`. /// Konzeption setzt `marriage_pregnancy_due_at` (+18 Stunden); Geburt über `QUERY_GET_MARRIAGE_BIRTH_DELIVERIES`.
/// Kein separates „Hochzeitsnacht“-Event — erste Konzeption am nächsten Tageslauf nach der Hochzeit. /// Kein separates „Hochzeitsnacht“-Event — erste Konzeption am nächsten Tageslauf nach der Hochzeit.
fn maybe_run_hourly_pregnancies(&mut self) { fn maybe_run_hourly_pregnancies(&mut self) {
let now = Instant::now(); let now = Instant::now();
@@ -677,7 +677,7 @@ impl UserCharacterWorker {
let father_uid = parse_opt_i32(row, "father_uid"); let father_uid = parse_opt_i32(row, "father_uid");
let mother_uid = parse_opt_i32(row, "mother_uid"); let mother_uid = parse_opt_i32(row, "mother_uid");
if mother_uid.is_some() && mother_already_had_birth_today(conn, mother_cid)? { if mother_already_had_birth_today(conn, mother_cid)? {
return Ok(()); return Ok(());
} }
@@ -738,7 +738,7 @@ impl UserCharacterWorker {
let father_uid = parse_opt_i32(row, "father_uid"); let father_uid = parse_opt_i32(row, "father_uid");
let mother_uid = parse_opt_i32(row, "mother_uid"); let mother_uid = parse_opt_i32(row, "mother_uid");
if mother_uid.is_some() && mother_already_had_birth_today(conn, mother_cid)? { if mother_already_had_birth_today(conn, mother_cid)? {
return Ok(()); return Ok(());
} }