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());
|
return this.httpResponse.getBody(this.weatherController.generate());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Handle client/localGame/weather */
|
||||||
public getLocalWeather(
|
public getLocalWeather(
|
||||||
url: string,
|
url: string,
|
||||||
info: IEmptyRequestData,
|
info: IEmptyRequestData,
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { WeatherGenerator } from "@spt/generators/WeatherGenerator";
|
import { WeatherGenerator } from "@spt/generators/WeatherGenerator";
|
||||||
import { WeatherHelper } from "@spt/helpers/WeatherHelper";
|
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 { ConfigTypes } from "@spt/models/enums/ConfigTypes";
|
||||||
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 { IGetLocalWeatherResponseData } from "@spt/models/spt/weather/IGetLocalWeatherResponseData";
|
import { IGetLocalWeatherResponseData } from "@spt/models/spt/weather/IGetLocalWeatherResponseData";
|
||||||
import { ConfigServer } from "@spt/servers/ConfigServer";
|
import { ConfigServer } from "@spt/servers/ConfigServer";
|
||||||
|
import { RaidWeatherService } from "@spt/services/RaidWeatherService";
|
||||||
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
|
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
|
||||||
import { inject, injectable } from "tsyringe";
|
import { inject, injectable } from "tsyringe";
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ export class WeatherController {
|
|||||||
@inject("PrimaryLogger") protected logger: ILogger,
|
@inject("PrimaryLogger") protected logger: ILogger,
|
||||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||||
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
|
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
|
||||||
|
@inject("RaidWeatherService") protected raidWeatherService: RaidWeatherService,
|
||||||
@inject("WeatherHelper") protected weatherHelper: WeatherHelper,
|
@inject("WeatherHelper") protected weatherHelper: WeatherHelper,
|
||||||
) {
|
) {
|
||||||
this.weatherConfig = this.configServer.getConfig(ConfigTypes.WEATHER);
|
this.weatherConfig = this.configServer.getConfig(ConfigTypes.WEATHER);
|
||||||
@ -41,6 +43,7 @@ export class WeatherController {
|
|||||||
return this.weatherHelper.getInRaidTime();
|
return this.weatherHelper.getInRaidTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Handle client/localGame/weather */
|
||||||
public generateLocal(sesssionId: string): IGetLocalWeatherResponseData {
|
public generateLocal(sesssionId: string): IGetLocalWeatherResponseData {
|
||||||
const result: IGetLocalWeatherResponseData = {
|
const result: IGetLocalWeatherResponseData = {
|
||||||
season:
|
season:
|
||||||
@ -50,7 +53,7 @@ export class WeatherController {
|
|||||||
weather: [],
|
weather: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
result.weather.push(this.weatherGenerator.generateWeather());
|
result.weather.push(...this.raidWeatherService.getUpcomingWeather());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -235,6 +235,7 @@ import { RagfairPriceService } from "@spt/services/RagfairPriceService";
|
|||||||
import { RagfairRequiredItemsService } from "@spt/services/RagfairRequiredItemsService";
|
import { RagfairRequiredItemsService } from "@spt/services/RagfairRequiredItemsService";
|
||||||
import { RagfairTaxService } from "@spt/services/RagfairTaxService";
|
import { RagfairTaxService } from "@spt/services/RagfairTaxService";
|
||||||
import { RaidTimeAdjustmentService } from "@spt/services/RaidTimeAdjustmentService";
|
import { RaidTimeAdjustmentService } from "@spt/services/RaidTimeAdjustmentService";
|
||||||
|
import { RaidWeatherService } from "@spt/services/RaidWeatherService";
|
||||||
import { RepairService } from "@spt/services/RepairService";
|
import { RepairService } from "@spt/services/RepairService";
|
||||||
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
|
import { SeasonalEventService } from "@spt/services/SeasonalEventService";
|
||||||
import { TraderAssortService } from "@spt/services/TraderAssortService";
|
import { TraderAssortService } from "@spt/services/TraderAssortService";
|
||||||
@ -803,6 +804,9 @@ export class Container {
|
|||||||
depContainer.register<BotNameService>("BotNameService", BotNameService, {
|
depContainer.register<BotNameService>("BotNameService", BotNameService, {
|
||||||
lifecycle: Lifecycle.Singleton,
|
lifecycle: Lifecycle.Singleton,
|
||||||
});
|
});
|
||||||
|
depContainer.register<RaidWeatherService>("RaidWeatherService", RaidWeatherService, {
|
||||||
|
lifecycle: Lifecycle.Singleton,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static registerServers(depContainer: DependencyContainer): void {
|
private static registerServers(depContainer: DependencyContainer): void {
|
||||||
|
@ -79,7 +79,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(): IWeather {
|
public generateWeather(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,14 +93,14 @@ 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"),
|
temp: this.getRandomFloat("temp"), // TODO - lower value at night / take into account season
|
||||||
pressure: this.getRandomFloat("pressure"),
|
pressure: this.getRandomFloat("pressure"),
|
||||||
time: "",
|
time: "",
|
||||||
date: "",
|
date: "",
|
||||||
timestamp: 0,
|
timestamp: 0, // Added below
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setCurrentDateTime(result);
|
this.setCurrentDateTime(result, timestamp);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -108,16 +108,17 @@ export class WeatherGenerator {
|
|||||||
/**
|
/**
|
||||||
* 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
|
||||||
|
* @param timestamp OPTIONAL, define timestamp used
|
||||||
*/
|
*/
|
||||||
protected setCurrentDateTime(weather: IWeather): void {
|
protected setCurrentDateTime(weather: IWeather, timestamp?: number): void {
|
||||||
const currentDate = this.weatherHelper.getInRaidTime();
|
const inRaidTime = this.weatherHelper.getInRaidTime(timestamp);
|
||||||
const normalTime = this.getBSGFormattedTime(currentDate);
|
const normalTime = this.getBSGFormattedTime(inRaidTime);
|
||||||
const formattedDate = this.timeUtil.formatDate(currentDate);
|
const formattedDate = this.timeUtil.formatDate(inRaidTime);
|
||||||
const datetime = `${formattedDate} ${normalTime}`;
|
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.date = formattedDate; // matches weather.timestamp
|
||||||
weather.time = datetime; // matches weather.timestamp
|
weather.time = datetimeBsgFormat; // matches weather.timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getWeightedWindDirection(): WindDirection {
|
protected getWeightedWindDirection(): WindDirection {
|
||||||
|
@ -23,12 +23,16 @@ export class WeatherHelper {
|
|||||||
* @param currentDate (new Date())
|
* @param currentDate (new Date())
|
||||||
* @returns Date object of current in-raid time
|
* @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
|
// 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(
|
return new Date(
|
||||||
(russiaOffset + new Date().getTime() * this.weatherConfig.acceleration) %
|
((russiaOffsetMilliseconds + currentTimestampMilliSeconds * this.weatherConfig.acceleration) %
|
||||||
(this.timeUtil.getHoursAsSeconds(24) * 1000),
|
twentyFourHoursMilliseconds) +
|
||||||
|
this.timeUtil.getStartOfDayTimestamp(timestamp),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +44,7 @@ export class WeatherHelper {
|
|||||||
public isNightTime(timeVariant: DateTime) {
|
public isNightTime(timeVariant: DateTime) {
|
||||||
const time = this.getInRaidTime();
|
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") {
|
if (timeVariant === "PAST") {
|
||||||
time.setHours(time.getHours() + 12);
|
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);
|
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
|
* Get timestamp of today + passed in day count
|
||||||
* @param daysFromNow Days from now
|
* @param daysFromNow Days from now
|
||||||
@ -82,6 +93,17 @@ export class TimeUtil {
|
|||||||
return currentTimeStamp + desiredDaysAsSeconds;
|
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.
|
* Gets the current time in UTC in a format suitable for mail in EFT.
|
||||||
*
|
*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user