0
0
mirror of https://github.com/sp-tarkov/modules.git synced 2025-02-13 09:50:43 -05:00

238 lines
9.3 KiB
C#

using SPT.Common.Http;
using Comfort.Common;
using EFT;
using EFT.Quests;
using HarmonyLib;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using static BackendConfigSettingsClass;
using StandingListClass = GClass2188<float>;
namespace SPT.SinglePlayer.Utils.TraderServices
{
public class TraderServicesManager
{
/// <summary>
/// Subscribe to this event to trigger trader service logic.
/// </summary>
public event Action<ETraderServiceType, string> OnTraderServicePurchased;
private static TraderServicesManager _instance;
public static TraderServicesManager Instance
{
get
{
if (_instance == null)
{
_instance = new TraderServicesManager();
}
return _instance;
}
}
private Dictionary<ETraderServiceType, Dictionary<string, bool>> _servicePurchased { get; set; }
private HashSet<string> _cachedTraders = new HashSet<string>();
private FieldInfo _playerQuestControllerField;
public TraderServicesManager()
{
_servicePurchased = new Dictionary<ETraderServiceType, Dictionary<string, bool>>();
_playerQuestControllerField = AccessTools.Field(typeof(Player), "_questController");
}
public void Clear()
{
_servicePurchased.Clear();
_cachedTraders.Clear();
}
public void GetTraderServicesDataFromServer(string traderId)
{
Dictionary<ETraderServiceType, ServiceData> servicesData = Singleton<BackendConfigSettingsClass>.Instance.ServicesData;
var gameWorld = Singleton<GameWorld>.Instance;
var player = gameWorld?.MainPlayer;
if (gameWorld == null || player == null)
{
Debug.LogError("GetTraderServicesDataFromServer - Error fetching game objects");
return;
}
if (!player.Profile.TradersInfo.TryGetValue(traderId, out Profile.TraderInfo traderInfo))
{
Debug.LogError("GetTraderServicesDataFromServer - Error fetching profile trader info");
return;
}
// Only request data from the server if it's not already cached
if (!_cachedTraders.Contains(traderId))
{
var json = RequestHandler.GetJson($"/singleplayer/traderServices/getTraderServices/{traderId}");
var traderServiceModels = JsonConvert.DeserializeObject<List<TraderServiceModel>>(json);
foreach (var traderServiceModel in traderServiceModels)
{
ETraderServiceType serviceType = traderServiceModel.ServiceType;
ServiceData serviceData;
// Only populate trader services that don't exist yet
if (!servicesData.ContainsKey(traderServiceModel.ServiceType))
{
TraderServicesClass traderService = new TraderServicesClass
{
TraderId = traderId,
ServiceType = serviceType,
UniqueItems = traderServiceModel.ItemsToReceive ?? new MongoID[0],
ItemsToPay = traderServiceModel.ItemsToPay ?? new Dictionary<MongoID, int>(),
// SubServices seem to be populated dynamically in the client (For BTR taxi atleast), so we can just ignore it
// NOTE: For future reference, this is a dict of `point id` to `price` for the BTR taxi
SubServices = new Dictionary<string, int>()
};
// Convert our format to the backend settings format
serviceData = new ServiceData(traderService);
// Populate requirements if provided
if (traderServiceModel.Requirements != null)
{
if (traderServiceModel.Requirements.Standings != null)
{
serviceData.TraderServiceRequirements.Standings = new StandingListClass();
serviceData.TraderServiceRequirements.Standings.AddRange(traderServiceModel.Requirements.Standings);
// BSG has a bug in their code, we _need_ to initialize this if Standings isn't null
serviceData.TraderServiceRequirements.CompletedQuests = new QuestDictionaryClass<string>();
}
if (traderServiceModel.Requirements.CompletedQuests != null)
{
serviceData.TraderServiceRequirements.CompletedQuests = new QuestDictionaryClass<string>();
serviceData.TraderServiceRequirements.CompletedQuests.Concat(traderServiceModel.Requirements.CompletedQuests);
}
}
servicesData[serviceData.ServiceType] = serviceData;
}
}
_cachedTraders.Add(traderId);
}
// Update service availability
foreach (var servicesDataPair in servicesData)
{
// Only update this trader's services
if (servicesDataPair.Value.TraderId != traderId)
{
continue;
}
var IsServiceAvailable = this.IsServiceAvailable(player, servicesDataPair.Value.TraderServiceRequirements);
// Check whether we've purchased this service yet
var traderService = servicesDataPair.Key;
var WasPurchasedInThisRaid = IsServicePurchased(traderService, traderId);
traderInfo.SetServiceAvailability(traderService, IsServiceAvailable, WasPurchasedInThisRaid);
}
}
private bool IsServiceAvailable(Player player, ServiceRequirements requirements)
{
// Handle standing requirements
if (requirements.Standings != null)
{
foreach (var entry in requirements.Standings)
{
if (!player.Profile.TradersInfo.ContainsKey(entry.Key) ||
player.Profile.TradersInfo[entry.Key].Standing < entry.Value)
{
return false;
}
}
}
// Handle quest requirements
if (requirements.CompletedQuests != null)
{
AbstractQuestControllerClass questController = _playerQuestControllerField.GetValue(player) as AbstractQuestControllerClass;
foreach (string questId in requirements.CompletedQuests)
{
var conditional = questController.Quests.GetConditional(questId);
if (conditional == null || conditional.QuestStatus != EQuestStatus.Success)
{
return false;
}
}
}
return true;
}
public void AfterPurchaseTraderService(ETraderServiceType serviceType, AbstractQuestControllerClass questController, string subServiceId = null)
{
GameWorld gameWorld = Singleton<GameWorld>.Instance;
Player player = gameWorld?.MainPlayer;
if (gameWorld == null || player == null)
{
Debug.LogError("TryPurchaseTraderService - Error fetching game objects");
return;
}
// Service doesn't exist
if (!Singleton<BackendConfigSettingsClass>.Instance.ServicesData.TryGetValue(serviceType, out var serviceData))
{
return;
}
SetServicePurchased(serviceType, subServiceId, serviceData.TraderId);
}
public void SetServicePurchased(ETraderServiceType serviceType, string subserviceId, string traderId)
{
if (_servicePurchased.TryGetValue(serviceType, out var traderDict))
{
traderDict[traderId] = true;
}
else
{
_servicePurchased[serviceType] = new Dictionary<string, bool>();
_servicePurchased[serviceType][traderId] = true;
}
if (OnTraderServicePurchased != null)
{
OnTraderServicePurchased.Invoke(serviceType, subserviceId);
}
}
public void RemovePurchasedService(ETraderServiceType serviceType, string traderId)
{
if (_servicePurchased.TryGetValue(serviceType, out var traderDict))
{
traderDict[traderId] = false;
}
}
public bool IsServicePurchased(ETraderServiceType serviceType, string traderId)
{
if (_servicePurchased.TryGetValue(serviceType, out var traderDict))
{
if (traderDict.TryGetValue(traderId, out var result))
{
return result;
}
}
return false;
}
}
}