First pass at improving weather simulation logic
This commit is contained in:
parent
c7904a3a41
commit
7330f6fb82
@ -21,6 +21,7 @@ export class WeatherCallbacks {
|
||||
return this.httpResponse.getBody(this.weatherController.generate());
|
||||
}
|
||||
|
||||
/** Handle client/localGame/weather */
|
||||
public getLocalWeather(
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { WeatherGenerator } from "@spt/generators/WeatherGenerator";
|
||||
import { WeatherHelper } from "@spt/helpers/WeatherHelper";
|
||||
import { IWeatherData } from "@spt/models/eft/weather/IWeatherData";
|
||||
import { IWeather, IWeatherData } from "@spt/models/eft/weather/IWeatherData";
|
||||
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
|
||||
import { IWeatherConfig } from "@spt/models/spt/config/IWeatherConfig";
|
||||
import { ILogger } from "@spt/models/spt/utils/ILogger";
|
||||
import { IGetLocalWeatherResponseData } from "@spt/models/spt/weather/IGetLocalWeatherResponseData";
|
||||
import { ConfigServer } from "@spt/servers/ConfigServer";
|
||||
import { RaidWeatherService } from "@spt/services/RaidWeatherService";
|
||||
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@ -18,6 +19,7 @@ export class WeatherController {
|
||||
@inject("PrimaryLogger") protected logger: ILogger,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
|
||||
@inject("RaidWeatherService") protected raidWeatherService: RaidWeatherService,
|
||||
@inject("WeatherHelper") protected weatherHelper: WeatherHelper,
|
||||
) {
|
||||
this.weatherConfig = this.configServer.getConfig(ConfigTypes.WEATHER);
|
||||
@ -41,6 +43,7 @@ export class WeatherController {
|
||||
return this.weatherHelper.getInRaidTime();
|
||||
}
|
||||
|
||||
/** Handle client/localGame/weather */
|
||||
public generateLocal(sesssionId: string): IGetLocalWeatherResponseData {
|
||||
const result: IGetLocalWeatherResponseData = {
|
||||
season:
|
||||
@ -50,7 +53,7 @@ export class WeatherController {
|
||||
weather: [],
|
||||
};
|
||||
|
||||
result.weather.push(this.weatherGenerator.generateWeather());
|
||||
result.weather.push(...this.raidWeatherService.getUpcomingWeather());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -235,6 +235,7 @@ import { RagfairPriceService } from "@spt/services/RagfairPriceService";
|
||||
import { RagfairRequiredItemsService } from "@spt/services/RagfairRequiredItemsService";
|
||||
import { RagfairTaxService } from "@spt/services/RagfairTaxService";
|
||||
import { RaidTimeAdjustmentService } from "@spt/services/RaidTimeAdjustmentService";
|
||||
import { RaidWeatherService } from "@spt/services/RaidWeatherService";
|
||||
import { RepairService } from "@spt/services/RepairService";
|
||||
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
|
||||
import { TraderAssortService } from "@spt/services/TraderAssortService";
|
||||
@ -803,6 +804,9 @@ export class Container {
|
||||
depContainer.register<BotNameService>("BotNameService", BotNameService, {
|
||||
lifecycle: Lifecycle.Singleton,
|
||||
});
|
||||
depContainer.register<RaidWeatherService>("RaidWeatherService", RaidWeatherService, {
|
||||
lifecycle: Lifecycle.Singleton,
|
||||
});
|
||||
}
|
||||
|
||||
private static registerServers(depContainer: DependencyContainer): void {
|
||||
|
@ -79,7 +79,7 @@ export class WeatherGenerator {
|
||||
* Return randomised Weather data with help of config/weather.json
|
||||
* @returns Randomised weather data
|
||||
*/
|
||||
public generateWeather(): IWeather {
|
||||
public generateWeather(timestamp?: number): IWeather {
|
||||
const clouds = this.getWeightedClouds();
|
||||
|
||||
// Force rain to off if no clouds
|
||||
@ -93,14 +93,14 @@ export class WeatherGenerator {
|
||||
rain: rain,
|
||||
rain_intensity: rain > 1 ? this.getRandomFloat("rainIntensity") : 0,
|
||||
fog: this.getWeightedFog(),
|
||||
temp: this.getRandomFloat("temp"),
|
||||
temp: this.getRandomFloat("temp"), // TODO - lower value at night / take into account season
|
||||
pressure: this.getRandomFloat("pressure"),
|
||||
time: "",
|
||||
date: "",
|
||||
timestamp: 0,
|
||||
timestamp: 0, // Added below
|
||||
};
|
||||
|
||||
this.setCurrentDateTime(result);
|
||||
this.setCurrentDateTime(result, timestamp);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -108,16 +108,17 @@ export class WeatherGenerator {
|
||||
/**
|
||||
* Set IWeather date/time/timestamp values to now
|
||||
* @param weather Object to update
|
||||
* @param timestamp OPTIONAL, define timestamp used
|
||||
*/
|
||||
protected setCurrentDateTime(weather: IWeather): void {
|
||||
const currentDate = this.weatherHelper.getInRaidTime();
|
||||
const normalTime = this.getBSGFormattedTime(currentDate);
|
||||
const formattedDate = this.timeUtil.formatDate(currentDate);
|
||||
const datetime = `${formattedDate} ${normalTime}`;
|
||||
protected setCurrentDateTime(weather: IWeather, timestamp?: number): void {
|
||||
const inRaidTime = this.weatherHelper.getInRaidTime(timestamp);
|
||||
const normalTime = this.getBSGFormattedTime(inRaidTime);
|
||||
const formattedDate = this.timeUtil.formatDate(inRaidTime);
|
||||
const datetimeBsgFormat = `${formattedDate} ${normalTime}`;
|
||||
|
||||
weather.timestamp = Math.floor(currentDate.getTime() / 1000); // matches weather.date
|
||||
weather.timestamp = Math.floor(timestamp ? timestamp : inRaidTime.getTime() / 1000); // matches weather.date
|
||||
weather.date = formattedDate; // matches weather.timestamp
|
||||
weather.time = datetime; // matches weather.timestamp
|
||||
weather.time = datetimeBsgFormat; // matches weather.timestamp
|
||||
}
|
||||
|
||||
protected getWeightedWindDirection(): WindDirection {
|
||||
|
@ -23,12 +23,16 @@ export class WeatherHelper {
|
||||
* @param currentDate (new Date())
|
||||
* @returns Date object of current in-raid time
|
||||
*/
|
||||
public getInRaidTime(): Date {
|
||||
public getInRaidTime(timestamp?: number): Date {
|
||||
// tarkov time = (real time * 7 % 24 hr) + 3 hour
|
||||
const russiaOffset = this.timeUtil.getHoursAsSeconds(3) * 1000;
|
||||
const russiaOffsetMilliseconds = this.timeUtil.getHoursAsSeconds(2) * 1000;
|
||||
const twentyFourHoursMilliseconds = this.timeUtil.getHoursAsSeconds(24) * 1000;
|
||||
const currentTimestampMilliSeconds = timestamp ? timestamp : new Date().getTime();
|
||||
|
||||
return new Date(
|
||||
(russiaOffset + new Date().getTime() * this.weatherConfig.acceleration) %
|
||||
(this.timeUtil.getHoursAsSeconds(24) * 1000),
|
||||
((russiaOffsetMilliseconds + currentTimestampMilliSeconds * this.weatherConfig.acceleration) %
|
||||
twentyFourHoursMilliseconds) +
|
||||
this.timeUtil.getStartOfDayTimestamp(timestamp),
|
||||
);
|
||||
}
|
||||
|
||||
@ -40,7 +44,7 @@ export class WeatherHelper {
|
||||
public isNightTime(timeVariant: DateTime) {
|
||||
const time = this.getInRaidTime();
|
||||
|
||||
// We get left side value, if player chose right side, set ahead 12 hrs
|
||||
// getInRaidTime() provides left side value, if player chose right side, set ahead 12 hrs
|
||||
if (timeVariant === "PAST") {
|
||||
time.setHours(time.getHours() + 12);
|
||||
}
|
||||
|
86
project/src/services/RaidWeatherService.ts
Normal file
86
project/src/services/RaidWeatherService.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import { WeatherGenerator } from "@spt/generators/WeatherGenerator";
|
||||
import { IWeather } from "@spt/models/eft/weather/IWeatherData";
|
||||
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
|
||||
import { IWeatherConfig } from "@spt/models/spt/config/IWeatherConfig";
|
||||
import { ILogger } from "@spt/models/spt/utils/ILogger";
|
||||
import { ConfigServer } from "@spt/servers/ConfigServer";
|
||||
import { DatabaseService } from "@spt/services/DatabaseService";
|
||||
import { TimeUtil } from "@spt/utils/TimeUtil";
|
||||
import { inject, injectable } from "tsyringe";
|
||||
|
||||
@injectable()
|
||||
export class RaidWeatherService {
|
||||
protected weatherConfig: IWeatherConfig;
|
||||
protected weatherForecast: IWeather[] = [];
|
||||
|
||||
constructor(
|
||||
@inject("PrimaryLogger") protected logger: ILogger,
|
||||
@inject("DatabaseService") protected databaseService: DatabaseService,
|
||||
@inject("TimeUtil") protected timeUtil: TimeUtil,
|
||||
@inject("WeatherGenerator") protected weatherGenerator: WeatherGenerator,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
) {
|
||||
this.weatherConfig = this.configServer.getConfig(ConfigTypes.WEATHER);
|
||||
|
||||
this.generateWeather();
|
||||
}
|
||||
|
||||
public generateWeather() {
|
||||
// When to start generating weather from
|
||||
const staringTimestamp = this.getLastFullHourTimestamp();
|
||||
|
||||
// How far into future do we generate weather
|
||||
const futureTimestampToReach = staringTimestamp + this.timeUtil.getHoursAsSeconds(24) * 1000; // TODO move 24 to config
|
||||
// Keep adding new weather until we have reached desired future date
|
||||
let nextTimestamp = staringTimestamp;
|
||||
while (nextTimestamp <= futureTimestampToReach) {
|
||||
const newWeather = this.weatherGenerator.generateWeather(nextTimestamp);
|
||||
this.logger.warning(`Handling ${new Date(nextTimestamp)}`);
|
||||
this.weatherForecast.push(newWeather);
|
||||
nextTimestamp += 30 * 60 * 1000; // TODO move to config
|
||||
}
|
||||
}
|
||||
|
||||
protected getLastFullHourTimestamp() {
|
||||
const now = new Date();
|
||||
let hours = now.getHours();
|
||||
const minutes = now.getMinutes();
|
||||
|
||||
// If minutes are greater than 0, subtract 1 hour to get last full hour
|
||||
if (minutes > 0) {
|
||||
hours--;
|
||||
}
|
||||
|
||||
// Create a new Date object with the last full hour, 0 minutes, and 0 seconds
|
||||
const lastFullHour = new Date(now.getFullYear(), now.getMonth(), now.getDate(), hours, 0, 0);
|
||||
|
||||
// Return timestamp of last full hour
|
||||
return lastFullHour.getTime();
|
||||
}
|
||||
|
||||
public getCurrentWeather(): IWeather {
|
||||
// Clear expired weather data
|
||||
this.weatherForecast = this.weatherForecast.filter((x) => x.timestamp < this.timeUtil.getTimestamp());
|
||||
|
||||
// return first weather object that is greater than/equal to now
|
||||
const result = this.weatherForecast.find((x) => x.timestamp >= this.timeUtil.getTimestamp());
|
||||
if (!result) {
|
||||
this.generateWeather();
|
||||
}
|
||||
|
||||
return this.weatherForecast.find((x) => x.timestamp >= this.timeUtil.getTimestamp());
|
||||
}
|
||||
|
||||
public getUpcomingWeather(): IWeather[] {
|
||||
// Clear expired weather data
|
||||
this.weatherForecast = this.weatherForecast.filter((x) => x.timestamp < this.timeUtil.getTimestamp());
|
||||
|
||||
// return first weather object that is greater than/equal to now
|
||||
const result = this.weatherForecast.filter((x) => x.timestamp >= this.timeUtil.getTimestamp());
|
||||
if (result.length === 0) {
|
||||
this.generateWeather();
|
||||
}
|
||||
|
||||
return this.weatherForecast.filter((x) => x.timestamp >= this.timeUtil.getTimestamp());
|
||||
}
|
||||
}
|
@ -71,6 +71,17 @@ export class TimeUtil {
|
||||
return Math.floor(new Date().getTime() / 1000);
|
||||
}
|
||||
|
||||
public getStartOfDayTimestamp(timestamp?: number): number {
|
||||
const now = timestamp ? new Date(timestamp) : new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = now.getMonth();
|
||||
const day = now.getDate();
|
||||
|
||||
const todayTimestamp = new Date(year, month, day, 0, 0, 0);
|
||||
|
||||
return todayTimestamp.getTime();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get timestamp of today + passed in day count
|
||||
* @param daysFromNow Days from now
|
||||
@ -82,6 +93,17 @@ export class TimeUtil {
|
||||
return currentTimeStamp + desiredDaysAsSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get timestamp of today + passed in hour count
|
||||
* @param daysFromNow Days from now
|
||||
*/
|
||||
public getTimeStampFromNowHours(hoursFromNow: number): number {
|
||||
const currentTimeStamp = this.getTimestamp();
|
||||
const desiredHoursAsSeconds = this.getHoursAsSeconds(hoursFromNow);
|
||||
|
||||
return currentTimeStamp + desiredHoursAsSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current time in UTC in a format suitable for mail in EFT.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user