insurance service done with raidtimeadjustmentservice

This commit is contained in:
CWX
2025-01-20 15:31:53 +00:00
parent 3cc7e91be8
commit b1d9035801
10 changed files with 441 additions and 46 deletions
+2 -2
View File
@@ -34,7 +34,7 @@ public class TraderHelper(
/// <param name="traderID">Traders Id to get</param>
/// <param name="sessionID">Players id</param>
/// <returns>Trader base</returns>
public TraderBase GetTrader(string traderID, string sessionID)
public TraderBase? GetTrader(string traderID, string sessionID)
{
if (traderID == "ragfair")
{
@@ -274,7 +274,7 @@ public class TraderHelper(
/// </summary>
/// <param name="traderId">Traders id</param>
/// <returns>Traders key</returns>
public Trader GetTraderById(string traderId)
public Trader? GetTraderById(string traderId)
{
throw new NotImplementedException();
}
@@ -253,7 +253,7 @@ public record LocationBase
public List<object>? Doors { get; set; }
[JsonPropertyName("EscapeTimeLimit")]
public int? EscapeTimeLimit { get; set; }
public double? EscapeTimeLimit { get; set; }
// BSG fucked up another property name
[JsonPropertyName("escape_time_limit")]
@@ -14,7 +14,7 @@ public record Trader
public TraderBase? Base { get; set; }
[JsonPropertyName("dialogue")]
public Dictionary<string, List<string>>? Dialogue { get; set; }
public Dictionary<string, List<string>?>? Dialogue { get; set; }
[JsonPropertyName("questassort")]
public Dictionary<string, Dictionary<string, string>>? QuestAssort { get; set; }
@@ -5,13 +5,13 @@ namespace Core.Models.Eft.Game;
public record GetRaidTimeResponse
{
[JsonPropertyName("RaidTimeMinutes")]
public int? RaidTimeMinutes { get; set; }
public double? RaidTimeMinutes { get; set; }
[JsonPropertyName("NewSurviveTimeSeconds")]
public int? NewSurviveTimeSeconds { get; set; }
public double? NewSurviveTimeSeconds { get; set; }
[JsonPropertyName("OriginalSurvivalTimeSeconds")]
public int? OriginalSurvivalTimeSeconds { get; set; }
public double? OriginalSurvivalTimeSeconds { get; set; }
[JsonPropertyName("ExitChanges")]
public List<ExtractChange>? ExitChanges { get; set; }
@@ -23,11 +23,11 @@ public record ExtractChange
public string? Name { get; set; }
[JsonPropertyName("MinTime")]
public int? MinTime { get; set; }
public double? MinTime { get; set; }
[JsonPropertyName("MaxTime")]
public int? MaxTime { get; set; }
public double? MaxTime { get; set; }
[JsonPropertyName("Chance")]
public int? Chance { get; set; }
public double? Chance { get; set; }
}
@@ -291,7 +291,7 @@ public record ScavRaidTimeSettings
public ScavRaidTimeConfigSettings Settings { get; set; }
[JsonPropertyName("maps")]
public Dictionary<string, ScavRaidTimeLocationSettings> Maps { get; set; }
public Dictionary<string, ScavRaidTimeLocationSettings?>? Maps { get; set; }
}
public record ScavRaidTimeConfigSettings
@@ -5,10 +5,10 @@ namespace Core.Models.Spt.Location;
public record RaidChanges
{
[JsonPropertyName("dynamicLootPercent")]
public float? DynamicLootPercent { get; set; }
public double? DynamicLootPercent { get; set; }
[JsonPropertyName("staticLootPercent")]
public float? StaticLootPercent { get; set; }
public double? StaticLootPercent { get; set; }
[JsonPropertyName("simulatedRaidStartSeconds")]
public int? SimulatedRaidStartSeconds { get; set; }
+224 -21
View File
@@ -1,13 +1,39 @@
using SptCommon.Annotations;
using Core.Helpers;
using SptCommon.Annotations;
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Eft.Profile;
using Core.Models.Enums;
using Core.Models.Spt.Config;
using Core.Models.Spt.Services;
using Core.Models.Utils;
using Core.Servers;
using Core.Utils;
using Core.Utils.Cloners;
using Insurance = Core.Models.Eft.Profile.Insurance;
namespace Core.Services;
[Injectable(InjectionType.Singleton)]
public class InsuranceService
public class InsuranceService(
ISptLogger<InsuranceService> _logger,
DatabaseService _databaseService,
RandomUtil _randomUtil,
ItemHelper _itemHelper,
HashUtil _hashUtil,
TimeUtil _timeUtil,
SaveServer _saveServer,
TraderHelper _traderHelper,
ProfileHelper _profileHelper,
LocalisationService _localisationService,
MailSendService _mailSendService,
ConfigServer _configServer,
ICloner _cloner
)
{
protected InsuranceConfig _insuranceConfig = _configServer.GetConfig<InsuranceConfig>();
protected Dictionary<string, Dictionary<string, List<Item>>?> _insured = new Dictionary<string, Dictionary<string, List<Item>>?>();
/// <summary>
/// Does player have insurance dictionary exists
/// </summary>
@@ -15,7 +41,7 @@ public class InsuranceService
/// <returns>True if exists</returns>
public bool InsuranceDictionaryExists(string sessionId)
{
throw new NotImplementedException();
return _insured.TryGetValue(sessionId, out var _);
}
/// <summary>
@@ -23,14 +49,17 @@ public class InsuranceService
/// </summary>
/// <param name="sessionId">Profile id (session id)</param>
/// <returns>Item list</returns>
public Dictionary<string, List<Item>> GetInsurance(string sessionId)
public Dictionary<string, List<Item>>? GetInsurance(string sessionId)
{
throw new NotImplementedException();
return _insured[sessionId];
}
public void ResetInsurance(string sessionId)
{
throw new NotImplementedException();
if (!_insured.TryAdd(sessionId, new Dictionary<string, List<Item>>()))
{
_insured[sessionId] = new Dictionary<string, List<Item>>();
}
}
/// <summary>
@@ -42,7 +71,70 @@ public class InsuranceService
/// <param name="mapId">Id of the location player died/exited that caused the insurance to be issued on</param>
public void StartPostRaidInsuranceLostProcess(PmcData pmcData, string sessionID, string mapId)
{
throw new NotImplementedException();
// Get insurance items for each trader
var globals = _databaseService.GetGlobals();
foreach (var trader in GetInsurance(sessionID))
{
var traderEnum = _traderHelper.GetTraderById(trader.Key);
if (traderEnum is null)
{
_logger.Error(_localisationService.GetText("insurance-trader_missing_from_enum", trader.Key));
continue;
}
var traderBase = _traderHelper.GetTrader(trader.Key, sessionID);
if (traderBase is null)
{
_logger.Error(_localisationService.GetText("insurance-unable_to_find_trader_by_id", trader.Key));
continue;
}
var dialogueTemplates = _databaseService.GetTrader(trader.Key).Dialogue;
if (dialogueTemplates is null)
{
_logger.Error(_localisationService.GetText("insurance-trader_lacks_dialogue_property", trader.Key));
continue;
}
SystemData systemData = new SystemData
{
Date = _timeUtil.GetDateMailFormat(),
Time = _timeUtil.GetTimeMailFormat(),
Location = mapId,
};
// Send "i will go look for your stuff" message from trader to player
_mailSendService.SendLocalisedNpcMessageToPlayer(
sessionID,
traderEnum.ToString(),
MessageType.NPC_TRADER,
_randomUtil.GetArrayValue(dialogueTemplates["insuranceStart"] ?? ["INSURANCE START MESSAGE MISSING"]),
null,
_timeUtil.GetHoursAsSeconds((int)globals.Configuration?.Insurance?.MaxStorageTimeInHour),
systemData
);
// Store insurance to send to player later in profile
// Store insurance return details in profile + "hey i found your stuff, here you go!" message details to send to player at a later date
_saveServer.GetProfile(sessionID)
.InsuranceList.Add(
new Insurance
{
ScheduledTime = (int)GetInsuranceReturnTimestamp(pmcData, traderBase),
TraderId = trader.ToString(),
MaxStorageTime = (int)GetMaxInsuranceStorageTime(traderBase),
SystemData = systemData,
MessageType = MessageType.INSURANCE_RETURN,
MessageTemplateId = _randomUtil.GetArrayValue(dialogueTemplates["insuranceFound"]),
Items = GetInsurance(sessionID)[trader.Key],
}
);
}
ResetInsurance(sessionID);
}
/// <summary>
@@ -54,12 +146,59 @@ public class InsuranceService
/// <returns>Timestamp to return items to player in seconds</returns>
protected double GetInsuranceReturnTimestamp(PmcData pmcData, TraderBase trader)
{
throw new NotImplementedException();
// If override in config is non-zero, use that instead of trader values
if (_insuranceConfig.ReturnTimeOverrideSeconds > 0)
{
_logger.Debug($"Insurance override used: returning in {_insuranceConfig.ReturnTimeOverrideSeconds} seconds");
return _timeUtil.GetTimeStamp() + _insuranceConfig.ReturnTimeOverrideSeconds;
}
var insuranceReturnTimeBonusSum = _profileHelper.GetBonusValueFromProfile(
pmcData,
BonusType.InsuranceReturnTime
);
// A negative bonus implies a faster return, since we subtract later, invert the value here
var insuranceReturnTimeBonusPercent = -(insuranceReturnTimeBonusSum / 100);
var traderMinReturnAsSeconds = trader.Insurance.MinReturnHour * TimeUtil.OneHourAsSeconds;
var traderMaxReturnAsSeconds = trader.Insurance.MaxReturnHour * TimeUtil.OneHourAsSeconds;
var randomisedReturnTimeSeconds = _randomUtil.GetInt((int)traderMinReturnAsSeconds, (int)traderMaxReturnAsSeconds);
// Check for Mark of The Unheard in players special slots (only slot item can fit)
var globals = _databaseService.GetGlobals();
var hasMarkOfUnheard = _itemHelper.hasItemWithTpl(
pmcData.Inventory.Items,
ItemTpl.MARKOFUNKNOWN_MARK_OF_THE_UNHEARD,
"SpecialSlot"
);
if (hasMarkOfUnheard)
{
// Reduce return time by globals multipler value
randomisedReturnTimeSeconds *= (int)globals.Configuration.Insurance.CoefOfHavingMarkOfUnknown;
}
// EoD has 30% faster returns
var editionModifier = globals.Configuration.Insurance.EditionSendingMessageTime[pmcData.Info.GameVersion];
if (editionModifier is not null)
{
randomisedReturnTimeSeconds *= (int)editionModifier.Multiplier;
}
// Calculate the final return time based on our bonus percent
var finalReturnTimeSeconds = randomisedReturnTimeSeconds * (1.0 - insuranceReturnTimeBonusPercent);
return _timeUtil.GetTimeStamp() + finalReturnTimeSeconds;
}
protected double GetMaxInsuranceStorageTime(TraderBase traderBase)
{
throw new NotImplementedException();
if (_insuranceConfig.StorageTimeOverrideSeconds > 0)
{
// Override exists, use instead of traders value
return _insuranceConfig.StorageTimeOverrideSeconds;
}
return _timeUtil.GetHoursAsSeconds((int)traderBase.Insurance.MaxStorageTime);
}
/// <summary>
@@ -68,7 +207,11 @@ public class InsuranceService
/// <param name="equipmentPkg">Gear to store - generated by GetGearLostInRaid()</param>
public void StoreGearLostInRaidToSendLater(string sessionID, List<InsuranceEquipmentPkg> equipmentPkg)
{
throw new NotImplementedException();
// Process all insured items lost in-raid
foreach (var gear in equipmentPkg)
{
AddGearToSend(gear);
}
}
/// <summary>
@@ -78,12 +221,38 @@ public class InsuranceService
/// <param name="lostInsuredItems">Insured items lost in a raid</param>
/// <param name="pmcProfile">Player profile</param>
/// <returns>InsuranceEquipmentPkg list</returns>
public List<InsuranceEquipmentPkg> MapInsuredItemsToTrader(
string sessionId,
List<Item> lostInsuredItems,
PmcData pmcProfile)
public List<InsuranceEquipmentPkg> MapInsuredItemsToTrader(string sessionId, List<Item> lostInsuredItems, PmcData pmcProfile)
{
throw new NotImplementedException();
List<InsuranceEquipmentPkg> result = [];
foreach (var lostItem in lostInsuredItems)
{
var insuranceDetails = pmcProfile.InsuredItems.FirstOrDefault(insuredItem => insuredItem.ItemId == lostItem.Id);
if (insuranceDetails is null)
{
_logger.Error($"unable to find insurance details for item id: {lostItem.Id} with tpl: {lostItem.Template}");
continue;
}
if (ItemCannotBeLostOnDeath(lostItem, pmcProfile.Inventory.Items))
{
continue;
}
// Add insured item + details to return array
result.Add(
new InsuranceEquipmentPkg
{
SessionId = sessionId,
ItemToReturnToPlayer = lostItem,
PmcData = pmcProfile,
TraderId = insuranceDetails.TId,
}
);
}
return result;
}
/// <summary>
@@ -94,7 +263,18 @@ public class InsuranceService
/// <returns>True if item</returns>
protected bool ItemCannotBeLostOnDeath(Item lostItem, List<Item> inventoryItems)
{
throw new NotImplementedException();
if (lostItem.SlotId?.ToLower().StartsWith("specialslot") ?? false)
{
return true;
}
// We check secure container items even tho they are omitted from lostInsuredItems, just in case
if (_itemHelper.ItemIsInsideContainer(lostItem, "SecuredContainer", inventoryItems))
{
return true;
}
return false;
}
/// <summary>
@@ -103,7 +283,27 @@ public class InsuranceService
/// <param name="gear">Gear to send</param>
protected void AddGearToSend(InsuranceEquipmentPkg gear)
{
throw new NotImplementedException();
var sessionId = gear.SessionId;
var pmcData = gear.PmcData;
var itemToReturnToPlayer = gear.ItemToReturnToPlayer;
var traderId = gear.TraderId;
// Ensure insurance array is init
if (!InsuranceDictionaryExists(sessionId))
{
ResetInsurance(sessionId);
}
// init trader insurance array
if (!InsuranceTraderArrayExists(sessionId, traderId))
{
ResetInsuranceTraderArray(sessionId, traderId);
}
AddInsuranceItemToArray(sessionId, traderId, itemToReturnToPlayer);
// Remove item from insured items array as its been processed
pmcData.InsuredItems = pmcData.InsuredItems.Where(item => { return item.ItemId != itemToReturnToPlayer.Id; }).ToList();
}
/// <summary>
@@ -114,7 +314,7 @@ public class InsuranceService
/// <returns>True if exists</returns>
protected bool InsuranceTraderArrayExists(string sessionId, string traderId)
{
throw new NotImplementedException();
return this._insured[sessionId][traderId] is not null;
}
/// <summary>
@@ -124,7 +324,7 @@ public class InsuranceService
/// <param name="traderId">Trader items insured with</param>
public void ResetInsuranceTraderArray(string sessionId, string traderId)
{
throw new NotImplementedException();
_insured[sessionId][traderId] = [];
}
/// <summary>
@@ -135,7 +335,7 @@ public class InsuranceService
/// <param name="itemToAdd">Insured item (with children)</param>
public void AddInsuranceItemToArray(string sessionId, string traderId, Item itemToAdd)
{
throw new NotImplementedException();
_insured[sessionId][traderId].Add(itemToAdd);
}
/// <summary>
@@ -147,6 +347,9 @@ public class InsuranceService
/// <returns>price in roubles</returns>
public double GetRoublePriceToInsureItemWithTrader(PmcData? pmcData, Item inventoryItem, string traderId)
{
throw new NotImplementedException();
var price = _itemHelper.GetStaticItemPrice(inventoryItem.Template) *
(_traderHelper.GetLoyaltyLevel(traderId, pmcData).InsurancePriceCoefficient / 100);
return Math.Ceiling(price ?? 1);
}
}
@@ -1,14 +1,28 @@
using SptCommon.Annotations;
using Core.Context;
using Core.Helpers;
using SptCommon.Annotations;
using Core.Models.Eft.Common;
using Core.Models.Eft.Game;
using Core.Models.Spt.Config;
using Core.Models.Spt.Location;
using Core.Models.Utils;
using Core.Servers;
using Core.Utils;
namespace Core.Services;
[Injectable(InjectionType.Singleton)]
public class RaidTimeAdjustmentService
public class RaidTimeAdjustmentService(
ISptLogger<RaidTimeAdjustmentService> _logger,
DatabaseService _databaseService,
RandomUtil _randomUtil,
WeightedRandomHelper _weightedRandomHelper,
ApplicationContext _applicationContext,
ConfigServer _configServer
)
{
protected LocationConfig _locationConfig = _configServer.GetConfig<LocationConfig>();
/// <summary>
/// Make alterations to the base map data passed in
/// Loot multipliers/waves/wave start times
@@ -17,7 +31,20 @@ public class RaidTimeAdjustmentService
/// <param name="mapBase">Map to adjust</param>
public void MakeAdjustmentsToMap(RaidChanges raidAdjustments, LocationBase mapBase)
{
throw new NotImplementedException();
_logger.Debug(
$"Adjusting dynamic loot multipliers to {raidAdjustments.DynamicLootPercent}% and static loot multipliers to {raidAdjustments.StaticLootPercent}% of original"
);
// Change loot multipler values before they're used below
AdjustLootMultipliers(_locationConfig.LooseLootMultiplier, raidAdjustments.DynamicLootPercent);
AdjustLootMultipliers(_locationConfig.StaticLootMultiplier, raidAdjustments.StaticLootPercent);
var mapSettings = GetMapSettings(mapBase.Id);
if (mapSettings.AdjustWaves)
{
// Make alterations to bot spawn waves now player is simulated spawning later
AdjustWaves(mapBase, raidAdjustments);
}
}
/// <summary>
@@ -25,9 +52,14 @@ public class RaidTimeAdjustmentService
/// </summary>
/// <param name="mapLootMultipliers">Multipliers to adjust</param>
/// <param name="loosePercent">Percent to change values to</param>
protected void AdjustLootMultipliers(LootMultiplier mapLootMultipliers, float loosePercent)
protected void AdjustLootMultipliers(LootMultiplier mapLootMultiplers, double? loosePercent)
{
throw new NotImplementedException();
var props = mapLootMultiplers.GetType().GetProperties();
foreach (var multiplier in props)
{
var propValue = (double)multiplier.GetValue(mapLootMultiplers);
multiplier.SetValue(mapLootMultiplers, _randomUtil.GetPercentOfValue(propValue, loosePercent ?? 1));
}
}
/// <summary>
@@ -37,7 +69,21 @@ public class RaidTimeAdjustmentService
/// <param name="raidAdjustments">Map adjustments</param>
protected void AdjustWaves(LocationBase mapBase, RaidChanges raidAdjustments)
{
throw new NotImplementedException();
// Remove waves that spawned before the player joined
var originalWaveCount = mapBase.Waves.Count;
mapBase.Waves = mapBase.Waves.Where(x => x.TimeMax > raidAdjustments.SimulatedRaidStartSeconds).ToList();
// Adjust wave min/max times to match new simulated start
foreach (var wave in mapBase.Waves)
{
// Dont let time fall below 0
wave.TimeMax -= Math.Max(raidAdjustments.SimulatedRaidStartSeconds ?? 1, 0);
wave.TimeMax -= Math.Max(raidAdjustments.SimulatedRaidStartSeconds ?? 1, 0);
}
_logger.Debug(
$"Removed {originalWaveCount - mapBase.Waves.Count} wave from map due to simulated raid start time of {raidAdjustments.SimulatedRaidStartSeconds / 60} minutes"
);
}
/// <summary>
@@ -48,7 +94,81 @@ public class RaidTimeAdjustmentService
/// <returns>Response to send to client</returns>
public GetRaidTimeResponse GetRaidAdjustments(string sessionId, GetRaidTimeRequest request)
{
throw new NotImplementedException();
var globals = _databaseService.GetGlobals();
LocationBase mapBase = _databaseService.GetLocation(request.Location.ToLower()).Base;
var baseEscapeTimeMinutes = mapBase.EscapeTimeLimit;
// Prep result object to return
GetRaidTimeResponse result = new GetRaidTimeResponse
{
RaidTimeMinutes = baseEscapeTimeMinutes,
ExitChanges = [],
NewSurviveTimeSeconds = null,
OriginalSurvivalTimeSeconds = globals.Configuration.Exp.MatchEnd.SurvivedSecondsRequirement,
};
// Pmc raid, send default
if (request.Side.ToLower() == "pmc")
{
return result;
}
// We're scav adjust values
var mapSettings = GetMapSettings(request.Location);
// Chance of reducing raid time for scav, not guaranteed
if (!_randomUtil.GetChance100(mapSettings.ReducedChancePercent))
{
// Send default
return result;
}
// Get the weighted percent to reduce the raid time by
var chosenRaidReductionPercent = int.Parse(
_weightedRandomHelper.GetWeightedValue(mapSettings.ReductionPercentWeights)
);
var raidTimeRemainingPercent = 100 - chosenRaidReductionPercent;
// How many minutes raid will last
var newRaidTimeMinutes = Math.Floor(
_randomUtil.ReduceValueByPercent(baseEscapeTimeMinutes ?? 1, chosenRaidReductionPercent)
);
// Time player spawns into the raid if it was online
var simulatedRaidStartTimeMinutes = baseEscapeTimeMinutes - newRaidTimeMinutes;
if (mapSettings.ReduceLootByPercent)
{
// Store time reduction percent in app context so loot gen can pick it up later
_applicationContext.AddValue(
ContextVariableType.RAID_ADJUSTMENTS,
new
{
dynamicLootPercent = Math.Max(raidTimeRemainingPercent, mapSettings.MinDynamicLootPercent),
staticLootPercent = Math.Max(raidTimeRemainingPercent, mapSettings.MinStaticLootPercent),
simulatedRaidStartSeconds = simulatedRaidStartTimeMinutes * 60,
}
);
}
// Update result object with new time
result.RaidTimeMinutes = newRaidTimeMinutes;
_logger.Debug($"Reduced: {request.Location} raid time by: {chosenRaidReductionPercent}% to {newRaidTimeMinutes} minutes");
// Calculate how long player needs to be in raid to get a `survived` extract status
result.NewSurviveTimeSeconds = Math.Max(
(result.OriginalSurvivalTimeSeconds - (baseEscapeTimeMinutes - newRaidTimeMinutes) * 60) ?? 0,
0D
);
var exitAdjustments = GetExitAdjustments(mapBase, newRaidTimeMinutes);
if (exitAdjustments is not null)
{
result.ExitChanges.AddRange(exitAdjustments);
}
return result;
}
/// <summary>
@@ -58,7 +178,14 @@ public class RaidTimeAdjustmentService
/// <returns>ScavRaidTimeLocationSettings</returns>
protected ScavRaidTimeLocationSettings GetMapSettings(string location)
{
throw new NotImplementedException();
var mapSettings = _locationConfig.ScavRaidTimeSettings.Maps?[location.ToLower()];
if (mapSettings is null)
{
_logger.Warning($"Unable to find scav raid time settings for map: {location}, using defaults");
return new ScavRaidTimeLocationSettings();
}
return mapSettings;
}
/// <summary>
@@ -67,8 +194,71 @@ public class RaidTimeAdjustmentService
/// <param name="mapBase">Map base file player is on</param>
/// <param name="newRaidTimeMinutes">How long raid is in minutes</param>
/// <returns>List of exit changes to send to client</returns>
protected List<ExtractChange> GetExitAdjustments(LocationBase mapBase, int newRaidTimeMinutes)
protected List<ExtractChange> GetExitAdjustments(LocationBase mapBase, double newRaidTimeMinutes)
{
throw new NotImplementedException();
List<ExtractChange> result = [];
// Adjust train exits only
foreach (var exit in mapBase.Exits) {
if (exit.PassageRequirement != "Train") {
continue;
}
// Prepare train adjustment object
ExtractChange exitChange = new ExtractChange {
Name = exit.Name,
MinTime = null,
MaxTime = null,
Chance = null,
};
// At what minute we simulate the player joining the raid
var simulatedRaidEntryTimeMinutes = mapBase.EscapeTimeLimit - newRaidTimeMinutes;
// How many seconds have elapsed in the raid when the player joins
var reductionSeconds = simulatedRaidEntryTimeMinutes * 60;
// Delay between the train extract activating and it becoming available to board
//
// Test method for determining this value:
// 1) Set MinTime, MaxTime, and Count for the train extract all to 120
// 2) Load into Reserve or Lighthouse as a PMC (both have the same result)
// 3) Board the train when it arrives
// 4) Check the raid time on the Raid Ended Screen (it should always be the same)
//
// trainArrivalDelaySeconds = [raid time on raid-ended screen] - MaxTime - Count - ExfiltrationTime
// Example: Raid Time = 5:33 = 333 seconds
// trainArrivalDelaySeconds = 333 - 120 - 120 - 5 = 88
//
// I added 2 seconds just to be safe...
//
var trainArrivalDelaySeconds =
_locationConfig.ScavRaidTimeSettings.Settings.TrainArrivalDelayObservedSeconds;
// Determine the earliest possible time in the raid when the train would leave
var earliestPossibleDepartureMinutes =
(exit.MinTime + exit.Count + exit.ExfiltrationTime + trainArrivalDelaySeconds) / 60;
// If raid is after last moment train can leave, assume train has already left, disable extract
var mostPossibleTimeRemainingAfterDeparture = mapBase.EscapeTimeLimit - earliestPossibleDepartureMinutes;
if (newRaidTimeMinutes < mostPossibleTimeRemainingAfterDeparture) {
exitChange.Chance = 0;
_logger.Debug($"Train Exit: {exit.Name} disabled as new raid time {newRaidTimeMinutes} minutes is below {mostPossibleTimeRemainingAfterDeparture} minutes");
result.Add(exitChange);
continue;
}
// Reduce extract arrival times. Negative values seem to make extract turn red in game.
exitChange.MinTime = Math.Max((exit.MinTime - reductionSeconds) ?? 0, 0);
exitChange.MaxTime = Math.Max((exit.MaxTime - reductionSeconds) ?? 0, 0);
_logger.Debug($"Train appears between: {exitChange.MinTime} and {exitChange.MaxTime} seconds raid time");
result.Add(exitChange);
}
return result.Count > 0 ? result : null;
}
}
+1 -1
View File
@@ -105,7 +105,7 @@ public class RandomUtil
/// <param name="number">The original number to be reduced.</param>
/// <param name="percentage">The percentage by which to reduce the number.</param>
/// <returns>The reduced number after applying the percentage reduction.</returns>
public float ReduceValueByPercent(float number, float percentage)
public double ReduceValueByPercent(double number, double percentage)
{
var reductionAmount = number * percentage / 100;
+3 -1
View File
@@ -1,3 +1,4 @@
using System.Runtime;
using Core.Context;
using Core.Models.External;
using Core.Models.Spt.Config;
@@ -52,7 +53,8 @@ public static class Program
// Get the Built app and run it
var app = serviceProvider.GetService<App>();
app?.Run().Wait();
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive);
var httpConfig = serviceProvider.GetService<ConfigServer>()?.GetConfig<HttpConfig>();
// When we application gets started by the HttpServer it will add into the AppContext the WebApplication
// object, which we can use here to start the webapp.