From c0f9fc8970e469a196035d1369e4aa351f9b0ce2 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Sun, 22 Mar 2026 10:05:28 +0100 Subject: [PATCH] Add lightweight mode to Character3D component: Introduce a new `lightweight` prop for optimized model loading based on age group. Update NoLoginView to utilize lightweight characters. Adjust styling for better layout and overflow handling in home view components. --- ...FALUKANT_LOVERS_UNDERGROUND_DAEMON_SPEC.md | 476 ++++++++++++++++++ frontend/src/components/Character3D.vue | 34 +- frontend/src/views/home/NoLoginView.vue | 14 +- 3 files changed, 509 insertions(+), 15 deletions(-) create mode 100644 docs/FALUKANT_LOVERS_UNDERGROUND_DAEMON_SPEC.md diff --git a/docs/FALUKANT_LOVERS_UNDERGROUND_DAEMON_SPEC.md b/docs/FALUKANT_LOVERS_UNDERGROUND_DAEMON_SPEC.md new file mode 100644 index 0000000..dc020e0 --- /dev/null +++ b/docs/FALUKANT_LOVERS_UNDERGROUND_DAEMON_SPEC.md @@ -0,0 +1,476 @@ +# Falukant: Daemon-Spezifikation für die Beziehung zwischen Liebschaften und Untergrund + +Dieses Dokument beschreibt die konkrete Daemon-Logik für die Verbindung zwischen: +- aktiven Liebschaften +- Sichtbarkeit und Diskretion +- Untergrundaktivitäten vom Typ `investigate_affair` +- Aufdeckung, Skandal und Erpressung + +Es ist die technische Spezifikation für den externen Daemon und ergänzt: +- [FALUKANT_LOVERS_DAEMON_SPEC.md](/mnt/share/torsten/Programs/YourPart3/docs/FALUKANT_LOVERS_DAEMON_SPEC.md) +- [FALUKANT_LOVERS_DAEMON_HANDOFF.md](/mnt/share/torsten/Programs/YourPart3/docs/FALUKANT_LOVERS_DAEMON_HANDOFF.md) +- [FALUKANT_UNDERGROUND_AFFAIR_DAEMON_HANDOFF.md](/mnt/share/torsten/Programs/YourPart3/docs/FALUKANT_UNDERGROUND_AFFAIR_DAEMON_HANDOFF.md) +- [FALUKANT_SERVANTS_IMPLEMENTATION_SPEC.md](/mnt/share/torsten/Programs/YourPart3/docs/FALUKANT_SERVANTS_IMPLEMENTATION_SPEC.md) + +## 1. Ziel + +Der Untergrund soll Liebschaften nicht nur "finden oder nicht finden", sondern auf dieselben Zustände zugreifen, die das Lovers-System ohnehin täglich verarbeitet: +- `visibility` +- `discretion` +- `acknowledged` +- `loverRole` +- `publicKnown` unehelicher Kinder +- Ruflage und Stand +- Haushalts- und Dienerschaftseffekte + +Dadurch entsteht ein gemeinsames System statt zweier getrennter Minigames. + +## 2. Grundprinzip + +Untergrundaktivitäten gegen Liebschaften sind keine völlig unabhängigen Zufallstests. + +Sie hängen ab von: +- wie sichtbar die Beziehung ohnehin schon ist +- wie diskret sie geführt wird +- wie groß und unruhig der Haushalt ist +- ob Kinder oder Anerkennung die Sache bereits schwer verbergen machen +- wie gut das Opfer sozial abgesichert ist + +Faustregel: +- hohe Sichtbarkeit + geringe Diskretion + schlechte Dienerschaft = gute Aufdeckungschance +- niedrige Sichtbarkeit + hohe Diskretion + geordneter Haushalt = geringe Aufdeckungschance + +## 3. Betroffene Aktivitäten + +In Phase 1 betrifft diese Spezifikation nur: +- `investigate_affair` + +mit den Zielen: +- `expose` +- `blackmail` + +## 4. Pflichtdaten für den Daemon + +## 4.1 Aktivität + +Aus `falukant_data.underground`: +- `id` +- `performer_id` +- `victim_id` +- `type_id` +- `parameters.goal` +- `result` +- `created_at` + +Aus `falukant_type.underground`: +- `tr` + +## 4.2 Opferdaten + +Für das Opfer: +- `falukant_user.id` +- `user.hashed_id` +- `character.id` +- `character.reputation` +- `character.birthdate` +- `character.title_of_nobility` + +## 4.3 Liebschaftsdaten + +Für alle aktiven Liebschaften des Opfers: +- `relationship.id` +- `relationship.character1_id` +- `relationship.character2_id` +- `relationship_type.tr` +- `relationship_state.lover_role` +- `relationship_state.visibility` +- `relationship_state.discretion` +- `relationship_state.affection` +- `relationship_state.acknowledged` +- `relationship_state.status_fit` +- `relationship_state.active` + +## 4.4 Kinderdaten + +Optional, aber empfohlen: +- `child_relation.legitimacy` +- `child_relation.birth_context` +- `child_relation.public_known` + +## 4.5 Haushalts-/Dienerdaten + +Wenn das Dienersystem aktiv ist: +- `user_house.servant_count` +- `user_house.servant_quality` +- `user_house.servant_pay_level` +- `user_house.household_order` +- daraus abgeleitete Haushalts-/Diskretionswerte + +## 5. Auswahl des Zielobjekts + +Wenn ein Opfer mehrere aktive Liebschaften hat, muss der Daemon eine Ziellogik verwenden. + +Empfohlene Reihenfolge: + +1. Nur aktive `lover`-Beziehungen betrachten +2. je Beziehung einen `discoveryScore` berechnen +3. die Beziehung mit dem höchsten `discoveryScore` als primäres Ziel verwenden +4. bei fast gleichen Werten darf der Daemon zufällig zwischen den besten Kandidaten wählen + +## 6. Discovery-Score + +Der `discoveryScore` bestimmt, wie leicht eine konkrete Liebschaft durch den Untergrund verwertbar wird. + +## 6.1 Formel + +```text +discoveryScore = + visibility * 0.45 + + (100 - discretion) * 0.30 + + acknowledgedBonus + + childBonus + + ageMalusVisibilityBonus + + householdLeakBonus + + multipleAffairBonus + + statusMismatchBonus +``` + +## 6.2 Teilwerte + +### acknowledgedBonus + +```text +if acknowledged = true => +10 +sonst => +0 +``` + +### childBonus + +```text +if hidden bastard exists => +8 +if public_known bastard exists => +18 +``` + +Wenn mehrere Kinder existieren: +- maximal `+20` in Summe + +### ageMalusVisibilityBonus + +Wenn die Liebschaft wegen jungen Alters bereits reputationsschädlich ist: + +```text +minAge <= 13 => +18 +minAge <= 15 => +12 +minAge <= 17 => +6 +sonst => +0 +``` + +### householdLeakBonus + +Aus dem Dienersystem: + +```text +if no house data => +0 +if householdOrder <= 35 => +8 +if servantPayLevel = low => +5 +if servantCount > expectedMax + 1 => +4 +if servantQuality <= 35 => +6 +``` + +Deckel: +- maximal `+15` + +### multipleAffairBonus + +```text +if victim has 2 active lovers => +8 +if victim has 3 or more active lovers => +14 +``` + +### statusMismatchBonus + +Wenn die Beziehung standesmäßig auffällig ist: + +```text +status_fit = -2 => +10 +status_fit = -1 => +5 +sonst => +0 +``` + +## 7. Erfolgswahrscheinlichkeit + +## 7.1 Grundwurf + +Auf Basis des höchsten `discoveryScore`: + +```text +successChance = clamp(20 + discoveryScore * 0.55, 5, 95) +``` + +Interpretation: +- selbst sehr diskrete Beziehungen bleiben mit kleinem Restrisiko auffindbar +- offen geführte, chaotische Beziehungen werden fast sicher entdeckt + +## 7.2 Ergebnisstufen + +```text +roll <= successChance * 0.55 => full success +roll <= successChance => partial success +sonst => failure +``` + +## 8. Behandlung von `goal = expose` + +## 8.1 Ziel + +Die Beziehung soll öffentlich sichtbar und reputationsschädlich werden. + +## 8.2 Wirkung bei vollem Erfolg + +Auf Ziel-Liebschaft: +- `visibility += 18..30` +- `discretion -= 8..18` + +Auf Opfer: +- `reputationDelta = -2 .. -6` + +Zusätzlich: +- wenn `visibility >= 60` nach Anpassung: Skandalprüfung sofort auslösen +- wenn `public_known` uneheliches Kind bereits existiert: zusätzlicher Rufschaden `-1` + +## 8.3 Wirkung bei Teilerfolg + +Auf Ziel-Liebschaft: +- `visibility += 8..15` +- `discretion -= 3..8` + +Auf Opfer: +- `reputationDelta = -1 .. -3` + +Kein garantierter sofortiger Skandal, aber deutlich erhöhte Folgewahrscheinlichkeit. + +## 8.4 Wirkung bei Fehlschlag + +Keine öffentliche Wirkung, aber optional: +- kleines Gegenrisiko für den Untergrund später +- oder `notes` mit "no proof" + +Für Phase 1 genügt: +- `status = failed` +- `outcome = failure` + +## 9. Behandlung von `goal = blackmail` + +## 9.1 Ziel + +Belastendes Wissen beschaffen, ohne sofort volle Öffentlichkeit zu erzeugen. + +## 9.2 Wirkung bei vollem Erfolg + +Auf Ziel-Liebschaft: +- `visibility += 4..9` +- `discretion -= 2..6` + +Auf Aktivität: +- `blackmailAmount` setzen +- `discoveries` mit verwertbaren Details befüllen + +Auf Opfer: +- kein großer Sofort-Rufschaden +- optional `reputationDelta = 0 .. -1` + +## 9.3 Wirkung bei Teilerfolg + +Auf Ziel-Liebschaft: +- `visibility += 2..5` + +Auf Aktivität: +- kleinerer `blackmailAmount` +- `outcome = partial` + +## 9.4 Wirkung bei Fehlschlag + +- keine verwertbare Entdeckung +- `status = failed` +- `outcome = failure` + +## 10. Berechnung der Erpressungssumme + +Die Erpressungssumme soll aus sozialer Fallhöhe und Beweiswert entstehen. + +## 10.1 Formel + +```text +base = + 500 + + visibility * 12 + + max(0, reputation) * 15 + + titleGroupBonus + + childBlackmailBonus + +blackmailAmount = round(base * outcomeFactor) +``` + +## 10.2 titleGroupBonus + +Aus der Standesgruppe des Lovers-Systems: + +```text +group 0 => +0 +group 1 => +600 +group 2 => +1800 +group 3 => +4200 +``` + +## 10.3 childBlackmailBonus + +```text +hidden_bastard exists => +900 +public_known bastard exists => +1600 +``` + +## 10.4 outcomeFactor + +```text +full success => 1.0 +partial success => 0.55 +failure => 0 +``` + +## 11. Sofortige Skandalprüfung + +Bei `goal = expose` und starker Sichtbarkeitssteigerung darf der Untergrund direkt einen Skandal anstoßen. + +## 11.1 Triggerschwelle + +```text +if visibility_after >= 60: + trigger scandal check +``` + +Zusatzbonus: +- `+10` Punkte auf die reguläre Skandalchance bei sehr jungem Alter `<= 15` +- `+6` Punkte bei `public_known` Kind +- `+5` Punkte bei `householdOrder <= 35` + +## 11.2 Socket-Events + +Wenn daraus ein Skandal resultiert: +- `falukant_family_scandal_hint` +- `falukantUpdateFamily` mit `reason = scandal` +- `falukantUpdateStatus` + +## 12. Struktur von `underground.result` + +Der Daemon schreibt mindestens: + +```json +{ + "status": "resolved", + "outcome": "success", + "discoveries": { + "relationshipId": 123, + "loverRole": "secret_affair", + "visibility": 58, + "acknowledged": false, + "publicKnownChild": false, + "householdLeak": true + }, + "visibilityDelta": 14, + "reputationDelta": -3, + "blackmailAmount": 2400, + "notes": "Servants and low discretion made the affair easy to trace." +} +``` + +## 13. Pflichtregeln für `discoveries` + +`discoveries` soll mindestens enthalten: +- `relationshipId` +- `loverRole` +- `visibility` +- `acknowledged` + +Optional, aber sehr nützlich: +- `publicKnownChild` +- `hiddenChild` +- `householdLeak` +- `minAgeBracket` +- `multipleAffairs` + +## 14. Interaktion mit Dienerschaft + +Das Dienersystem ist ein eigenständiger Modifikator, kein Ersatz für Sichtbarkeit oder Diskretion. + +Der Untergrund soll Dienerschaft nur als Verstärker oder Dämpfer nutzen: + +Günstig für Aufdeckung: +- niedrige Bezahlung +- schlechte Qualität +- chaotischer Haushalt +- übergroße Dienerschaft + +Ungünstig für Aufdeckung: +- hohe Qualität +- großzügige Bezahlung +- geordneter Haushalt +- passende, nicht zu große Dienerschaft + +## 15. Interaktion mit Lover-Daily + +Wichtig: +- Der Untergrund darf Lovers-Zustände verändern. +- Danach verarbeitet das normale Daily-System diese Zustände weiter. + +Das heißt: +- `visibility`-Erhöhungen aus dem Untergrund laufen später in Daily-Skandale und Rufdrift hinein. +- Untergrund ersetzt nicht die Daily-Logik, sondern stößt sie an. + +## 16. Idempotenz + +Jede `investigate_affair`-Aktivität darf genau einmal verarbeitet werden. + +Verarbeitbar nur wenn: +- `underground_type.tr = investigate_affair` +- `result.status = pending` + +Nach Verarbeitung: +- `result.status` auf `resolved` oder `failed` + +Der Daemon darf keine Aktivität erneut anfassen, deren `result.status` nicht mehr `pending` ist. + +## 17. Transaktionsgrenze + +Folgendes soll atomar laufen: +- Ziel-Liebschaft bestimmen +- Erfolgswurf +- Sichtbarkeit/Diskretion ändern +- Rufänderung anwenden +- `underground.result` schreiben +- optionale Skandalereignisse vorbereiten + +Empfehlung: +- eine DB-Transaktion pro Aktivität + +## 18. Definition of Done + +Die Daemon-Umsetzung ist ausreichend, wenn: + +1. `investigate_affair` für `expose` und `blackmail` verschieden behandelt wird +2. nicht beliebige, sondern die plausibelste aktive Liebschaft des Opfers gewählt wird +3. Sichtbarkeit und Diskretion aus dem Lovers-System als Eingangsgrößen verwendet werden +4. Dienerschaft optional als Leck-/Diskretionsfaktor einfließt +5. `expose` Sichtbarkeit und Ruf spürbar verschieben kann +6. `blackmail` belastbare `blackmailAmount`-Werte produziert +7. Skandale bei starken Fällen sofort ausgelöst werden können +8. `underground.result` vollständig und UI-lesbar gefüllt wird + +## 19. Empfehlung für die Implementierungsreihenfolge + +1. Ziel-Liebschaft und `discoveryScore` implementieren +2. `success / partial / failure` für `expose` +3. `blackmailAmount` für `blackmail` +4. `discoveries`-Füllung +5. Sofort-Skandalprüfung +6. Dienerschaftsmodifikator ergänzen +7. Balancing nach ersten Tests diff --git a/frontend/src/components/Character3D.vue b/frontend/src/components/Character3D.vue index e057d08..4aa6581 100644 --- a/frontend/src/components/Character3D.vue +++ b/frontend/src/components/Character3D.vue @@ -60,6 +60,10 @@ export default { noBackground: { type: Boolean, default: false + }, + lightweight: { + type: Boolean, + default: false } }, data() { @@ -290,29 +294,35 @@ export default { const prefix = base ? `${base}${MODELS_API_PATH}` : MODELS_API_PATH; // Fallback-Hierarchie: - // 1. Zuerst versuchen, Modell für genaues Alter zu laden (z.B. female_1y.glb) - // 2. Falls nicht vorhanden, Altersbereich verwenden (z.B. female_toddler.glb) - // 3. Falls auch nicht vorhanden, Basis-Modell verwenden (z.B. female.glb) + // Standard: + // 1. Exaktes Altersmodell + // 2. Altersbereich + // 3. Basis-Modell + // Lightweight: + // 1. Altersbereich + // 2. Basis-Modell const exactAgePath = this.exactAgeModelPath; const ageGroupPath = this.modelPath; const fallbackPath = `${prefix}/${this.actualGender}.glb`; let gltf; try { - // Versuche zuerst genaues Alter - try { - gltf = await loader.loadAsync(exactAgePath); - console.debug(`Loaded exact age model: ${exactAgePath}`); - } catch (exactAgeError) { - // Falls genaues Alter nicht existiert, versuche Altersbereich + if (this.lightweight) { try { gltf = await loader.loadAsync(ageGroupPath); - console.debug(`Loaded age group model: ${ageGroupPath}`); } catch (ageGroupError) { - // Falls Altersbereich nicht existiert, verwende Basis-Modell - console.warn(`Could not load ${ageGroupPath}, trying fallback model`); gltf = await loader.loadAsync(fallbackPath); } + } else { + try { + gltf = await loader.loadAsync(exactAgePath); + } catch (exactAgeError) { + try { + gltf = await loader.loadAsync(ageGroupPath); + } catch (ageGroupError) { + gltf = await loader.loadAsync(fallbackPath); + } + } } } finally { dracoLoader.dispose(); diff --git a/frontend/src/views/home/NoLoginView.vue b/frontend/src/views/home/NoLoginView.vue index a0a2059..0206094 100644 --- a/frontend/src/views/home/NoLoginView.vue +++ b/frontend/src/views/home/NoLoginView.vue @@ -5,7 +5,7 @@
- +
@@ -95,7 +95,7 @@
- +
@@ -185,6 +185,7 @@ export default { height: 100%; flex: 1; min-height: 0; + overflow: hidden; } .home-structure>div { @@ -214,10 +215,13 @@ export default { gap: 1rem; flex: 1 1 auto; min-height: 0; + height: 100%; + overflow: hidden; } .actions-panel { - flex: 1; + flex: 0 0 40%; + max-height: 40%; min-height: 0; background: linear-gradient(180deg, rgba(255, 251, 246, 0.96) 0%, rgba(248, 240, 231, 0.96) 100%); @@ -433,9 +437,13 @@ export default { .actions { min-height: auto; + height: auto; + overflow: visible; } .actions-panel { + flex: 0 0 auto; + max-height: none; min-height: 260px; }