Improvements to how weather temperature is calculated - takes into account current season

Centralised the season override into `getActiveWeatherSeason()`
Adjusted weather values based on client data
This commit is contained in:
Dev 2024-10-17 13:05:01 +01:00
parent eb6e61c1f0
commit 0b729fba11
6 changed files with 68 additions and 30 deletions

View File

@ -3,12 +3,12 @@
"weather": { "weather": {
"generateWeatherAmountHours": 24, "generateWeatherAmountHours": 24,
"clouds": { "clouds": {
"values": [-1.5, -1, 0, 0.5, 1, 1.5], "values": [-1, 0, 0.5, 1, 1.5],
"weights": [60, 50, 15, 5, 4, 3] "weights": [100, 15, 5, 4, 3]
}, },
"windSpeed": { "windSpeed": {
"values": [0, 1, 2, 3], "values": [0, 1, 2, 3, 4],
"weights": [4, 3, 2, 1] "weights": [6, 3, 2, 1, 1]
}, },
"windDirection": { "windDirection": {
"values": [1, 2, 3, 4, 5, 6, 7, 8], "values": [1, 2, 3, 4, 5, 6, 7, 8],
@ -19,8 +19,8 @@
"max": 1 "max": 1
}, },
"rain": { "rain": {
"values": [1, 2, 3], "values": [1, 2, 3, 4, 5],
"weights": [25, 1, 1] "weights": [25, 1, 1, 1, 1]
}, },
"rainIntensity": { "rainIntensity": {
"min": 0, "min": 0,
@ -31,12 +31,30 @@
"weights": [25, 8, 5, 5, 1] "weights": [25, 8, 5, 5, 1]
}, },
"temp": { "temp": {
"min": 0, "0": {
"max": 24 "min": 17,
"max": 32
},
"1": {
"min": 7,
"max": 15
},
"2": {
"min": -10,
"max": 5
},
"3": {
"min": 1,
"max": 15
},
"4": {
"min": 0,
"max": 24
}
}, },
"pressure": { "pressure": {
"min": 760, "min": 760,
"max": 764 "max": 780
}, },
"timePeriod": { "timePeriod": {
"values": [15, 30], "values": [15, 30],

View File

@ -30,7 +30,7 @@ export class WeatherController {
let result: IWeatherData = { acceleration: 0, time: "", date: "", weather: undefined, season: 1 }; // defaults, hydrated below let result: IWeatherData = { acceleration: 0, time: "", date: "", weather: undefined, season: 1 }; // defaults, hydrated below
result = this.weatherGenerator.calculateGameTime(result); result = this.weatherGenerator.calculateGameTime(result);
result.weather = this.weatherGenerator.generateWeather(); result.weather = this.weatherGenerator.generateWeather(result.season);
return result; return result;
} }
@ -46,10 +46,7 @@ export class WeatherController {
/** Handle client/localGame/weather */ /** Handle client/localGame/weather */
public generateLocal(sesssionId: string): IGetLocalWeatherResponseData { public generateLocal(sesssionId: string): IGetLocalWeatherResponseData {
const result: IGetLocalWeatherResponseData = { const result: IGetLocalWeatherResponseData = {
season: season: this.seasonalEventService.getActiveWeatherSeason(),
this.weatherConfig.overrideSeason !== null
? this.weatherConfig.overrideSeason
: this.seasonalEventService.getActiveWeatherSeason(),
weather: [], weather: [],
}; };

View File

@ -3,6 +3,7 @@ import { WeatherHelper } from "@spt/helpers/WeatherHelper";
import { WeightedRandomHelper } from "@spt/helpers/WeightedRandomHelper"; import { WeightedRandomHelper } from "@spt/helpers/WeightedRandomHelper";
import { IWeather, IWeatherData } from "@spt/models/eft/weather/IWeatherData"; import { IWeather, IWeatherData } from "@spt/models/eft/weather/IWeatherData";
import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
import { Season } from "@spt/models/enums/Season";
import { WindDirection } from "@spt/models/enums/WindDirection"; import { WindDirection } from "@spt/models/enums/WindDirection";
import { IWeatherConfig } from "@spt/models/spt/config/IWeatherConfig"; import { IWeatherConfig } from "@spt/models/spt/config/IWeatherConfig";
import { ILogger } from "@spt/models/spt/utils/ILogger"; import { ILogger } from "@spt/models/spt/utils/ILogger";
@ -46,10 +47,7 @@ export class WeatherGenerator {
data.time = this.getBsgFormattedInRaidTime(); data.time = this.getBsgFormattedInRaidTime();
data.acceleration = this.weatherConfig.acceleration; data.acceleration = this.weatherConfig.acceleration;
data.season = data.season = this.seasonalEventService.getActiveWeatherSeason();
this.weatherConfig.overrideSeason !== null
? this.weatherConfig.overrideSeason
: this.seasonalEventService.getActiveWeatherSeason();
return data; return data;
} }
@ -79,7 +77,7 @@ export class WeatherGenerator {
* Return randomised Weather data with help of config/weather.json * Return randomised Weather data with help of config/weather.json
* @returns Randomised weather data * @returns Randomised weather data
*/ */
public generateWeather(timestamp?: number): IWeather { public generateWeather(currentSeason: Season, timestamp?: number): IWeather {
const clouds = this.getWeightedClouds(); const clouds = this.getWeightedClouds();
// Force rain to off if no clouds // Force rain to off if no clouds
@ -93,7 +91,7 @@ export class WeatherGenerator {
rain: rain, rain: rain,
rain_intensity: rain > 1 ? this.getRandomFloat("rainIntensity") : 0, rain_intensity: rain > 1 ? this.getRandomFloat("rainIntensity") : 0,
fog: this.getWeightedFog(), fog: this.getWeightedFog(),
temp: this.getRandomFloat("temp"), // TODO - lower value at night / take into account season temp: 0, // TODO - lower value at night / take into account season
pressure: this.getRandomFloat("pressure"), pressure: this.getRandomFloat("pressure"),
time: "", time: "",
date: "", date: "",
@ -102,9 +100,17 @@ export class WeatherGenerator {
this.setCurrentDateTime(result, timestamp); this.setCurrentDateTime(result, timestamp);
result.temp = this.getRaidTemperature(currentSeason, result.time);
return result; return result;
} }
protected getRaidTemperature(currentSeason: Season, currentTimeBsgFormatted: string): number {
// TODO, take into account time of day
const minMax = this.weatherConfig.weather.temp[currentSeason];
return Number.parseFloat(this.randomUtil.getFloat(minMax.min, minMax.max).toPrecision(2));
}
/** /**
* Set IWeather date/time/timestamp values to now * Set IWeather date/time/timestamp values to now
* @param weather Object to update * @param weather Object to update

View File

@ -30,7 +30,7 @@ export interface IWeatherValues {
rain: WeatherSettings<number>; rain: WeatherSettings<number>;
rainIntensity: MinMax; rainIntensity: MinMax;
fog: WeatherSettings<string>; fog: WeatherSettings<string>;
temp: MinMax; temp: Record<Season, MinMax>;
pressure: MinMax; pressure: MinMax;
/** Length of each weather period */ /** Length of each weather period */
timePeriod: WeatherSettings<number>; timePeriod: WeatherSettings<number>;

View File

@ -2,10 +2,12 @@ import { WeatherGenerator } from "@spt/generators/WeatherGenerator";
import { WeightedRandomHelper } from "@spt/helpers/WeightedRandomHelper"; import { WeightedRandomHelper } from "@spt/helpers/WeightedRandomHelper";
import { IWeather } from "@spt/models/eft/weather/IWeatherData"; import { IWeather } from "@spt/models/eft/weather/IWeatherData";
import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
import { Season } from "@spt/models/enums/Season";
import { IWeatherConfig } from "@spt/models/spt/config/IWeatherConfig"; import { IWeatherConfig } from "@spt/models/spt/config/IWeatherConfig";
import { ILogger } from "@spt/models/spt/utils/ILogger"; import { ILogger } from "@spt/models/spt/utils/ILogger";
import { ConfigServer } from "@spt/servers/ConfigServer"; import { ConfigServer } from "@spt/servers/ConfigServer";
import { DatabaseService } from "@spt/services/DatabaseService"; import { DatabaseService } from "@spt/services/DatabaseService";
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
import { TimeUtil } from "@spt/utils/TimeUtil"; import { TimeUtil } from "@spt/utils/TimeUtil";
import { inject, injectable } from "tsyringe"; import { inject, injectable } from "tsyringe";
@ -19,18 +21,20 @@ export class RaidWeatherService {
@inject("DatabaseService") protected databaseService: DatabaseService, @inject("DatabaseService") protected databaseService: DatabaseService,
@inject("TimeUtil") protected timeUtil: TimeUtil, @inject("TimeUtil") protected timeUtil: TimeUtil,
@inject("WeatherGenerator") protected weatherGenerator: WeatherGenerator, @inject("WeatherGenerator") protected weatherGenerator: WeatherGenerator,
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
@inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper, @inject("WeightedRandomHelper") protected weightedRandomHelper: WeightedRandomHelper,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
) { ) {
this.weatherConfig = this.configServer.getConfig(ConfigTypes.WEATHER); this.weatherConfig = this.configServer.getConfig(ConfigTypes.WEATHER);
this.generateWeather(); const currentSeason = this.seasonalEventService.getActiveWeatherSeason();
this.generateWeather(currentSeason);
} }
/** /**
* Generate 24 hours of weather data starting from midnight today * Generate 24 hours of weather data starting from midnight today
*/ */
public generateWeather() { public generateWeather(currentSeason: Season) {
// When to start generating weather from in milliseconds // When to start generating weather from in milliseconds
const staringTimestampMs = this.timeUtil.getTodaysMidnightTimestamp(); const staringTimestampMs = this.timeUtil.getTodaysMidnightTimestamp();
@ -42,13 +46,20 @@ export class RaidWeatherService {
// Keep adding new weather until we have reached desired future date // Keep adding new weather until we have reached desired future date
let nextTimestampMs = staringTimestampMs; let nextTimestampMs = staringTimestampMs;
while (nextTimestampMs <= futureTimestampToReachMs) { while (nextTimestampMs <= futureTimestampToReachMs) {
const newWeather = this.weatherGenerator.generateWeather(nextTimestampMs); const newWeatherToAddToCache = this.weatherGenerator.generateWeather(currentSeason, nextTimestampMs);
this.logger.warning(`Handling ${new Date(nextTimestampMs)}`);
this.weatherForecast.push(newWeather); // Add generated weather for time period to cache
this.weatherForecast.push(newWeatherToAddToCache);
// Increment timestamp so next loop can begin at correct time
nextTimestampMs += this.getWeightedWeatherTimePeriodMs(); nextTimestampMs += this.getWeightedWeatherTimePeriodMs();
} }
} }
/**
* Get a time period to increment by, e.g 15 or 30 minutes as milliseconds
* @returns milliseconds
*/
protected getWeightedWeatherTimePeriodMs(): number { protected getWeightedWeatherTimePeriodMs(): number {
const chosenTimePeriodMinutes = this.weightedRandomHelper.weightedRandom( const chosenTimePeriodMinutes = this.weightedRandomHelper.weightedRandom(
this.weatherConfig.weather.timePeriod.values, this.weatherConfig.weather.timePeriod.values,
@ -62,7 +73,8 @@ export class RaidWeatherService {
* Find the first matching weather object that applies to the current time * Find the first matching weather object that applies to the current time
*/ */
public getCurrentWeather(): IWeather { public getCurrentWeather(): IWeather {
this.validateWeatherDataExists(); const currentSeason = this.seasonalEventService.getActiveWeatherSeason();
this.validateWeatherDataExists(currentSeason);
return this.weatherForecast.find((x) => x.timestamp >= this.timeUtil.getTimestamp()); return this.weatherForecast.find((x) => x.timestamp >= this.timeUtil.getTimestamp());
} }
@ -71,7 +83,8 @@ export class RaidWeatherService {
* Find the first matching weather object that applies to the current time + all following weather data generated * Find the first matching weather object that applies to the current time + all following weather data generated
*/ */
public getUpcomingWeather(): IWeather[] { public getUpcomingWeather(): IWeather[] {
this.validateWeatherDataExists(); const currentSeason = this.seasonalEventService.getActiveWeatherSeason();
this.validateWeatherDataExists(currentSeason);
return this.weatherForecast.filter((x) => x.timestamp >= this.timeUtil.getTimestamp()); return this.weatherForecast.filter((x) => x.timestamp >= this.timeUtil.getTimestamp());
} }
@ -79,7 +92,7 @@ export class RaidWeatherService {
/** /**
* Ensure future weather data exists * Ensure future weather data exists
*/ */
protected validateWeatherDataExists() { protected validateWeatherDataExists(currentSeason: Season) {
// Clear expired weather data // Clear expired weather data
this.weatherForecast = this.weatherForecast.filter((x) => x.timestamp < this.timeUtil.getTimestamp()); this.weatherForecast = this.weatherForecast.filter((x) => x.timestamp < this.timeUtil.getTimestamp());
@ -87,7 +100,7 @@ export class RaidWeatherService {
const result = this.weatherForecast.filter((x) => x.timestamp >= this.timeUtil.getTimestamp()); const result = this.weatherForecast.filter((x) => x.timestamp >= this.timeUtil.getTimestamp());
if (result.length === 0) { if (result.length === 0) {
// TODO - replace with better check, if < 1 hours worth of data exists? // TODO - replace with better check, if < 1 hours worth of data exists?
this.generateWeather(); this.generateWeather(currentSeason);
} }
} }
} }

View File

@ -232,6 +232,10 @@ export class SeasonalEventService {
} }
public getActiveWeatherSeason(): Season { public getActiveWeatherSeason(): Season {
if (this.weatherConfig.overrideSeason !== null) {
return this.weatherConfig.overrideSeason;
}
const currentDate = new Date(); const currentDate = new Date();
for (const seasonRange of this.weatherConfig.seasonDates) { for (const seasonRange of this.weatherConfig.seasonDates) {
// Figure out start and end dates to get range of season // Figure out start and end dates to get range of season