From 0178d33a216b94909dd6ad4bd69c5a535710fca5 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Wed, 4 Nov 2026 15:05:17 +0100 Subject: [PATCH] =?UTF-8?q?Pr=C3=BCfe=20Liebschaftskinder=20neben=20besteh?= =?UTF-8?q?enden=20Ehen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/falukant-character-development.md | 13 ++++ backend/tools/characterDevelopment.test.mjs | 64 +++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/backend/docs/falukant-character-development.md b/backend/docs/falukant-character-development.md index 04bd0d1..3310b74 100644 --- a/backend/docs/falukant-character-development.md +++ b/backend/docs/falukant-character-development.md @@ -49,6 +49,19 @@ nicht öffentlich bekannt). Heiratet das Paar vor der Geburt, wird das Kind übe den bestehenden Geburtsweg als ehelich erfasst. Die Schwangerschaft bleibt dabei erhalten. Für diese Freischaltung ist keine weitere Datenbankmigration nötig. +Auch verheiratete Charaktere können mit einer Liebschaft ein Kind bekommen. +Bei einer Mutter blockiert eine bereits geplante Schwangerschaft weitere +Konzeptionen unabhängig vom Partner. Der Eheweg hat eine Chance von 72 Prozent +pro regulärer Prüfung; der Liebschaftsweg verwendet je nach Rolle und Zustand +deutlich kleinere Chancen. Liebschaften benötigen Zuneigung ab 45, Unterhalt ab +30 und einen aktuellen Abrechnungsstempel. Ein Ehepartner wird bei einer bereits +geplanten Liebschaftsschwangerschaft nicht nachträglich als Vater eingesetzt. + +Auch bei Altersmalussen, Sichtbarkeit und Unterhaltsprüfungen gilt ein +Kalendertag als Spieljahr. Die drei Liebschaftsabfragen für Tageslauf, +Abrechnungsstempel und Teilzahlungen verwenden deshalb die direkte Differenz +der Kalendertage ohne Division durch 365. + Eine bereits geplante Geburt wird nicht nachträglich durch die neuen Werte blockiert. Die Migration verändert weder Schwangerschaftstermine noch Beziehungen. Admin-Funktionen zum expliziten Setzen von Schwangerschaften diff --git a/backend/tools/characterDevelopment.test.mjs b/backend/tools/characterDevelopment.test.mjs index 6151668..77a375a 100644 --- a/backend/tools/characterDevelopment.test.mjs +++ b/backend/tools/characterDevelopment.test.mjs @@ -235,6 +235,70 @@ test('database migration, persistence and daemon conception paths', { marriage_pregnancy_due_at = NOW() - INTERVAL '1 hour'`); const [births] = await db.query(sql('QUERY_GET_MARRIAGE_BIRTH_DELIVERIES')); assert.equal(births.length, 1); + + await t.test('a married mother can conceive with a lover and keeps the correct father', async () => { + await db.query(` + UPDATE falukant_data.character SET birthdate = CURRENT_DATE - INTERVAL '20 days', + fertile_from_age = 13, pregnancy_due_at = NULL; + INSERT INTO falukant_data.character (gender, birthdate, puberty_start_age, fertile_from_age) + VALUES ('male', CURRENT_DATE - INTERVAL '24 days', 10, 13); + UPDATE falukant_data.relationship SET relationship_type_id = 2, marriage_pregnancy_due_at = NULL; + INSERT INTO falukant_data.relationship VALUES (2, 1, 3, 1, NULL); + INSERT INTO falukant_data.relationship_state VALUES (1, true, 80, 50, NOW(), 'lover', 20); + `); + const [candidates] = await db.query(sql('QUERY_GET_LOVER_PREGNANCY_CANDIDATES')); + assert.equal(candidates.length, 1); + assert.equal(candidates[0].father_cid, 2); + // An already pending marital pregnancy blocks the same mother. + await db.query("UPDATE falukant_data.relationship SET marriage_pregnancy_due_at = NOW() + INTERVAL '18 hours' WHERE id = 2"); + const [blocked] = await db.query(sql('QUERY_GET_LOVER_PREGNANCY_CANDIDATES')); + assert.equal(blocked.length, 0); + await db.query('UPDATE falukant_data.relationship SET marriage_pregnancy_due_at = NULL WHERE id = 2'); + await db.query(sql('QUERY_SET_CHARACTER_PREGNANCY_FROM_LOVER'), { bind: [1, 2] }); + await db.query(sql('QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE')); + const [[marriage]] = await db.query('SELECT marriage_pregnancy_due_at FROM falukant_data.relationship WHERE id = 2'); + assert.equal(marriage.marriage_pregnancy_due_at, null); + await db.query("UPDATE falukant_data.character SET pregnancy_due_at = NOW() - INTERVAL '1 hour' WHERE id = 1"); + const [deliveries] = await db.query(sql('QUERY_GET_PLANNED_CHARACTER_BIRTH_DELIVERIES')); + assert.equal(deliveries.length, 1); + assert.equal(deliveries[0].father_cid, 2); + assert.equal(deliveries[0].birth_context, 'lover'); + await db.query(sql('QUERY_INSERT_CHILD_RELATION_PLANNED_BIRTH'), { bind: [2, 1, 4, deliveries[0].birth_context] }); + const [[child]] = await db.query('SELECT father_character_id, mother_character_id, birth_context FROM falukant_data.child_relation'); + assert.deepEqual(child, { father_character_id: 2, mother_character_id: 1, birth_context: 'lover' }); + }); + + await t.test('lover upkeep and penalties use game years rather than real years', async () => { + await db.query(` + ALTER TABLE falukant_data.character ADD COLUMN reputation INT DEFAULT 50; + ALTER TABLE falukant_data.relationship_state + ADD COLUMN discretion INT DEFAULT 50, + ADD COLUMN status_fit INT DEFAULT 0, + ADD COLUMN monthly_base_cost INT DEFAULT 30, + ADD COLUMN months_underfunded INT DEFAULT 0, + ADD COLUMN last_daily_processed_at TIMESTAMPTZ, + ADD COLUMN lover_last_installment_at TIMESTAMPTZ; + UPDATE falukant_data.relationship_state SET last_monthly_processed_at = NULL; + CREATE TABLE falukant_type.title (id INT PRIMARY KEY, tr TEXT); + INSERT INTO falukant_type.title VALUES (1, 'civil'); + CREATE TABLE falukant_data.user_house (user_id INT, servant_discretion_modifier INT); + `); + for (const [motherAge, fatherAge, youngerAge] of [[20, 34, 20], [35, 16, 16]]) { + await db.query(`UPDATE falukant_data.character SET birthdate = CURRENT_DATE - + (CASE WHEN id = 1 THEN :motherAge ELSE :fatherAge END) * INTERVAL '1 day'`, { + replacements: { motherAge, fatherAge }, + }); + for (const name of [ + 'QUERY_GET_ACTIVE_LOVER_ROWS_FOR_DAILY', + 'QUERY_GET_ACTIVE_LOVER_ROWS_FOR_MONTHLY', + 'QUERY_GET_ACTIVE_LOVER_ROWS_FOR_INSTALLMENT', + ]) { + const [rows] = await db.query(sql(name)); + assert.equal(rows.length, 1, name); + assert.equal(rows[0].min_age_years, youngerAge, name); + } + } + }); }); await migration.down(qi); const [[columns]] = await db.query(`SELECT count(*)::int AS count FROM information_schema.columns