# Falukant: Konzept — Daemon, Backend und UI für politische Amtsvorteile Dieses Dokument beschreibt die **noch nicht implementierten** Teile zu den Amtsvorteilen (`reputation_periodic`, `appoint_politicians`, `set_regional_tax`, `free_lover_slots`, sowie optional `guard_protection` / `court_immunity` als Spielwirkung). Die **Anzeige** in der Politik-Übersicht existiert bereits; hier geht es um **Ausführung**, **Persistenz** und **Bedienoberfläche**. --- ## 1. Ausgangslage im Code | Bereich | Stand | |--------|--------| | **Predefine** | `falukant_predefine.political_office_benefit` + Typen in `falukant_type.political_office_benefit_type`; JSON `value` pro Amt. | | **Auslesen / Anzeige** | `FalukantService.politicsBenefitEntriesFromRows`, Frontend `PoliticsView.vue`. | | **Bereits mechanisch** | `daily_salary` (beim Laden der Politik-Übersicht, `last_political_daily_salary_on` am `FalukantUser`); Steuerbefreiung in `getCumulativeTaxPercentForCharacter` (SQL mit exempt types). | | **Daemon (C++)** | `PoliticsWorker` läuft **täglich nach 03:00** (`src/politics_worker.cpp`): Wahlen, Nachbesetzung, Benachrichtigungen — **kein** Bezug zu den neuen JSON-Vorteilen. | | **Node-Daemon** | `backend/daemonServer.js` ist ein **WebSocket-Relay**, kein Spiel-Ticker. | | **Steuern im Spiel** | Regionen haben `tax_percent`; kumulativ über Eltern-Regionen; Verkaufsbuchung in `falukantService` (Steueranteil). | **Empfehlung zur Architektur:** zeitkritische **Batch-Jobs** (täglich, idempotent) entweder im **bestehenden C++-`PoliticsWorker`** direkt nach den bestehenden Schritten ausführen **oder** einen kleinen **Node-Cron-Job** (z. B. `node backend/jobs/politicalBenefitsTick.js` + systemd timer), der Sequelize nutzt. C++ vermeidet doppelte DB-Logik; Node vermeidet C++-Deploy für reine Sequelize-Regeln. Für **synchrone API-Aktionen** (Steuern setzen, Ernennungen) ist **ausschließlich das Node-Backend** sinnvoll. --- ## 2. Zielbild pro Vorteilstyp ### 2.1 `reputation_periodic` (Ansehen alle *x* Tage) **Ziel:** Charaktere mit aktivem Amt erhalten in einem festen Intervall `gain` Ansehenspunkte (oberhalb 0, Cap z. B. 100), **höchstens einmal pro Intervall pro (Charakter × konkreter Benefit-Zeile)**. **Persistenz (neu):** - Tabelle z. B. `falukant_data.political_benefit_last_tick` - `id`, `character_id`, `political_office_benefit_id` (FK auf Predefine-Zeile), `last_tick_at` (timestamptz), optional `ticks_count`. - **Unique** `(character_id, political_office_benefit_id)`. **Logik:** 1. Alle `political_office`-Zeilen mit `character_id IS NOT NULL` laden (oder seit letztem Lauf geänderte). 2. Pro Zeile zugehörige `office_type_id` → alle `PoliticalOfficeBenefit` mit `benefitDefinition.tr = 'reputation_periodic'`. 3. Für jede Kombination: `last_tick_at` lesen; wenn `now - last_tick_at >= intervalDays` (Kalendertage **Spielzeit** oder **UTC-Echtzeit** — festlegen; konsistent mit `daily_salary` ist **UTC-Datum** einfacher), dann `character.reputation` erhöhen, `last_tick_at = now`, Eintrag ins **Money-/Activity-Log** optional (`political_reputation_tick`). 4. **Mehrere Ämter:** mehrere Benefit-Zeilen = mehrere mögliche Ticks (jede mit eigenem Intervall), so wie konfiguriert. 5. **Idempotenz:** Pro Lauf nur ein Tick pro Unique-Key; Worker max. 1× täglich wie `PoliticsWorker` reicht, wenn Intervall ≥ 1 Tag; bei `intervalDays < 1` separaten Stunden-Job definieren (nicht empfohlen ohne Bedarf). **Daemon-Ort:** neuer Schritt `performPoliticalBenefitTicks()` am Ende von `PoliticsWorker::performDailyPoliticsTask()` **oder** Node-Job 03:05 UTC. **Push:** nach Tick `falukantUpdateStatus` an betroffene User (wie bei anderen Politics-Events). --- ### 2.2 `free_lover_slots` (kostenfreie Liebschaften / Mätressen) **Ziel:** Die ersten *N* aktiven Liebschaften (Relationship-Typ `lover` + sichtbarer `relationship_state`, Rolle z. B. `lover` oder `mistress_or_favorite`) verursachen **keinen monatlichen Unterhalt** (`monthly_base_cost` effektiv 0 für diese Slots). **Kein eigener Daemon nötig** — Anwendung beim **bestehenden monatlichen Abrechnungslauf** (C++ `UserCharacterWorker` oder Sequelize-Äquivalent), der `relationship_state` und Kosten verarbeitet. **Backend-Mechanik:** 1. Hilfsfunktion `getFreePoliticalLoverSlotCount(characterId)` - Aktive `PoliticalOffice` des Charakters → `office_type_id` → Summe `free_lover_slots.count` aus Benefits (max oder Summe — **Spec-Empfehlung: Summe**, Deckel optional `maxTotalFreeSlots: 5` in Config). 2. Liebschaften nach **Erstellungsdatum** oder fester Priorität sortieren; die ersten `N` erhalten `effectiveMonthlyCost = 0` für diese Abrechnung. 3. Optional: Flag in `relationship_state.flags_json` (`political_free_slot: true`) setzen/aktualisieren für Transparenz und UI — oder rein berechnet ohne Persistenz. **Fairness:** Nur Charaktere, die **Inhaber** sind (nicht nur Kandidaten). --- ### 2.3 `appoint_politicians` (Ernennungsrecht) **Ziel:** Amtsträger dürfen unter definierten Regeln **andere Ämter** in ihrer **Zuständigkeit** besetzen (NPC oder Spieler), ohne vollständige Wahl — oder: **Sonderwahl / Nachrücker** auslösen. **Varianten (Priorisierung empfohlen):** | Variante | Beschreibung | Aufwand | |----------|--------------|--------| | **A — Nachbesetzung** | Bei vakantem `political_office` in erlaubter Region: Appointer wählt **einen Kandidaten** aus Pool (nur Charaktere mit Titel/Alter wie bei Wahl); Server erstellt Eintrag `political_office` + Log. | Mittel | | **B — Nur NPC** | Appointer „ernennt“ nur NPCs (bestehende NPC-Erzeugung); kein PvP-Konflikt. | Geringer | | **C — Einladung** | `appointment_invite` an Spieler; Annahme erstellt Amt. | Höher | **Persistenz (neu):** - `falukant_data.political_appointment` - `id`, `appointer_character_id`, `target_character_id` (nullable bei NPC), `office_type_id`, `region_id`, `status` (`pending`, `accepted`, `rejected`, `expired`, `completed`), `created_at`, `expires_at`, `completed_office_id` (nullable). **Regeln:** - Appointer muss **aktuelles** Amt haben, dessen Benefit `officeTrs` das Zielamt enthält. - Zielregion muss zur **Kompetenz** passen (z. B. gleiche Stadt/Grafschaft wie Appointer-Amt oder laut `scope` aus JSON erweitert). - Anti-Spam: Cooldown, max. offene Ernennungen pro Appointer. **Daemon:** optional nur **Ablauf** (`expired`) einmal täglich im `PoliticsWorker` oder Node. --- ### 2.4 `set_regional_tax` (Steuersätze festlegen) **Ziel:** Berechtigte setzen `tax_percent` (oder einen **Aufschlag**) für Regionen innerhalb ihrer Kompetenz (`scope`: `local`, `shire`, `duchy`, `national`). **Datenmodell:** - **Option A (minimal):** direkt `falukant_data.region.tax_percent` ändern + **Audit-Tabelle** `region_tax_history` (`region_id`, `old_value`, `new_value`, `setter_character_id`, `office_id`, `created_at`). - **Option B (flexibler):** `region_political_tax_modifier` (additiv), Basis bleibt in `region`; effektive Steuer = Basis + Summe gültiger Modifier (mit TTL). Für „Rücknahme nach Amtsende“ einfacher. **Backend-Mechanik:** 1. `GET /api/falukant/politics/tax-jurisdiction` — Regionen, für die der Charakter nach aktuellem Amt + `set_regional_tax` schreiben darf, inkl. aktuellem Wert und Grenzen. 2. `PUT /api/falukant/politics/region/:regionId/tax` — Body `{ percent }` oder `{ delta }`; Server prüft: - Charakter hat passendes Amt; - Region liegt in erlaubter Hierarchie (lokal = nur Amtsregion; shire = Teilbaum unter shire; …); - **Clamp** min/max (global config, z. B. 0–25 %). 3. Verkaufs-/Steuer-SQL **unverändert** nutzen, sofern weiterhin `tax_percent` auf `region` liegt (bei Option B SQL um Modifier erweitern). **Daemon:** nicht nötig; bei **Amtsende** optional Modifier entfernen oder auf Historie zurücksetzen (täglicher Job oder Trigger auf `political_office` delete). --- ### 2.5 `guard_protection` / `court_immunity` (Spielwirkung) **Optional, später:** - **guard_protection:** Reduktion von Untergrund-Erfolgswahrscheinlichkeit gegen den Charakter oder geringerer `visibility`-Anstieg bei Affären (wenn solche Formeln existieren). - **court_immunity:** Abschwächung bestimmter Rechts-/Straf-Events (sobald es dafür Services gibt). Konkrete Zahlen als JSON in `value` (`attackMitigationPercent`, …). **Daemon:** nicht zwingend; eher **bei Event-Auswertung** abfragen. --- ## 3. Daemon — Gesamtüberblick ``` 03:00 (bestehend) PoliticsWorker ├─ … Wahlen, Nachbesetzung, Notifications … ├─ NEU: politicalBenefitReputationTicks() └─ NEU: politicalAppointmentExpiry() [optional] Monatlich / bestehender Character-Tick └─ relationship monthly cost ← berücksichtigt free_lover_slots ``` **Idempotenz-Prinzip:** Jeder Batch-Job nutzt klare **Unique-Keys** und **Transaktionen**; bei Crash-Wiederholung keine Doppel-Ticks (Reputation). **Observability:** strukturierte Logs `[PoliticalBenefits] characterId=… officeBenefitId=… action=reputation_tick`. --- ## 4. Backend-API (Node) — Übersicht | Endpoint | Zweck | |----------|--------| | `GET /api/falukant/politics/my-powers` | Für eingeloggten Charakter: Liste **aktionabler** Vorteile inkl. Parameter, nächster `reputation`-Tick (read-only aus `political_benefit_last_tick`), freie Liebschaft-Slots-Zahl. | | `GET /api/falukant/politics/tax-jurisdiction` | Regionen + aktuelle Steuer + erlaubter Bereich. | | `PUT /api/falukant/politics/region/:id/tax` | Steuer setzen (siehe oben). | | `GET /api/falukant/politics/appointable-offices` | Vakante oder ernennbare Posten + erlaubte Ziel-`officeTrs`. | | `POST /api/falukant/politics/appointments` | Ernennung anstoßen (Body: `targetCharacterId`, `officeTypeId`, `regionId`). | | `POST /api/falukant/politics/appointments/:id/accept` | Optional für Ziel-Spieler. | **Service-Schicht:** `falukantPoliticalPowersService.js` (oder Erweiterung `falukantService`) mit klaren Guards und Wiederverwendung der Benefit-Loader aus `PoliticalOfficeBenefit`. **Sicherheit:** Alle Routen mit `getFalukantUserByHashedId` + Charakter-Inhaber-Check; keine Überschreibung fremder Regionen. --- ## 5. UI-Konzept (Vue) ### 5.1 Einstieg - In **Politik** neuer Unter-Tab oder Sektion **„Amtsbefugnisse“** (sichtbar nur, wenn `my-powers` nicht leer). - Kurzinfo in **„Aktuelle Position“**: neben der Vorteilsliste ein Link „Befugnisse ausüben“. ### 5.2 Steuern (`set_regional_tax`) - **Ansicht:** Tabelle Region (Name, Typ, aktueller Steuersatz, Slider oder Zahleneingabe). - **Validierung clientseitig** spiegelt Server-Clamps. - **Speichern** ruft `PUT` auf; Erfolgstoast; ggf. Eintrag in lokaler Historie (letzte 5 Änderungen aus `region_tax_history`). ### 5.3 Ernennungen (`appoint_politicians`) - **Schritt 1:** Amt + Region wählen (nur erlaubte Kombinationen). - **Schritt 2:** Ziel-Charakter suchen (Autocomplete über Community/Falukant-Suche) oder „NPC vorschlagen“ (Variante B). - **Schritt 3:** Bestätigung, Anzeige der Regeln (Titel, Alter, Cooldown). - **Einladungs-Variante:** Posteingang / Notification mit Annehmen/Ablehnen. ### 5.4 `reputation_periodic` / Meta - Anzeige: **„Nächster Ansehens-Bonus: in X Tagen“** aus `last_tick_at` + `intervalDays` (nur lesend; Ausführung durch Daemon). - Optional: Eintrag im **Tagebuch** / Notification nach Tick. ### 5.5 `free_lover_slots` - In **Familie → Affären** (oder Liebschaften): Badge **„Amt: X Plätze ohne Unterhalt“**; in der Kostenübersicht pro Beziehung Kennzeichnung „politisch freigestellt“. - Kein eigener Daemon-Dialog nötig. ### 5.6 Internationalisierung - Alle neuen Strings unter `falukant.politics.powers.*` (de/en/es/ceb analog zu `benefits`). --- ## 6. Migrations-Reihenfolge (empfohlen) 1. `political_benefit_last_tick` (+ FKs, Indizes). 2. `region_tax_history` oder Modifier-Tabelle. 3. `political_appointment` (+ Indizes auf `status`, `appointer_character_id`). 4. Daemon-/Job-Implementierung Reputation. 5. API Steuern + UI. 6. API Ernennungen + UI. 7. Monatliche Kostenanpassung `free_lover_slots`. --- ## 7. Offene Produktentscheidungen - **Echtzeit vs. Spielzeit** für `intervalDays` (aktuell Mischung möglich mit `daily_salary` auf UTC-Datum). - **Summe vs. Maximum** bei mehreren Ämtern für `free_lover_slots`. - **Ernennung:** nur innerhalb der eigenen Region oder auch in Unterregionen? - **Steuer:** darf derselbe Region mehrfach von verschiedenen Ämtern gesetzt werden? (Empfehlung: letzte schreibende Instanz mit höherer `scope`-Priorität gewinnt, oder explizite Hierarchie-Regel.) --- *Dokumentstand: abgestimmt auf Repository mit C++-`PoliticsWorker`, Sequelize-Falukant-Backend und bestehenden `political_office_benefit`-Seeds.*