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.

This commit is contained in:
Torsten Schulz (local)
2026-03-22 10:05:28 +01:00
parent 876ee2ab49
commit c0f9fc8970
3 changed files with 509 additions and 15 deletions

View File

@@ -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

View File

@@ -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();

View File

@@ -5,7 +5,7 @@
</div>
<div class="home-structure">
<div class="mascot">
<Character3D gender="male" />
<Character3D gender="male" :lightweight="true" />
</div>
<div class="actions">
<section class="actions-panel actions-panel--story surface-card">
@@ -95,7 +95,7 @@
</section>
</div>
<div class="mascot">
<Character3D gender="female" />
<Character3D gender="female" :lightweight="true" />
</div>
<RandomChatDialog ref="randomChatDialog" />
<RegisterDialog ref="registerDialog" />
@@ -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;
}