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_INSTALLMENT, QUERY_GET_LOVER_PREGNANCY_CANDIDATES,
QUERY_HAS_MOTHER_BIRTH_TODAY,
QUERY_GET_MARRIAGE_ROWS, QUERY_GET_USER_HOUSE_ROW_BY_USER, QUERY_INSERT_CHILD,
QUERY_INSERT_CHILD_RELATION_LOVER, QUERY_LOVER_BIRTH_PENALTY_MARRIAGE,
QUERY_LOVER_BIRTH_PENALTY_REPUTATION, QUERY_LOVER_INSTALLMENT_SCHEMA_READY,
QUERY_GET_MARRIAGE_ROWS, QUERY_GET_USER_HOUSE_ROW_BY_USER, QUERY_LOVER_INSTALLMENT_SCHEMA_READY,
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_UPDATE_CHARACTER_REPUTATION, QUERY_UPDATE_LOVER_UNDERPAY_STATE,
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}")))?;
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", &[])?;
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)?;
for row in rows {
@@ -1024,60 +1020,28 @@ impl FalukantFamilyWorker {
if father_cid < 0 || mother_cid < 0 {
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 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;
}
let gender = if self.dist.sample(&mut self.rng) < 0.5 {
"male"
} 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 {
let set_rows = conn.execute("set_lover_preg", &[&mother_cid, &father_cid])?;
if set_rows.is_empty() {
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 {
self.publish_children(uid);
self.publish_falukant_update_family_and_status(uid, "lover_pregnancy");
}
if let Some(uid) = mother_uid {
self.publish_children(uid);
self.publish_falukant_update_family_and_status(uid, "lover_pregnancy");
}
}
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(
conn: &mut crate::db::DbConnection,
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.
// 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`.
/// 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';
"#;
/// 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#"
WITH paired AS (
SELECT
@@ -2333,8 +2333,8 @@ pub const QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE: &str = r#"
AND ma.mother_age_days < 18993
AND random() < ma.prob_year
)
UPDATE falukant_data.relationship r
SET marriage_pregnancy_due_at = NOW() + INTERVAL '5 days'
UPDATE falukant_data.relationship r
SET marriage_pregnancy_due_at = NOW() + INTERVAL '18 hours'
FROM conceivable c
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 fu2 ON fu2.id = c2.user_id
WHERE (c1.gender = 'female' AND c2.gender = 'male')
OR (c1.gender = 'male' AND c2.gender = 'female')
WHERE ((c1.gender = 'female' AND c2.gender = 'male')
OR (c1.gender = 'male' AND c2.gender = 'female'))
AND rs.affection >= 45
AND rs.maintenance_level >= 30
-- `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 >= NOW() - INTERVAL '50 days'
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 (
SELECT 1
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#"
UPDATE falukant_data.relationship_state rs
SET marriage_satisfaction = GREATEST(0, COALESCE(rs.marriage_satisfaction, 55) - 8),
updated_at = NOW()
FROM falukant_data.relationship r
JOIN falukant_type.relationship rt ON rt.id = r.relationship_type_id
WHERE rt.id = r.relationship_type_id
AND rs.relationship_id = r.id
AND rt.tr IN ('married', 'engaged', 'wooing')
AND (r.character1_id = $1::int OR r.character2_id = $1::int);
"#;
pub const QUERY_LOVER_BIRTH_PENALTY_REPUTATION: &str = r#"
UPDATE falukant_data.character
SET reputation = GREATEST(0::numeric, COALESCE(reputation, 50::numeric) - 4::numeric),
updated_at = NOW()
WHERE id = $1::int;
"#;
pub const QUERY_INSERT_CHILD_RELATION_LOVER: &str = r#"
INSERT INTO falukant_data.child_relation (
father_character_id,
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()
);
/// Liebschafts-Konzeption setzt eine geplante Schwangerschaft am Mutter-Charakter
/// (gleicher Geburtsweg wie Admin/Spiel): Geburt erfolgt, wenn `pregnancy_due_at <= NOW()`.
pub const QUERY_SET_CHARACTER_PREGNANCY_FROM_LOVER: &str = r#"
UPDATE falukant_data.character c_m
SET pregnancy_due_at = NOW() + INTERVAL '18 hours',
pregnancy_father_character_id = $2::int,
updated_at = NOW()
WHERE c_m.id = $1::int
AND c_m.health > 0
AND c_m.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_m.id)
OR (cm2.gender = 'female' AND cm2.id = c_m.id))
)
RETURNING c_m.id AS mother_cid;
"#;
/// 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`).
/// 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.
fn maybe_run_hourly_pregnancies(&mut self) {
let now = Instant::now();
@@ -677,7 +677,7 @@ impl UserCharacterWorker {
let father_uid = parse_opt_i32(row, "father_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(());
}
@@ -738,7 +738,7 @@ impl UserCharacterWorker {
let father_uid = parse_opt_i32(row, "father_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(());
}