using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using RoR2.EntitlementManagement; using RoR2.ExpansionManagement; using RoR2.Hologram; using RoR2.Stats; using UnityEngine; using UnityEngine.Networking; namespace RoR2; [RequireComponent(typeof(Highlight))] public sealed class PurchaseInteraction : NetworkBehaviour, IInteractable, IInspectable, ISupportsMultipleInspectZones, IHologramContentProvider, IDisplayNameProvider { [SyncVar] public string displayNameToken; [SyncVar] public string contextToken; public CostTypeIndex costType; [SyncVar] public bool available = true; [SyncVar] public int cost; public int solitudeCost; public bool automaticallyScaleCostWithDifficulty; [Tooltip("The unlockable that a player must have to be able to interact with this terminal.")] public string requiredUnlockable = ""; public bool ignoreSpherecastForInteractability; public string[] purchaseStatNames; public bool setUnavailableOnTeleporterActivated; public bool isShrine; public bool isGoldShrine; public bool shouldProximityHighlight = true; public bool saleStarCompatible; [SerializeField] public ExpansionDef requiredExpansion; private IInspectInfoProvider[] cachedInspectInfoProviders; [HideInInspector] public Interactor lastActivator; [SyncVar] public GameObject lockGameObject; private Xoroshiro128Plus rng; private GameObjectUnlockableFilter gameObjectUnlockableFilter; private static readonly StringBuilder sharedStringBuilder = new StringBuilder(); public PurchaseEvent onPurchase; private Transform viewerTransform; private CharacterBody viewerBody; private int currentInspectIndex; private NetworkInstanceId ___lockGameObjectNetId; public string NetworkdisplayNameToken { get { return displayNameToken; } [param: In] set { SetSyncVar(value, ref displayNameToken, 1u); } } public string NetworkcontextToken { get { return contextToken; } [param: In] set { SetSyncVar(value, ref contextToken, 2u); } } public bool Networkavailable { get { return available; } [param: In] set { SetSyncVar(value, ref available, 4u); } } public int Networkcost { get { return cost; } [param: In] set { SetSyncVar(value, ref cost, 8u); } } public GameObject NetworklockGameObject { get { return lockGameObject; } [param: In] set { SetSyncVarGameObject(value, ref lockGameObject, 16u, ref ___lockGameObjectNetId); } } public static event Action onItemSpentOnPurchase; public static event Action onEquipmentSpentOnPurchase; private void Awake() { if (NetworkServer.active) { if (automaticallyScaleCostWithDifficulty) { Networkcost = Run.instance.GetDifficultyScaledCost(cost); solitudeCost = cost; } rng = new Xoroshiro128Plus(Run.instance.treasureRng.nextUlong); } cachedInspectInfoProviders = GetComponents(); } private void Start() { gameObjectUnlockableFilter = base.gameObject.GetComponent(); } [Server] public void SetAvailable(bool newAvailable) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.PurchaseInteraction::SetAvailable(System.Boolean)' called on client"); } else { Networkavailable = newAvailable; } } [Server] public void SetUnavailableTemporarily(float time) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.PurchaseInteraction::SetUnavailableTemporarily(System.Single)' called on client"); return; } Networkavailable = false; Invoke("SetAvailableTrue", time); } private void SetAvailableTrue() { Networkavailable = true; } public string GetDisplayName() { return Language.GetString(displayNameToken); } private static bool ActivatorHasUnlockable(Interactor activator, string unlockableName) { NetworkUser networkUser = Util.LookUpBodyNetworkUser(activator.gameObject); if ((bool)networkUser) { LocalUser localUser = networkUser.localUser; if (localUser != null) { return localUser.userProfile.HasUnlockable(unlockableName); } } return true; } public string GetContextString(Interactor activator) { sharedStringBuilder.Clear(); sharedStringBuilder.Append(Language.GetString(contextToken)); if (costType != 0) { sharedStringBuilder.Append(" ("); if (costType == CostTypeIndex.Money) { if (TeamManager.LongstandingSolitudesInParty() > 0) { CostTypeCatalog.GetCostTypeDef(costType).BuildCostStringStyled(cost * (TeamManager.LongstandingSolitudesInParty() + 1), sharedStringBuilder, forWorldDisplay: false); } else { CostTypeCatalog.GetCostTypeDef(costType).BuildCostStringStyled(cost, sharedStringBuilder, forWorldDisplay: false); } } else { CostTypeCatalog.GetCostTypeDef(costType).BuildCostStringStyled(cost, sharedStringBuilder, forWorldDisplay: false); } sharedStringBuilder.Append(")"); } return sharedStringBuilder.ToString(); } public Interactability GetInteractability(Interactor activator) { if (!string.IsNullOrEmpty(requiredUnlockable) && !ActivatorHasUnlockable(activator, requiredUnlockable)) { return Interactability.Disabled; } if (!available || (bool)lockGameObject) { return Interactability.Disabled; } if (gameObjectUnlockableFilter != null) { NetworkUser networkUser = Util.LookUpBodyNetworkUser(activator.gameObject); if ((bool)networkUser) { LocalUser localUser = networkUser.localUser; if (localUser != null && localUser.userProfile.HasAchievement(gameObjectUnlockableFilter.AchievementToDisable)) { return Interactability.Disabled; } } } Run instance = Run.instance; if ((bool)instance && (bool)requiredExpansion) { if (!instance.IsExpansionEnabled(requiredExpansion)) { return Interactability.Disabled; } EntitlementDef requiredEntitlement = requiredExpansion.requiredEntitlement; CharacterBody component = activator.GetComponent(); if ((bool)component && (bool)requiredEntitlement) { PlayerCharacterMasterController playerCharacterMasterController = component.master.playerCharacterMasterController; if (!playerCharacterMasterController) { return Interactability.Disabled; } PlayerCharacterMasterControllerEntitlementTracker component2 = playerCharacterMasterController.GetComponent(); if (!component2) { Debug.LogWarning("Rejecting interact because the playerCharacterMasterController doesn't have a sibling PlayerCharacterMasterControllerEntitlementTracker"); return Interactability.Disabled; } if (!component2.HasEntitlement(requiredEntitlement)) { Debug.LogWarning("Rejecting interact because the player doesn't have entitlement " + requiredEntitlement.name); return Interactability.Disabled; } } } if (!CanBeAffordedByInteractor(activator)) { return Interactability.ConditionsNotMet; } return Interactability.Available; } public bool CanBeAffordedByInteractor(Interactor activator) { int num = cost; if (costType == CostTypeIndex.Money) { num = ((activator.GetComponent().GetBuffCount(DLC2Content.Buffs.FreeUnlocks) <= 0) ? (cost * (TeamManager.LongstandingSolitudesInParty() + 1)) : 0); } return CostTypeCatalog.GetCostTypeDef(costType).IsAffordable(num, activator); } public void OnInteractionBegin(Interactor activator) { if (!CanBeAffordedByInteractor(activator)) { return; } CharacterBody component = activator.GetComponent(); CostTypeDef costTypeDef = CostTypeCatalog.GetCostTypeDef(costType); ItemIndex itemIndex = ItemIndex.None; ShopTerminalBehavior component2 = GetComponent(); if ((bool)component2) { itemIndex = PickupCatalog.GetPickupDef(component2.CurrentPickupIndex())?.itemIndex ?? ItemIndex.None; } int num = cost; if (costType == CostTypeIndex.Money) { if (activator.GetComponent().GetBuffCount(DLC2Content.Buffs.FreeUnlocks) > 0 && !GetComponent()) { num = 0; activator.GetComponent().RemoveBuff(DLC2Content.Buffs.FreeUnlocks); Util.PlaySound("Play_item_proc_onLevelUpFreeUnlock_activate", base.gameObject); } else { num = cost * (TeamManager.LongstandingSolitudesInParty() + 1); } } if (component.inventory.GetItemCount(DLC2Content.Items.LowerPricedChests) > 0 && saleStarCompatible) { int itemCount = activator.GetComponent().inventory.GetItemCount(DLC2Content.Items.LowerPricedChests); int num2 = 0; float num3 = 1f; if (itemCount == 1) { num2 = 0; } else { for (int i = 1; i < 4; i++) { switch (i) { case 1: num3 = 30f; break; case 2: num3 = 15f; break; case 3: num3 = 1f; break; } if (itemCount >= 3) { float num4 = 1f - 1f / ((float)itemCount * 0.05f + 1f); if (!Util.CheckRoll(num3 + num4 * 100f, activator.GetComponent().master)) { break; } num2 = i; } else { if (!Util.CheckRoll(num3, activator.GetComponent().master)) { break; } num2 = i; } } } if ((bool)GetComponent()) { GetComponent().dropCount = 2 + num2; } else if ((bool)GetComponent()) { GetComponent().dropCount = 2 + num2; } component.inventory.RemoveItem(DLC2Content.Items.LowerPricedChests, activator.GetComponent().inventory.GetItemCount(DLC2Content.Items.LowerPricedChests)); component.inventory.GiveItem(DLC2Content.Items.LowerPricedChestsConsumed, itemCount); CharacterMasterNotificationQueue.SendTransformNotification(component.master, DLC2Content.Items.LowerPricedChests.itemIndex, DLC2Content.Items.LowerPricedChestsConsumed.itemIndex, CharacterMasterNotificationQueue.TransformationType.SaleStarRegen); Util.PlaySound("Play_item_proc_lowerPricedChest", base.gameObject); } CostTypeDef.PayCostResults payCostResults = costTypeDef.PayCost(num, activator, base.gameObject, rng, itemIndex); foreach (ItemIndex item in payCostResults.itemsTaken) { CreateItemTakenOrb(component.corePosition, base.gameObject, item); if (item != itemIndex) { PurchaseInteraction.onItemSpentOnPurchase?.Invoke(this, activator); } } foreach (EquipmentIndex item2 in payCostResults.equipmentTaken) { PurchaseInteraction.onEquipmentSpentOnPurchase?.Invoke(this, activator, item2); } IEnumerable statDefsToIncrement = purchaseStatNames.Select(StatDef.Find); StatManager.OnPurchase(component, costType, statDefsToIncrement); onPurchase.Invoke(activator); lastActivator = activator; } [Server] public static void CreateItemTakenOrb(Vector3 effectOrigin, GameObject targetObject, ItemIndex itemIndex) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.PurchaseInteraction::CreateItemTakenOrb(UnityEngine.Vector3,UnityEngine.GameObject,RoR2.ItemIndex)' called on client"); return; } GameObject effectPrefab = LegacyResourcesAPI.Load("Prefabs/Effects/OrbEffects/ItemTakenOrbEffect"); EffectData effectData = new EffectData { origin = effectOrigin, genericFloat = 1.5f, genericUInt = (uint)(itemIndex + 1) }; effectData.SetNetworkedObjectReference(targetObject); EffectManager.SpawnEffect(effectPrefab, effectData, transmit: true); } public bool ShouldDisplayHologram(GameObject viewer) { return available; } public GameObject GetHologramContentPrefab() { return LegacyResourcesAPI.Load("Prefabs/CostHologramContent"); } private void CacheViewerBody(Transform _viewer) { if (!(_viewer == null) && (!viewerBody || !(viewerTransform == _viewer)) && _viewer.TryGetComponent(out var component)) { viewerTransform = _viewer; viewerBody = component; } } public void ScaleCost(float scalar) { Networkcost = (int)Mathf.Floor((float)cost * scalar); } public void UpdateHologramContent(GameObject hologramContentObject, Transform viewer) { CostHologramContent component = hologramContentObject.GetComponent(); GetComponent(); CacheViewerBody(viewer); if (!component) { return; } if ((bool)viewerBody && costType.Equals(CostTypeIndex.Money)) { if (TeamManager.LongstandingSolitudesInParty() > 0) { component.displayValue = cost * (TeamManager.LongstandingSolitudesInParty() + 1); } else { component.displayValue = cost; } } else { component.displayValue = cost; } component.costType = costType; } public bool ShouldIgnoreSpherecastForInteractibility(Interactor activator) { return false; } public bool ShouldShowOnScanner() { return available; } public bool ShouldProximityHighlight() { return shouldProximityHighlight; } private void OnEnable() { InstanceTracker.Add(this); } private void OnDisable() { InstanceTracker.Remove(this); } [RuntimeInitializeOnLoadMethod] private static void Init() { TeleporterInteraction.onTeleporterBeginChargingGlobal += OnTeleporterBeginCharging; } private static void OnTeleporterBeginCharging(TeleporterInteraction teleporterInteraction) { if (!NetworkServer.active) { return; } foreach (PurchaseInteraction instances in InstanceTracker.GetInstancesList()) { if (instances.setUnavailableOnTeleporterActivated) { instances.SetAvailable(newAvailable: false); instances.CancelInvoke("SetUnavailableTemporarily"); } } } public IInspectInfoProvider GetInspectInfoProvider() { if (currentInspectIndex < cachedInspectInfoProviders.Length) { return cachedInspectInfoProviders[currentInspectIndex]; } return null; } public void SetInspectInfoProvider(int newInspectIndex) { currentInspectIndex = newInspectIndex; } private void UNetVersion() { } public override bool OnSerialize(NetworkWriter writer, bool forceAll) { if (forceAll) { writer.Write(displayNameToken); writer.Write(contextToken); writer.Write(available); writer.WritePackedUInt32((uint)cost); writer.Write(lockGameObject); return true; } bool flag = false; if ((base.syncVarDirtyBits & (true ? 1u : 0u)) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } writer.Write(displayNameToken); } if ((base.syncVarDirtyBits & 2u) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } writer.Write(contextToken); } if ((base.syncVarDirtyBits & 4u) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } writer.Write(available); } if ((base.syncVarDirtyBits & 8u) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } writer.WritePackedUInt32((uint)cost); } if ((base.syncVarDirtyBits & 0x10u) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } writer.Write(lockGameObject); } if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); } return flag; } public override void OnDeserialize(NetworkReader reader, bool initialState) { if (initialState) { displayNameToken = reader.ReadString(); contextToken = reader.ReadString(); available = reader.ReadBoolean(); cost = (int)reader.ReadPackedUInt32(); ___lockGameObjectNetId = reader.ReadNetworkId(); return; } int num = (int)reader.ReadPackedUInt32(); if (((uint)num & (true ? 1u : 0u)) != 0) { displayNameToken = reader.ReadString(); } if (((uint)num & 2u) != 0) { contextToken = reader.ReadString(); } if (((uint)num & 4u) != 0) { available = reader.ReadBoolean(); } if (((uint)num & 8u) != 0) { cost = (int)reader.ReadPackedUInt32(); } if (((uint)num & 0x10u) != 0) { lockGameObject = reader.ReadGameObject(); } } public override void PreStartClient() { if (!___lockGameObjectNetId.IsEmpty()) { NetworklockGameObject = ClientScene.FindLocalObject(___lockGameObjectNetId); } } }