using Core.Annotations; using Core.Helpers; using Core.Models.Eft.Common; using Core.Models.Eft.Common.Tables; using Core.Models.Enums; using Core.Models.Spt.Config; using Core.Servers; using Core.Utils; using ILogger = Core.Models.Utils.ILogger; namespace Core.Services; [Injectable(InjectionType.Singleton)] public class SeasonalEventService { private readonly ILogger _logger; private readonly DatabaseService _databaseService; //private readonly DatabaseImporter _databaseImporter; private readonly GiftService _giftService; private readonly LocalisationService _localisationService; private readonly BotHelper _botHelper; private readonly ProfileHelper _profileHelper; private readonly ConfigServer _configServer; private bool _christmasEventActive = false; private bool _halloweenEventActive = false; private readonly SeasonalEventConfig _seasonalEventConfig; private readonly QuestConfig _questConfig; private readonly HttpConfig _httpConfig; private readonly WeatherConfig _weatherConfig; private readonly LocationConfig _locationConfig; private readonly List _currentlyActiveEvents = []; private readonly IReadOnlyList _christmasEventItems = [ ItemTpl.FACECOVER_FAKE_WHITE_BEARD, ItemTpl.BARTER_CHRISTMAS_TREE_ORNAMENT_RED, ItemTpl.BARTER_CHRISTMAS_TREE_ORNAMENT_VIOLET, ItemTpl.BARTER_CHRISTMAS_TREE_ORNAMENT_SILVER, ItemTpl.HEADWEAR_DED_MOROZ_HAT, ItemTpl.HEADWEAR_SANTA_HAT, ItemTpl.BACKPACK_SANTAS_BAG, ItemTpl.RANDOMLOOTCONTAINER_NEW_YEAR_GIFT_BIG, ItemTpl.RANDOMLOOTCONTAINER_NEW_YEAR_GIFT_MEDIUM, ItemTpl.RANDOMLOOTCONTAINER_NEW_YEAR_GIFT_SMALL ]; private readonly IReadOnlyList _halloweenEventItems = [ ItemTpl.FACECOVER_SPOOKY_SKULL_MASK, ItemTpl.RANDOMLOOTCONTAINER_PUMPKIN_RAND_LOOT_CONTAINER, ItemTpl.HEADWEAR_JACKOLANTERN_TACTICAL_PUMPKIN_HELMET, ItemTpl.FACECOVER_FACELESS_MASK, ItemTpl.FACECOVER_JASON_MASK, ItemTpl.FACECOVER_MISHA_MAYOROV_MASK, ItemTpl.FACECOVER_SLENDER_MASK, ItemTpl.FACECOVER_GHOUL_MASK, ItemTpl.FACECOVER_HOCKEY_PLAYER_MASK_CAPTAIN, ItemTpl.FACECOVER_HOCKEY_PLAYER_MASK_BRAWLER, ItemTpl.FACECOVER_HOCKEY_PLAYER_MASK_QUIET ]; public SeasonalEventService( ILogger logger, DatabaseService databaseService, //DatabaseImporter databaseImporter, GiftService giftService, LocalisationService localisationService, BotHelper botHelper, ProfileHelper profileHelper, ConfigServer configServer ) { _logger = logger; _databaseService = databaseService; //_databaseImporter = databaseImporter; _giftService = giftService; _localisationService = localisationService; _botHelper = botHelper; _profileHelper = profileHelper; _configServer = configServer; _seasonalEventConfig = _configServer.GetConfig(ConfigTypes.SEASONAL_EVENT); _questConfig = _configServer.GetConfig(ConfigTypes.QUEST); _httpConfig = _configServer.GetConfig(ConfigTypes.HTTP); _weatherConfig = _configServer.GetConfig(ConfigTypes.WEATHER); _locationConfig = _configServer.GetConfig(ConfigTypes.LOCATION); } /// /// Get an array of christmas items found in bots inventories as loot /// /// array public IEnumerable GetChristmasEventItems() { return _christmasEventItems; } /// /// Get an array of halloween items found in bots inventories as loot /// /// array public IEnumerable GetHalloweenEventItems() { return _halloweenEventItems; } public bool ItemIsChristmasRelated(string itemTpl) { return _christmasEventItems.Contains(itemTpl); } public bool ItemIsHalloweenRelated(string itemTpl) { return _halloweenEventItems.Contains(itemTpl); } /// /// Check if item id exists in christmas or halloween event arrays /// /// item tpl to check for /// public bool ItemIsSeasonalRelated(string itemTpl) { return _christmasEventItems.Contains(itemTpl) || _halloweenEventItems.Contains(itemTpl); } /// /// Get active seasonal events /// /// Array of active events public List GetActiveEvents() { return _currentlyActiveEvents; } /// /// Get an array of seasonal items that should not appear /// e.g. if halloween is active, only return christmas items /// or, if halloween and christmas are inactive, return both sets of items /// /// array of tpl strings public List GetInactiveSeasonalEventItems() { var items = new List(); if (!ChristmasEventEnabled()) { items.AddRange(_christmasEventItems); } if (!HalloweenEventEnabled()) { items.AddRange(_halloweenEventItems); } return items; } /// /// Is a seasonal event currently active /// /// true if event is active public bool SeasonalEventEnabled() { return _christmasEventActive || _halloweenEventActive; } /// /// Is christmas event active /// /// true if active public bool ChristmasEventEnabled() { return _christmasEventActive; } /// /// is halloween event active /// /// true if active public bool HalloweenEventEnabled() { return _halloweenEventActive; } /// /// Is detection of seasonal events enabled (halloween / christmas) /// /// true if seasonal events should be checked for public bool IsAutomaticEventDetectionEnabled() { return _seasonalEventConfig.EnableSeasonalEventDetection; } /// /// Get a dictionary of gear changes to apply to bots for a specific event e.g. Christmas/Halloween /// /// Name of event to get gear changes for /// bots with equipment changes protected Dictionary>> GetEventBotGear(SeasonalEventType eventType) { return _seasonalEventConfig.EventGear.GetValueOrDefault(eventType, null); } /// /// Get a dictionary of loot changes to apply to bots for a specific event e.g. Christmas/Halloween /// /// Name of event to get gear changes for /// bots with loot changes protected Dictionary>> GetEventBotLoot(SeasonalEventType eventType) { return _seasonalEventConfig.EventLoot.GetValueOrDefault(eventType, null); } /// /// Get the dates each seasonal event starts and ends at /// /// Record with event name + start/end date public List GetEventDetails() { return _seasonalEventConfig.Events; } /// /// Look up quest in configs/quest.json /// /// Quest to look up /// event type (Christmas/Halloween/None) /// true if related public bool IsQuestRelatedToEvent(string questId, SeasonalEventType eventType) { var eventQuestData = _questConfig.EventQuests.GetValueOrDefault(questId, null); if (eventQuestData?.Season == eventType) { return true; } return false; } /// /// Handle activating seasonal events /// public void EnableSeasonalEvents() { if (_currentlyActiveEvents.Count > 0) { var globalConfig = _databaseService.GetGlobals().Configuration; foreach (var activeEvent in _currentlyActiveEvents) { UpdateGlobalEvents(globalConfig, activeEvent); } } } /// /// Force a seasonal event to be active /// /// Event to force active /// True if event was successfully force enabled public bool ForceSeasonalEvent(SeasonalEventType eventType) { throw new NotImplementedException(); } /// /// Store active events inside class list property `currentlyActiveEvents` + set class properties: christmasEventActive/halloweenEventActive /// public void CacheActiveEvents() { throw new NotImplementedException(); } /// /// Get the currently active weather season e.g. SUMMER/AUTUMN/WINTER /// /// Season enum value public Season GetActiveWeatherSeason() { if (_weatherConfig.OverrideSeason.HasValue) { return _weatherConfig.OverrideSeason.Value; } var currentDate = new DateTime(); foreach (var seasonRange in _weatherConfig.SeasonDates) { if ( DateIsBetweenTwoDates( currentDate, seasonRange.StartMonth, seasonRange.StartDay, seasonRange.EndMonth, seasonRange.EndDay) ) { return seasonRange.SeasonType; } } _logger.Warning(_localisationService.GetText("season-no_matching_season_found_for_date")); return Season.SUMMER; } /// /// Does the provided date fit between the two defined dates? /// Excludes year /// Inclusive of end date upto 23 hours 59 minutes /// /// Date to check is between 2 dates /// Lower bound for month /// Lower bound for day /// Upper bound for month /// Upper bound for day /// True when inside date range protected bool DateIsBetweenTwoDates(DateTime dateToCheck, int startMonth, int startDay, int endMonth, int endDay) { var eventStartDate = new DateTime(dateToCheck.Year, startMonth, startDay); var eventEndDate = new DateTime(dateToCheck.Year, endMonth, endDay, 23, 59, 0); return dateToCheck >= eventStartDate && dateToCheck <= eventEndDate; } /// /// Iterate through bots inventory and loot to find and remove christmas items (as defined in SeasonalEventService) /// /// Bots inventory to iterate over /// the role of the bot being processed public void RemoveChristmasItemsFromBotInventory(BotTypeInventory botInventory, string botRole) { throw new NotImplementedException(); } /// /// Make adjusted to server code based on the name of the event passed in /// /// globals.json /// Name of the event to enable. e.g. Christmas protected void UpdateGlobalEvents(Config globalConfig, SeasonalEvent eventType) { throw new NotImplementedException(); } protected void ApplyHalloweenEvent(SeasonalEvent eventType, Config globalConfig) { throw new NotImplementedException(); } protected void ApplyChristmasEvent(SeasonalEvent eventType, Config globalConfig) { throw new NotImplementedException(); } protected void ApplyNewYearsEvent(SeasonalEvent eventType, Config globalConfig) { throw new NotImplementedException(); } protected void AdjustBotAppearanceValues(SeasonalEventType season) { throw new NotImplementedException(); } protected void ReplaceBotHostility(Dictionary hostilitySettings) { throw new NotImplementedException(); } protected void RemoveEntryRequirement(List locationIds) { throw new NotImplementedException(); } public void GivePlayerSeasonalGifts(string sessionId) { if (_currentlyActiveEvents is null) { return; } foreach (var seasonEvent in _currentlyActiveEvents) { switch (seasonEvent.Type) { case SeasonalEventType.Christmas: GiveGift(sessionId, "Christmas2022"); break; case SeasonalEventType.NewYears: GiveGift(sessionId, "NewYear2023"); GiveGift(sessionId, "NewYear2024"); break; } } } /// /// Force zryachiy to always have a melee weapon /// protected void AdjustZryachiyMeleeChance() { throw new NotImplementedException(); } /// /// Enable the halloween zryachiy summon event /// protected void EnableHalloweenSummonEvent() { throw new NotImplementedException(); } protected void ConfigureZombies(ZombieSettings zombieSettings) { throw new NotImplementedException(); } /// /// Get location ids of maps with an infection above 0 /// /// Dict of locations with their infection percentage /// List of location ids protected List GetLocationsWithZombies(Dictionary locationInfections) { throw new NotImplementedException(); } /// /// BSG store the location ids differently inside `LocationInfection`, need to convert to matching location IDs /// /// Key to convert /// List of locations protected List GetLocationFromInfectedLocation(string infectedLocationKey) { throw new NotImplementedException(); } protected void AddEventWavesToMaps(string eventType) { throw new NotImplementedException(); } /// /// Add event bosses to maps /// /// Seasonal event, e.g. HALLOWEEN/CHRISTMAS /// OPTIONAL - Maps to add bosses to protected void AddEventBossesToMaps(string eventType, List mapIdWhitelist = null) { throw new NotImplementedException(); } /// /// Change trader icons to be more event themed (Halloween only so far) /// /// What event is active protected void AdjustTraderIcons(SeasonalEventType eventType) { throw new NotImplementedException(); } /// /// Add lootble items from backpack into patrol.ITEMS_TO_DROP difficulty property /// protected void AddLootItemsToGifterDropItemsList() { var gifterBot = _databaseService.GetBots().Types["gifter"]; var items = gifterBot.BotInventory.Items.Backpack.Keys.ToList(); gifterBot.BotDifficulty.Easy.Patrol["ITEMS_TO_DROP"] = items; gifterBot.BotDifficulty.Normal.Patrol["ITEMS_TO_DROP"] = items; gifterBot.BotDifficulty.Hard.Patrol["ITEMS_TO_DROP"] = items; gifterBot.BotDifficulty.Impossible.Patrol["ITEMS_TO_DROP"] = items; } /// /// Read in data from seasonalEvents.json and add found equipment items to bots /// /// Name of the event to read equipment in from config protected void AddEventGearToBots(SeasonalEventType eventType) { throw new NotImplementedException(); } /// /// Read in data from seasonalEvents.json and add found loot items to bots /// /// Name of the event to read loot in from config protected void AddEventLootToBots(SeasonalEventType eventType) { throw new NotImplementedException(); } /// /// Add pumpkin loot boxes to scavs /// protected void AddPumpkinsToScavBackpacks() { _databaseService.GetBots().Types["assault"].BotInventory.Items.Backpack[ ItemTpl.RANDOMLOOTCONTAINER_PUMPKIN_RAND_LOOT_CONTAINER ] = 400; } protected void RenameBitcoin() { var enLocale = _databaseService.GetLocales().Global["en"]; enLocale[$"{ItemTpl.BARTER_PHYSICAL_BITCOIN} Name"] = "Physical SPT Coin"; enLocale[$"{ItemTpl.BARTER_PHYSICAL_BITCOIN} ShortName"] = "0.2SPT"; } /// /// Set Khorovod(dancing tree) chance to 100% on all maps that support it /// protected void EnableDancingTree() { throw new NotImplementedException(); } /// /// Add santa to maps /// protected void AddGifterBotToMaps() { throw new NotImplementedException(); } protected void HandleModEvent(SeasonalEvent seasonalEvent, Config globalConfig) { throw new NotImplementedException(); } /// /// Send gift to player if they have not already received it /// /// Player to send gift to /// Key of gift to give protected void GiveGift(string playerId, string giftKey) { var gitftData = _giftService.GetGiftById(giftKey); if (!_profileHelper.PlayerHasRecievedMaxNumberOfGift(playerId, giftKey, gitftData.MaxToSendPlayer ?? 5)) { _giftService.SendGiftToPlayer(playerId, giftKey); } } /// /// Get the underlying bot type for an event bot e.g. `peacefullZryachiyEvent` will return `bossZryachiy` /// /// Event bot role type /// Bot role as string public string GetBaseRoleForEventBot(string eventBotRole) { return _seasonalEventConfig.EventBotMapping.GetValueOrDefault(eventBotRole, null); } /// /// Force the weather to be snow /// public void EnableSnow() { _weatherConfig.OverrideSeason = Season.WINTER; } }