Add reputation actions and localization updates: Implement getReputationActions method in FalukantService, enhancing reputation management. Update German and English localization files to include new reputation action terms and mood descriptions, improving user experience and clarity.

This commit is contained in:
Torsten Schulz (local)
2026-01-29 09:37:34 +01:00
parent eecd947377
commit 1ead06fd4f
5 changed files with 246 additions and 6 deletions

View File

@@ -70,6 +70,7 @@ import TownProductWorth from '../models/falukant/data/town_product_worth.js';
import ProductWeatherEffect from '../models/falukant/type/product_weather_effect.js';
import WeatherType from '../models/falukant/type/weather.js';
import ReputationActionType from '../models/falukant/type/reputation_action.js';
import ReputationActionLog from '../models/falukant/log/reputation_action.js';
function calcAge(birthdate) {
const b = new Date(birthdate); b.setHours(0, 0);
@@ -465,7 +466,7 @@ class FalukantService extends BaseService {
{
model: FalukantCharacter,
as: 'character',
attributes: ['id', 'birthdate', 'health'],
attributes: ['id', 'birthdate', 'health', 'reputation'],
include: [
{
model: Relationship,
@@ -3007,10 +3008,96 @@ class FalukantService extends BaseService {
return TitleOfNobility.findAll();
}
async getReputationActions() {
return ReputationActionType.findAll({
async getReputationActions(hashedUserId) {
const user = await getFalukantUserOrFail(hashedUserId);
// Lade alle Action-Typen
const actionTypes = await ReputationActionType.findAll({
order: [['cost', 'ASC']]
});
// Berechne tägliche Nutzung (heute)
const todayStart = new Date();
todayStart.setHours(0, 0, 0, 0);
const todayEnd = new Date();
todayEnd.setHours(23, 59, 59, 999);
const todayActions = await ReputationActionLog.count({
where: {
falukantUserId: user.id,
actionTimestamp: {
[Op.between]: [todayStart, todayEnd]
}
}
});
// Standard-Limits (können später konfigurierbar gemacht werden)
const dailyCap = 10; // Maximal 10 Actions pro Tag
const dailyUsed = todayActions;
const dailyRemaining = Math.max(0, dailyCap - dailyUsed);
// Cooldown: 60 Minuten zwischen Actions
const cooldownMinutes = 60;
const lastAction = await ReputationActionLog.findOne({
where: { falukantUserId: user.id },
order: [['actionTimestamp', 'DESC']],
attributes: ['actionTimestamp']
});
let cooldownRemainingSec = 0;
if (lastAction && lastAction.actionTimestamp) {
const now = new Date();
const lastActionTime = new Date(lastAction.actionTimestamp);
const secondsSinceLastAction = Math.floor((now - lastActionTime) / 1000);
const cooldownSec = cooldownMinutes * 60;
cooldownRemainingSec = Math.max(0, cooldownSec - secondsSinceLastAction);
}
// Berechne timesUsed für jede Action (basierend auf decay_window_days)
const actionsWithUsage = await Promise.all(actionTypes.map(async (actionType) => {
const windowStart = new Date();
windowStart.setDate(windowStart.getDate() - actionType.decayWindowDays);
const usageCount = await ReputationActionLog.count({
where: {
falukantUserId: user.id,
actionTypeId: actionType.id,
actionTimestamp: {
[Op.gte]: windowStart
}
}
});
// Berechne aktuellen Gewinn basierend auf decay
let currentGain = actionType.baseGain;
for (let i = 0; i < usageCount; i++) {
currentGain = Math.max(
actionType.minGain,
Math.floor(currentGain * actionType.decayFactor)
);
}
return {
id: actionType.id,
tr: actionType.tr,
cost: actionType.cost,
baseGain: actionType.baseGain,
currentGain: currentGain,
timesUsed: usageCount,
decayFactor: actionType.decayFactor,
minGain: actionType.minGain,
decayWindowDays: actionType.decayWindowDays
};
}));
return {
actions: actionsWithUsage,
dailyCap,
dailyUsed,
dailyRemaining,
cooldownMinutes,
cooldownRemainingSec
};
}
async getHouseTypes() {

View File

@@ -651,6 +651,7 @@
"happy": "Glücklich",
"sad": "Traurig",
"angry": "Wütend",
"calm": "Ruhig",
"nervous": "Nervös",
"excited": "Aufgeregt",
"bored": "Gelangweilt",
@@ -746,7 +747,34 @@
"reputation": {
"title": "Reputation",
"overview": {
"title": "Übersicht"
"title": "Übersicht",
"current": "Aktuelle Reputation"
},
"actions": {
"title": "Reputations-Aktionen",
"description": "Du kannst verschiedene Aktionen durchführen, um deine Reputation zu verbessern.",
"none": "Keine Reputations-Aktionen verfügbar.",
"action": "Aktion",
"cost": "Kosten",
"gain": "Gewinn",
"timesUsed": "Verwendet",
"execute": "Ausführen",
"running": "Läuft...",
"dailyLimit": "Tägliches Limit: {remaining} von {cap} Aktionen übrig",
"cooldown": "Cooldown: Noch {minutes} Minuten",
"type": {
"soup_kitchen": "Suppenküche",
"library_donation": "Bibliotheksspende",
"scholarships": "Stipendien",
"church_hospice": "Kirchenhospiz",
"school_funding": "Schulfinanzierung",
"orphanage_build": "Waisenhaus bauen",
"bridge_build": "Brücke bauen",
"hospital_donation": "Krankenhausspende",
"patronage": "Mäzenatentum",
"statue_build": "Statue errichten",
"well_build": "Brunnen bauen"
}
},
"party": {
"title": "Feste",

View File

@@ -221,6 +221,60 @@
"nobility": {
"cooldown": "You can only advance again on {date}."
},
"mood": {
"happy": "Happy",
"sad": "Sad",
"angry": "Angry",
"calm": "Calm",
"nervous": "Nervous",
"excited": "Excited",
"bored": "Bored",
"fearful": "Fearful",
"confident": "Confident",
"curious": "Curious",
"hopeful": "Hopeful",
"frustrated": "Frustrated",
"lonely": "Lonely",
"grateful": "Grateful",
"jealous": "Jealous",
"guilty": "Guilty",
"apathetic": "Apathetic",
"relieved": "Relieved",
"proud": "Proud",
"ashamed": "Ashamed"
},
"character": {
"brave": "Brave",
"kind": "Kind",
"greedy": "Greedy",
"wise": "Wise",
"loyal": "Loyal",
"cunning": "Cunning",
"generous": "Generous",
"arrogant": "Arrogant",
"honest": "Honest",
"ambitious": "Ambitious",
"patient": "Patient",
"impatient": "Impatient",
"selfish": "Selfish",
"charismatic": "Charismatic",
"empathetic": "Empathetic",
"timid": "Timid",
"stubborn": "Stubborn",
"resourceful": "Resourceful",
"reckless": "Reckless",
"disciplined": "Disciplined",
"optimistic": "Optimistic",
"pessimistic": "Pessimistic",
"manipulative": "Manipulative",
"independent": "Independent",
"dependent": "Dependent",
"adventurous": "Adventurous",
"humble": "Humble",
"vengeful": "Vengeful",
"pragmatic": "Pragmatic",
"idealistic": "Idealistic"
},
"healthview": {
"title": "Health",
"age": "Age",
@@ -436,6 +490,77 @@
"success": "The child has been baptized.",
"error": "The child could not be baptized."
}
},
"reputation": {
"title": "Reputation",
"overview": {
"title": "Overview",
"current": "Current Reputation"
},
"actions": {
"title": "Reputation Actions",
"description": "You can perform various actions to improve your reputation.",
"none": "No reputation actions available.",
"action": "Action",
"cost": "Cost",
"gain": "Gain",
"timesUsed": "Used",
"execute": "Execute",
"running": "Running...",
"dailyLimit": "Daily limit: {remaining} of {cap} actions remaining",
"cooldown": "Cooldown: {minutes} minutes remaining",
"type": {
"soup_kitchen": "Soup Kitchen",
"library_donation": "Library Donation",
"scholarships": "Scholarships",
"church_hospice": "Church Hospice",
"school_funding": "School Funding",
"orphanage_build": "Build Orphanage",
"bridge_build": "Build Bridge",
"hospital_donation": "Hospital Donation",
"patronage": "Patronage",
"statue_build": "Build Statue",
"well_build": "Build Well"
}
},
"party": {
"title": "Parties",
"totalCost": "Total Cost",
"order": "Order Party",
"inProgress": "Parties in Preparation",
"completed": "Completed Parties",
"newpartyview": {
"open": "Create New Party",
"close": "Hide New Party",
"type": "Party Type"
},
"music": {
"label": "Music",
"none": "No Music",
"bard": "A Bard",
"villageBand": "A Village Band",
"chamberOrchestra": "A Chamber Orchestra",
"symphonyOrchestra": "A Symphony Orchestra",
"symphonyOrchestraWithChorusAndSolists": "A Symphony Orchestra with Chorus and Soloists"
},
"banquette": {
"label": "Food",
"bread": "Bread",
"roastWithBeer": "Roast with Beer",
"poultryWithVegetablesAndWine": "Poultry with Vegetables and Wine",
"extensiveBuffet": "Festive Meal"
},
"servants": {
"label": "One servant per ",
"perPersons": " persons"
},
"esteemedInvites": {
"label": "Invited Estates"
},
"type": "Party Type",
"cost": "Cost",
"date": "Date"
}
}
}
}

View File

@@ -25,7 +25,7 @@
</tr>
<tr>
<td>{{ $t('falukant.family.spouse.mood') }}</td>
<td>{{ $t(`falukant.mood.${relationships[0].character2.mood.tr}`) }}</td>
<td>{{ relationships[0].character2.mood?.tr ? $t(`falukant.mood.${relationships[0].character2.mood.tr}`) : '—' }}</td>
</tr>
<tr>
<td>{{ $t('falukant.family.spouse.status') }}</td>

View File

@@ -20,7 +20,7 @@
<div v-else-if="activeTab === 'advance'">
<div class="advance-section" v-if="next && next.labelTr">
<p>
{{ $t('falukant.nobility.nextTitle') }}:
{{ $t('falukant.nobility.<<nextTitle') }}:
<strong>{{ $t(`falukant.titles.${gender}.${next.labelTr}`) }}</strong>
</p>
<ul class="prerequisites" v-if="next.requirements && next.requirements.length > 0">