Add logic to prevent multiple births for mothers in a single day: Implemented a new SQL query to check if a mother has already given birth today. Updated the FalukantFamilyWorker and UserCharacterWorker to utilize this check, enhancing the birth registration process and ensuring compliance with the new birth rule.
All checks were successful
Deploy yourpart (blue-green) / deploy (push) Successful in 1m35s

This commit is contained in:
Torsten Schulz (local)
2026-04-14 07:50:56 +02:00
parent 6e931c1069
commit b0ee0da722
3 changed files with 47 additions and 0 deletions

View File

@@ -22,6 +22,7 @@ use super::sql::{
QUERY_COUNT_LOVER_CHILDREN_FOR_USER, QUERY_FAMILY_SCHEMA_READY, QUERY_COUNT_LOVER_CHILDREN_FOR_USER, QUERY_FAMILY_SCHEMA_READY,
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_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_INSERT_CHILD,
QUERY_INSERT_CHILD_RELATION_LOVER, QUERY_LOVER_BIRTH_PENALTY_MARRIAGE, QUERY_INSERT_CHILD_RELATION_LOVER, QUERY_LOVER_BIRTH_PENALTY_MARRIAGE,
QUERY_LOVER_BIRTH_PENALTY_REPUTATION, QUERY_LOVER_INSTALLMENT_SCHEMA_READY, QUERY_LOVER_BIRTH_PENALTY_REPUTATION, QUERY_LOVER_INSTALLMENT_SCHEMA_READY,
@@ -1015,6 +1016,7 @@ impl FalukantFamilyWorker {
conn.prepare("insert_child_rel_lover", QUERY_INSERT_CHILD_RELATION_LOVER)?; conn.prepare("insert_child_rel_lover", QUERY_INSERT_CHILD_RELATION_LOVER)?;
conn.prepare("pen_mar", QUERY_LOVER_BIRTH_PENALTY_MARRIAGE)?; conn.prepare("pen_mar", QUERY_LOVER_BIRTH_PENALTY_MARRIAGE)?;
conn.prepare("pen_rep", QUERY_LOVER_BIRTH_PENALTY_REPUTATION)?; conn.prepare("pen_rep", QUERY_LOVER_BIRTH_PENALTY_REPUTATION)?;
conn.prepare("has_mother_birth_today", QUERY_HAS_MOTHER_BIRTH_TODAY)?;
for row in rows { for row in rows {
let father_cid = parse_i32(&row, "father_cid", -1); let father_cid = parse_i32(&row, "father_cid", -1);
@@ -1027,6 +1029,9 @@ impl FalukantFamilyWorker {
let region_id = parse_i32(&row, "region_id", 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)? {
continue;
}
let gender = if self.dist.sample(&mut self.rng) < 0.5 { let gender = if self.dist.sample(&mut self.rng) < 0.5 {
"male" "male"
@@ -1073,6 +1078,18 @@ impl FalukantFamilyWorker {
self.publish_falukant_update_family_and_status(user_id, "lover_birth"); 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,
) -> Result<bool, DbError> {
let rows = conn.execute("has_mother_birth_today", &[&mother_character_id])?;
Ok(rows
.first()
.and_then(|r| r.get("has_birth_today"))
.map(|v| v == "t" || v == "true")
.unwrap_or(false))
}
/// `falukantUpdateFamily` (mit `reason`) + `falukantUpdateStatus` — für UI-Refresh. /// `falukantUpdateFamily` (mit `reason`) + `falukantUpdateStatus` — für UI-Refresh.
fn publish_falukant_update_family_and_status(&self, user_id: i32, reason: &str) { fn publish_falukant_update_family_and_status(&self, user_id: i32, reason: &str) {
let family = format!( let family = format!(

View File

@@ -3886,6 +3886,16 @@ pub const QUERY_INSERT_CHILD_RELATION_LOVER: &str = r#"
); );
"#; "#;
/// Realismus-Regel: pro Mutter maximal eine Geburt pro Kalendertag.
pub const QUERY_HAS_MOTHER_BIRTH_TODAY: &str = r#"
SELECT EXISTS (
SELECT 1
FROM falukant_data.child_relation cr
WHERE cr.mother_character_id = $1::int
AND cr.created_at::date = CURRENT_DATE
) AS has_birth_today;
"#;
// --- Produktionszertifikat (Daemon Daily, Spec: Produktionszertifikate) --- // --- Produktionszertifikat (Daemon Daily, Spec: Produktionszertifikate) ---
/// Ein Spielercharakter pro Falukant-User (bei mehreren lebenden: **höchste** `character.id`, /// Ein Spielercharakter pro Falukant-User (bei mehreren lebenden: **höchste** `character.id`,

View File

@@ -44,6 +44,7 @@ use crate::worker::sql::{
QUERY_INSERT_CHILD, QUERY_INSERT_CHILD,
QUERY_INSERT_CHILD_RELATION, QUERY_INSERT_CHILD_RELATION,
QUERY_INSERT_CHILD_RELATION_PLANNED_BIRTH, QUERY_INSERT_CHILD_RELATION_PLANNED_BIRTH,
QUERY_HAS_MOTHER_BIRTH_TODAY,
QUERY_INSERT_NOTIFICATION, QUERY_INSERT_NOTIFICATION,
QUERY_DELETE_DIRECTOR, QUERY_DELETE_DIRECTOR,
QUERY_DELETE_RELATIONSHIP, QUERY_DELETE_RELATIONSHIP,
@@ -569,6 +570,7 @@ impl UserCharacterWorker {
conn.prepare("insert_child_relation", QUERY_INSERT_CHILD_RELATION)?; conn.prepare("insert_child_relation", QUERY_INSERT_CHILD_RELATION)?;
conn.prepare("insert_child_rel_planned", QUERY_INSERT_CHILD_RELATION_PLANNED_BIRTH)?; conn.prepare("insert_child_rel_planned", QUERY_INSERT_CHILD_RELATION_PLANNED_BIRTH)?;
conn.prepare("clear_char_preg", QUERY_CLEAR_CHARACTER_PREGNANCY_AFTER_BIRTH)?; conn.prepare("clear_char_preg", QUERY_CLEAR_CHARACTER_PREGNANCY_AFTER_BIRTH)?;
conn.prepare("has_mother_birth_today", QUERY_HAS_MOTHER_BIRTH_TODAY)?;
let use_gestation = self.marriage_pregnancy_column_ready.unwrap_or(false); let use_gestation = self.marriage_pregnancy_column_ready.unwrap_or(false);
let planned_pregnancy_ready = self.character_planned_pregnancy_columns_ready.unwrap_or(false); let planned_pregnancy_ready = self.character_planned_pregnancy_columns_ready.unwrap_or(false);
@@ -674,6 +676,9 @@ 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)? {
return Ok(());
}
let birth_context = row let birth_context = row
.get("birth_context") .get("birth_context")
@@ -732,6 +737,9 @@ 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)? {
return Ok(());
}
let gender = if self.dist.sample(&mut self.rng) < 0.5 { let gender = if self.dist.sample(&mut self.rng) < 0.5 {
"male" "male"
@@ -1193,6 +1201,18 @@ fn parse_opt_i32(row: &crate::db::Row, key: &str) -> Option<i32> {
row.get(key).and_then(|v| v.parse::<i32>().ok()) row.get(key).and_then(|v| v.parse::<i32>().ok())
} }
fn mother_already_had_birth_today(
conn: &mut crate::db::DbConnection,
mother_character_id: i32,
) -> Result<bool, DbError> {
let rows = conn.execute("has_mother_birth_today", &[&mother_character_id])?;
Ok(rows
.first()
.and_then(|r| r.get("has_birth_today"))
.map(|v| v == "t" || v == "true")
.unwrap_or(false))
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct Credit { struct Credit {
amount: f64, amount: f64,