using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Models.Eft.Profile; using SPTarkov.Server.Core.Models.Enums; using SPTarkov.Server.Core.Models.Spt.Config; using SPTarkov.Server.Core.Models.Spt.Dialog; using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Servers; using SPTarkov.Server.Core.Utils; using SPTarkov.Common.Annotations; using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel; namespace SPTarkov.Server.Core.Services; [Injectable(InjectionType.Singleton)] public class GiftService( ISptLogger _logger, MailSendService _mailSendService, LocalisationService _localisationService, HashUtil _hashUtil, TimeUtil _timeUtil, ProfileHelper _profileHelper, ConfigServer _configServer) { protected GiftsConfig _giftConfig = _configServer.GetConfig(); /// /// Does a gift with a specific ID exist in db /// /// Gift id to check for /// True if it exists in db public bool GiftExists(string giftId) { return _giftConfig.Gifts.ContainsKey(giftId); } public Gift GetGiftById(string giftId) { _giftConfig.Gifts.TryGetValue(giftId, out var gift); return gift; } /// /// Get dictionary of all gifts /// /// Dict keyed by gift id public Dictionary GetGifts() { return _giftConfig.Gifts; } /// /// Get an array of all gift ids /// /// String list of gift ids public List GetGiftIds() { return _giftConfig.Gifts.Keys.ToList(); } /// /// Send player a gift from a range of sources /// /// Player to send gift to / sessionID /// ID of gift in configs/gifts.json to send player /// Outcome of sending gift to player public GiftSentResult SendGiftToPlayer(string playerId, string giftId) { var giftData = GetGiftById(giftId); if (giftData is null) { return GiftSentResult.FAILED_GIFT_DOESNT_EXIST; } var maxGiftsToSendCount = giftData.MaxToSendPlayer ?? 1; if (_profileHelper.PlayerHasRecievedMaxNumberOfGift(playerId, giftId, maxGiftsToSendCount)) { if (_logger.IsLogEnabled(LogLevel.Debug)) { _logger.Debug($"Player already received gift: {giftId}"); } return GiftSentResult.FAILED_GIFT_ALREADY_RECEIVED; } if (giftData.Items?.Count > 0 && giftData.CollectionTimeHours is not null) { _logger.Warning($"Gift {giftId} has items but no collection time limit, defaulting to 48 hours"); } // Handle system messsages if (giftData.Sender == GiftSenderType.System) { // Has a localisable text id to send to player if (giftData.LocaleTextId is not null) { _mailSendService.SendLocalisedSystemMessageToPlayer( playerId, giftData.LocaleTextId, giftData.Items, giftData.ProfileChangeEvents, _timeUtil.GetHoursAsSeconds(giftData.CollectionTimeHours ?? 1) ); } else { _mailSendService.SendSystemMessageToPlayer( playerId, giftData.MessageText, giftData.Items, _timeUtil.GetHoursAsSeconds(giftData.CollectionTimeHours ?? 1), giftData.ProfileChangeEvents ); } } // Handle user messages else if (giftData.Sender == GiftSenderType.User) { _mailSendService.SendUserMessageToPlayer( playerId, giftData.SenderDetails, giftData.MessageText, giftData.Items, _timeUtil.GetHoursAsSeconds(giftData.CollectionTimeHours ?? 1) ); } else if (giftData.Sender == GiftSenderType.Trader) { if (giftData.LocaleTextId is not null) { _mailSendService.SendLocalisedNpcMessageToPlayer( playerId, giftData.Trader, MessageType.MESSAGE_WITH_ITEMS, giftData.LocaleTextId, giftData.Items, _timeUtil.GetHoursAsSeconds(giftData.CollectionTimeHours ?? 1) ); } else { _mailSendService.SendLocalisedNpcMessageToPlayer( playerId, giftData.Trader, MessageType.MESSAGE_WITH_ITEMS, giftData.MessageText, giftData.Items, _timeUtil.GetHoursAsSeconds(giftData.CollectionTimeHours ?? 1) ); } } else { // TODO: further split out into different message systems like above SYSTEM method // Trader / ragfair SendMessageDetails details = new() { RecipientId = playerId, Sender = GetMessageType(giftData), SenderDetails = new UserDialogInfo { Id = GetSenderId(giftData), Aid = 1234567, // TODO - pass proper aid value Info = null }, MessageText = giftData.MessageText, Items = giftData.Items, ItemsMaxStorageLifetimeSeconds = _timeUtil.GetHoursAsSeconds(giftData.CollectionTimeHours ?? 0) }; if (giftData.Trader is not null) { details.Trader = giftData.Trader; } _mailSendService.SendMessageToPlayer(details); } _profileHelper.FlagGiftReceivedInProfile(playerId, giftId, maxGiftsToSendCount); return GiftSentResult.SUCCESS; } /// /// Get sender id based on gifts sender type enum /// /// Gift to send player /// trader/user/system id private string? GetSenderId(Gift giftData) { if (giftData.Sender == GiftSenderType.Trader) { return Enum.GetName(typeof(GiftSenderType), giftData.Sender); } if (giftData.Sender == GiftSenderType.User) { return giftData.Sender.ToString(); } return null; } /// /// Convert GiftSenderType into a dialog MessageType /// /// Gift to send player /// MessageType enum value protected MessageType? GetMessageType(Gift giftData) { switch (giftData.Sender) { case GiftSenderType.System: return MessageType.SYSTEM_MESSAGE; case GiftSenderType.Trader: return MessageType.NPC_TRADER; case GiftSenderType.User: return MessageType.USER_MESSAGE; default: _logger.Error(_localisationService.GetText("gift-unable_to_handle_message_type_command", giftData.Sender)); return null; } } /// /// Prapor sends gifts to player for first week after profile creation /// /// Player ID /// What day to give gift for public void SendPraporStartingGift(string sessionId, int day) { var giftId = day switch { 1 => "PraporGiftDay1", 2 => "PraporGiftDay2", _ => null }; if (giftId is not null) { if (!_profileHelper.PlayerHasRecievedMaxNumberOfGift(sessionId, giftId, 1)) { SendGiftToPlayer(sessionId, giftId); } } } /// /// Send player a gift with silent received check /// /// ID of gift to send /// Session ID of player to send to /// Optional, how many to send public void SendGiftWithSilentReceivedCheck(string giftId, string? sessionId, int giftCount) { if (!_profileHelper.PlayerHasRecievedMaxNumberOfGift(sessionId, giftId, giftCount)) { SendGiftToPlayer(sessionId, giftId); } } }