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:
476
docs/FALUKANT_LOVERS_UNDERGROUND_DAEMON_SPEC.md
Normal file
476
docs/FALUKANT_LOVERS_UNDERGROUND_DAEMON_SPEC.md
Normal 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
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user