Add product weather effects and regional pricing enhancements

- Introduced a new endpoint in FalukantController to retrieve product prices based on region and product ID.
- Implemented logic in FalukantService to calculate product prices considering user knowledge and regional factors.
- Added weather-related data models and associations to enhance product pricing accuracy based on weather conditions.
- Updated frontend components to cache and display regional product prices effectively, improving user experience.
This commit is contained in:
Torsten Schulz (local)
2025-12-02 09:55:08 +01:00
parent 39716b1f40
commit ba1a12402d
11 changed files with 755 additions and 10 deletions

View File

@@ -61,7 +61,10 @@ import VehicleType from '../models/falukant/type/vehicle.js';
import Vehicle from '../models/falukant/data/vehicle.js';
import Transport from '../models/falukant/data/transport.js';
import RegionDistance from '../models/falukant/data/region_distance.js';
import Weather from '../models/falukant/data/weather.js';
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';
function calcAge(birthdate) {
const b = new Date(birthdate); b.setHours(0, 0);
@@ -1036,7 +1039,19 @@ class FalukantService extends BaseService {
if (u.money < cost) throw new Error('notenoughmoney');
const r = await updateFalukantUserMoney(u.id, -cost, 'Production cost', u.id);
if (!r.success) throw new Error('Failed to update money');
const d = await Production.create({ branchId: b.id, productId, quantity });
// Hole aktuelles Wetter der Region
const currentWeather = await Weather.findOne({
where: { regionId: b.regionId }
});
const weatherTypeId = currentWeather?.weatherTypeId || null;
const d = await Production.create({
branchId: b.id,
productId,
quantity,
weatherTypeId
});
notifyUser(u.user.hashedId, 'falukantUpdateStatus', {});
notifyUser(u.user.hashedId, 'falukantBranchUpdate', { branchId: b.id });
return d;
@@ -1045,7 +1060,41 @@ class FalukantService extends BaseService {
async getProduction(hashedUserId, branchId) {
const u = await getFalukantUserOrFail(hashedUserId);
const b = await getBranchOrFail(u.id, branchId);
return Production.findOne({ where: { regionId: b.regionId } });
const production = await Production.findOne({
where: { branchId: b.id },
include: [
{ model: ProductType, as: 'productType' },
{ model: WeatherType, as: 'weatherType' }
]
});
if (!production) {
return null;
}
// Berechne Qualität basierend auf Wettereffekt
let quality = 50; // Basisqualität (50%)
if (production.weatherTypeId) {
const weatherEffect = await ProductWeatherEffect.findOne({
where: {
productId: production.productId,
weatherTypeId: production.weatherTypeId
}
});
if (weatherEffect) {
// Wettereffekt: -2 bis +2, wird auf Qualität angewendet
// Basisqualität 50%, Effekt wird als Prozentpunkte addiert
quality = Math.max(0, Math.min(100, 50 + (weatherEffect.qualityEffect * 10)));
}
}
// Konvertiere zu JSON und füge berechnete Qualität hinzu
const result = production.toJSON();
result.quality = Math.round(quality);
return result;
}
async getProducts(hashedUserId) {
@@ -1507,20 +1556,43 @@ class FalukantService extends BaseService {
],
where: { falukantUserId: user.id }
},
{ model: ProductType, as: 'productType', attributes: ['labelTr', 'productionTime'] }
{ model: ProductType, as: 'productType', attributes: ['labelTr', 'productionTime', 'id'] },
{ model: WeatherType, as: 'weatherType' }
],
attributes: ['startTimestamp', 'quantity'],
attributes: ['startTimestamp', 'quantity', 'productId', 'weatherTypeId'],
});
const formattedProductions = productions.map((production) => {
// Berechne Qualität für jede Produktion
const formattedProductions = await Promise.all(productions.map(async (production) => {
const startTimestamp = new Date(production.startTimestamp).getTime();
const endTimestamp = startTimestamp + production.productType.productionTime * 60 * 1000;
// Berechne Qualität basierend auf Wettereffekt
let quality = 50; // Basisqualität (50%)
if (production.weatherTypeId) {
const weatherEffect = await ProductWeatherEffect.findOne({
where: {
productId: production.productId,
weatherTypeId: production.weatherTypeId
}
});
if (weatherEffect) {
// Wettereffekt: -2 bis +2, wird auf Qualität angewendet
quality = Math.max(0, Math.min(100, 50 + (weatherEffect.qualityEffect * 10)));
}
}
return {
cityName: production.branch.region.name,
productName: production.productType.labelTr,
quantity: production.quantity,
quality: Math.round(quality),
endTimestamp: new Date(endTimestamp).toISOString(),
};
});
}));
formattedProductions.sort((a, b) => new Date(a.endTimestamp) - new Date(b.endTimestamp));
return formattedProductions;
}
@@ -3538,6 +3610,31 @@ class FalukantService extends BaseService {
return regions;
}
async getProductPriceInRegion(hashedUserId, productId, regionId) {
const user = await this.getFalukantUserByHashedId(hashedUserId);
const character = await FalukantCharacter.findOne({ where: { userId: user.id } });
if (!character) {
throw new Error(`No FalukantCharacter found for user with id ${user.id}`);
}
// Produkt abrufen
const product = await ProductType.findOne({ where: { id: productId } });
if (!product) {
throw new Error(`Product not found with id ${productId}`);
}
// Knowledge für dieses Produkt abrufen
const knowledge = await Knowledge.findOne({
where: { characterId: character.id, productId: productId }
});
const knowledgeFactor = knowledge?.knowledge || 0;
// Verwende die bereits existierende calcRegionalSellPrice Funktion
const price = await calcRegionalSellPrice(product, knowledgeFactor, regionId);
return { price };
}
async getProductPricesInCities(hashedUserId, productId, currentPrice) {
const user = await this.getFalukantUserByHashedId(hashedUserId);
const character = await FalukantCharacter.findOne({ where: { userId: user.id } });