diff --git a/src/worker/falukant_family.rs b/src/worker/falukant_family.rs index 37ec680..361d0a4 100644 --- a/src/worker/falukant_family.rs +++ b/src/worker/falukant_family.rs @@ -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", &[®ion_id, &gender, &last_name, &title_of_nobility])?; - let child_cid = inserted - .first() - .and_then(|r| r.get("child_cid")) - .and_then(|v| v.parse::().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, diff --git a/src/worker/sql.rs b/src/worker/sql.rs index 57e80eb..43fd4b7 100644 --- a/src/worker/sql.rs +++ b/src/worker/sql.rs @@ -2179,7 +2179,7 @@ pub const QUERY_AUTOBATISM: &str = r#" // im Alter 18–~44 (6570–16000 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. diff --git a/src/worker/user_character.rs b/src/worker/user_character.rs index 1f4c593..192ff27 100644 --- a/src/worker/user_character.rs +++ b/src/worker/user_character.rs @@ -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(()); }