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

@@ -18,6 +18,10 @@ import PoliticalOfficeType from "../../models/falukant/type/political_office_typ
import PoliticalOfficeBenefitType from "../../models/falukant/type/political_office_benefit_type.js";
import PoliticalOfficePrerequisite from "../../models/falukant/predefine/political_office_prerequisite.js";
import UndergroundType from "../../models/falukant/type/underground.js";
import WeatherType from "../../models/falukant/type/weather.js";
import Weather from "../../models/falukant/data/weather.js";
import ProductWeatherEffect from "../../models/falukant/type/product_weather_effect.js";
import ProductType from "../../models/falukant/type/product.js";
// Debug-Flag: Nur wenn DEBUG_FALUKANT=1 gesetzt ist, werden ausführliche Logs ausgegeben.
const falukantDebug = process.env.DEBUG_FALUKANT === '1';
@@ -43,6 +47,9 @@ export const initializeFalukantTypes = async () => {
await initializePoliticalOfficePrerequisites();
await initializeUndergroundTypes();
await initializeVehicleTypes();
await initializeFalukantWeatherTypes();
await initializeFalukantWeathers();
await initializeFalukantProductWeatherEffects();
};
const regionTypes = [];
@@ -1010,3 +1017,445 @@ export const initializeFalukantTitlesOfNobility = async () => {
throw error;
}
};
const weatherTypes = [
{ tr: "sunny" },
{ tr: "cloudy" },
{ tr: "rainy" },
{ tr: "stormy" },
{ tr: "snowy" },
{ tr: "foggy" },
{ tr: "windy" },
{ tr: "clear" }
];
export const initializeFalukantWeatherTypes = async () => {
try {
for (const weatherType of weatherTypes) {
await WeatherType.findOrCreate({
where: { tr: weatherType.tr },
});
}
console.log(`[Falukant] Wettertypen initialisiert: ${weatherTypes.length} Typen`);
} catch (error) {
console.error('❌ Fehler beim Initialisieren der Falukant-Wettertypen:', error);
throw error;
}
};
export const initializeFalukantWeathers = async () => {
try {
// Hole alle Städte (Regions vom Typ "city")
const cityRegionType = await RegionType.findOne({ where: { labelTr: 'city' } });
if (!cityRegionType) {
console.warn('[Falukant] Kein RegionType "city" gefunden, überspringe Wetter-Initialisierung');
return;
}
const cities = await RegionData.findAll({
where: { regionTypeId: cityRegionType.id },
attributes: ['id', 'name']
});
// Hole alle Wettertypen
const allWeatherTypes = await WeatherType.findAll();
if (allWeatherTypes.length === 0) {
console.warn('[Falukant] Keine Wettertypen gefunden, überspringe Wetter-Initialisierung');
return;
}
// Weise jeder Stadt zufällig ein Wetter zu
for (const city of cities) {
const randomWeatherType = allWeatherTypes[Math.floor(Math.random() * allWeatherTypes.length)];
await Weather.findOrCreate({
where: { regionId: city.id },
defaults: {
weatherTypeId: randomWeatherType.id
}
});
}
console.log(`[Falukant] Wetter für ${cities.length} Städte initialisiert`);
} catch (error) {
console.error('❌ Fehler beim Initialisieren der Falukant-Wetter:', error);
throw error;
}
};
export const initializeFalukantProductWeatherEffects = async () => {
try {
// Hole alle Produkte und Wettertypen
const products = await ProductType.findAll();
const weatherTypes = await WeatherType.findAll();
if (products.length === 0 || weatherTypes.length === 0) {
console.warn('[Falukant] Keine Produkte oder Wettertypen gefunden, überspringe Produkt-Wetter-Effekte');
return;
}
// Erstelle Map für schnellen Zugriff
const productMap = new Map(products.map(p => [p.labelTr, p.id]));
const weatherMap = new Map(weatherTypes.map(w => [w.tr, w.id]));
// Definiere Effekte für jedes Produkt-Wetter-Paar
// Format: { productLabel: { weatherTr: effectValue } }
// effectValue: -2 (sehr negativ), -1 (negativ), 0 (neutral), 1 (positiv), 2 (sehr positiv)
const effects = {
// Landwirtschaftliche Produkte
wheat: {
sunny: 1, // Gutes Wachstum
cloudy: 0,
rainy: 2, // Wasser ist essentiell
stormy: -1, // Kann Ernte beschädigen
snowy: -2, // Kein Wachstum
foggy: 0,
windy: 0,
clear: 1
},
grain: {
sunny: 1,
cloudy: 0,
rainy: 2,
stormy: -1,
snowy: -2,
foggy: 0,
windy: 0,
clear: 1
},
carrot: {
sunny: 1,
cloudy: 0,
rainy: 2,
stormy: -1,
snowy: -2,
foggy: 0,
windy: 0,
clear: 1
},
fish: {
sunny: 0,
cloudy: 0,
rainy: 0,
stormy: -2, // Gefährlich zu fischen
snowy: -1, // Kaltes Wasser
foggy: -1, // Schlechte Sicht
windy: -1, // Schwierig zu fischen
clear: 1
},
meat: {
sunny: -1, // Kann verderben
cloudy: 0,
rainy: -1, // Feucht
stormy: -2,
snowy: 1, // Kühlt
foggy: 0,
windy: 0,
clear: 0
},
leather: {
sunny: -1, // Kann austrocknen
cloudy: 0,
rainy: -1, // Feucht
stormy: -2,
snowy: 1, // Kühlt
foggy: 0,
windy: 0,
clear: 0
},
wood: {
sunny: 1, // Trocknet gut
cloudy: 0,
rainy: -1, // Feucht
stormy: -2, // Kann beschädigt werden
snowy: 0,
foggy: -1, // Feucht
windy: 0,
clear: 1
},
stone: {
sunny: 0,
cloudy: 0,
rainy: 0,
stormy: 0,
snowy: 0,
foggy: 0,
windy: 0,
clear: 0
},
milk: {
sunny: -1, // Kann sauer werden
cloudy: 0,
rainy: 0,
stormy: -1,
snowy: 1, // Kühlt
foggy: 0,
windy: 0,
clear: 0
},
cheese: {
sunny: -1,
cloudy: 0,
rainy: -1, // Feucht
stormy: -1,
snowy: 1, // Kühlt
foggy: 0,
windy: 0,
clear: 0
},
bread: {
sunny: 0,
cloudy: 0,
rainy: -1, // Feucht
stormy: -1,
snowy: 0,
foggy: -1, // Feucht
windy: 0,
clear: 0
},
beer: {
sunny: 0,
cloudy: 0,
rainy: 0,
stormy: 0,
snowy: 1, // Kühlt
foggy: 0,
windy: 0,
clear: 0
},
iron: {
sunny: 0,
cloudy: 0,
rainy: -1, // Rost
stormy: -2, // Rost
snowy: 0,
foggy: -1, // Feucht
windy: 0,
clear: 0
},
copper: {
sunny: 0,
cloudy: 0,
rainy: -1, // Oxidation
stormy: -2,
snowy: 0,
foggy: -1,
windy: 0,
clear: 0
},
spices: {
sunny: 0,
cloudy: 0,
rainy: -1, // Feucht
stormy: -1,
snowy: 0,
foggy: -1, // Feucht
windy: 0,
clear: 0
},
salt: {
sunny: 1, // Trocknet gut
cloudy: 0,
rainy: -2, // Löst sich auf
stormy: -2,
snowy: 0,
foggy: -1, // Feucht
windy: 0,
clear: 1
},
sugar: {
sunny: 0,
cloudy: 0,
rainy: -2, // Löst sich auf
stormy: -2,
snowy: 0,
foggy: -1, // Feucht
windy: 0,
clear: 0
},
vinegar: {
sunny: 1, // Heißes Wetter fördert Gärung
cloudy: 0,
rainy: 0,
stormy: 0,
snowy: -1, // Kaltes Wetter hemmt Gärung
foggy: 0,
windy: 0,
clear: 1 // Heißes Wetter fördert Gärung
},
cotton: {
sunny: 1, // Trocknet gut
cloudy: 0,
rainy: -1, // Feucht
stormy: -2,
snowy: 0,
foggy: -1, // Feucht
windy: 0,
clear: 1
},
wine: {
sunny: 0,
cloudy: 0,
rainy: 0,
stormy: 0,
snowy: 1, // Kühlt
foggy: 0,
windy: 0,
clear: 0
},
gold: {
sunny: 0,
cloudy: 0,
rainy: 0,
stormy: 0,
snowy: 0,
foggy: 0,
windy: 0,
clear: 0
},
diamond: {
sunny: 0,
cloudy: 0,
rainy: 0,
stormy: 0,
snowy: 0,
foggy: 0,
windy: 0,
clear: 0
},
furniture: {
sunny: 0,
cloudy: 0,
rainy: -1, // Feucht
stormy: -2,
snowy: 0,
foggy: -1, // Feucht
windy: 0,
clear: 0
},
clothing: {
sunny: 0,
cloudy: 0,
rainy: -1, // Feucht
stormy: -2,
snowy: 0,
foggy: -1, // Feucht
windy: 0,
clear: 0
},
jewelry: {
sunny: 0,
cloudy: 0,
rainy: -2, // Kann beschädigt werden
stormy: -2,
snowy: 0,
foggy: -1, // Feucht
windy: 0,
clear: 0
},
painting: {
sunny: -1, // Kann verblassen
cloudy: 0,
rainy: -2, // Feucht
stormy: -2,
snowy: 0,
foggy: -1, // Feucht
windy: 0,
clear: -1
},
book: {
sunny: -1, // Kann verblassen
cloudy: 0,
rainy: -2, // Feucht
stormy: -2,
snowy: 0,
foggy: -1, // Feucht
windy: 0,
clear: -1
},
weapon: {
sunny: 0,
cloudy: 0,
rainy: -1, // Rost
stormy: -2, // Rost
snowy: 0,
foggy: -1, // Feucht
windy: 0,
clear: 0
},
armor: {
sunny: 0,
cloudy: 0,
rainy: -1, // Rost
stormy: -2, // Rost
snowy: 0,
foggy: -1, // Feucht
windy: 0,
clear: 0
},
shield: {
sunny: 0,
cloudy: 0,
rainy: -1, // Rost
stormy: -2, // Rost
snowy: 0,
foggy: -1, // Feucht
windy: 0,
clear: 0
},
horse: {
sunny: 1, // Gutes Wetter
cloudy: 0,
rainy: -1, // Nass
stormy: -2, // Angst
snowy: -1, // Kalt
foggy: 0,
windy: 0,
clear: 1
},
ox: {
sunny: 1, // Gutes Wetter
cloudy: 0,
rainy: -1, // Nass
stormy: -2, // Angst
snowy: -1, // Kalt
foggy: 0,
windy: 0,
clear: 1
}
};
// Erstelle alle Produkt-Wetter-Effekte
const effectEntries = [];
for (const [productLabel, weatherEffects] of Object.entries(effects)) {
const productId = productMap.get(productLabel);
if (!productId) {
console.warn(`[Falukant] Produkt "${productLabel}" nicht gefunden, überspringe`);
continue;
}
for (const [weatherTr, effectValue] of Object.entries(weatherEffects)) {
const weatherTypeId = weatherMap.get(weatherTr);
if (!weatherTypeId) {
console.warn(`[Falukant] Wettertyp "${weatherTr}" nicht gefunden, überspringe`);
continue;
}
effectEntries.push({
productId,
weatherTypeId,
qualityEffect: effectValue
});
}
}
// Bulk insert mit ignoreDuplicates
await ProductWeatherEffect.bulkCreate(effectEntries, {
ignoreDuplicates: true
});
console.log(`[Falukant] Produkt-Wetter-Effekte initialisiert: ${effectEntries.length} Einträge`);
} catch (error) {
console.error('❌ Fehler beim Initialisieren der Produkt-Wetter-Effekte:', error);
throw error;
}
};