Dokumentiere Schwangerschaften bei Verlobten und prüfe Übergänge
All checks were successful
Deploy to production / deploy (push) Successful in 2m35s
All checks were successful
Deploy to production / deploy (push) Successful in 2m35s
This commit is contained in:
@@ -32,10 +32,22 @@ und der [Endocrine Society zur zeitlichen Entwicklung](https://www.endocrine.org
|
||||
## Spielregeln und API
|
||||
|
||||
Die Heiratsgrenze bleibt unabhängig davon bei 14 Spieljahren für beide
|
||||
Verlobten. Automatische Schwangerschaften in bestehenden Ehen und Liebschaften
|
||||
Verlobten. Automatische Schwangerschaften in bestehenden Ehen, Verlobungen und Liebschaften
|
||||
sowie der Backend-Sonderweg nach einer Hochzeit prüfen das individuelle
|
||||
Fruchtbarkeitsalter beider Eltern. Die bisherigen Beziehungsarten,
|
||||
Schwangerschaftschancen und Schwangerschaftsdauern gelten weiterhin.
|
||||
Fruchtbarkeitsalter beider Eltern. Verlobungen bleiben als Beziehungsstatus erhalten.
|
||||
Verlobte benötigen keinen Liebschaftsunterhalt oder vorherigen Abrechnungslauf.
|
||||
Die Zuneigung muss mindestens 45 betragen; bei fehlendem Beziehungszustand gilt
|
||||
wie im Backend der Standardwert 50. Inaktive Beziehungen und verstorbene Partner
|
||||
sind ausgeschlossen. Die Chance beträgt pro Prüfung 4 Prozent, ab Zuneigung 75
|
||||
zwei Prozentpunkte mehr; hohe Sichtbarkeit bei Zuneigung unter 50 reduziert sie
|
||||
um zwei Prozentpunkte. Schwangerschaftsdauer und Sperren gegen parallele
|
||||
Schwangerschaften entsprechen dem bestehenden Liebschaftsweg.
|
||||
|
||||
Eine Geburt während der Verlobung nutzt die vorhandene Kennzeichnung für
|
||||
außereheliche Kinder (`birth_context = 'lover'`, zunächst `hidden_bastard` und
|
||||
nicht öffentlich bekannt). Heiratet das Paar vor der Geburt, wird das Kind über
|
||||
den bestehenden Geburtsweg als ehelich erfasst. Die Schwangerschaft bleibt dabei
|
||||
erhalten. Für diese Freischaltung ist keine weitere Datenbankmigration nötig.
|
||||
|
||||
Eine bereits geplante Geburt wird nicht nachträglich durch die neuen Werte
|
||||
blockiert. Die Migration verändert weder Schwangerschaftstermine noch
|
||||
|
||||
@@ -98,7 +98,7 @@ test('database migration, persistence and daemon conception paths', {
|
||||
|
||||
await t.test('daemon checks both parents and preserves already scheduled births', {
|
||||
skip: !process.env.DAEMON_SQL_PATH,
|
||||
}, async () => {
|
||||
}, async (t) => {
|
||||
const source = await readFile(process.env.DAEMON_SQL_PATH, 'utf8');
|
||||
const sql = (name) => {
|
||||
const match = source.match(new RegExp(`pub const ${name}: &str = r#"([\\s\\S]*?)"#;`));
|
||||
@@ -109,13 +109,17 @@ test('database migration, persistence and daemon conception paths', {
|
||||
await db.query(`
|
||||
TRUNCATE falukant_data.character RESTART IDENTITY;
|
||||
CREATE TABLE falukant_type.relationship (id INT PRIMARY KEY, tr TEXT);
|
||||
INSERT INTO falukant_type.relationship VALUES (1, 'married'), (2, 'lover');
|
||||
INSERT INTO falukant_type.relationship VALUES (1, 'married'), (2, 'lover'), (3, 'engaged'), (4, 'wooing');
|
||||
CREATE TABLE falukant_data.relationship (
|
||||
id INT PRIMARY KEY, character1_id INT, character2_id INT,
|
||||
relationship_type_id INT, marriage_pregnancy_due_at TIMESTAMPTZ
|
||||
);
|
||||
CREATE TABLE falukant_data.falukant_user (id INT PRIMARY KEY, user_id INT);
|
||||
CREATE TABLE falukant_data.child_relation (mother_character_id INT, created_at TIMESTAMPTZ);
|
||||
CREATE TABLE falukant_data.child_relation (
|
||||
father_character_id INT, mother_character_id INT, child_character_id INT,
|
||||
name_set BOOLEAN, legitimacy TEXT, birth_context TEXT, public_known BOOLEAN,
|
||||
created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ
|
||||
);
|
||||
CREATE TABLE falukant_data.relationship_state (
|
||||
relationship_id INT, active BOOLEAN, affection INT, maintenance_level INT,
|
||||
last_monthly_processed_at TIMESTAMPTZ, lover_role TEXT, visibility INT
|
||||
@@ -131,6 +135,7 @@ test('database migration, persistence and daemon conception paths', {
|
||||
['QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE', 1],
|
||||
['QUERY_GET_LEGACY_MARRIAGE_INSTANT_PREGNANCY_CANDIDATES', 1],
|
||||
['QUERY_GET_LOVER_PREGNANCY_CANDIDATES', 2],
|
||||
['QUERY_GET_LOVER_PREGNANCY_CANDIDATES', 3],
|
||||
]) {
|
||||
for (const [motherAge, fatherAge, eligible] of [[13, 13, true], [12, 13, false], [13, 12, false]]) {
|
||||
await db.query(`UPDATE falukant_data.character SET birthdate = CURRENT_DATE -
|
||||
@@ -149,6 +154,73 @@ test('database migration, persistence and daemon conception paths', {
|
||||
}
|
||||
}
|
||||
}
|
||||
await t.test('engaged couples need no lover maintenance and retain their relationship', async () => {
|
||||
const candidates = () => db.query(sql('QUERY_GET_LOVER_PREGNANCY_CANDIDATES')).then(([rows]) => rows);
|
||||
await db.query(`UPDATE falukant_data.character SET birthdate = CURRENT_DATE - INTERVAL '13 days',
|
||||
pregnancy_due_at = NULL, health = 100;
|
||||
UPDATE falukant_data.relationship SET relationship_type_id = 3, marriage_pregnancy_due_at = NULL;
|
||||
UPDATE falukant_data.relationship_state SET affection = 50, lover_role = NULL,
|
||||
maintenance_level = 0, last_monthly_processed_at = NULL`);
|
||||
let rows = await candidates();
|
||||
assert.equal(rows.length, 1);
|
||||
assert.equal(rows[0].relationship_type, 'engaged');
|
||||
assert.equal(rows[0].mother_cid, 1);
|
||||
assert.equal(rows[0].father_cid, 2);
|
||||
// Selection and conception must work for either partner as the player.
|
||||
await db.query('INSERT INTO falukant_data.falukant_user VALUES (10, 100)');
|
||||
for (const playerId of [1, 2]) {
|
||||
await db.query('UPDATE falukant_data.character SET user_id = CASE WHEN id = :id THEN 10 ELSE NULL END', {
|
||||
replacements: { id: playerId },
|
||||
});
|
||||
rows = await candidates();
|
||||
assert.equal(rows.length, 1);
|
||||
assert.equal(rows[0][playerId === 1 ? 'mother_uid' : 'father_uid'], 10);
|
||||
}
|
||||
await db.query('UPDATE falukant_data.relationship SET character1_id = 2, character2_id = 1');
|
||||
rows = await candidates();
|
||||
assert.equal(rows[0].mother_cid, 1);
|
||||
assert.equal(rows[0].father_cid, 2);
|
||||
|
||||
for (const id of [1, 2]) {
|
||||
await db.query('UPDATE falukant_data.character SET health = 0 WHERE id = :id', { replacements: { id } });
|
||||
assert.equal((await candidates()).length, 0);
|
||||
await db.query('UPDATE falukant_data.character SET health = 100');
|
||||
}
|
||||
await db.query('UPDATE falukant_data.relationship_state SET active = false');
|
||||
assert.equal((await candidates()).length, 0);
|
||||
await db.query('UPDATE falukant_data.relationship_state SET active = true, affection = 44');
|
||||
assert.equal((await candidates()).length, 0);
|
||||
await db.query('DELETE FROM falukant_data.relationship_state');
|
||||
assert.equal((await candidates()).length, 1);
|
||||
for (const type of [1, 2, 4]) {
|
||||
await db.query('UPDATE falukant_data.relationship SET relationship_type_id = :type', { replacements: { type } });
|
||||
assert.equal((await candidates()).length, 0);
|
||||
}
|
||||
await db.query('UPDATE falukant_data.relationship SET relationship_type_id = 3');
|
||||
await db.query(sql('QUERY_SET_CHARACTER_PREGNANCY_FROM_LOVER'), { bind: [1, 2] });
|
||||
assert.equal((await candidates()).length, 0, 'Existing pregnancy blocks another conception');
|
||||
const [[pregnancy]] = await db.query('SELECT pregnancy_due_at, pregnancy_father_character_id FROM falukant_data.character WHERE id = 1');
|
||||
assert.equal(pregnancy.pregnancy_father_character_id, 2);
|
||||
assert.ok(pregnancy.pregnancy_due_at > new Date());
|
||||
const [notDue] = await db.query(sql('QUERY_GET_PLANNED_CHARACTER_BIRTH_DELIVERIES'));
|
||||
assert.equal(notDue.length, 0);
|
||||
await db.query("UPDATE falukant_data.character SET pregnancy_due_at = NOW() - INTERVAL '1 hour' WHERE id = 1");
|
||||
for (const [type, context] of [[3, 'lover'], [1, 'marriage']]) {
|
||||
await db.query('UPDATE falukant_data.relationship SET relationship_type_id = :type', { replacements: { type } });
|
||||
const [births] = await db.query(sql('QUERY_GET_PLANNED_CHARACTER_BIRTH_DELIVERIES'));
|
||||
assert.equal(births.length, 1);
|
||||
assert.equal(births[0].birth_context, context);
|
||||
const [[stillPregnant]] = await db.query('SELECT pregnancy_father_character_id FROM falukant_data.character WHERE id = 1');
|
||||
assert.equal(stillPregnant.pregnancy_father_character_id, 2);
|
||||
}
|
||||
await db.query('UPDATE falukant_data.relationship SET relationship_type_id = 3');
|
||||
await db.query(sql('QUERY_INSERT_CHILD_RELATION_PLANNED_BIRTH'), { bind: [2, 1, 3, 'lover'] });
|
||||
const [[child]] = await db.query('SELECT legitimacy, birth_context, public_known FROM falukant_data.child_relation');
|
||||
assert.deepEqual(child, { legitimacy: 'hidden_bastard', birth_context: 'lover', public_known: false });
|
||||
const [[relationship]] = await db.query('SELECT relationship_type_id FROM falukant_data.relationship');
|
||||
assert.equal(relationship.relationship_type_id, 3);
|
||||
await db.query('DELETE FROM falukant_data.child_relation');
|
||||
});
|
||||
await db.query(`UPDATE falukant_data.relationship SET marriage_pregnancy_due_at = NULL`);
|
||||
for (const [motherAge, fatherAge, eligible] of [[12, 13, false], [13, 12, false], [13, 13, true]]) {
|
||||
await db.query(`UPDATE falukant_data.character SET birthdate = CURRENT_DATE -
|
||||
|
||||
@@ -704,7 +704,7 @@
|
||||
},
|
||||
"mood": "Stimmung",
|
||||
"progress": "Zuneigung",
|
||||
"jumpToPartyForm": "Hochzeitsfeier veranstalten (Nötig für Hochzeit und Kinder)"
|
||||
"jumpToPartyForm": "Hochzeitsfeier veranstalten"
|
||||
},
|
||||
"marriageState": {
|
||||
"stable": "Stabil",
|
||||
|
||||
@@ -692,7 +692,7 @@
|
||||
},
|
||||
"mood": "Estado de ánimo",
|
||||
"progress": "Afecto",
|
||||
"jumpToPartyForm": "Organizar banquete de boda (necesario para boda e hijos)"
|
||||
"jumpToPartyForm": "Organizar banquete de boda"
|
||||
},
|
||||
"marriageState": {
|
||||
"stable": "Estable",
|
||||
|
||||
@@ -696,7 +696,7 @@
|
||||
},
|
||||
"mood": "Humeur",
|
||||
"progress": "affection",
|
||||
"jumpToPartyForm": "Organiser une fête de mariage (nécessaire pour le mariage et les enfants)"
|
||||
"jumpToPartyForm": "Organiser une fête de mariage"
|
||||
},
|
||||
"marriageState": {
|
||||
"stable": "Écurie",
|
||||
|
||||
Reference in New Issue
Block a user