using System; using System.Runtime.InteropServices; using RoR2.Items; using RoR2.Networking; using RoR2.UI; using Unity; using UnityEngine; using UnityEngine.Networking; namespace RoR2; public sealed class GenericPickupController : NetworkBehaviour, IInteractable, IInspectable, IInspectInfoProvider, IDisplayNameProvider { private class PickupMessage : MessageBase { public GameObject masterGameObject; public PickupIndex pickupIndex; public uint pickupQuantity; public void Reset() { masterGameObject = null; pickupIndex = PickupIndex.none; pickupQuantity = 0u; } public override void Serialize(NetworkWriter writer) { writer.Write(masterGameObject); GeneratedNetworkCode._WritePickupIndex_None(writer, pickupIndex); writer.WritePackedUInt32(pickupQuantity); } public override void Deserialize(NetworkReader reader) { masterGameObject = reader.ReadGameObject(); pickupIndex = GeneratedNetworkCode._ReadPickupIndex_None(reader); pickupQuantity = reader.ReadPackedUInt32(); } } public enum PickupArtifactFlag { NONE, COMMAND, DELUSION } public struct CreatePickupInfo { public Vector3 position; public Quaternion rotation; private PickupIndex? _pickupIndex; public PickupPickerController.Option[] pickerOptions; public GameObject prefabOverride; public ChestBehavior chest; public PickupArtifactFlag artifactFlag; public ItemIndex delusionItemIndex; public ItemIndex falseChoice1; public ItemIndex falseChoice2; public PickupIndex pickupIndex { get { if (!_pickupIndex.HasValue) { return PickupIndex.none; } return _pickupIndex.Value; } set { _pickupIndex = value; } } } public PickupDisplay pickupDisplay; public ChestBehavior chestGeneratedFrom; [SyncVar(hook = "SyncPickupIndex")] public PickupIndex pickupIndex = PickupIndex.none; [SyncVar(hook = "SyncRecycled")] public bool Recycled; public bool selfDestructIfPickupIndexIsNotIdeal; public SerializablePickupIndex idealPickupIndex; private static readonly PickupMessage pickupMessageInstance = new PickupMessage(); public float waitDuration = 0.5f; private Run.FixedTimeStamp waitStartTime; private bool consumed; public const string pickupSoundString = "Play_UI_item_pickup"; private static GameObject pickupPrefab; public PickupIndex NetworkpickupIndex { get { return pickupIndex; } [param: In] set { if (NetworkServer.localClientActive && !base.syncVarHookGuard) { base.syncVarHookGuard = true; SyncPickupIndex(value); base.syncVarHookGuard = false; } SetSyncVar(value, ref pickupIndex, 1u); } } public bool NetworkRecycled { get { return Recycled; } [param: In] set { if (NetworkServer.localClientActive && !base.syncVarHookGuard) { base.syncVarHookGuard = true; SyncRecycled(value); base.syncVarHookGuard = false; } SetSyncVar(value, ref Recycled, 2u); } } private void SyncPickupIndex(PickupIndex newPickupIndex) { NetworkpickupIndex = newPickupIndex; UpdatePickupDisplay(); } private void SyncRecycled(bool isRecycled) { NetworkRecycled = isRecycled; } [Server] public static void SendPickupMessage(CharacterMaster master, PickupIndex pickupIndex) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.GenericPickupController::SendPickupMessage(RoR2.CharacterMaster,RoR2.PickupIndex)' called on client"); return; } uint pickupQuantity = 1u; if ((bool)master.inventory) { ItemIndex itemIndex = PickupCatalog.GetPickupDef(pickupIndex)?.itemIndex ?? ItemIndex.None; if (itemIndex != ItemIndex.None) { pickupQuantity = (uint)master.inventory.GetItemCount(itemIndex); } } try { PickupMessage msg = new PickupMessage { masterGameObject = master.gameObject, pickupIndex = pickupIndex, pickupQuantity = pickupQuantity }; NetworkServer.SendByChannelToAll(57, msg, QosChannelIndex.chat.intVal); } catch (Exception arg) { Debug.Log($"Failed to send pickupMessage for pickupIndex {pickupIndex} - gameObject {master.gameObject.name}\r\n{arg}"); } } [NetworkMessageHandler(msgType = 57, client = true)] private static void HandlePickupMessage(NetworkMessage netMsg) { PickupMessage pickupMessage = pickupMessageInstance; netMsg.ReadMessage(pickupMessage); GameObject masterGameObject = pickupMessage.masterGameObject; PickupIndex pickupIndex = pickupMessage.pickupIndex; PickupDef pickupDef = PickupCatalog.GetPickupDef(pickupIndex); uint pickupQuantity = pickupMessage.pickupQuantity; pickupMessage.Reset(); if (!masterGameObject) { return; } CharacterMaster component = masterGameObject.GetComponent(); if (!component) { return; } PlayerCharacterMasterController component2 = component.GetComponent(); if ((bool)component2) { NetworkUser networkUser = component2.networkUser; if ((bool)networkUser) { networkUser.localUser?.userProfile.DiscoverPickup(pickupIndex); } } CharacterBody body = component.GetBody(); _ = (bool)body; ItemDef itemDef = ItemCatalog.GetItemDef(pickupDef?.itemIndex ?? ItemIndex.None); if (itemDef != null && itemDef.hidden) { return; } if (pickupIndex != PickupIndex.none) { ItemIndex transformedItemIndex = ContagiousItemManager.GetTransformedItemIndex(itemDef?.itemIndex ?? ItemIndex.None); if (itemDef == null || transformedItemIndex == ItemIndex.None || component.inventory.GetItemCount(transformedItemIndex) <= 0) { CharacterMasterNotificationQueue.PushPickupNotification(component, pickupIndex); } } Chat.AddPickupMessage(body, pickupDef?.nameToken ?? PickupCatalog.invalidPickupToken, pickupDef?.baseColor ?? Color.black, pickupQuantity); if ((bool)body) { Util.PlaySound("Play_UI_item_pickup", body.gameObject); } } public void StartWaitTime() { waitStartTime = Run.FixedTimeStamp.now; } private void OnTriggerStay(Collider other) { if (!NetworkServer.active || !(waitStartTime.timeSince >= waitDuration) || consumed) { return; } CharacterBody component = other.GetComponent(); if (!component) { return; } PickupDef pickupDef = PickupCatalog.GetPickupDef(pickupIndex); ItemIndex itemIndex = pickupDef?.itemIndex ?? ItemIndex.None; if (itemIndex != ItemIndex.None) { ItemTierDef itemTierDef = ItemTierCatalog.GetItemTierDef(ItemCatalog.GetItemDef(itemIndex).tier); if ((bool)itemTierDef && (itemTierDef.pickupRules == ItemTierDef.PickupRules.ConfirmAll || (itemTierDef.pickupRules == ItemTierDef.PickupRules.ConfirmFirst && (bool)component.inventory && component.inventory.GetItemCount(itemIndex) <= 0))) { return; } } EquipmentIndex equipmentIndex = pickupDef?.equipmentIndex ?? EquipmentIndex.None; if ((equipmentIndex == EquipmentIndex.None || (!EquipmentCatalog.GetEquipmentDef(equipmentIndex).isLunar && (!component.inventory || component.inventory.currentEquipmentIndex == EquipmentIndex.None))) && (pickupDef == null || pickupDef.coinValue == 0) && BodyHasPickupPermission(component)) { AttemptGrant(component); } } private static bool BodyHasPickupPermission(CharacterBody body) { if ((bool)(body.masterObject ? body.masterObject.GetComponent() : null)) { return body.inventory; } return false; } public bool ShouldIgnoreSpherecastForInteractibility(Interactor activator) { return false; } public string GetContextString(Interactor activator) { return string.Format(Language.GetString(PickupCatalog.GetPickupDef(pickupIndex)?.interactContextToken ?? string.Empty), GetDisplayName()); } private void UpdatePickupDisplay() { if (!pickupDisplay) { return; } pickupDisplay.SetPickupIndex(pickupIndex); if ((bool)pickupDisplay.modelRenderer) { Highlight component = GetComponent(); if ((bool)component) { component.targetRenderer = pickupDisplay.modelRenderer; component.ResetHighlight(); } } } [Server] private void AttemptGrant(CharacterBody body) { if (!NetworkServer.active) { Debug.LogWarning("[Server] function 'System.Void RoR2.GenericPickupController::AttemptGrant(RoR2.CharacterBody)' called on client"); return; } TeamComponent component = body.GetComponent(); if (!component || component.teamIndex != TeamIndex.Player) { return; } PickupDef pickupDef = PickupCatalog.GetPickupDef(pickupIndex); if ((bool)body.inventory && pickupDef != null) { PickupDef.GrantContext grantContext = default(PickupDef.GrantContext); grantContext.body = body; grantContext.controller = this; PickupDef.GrantContext context = grantContext; pickupDef.attemptGrant?.Invoke(ref context); consumed = context.shouldDestroy; if (context.shouldNotify) { SendPickupMessage(body.master, pickupDef.pickupIndex); } if ((bool)chestGeneratedFrom && DelusionChestController.isDelusionEnable) { chestGeneratedFrom.CallRpcSetDelusionPickupIndex(pickupIndex); } if (context.shouldDestroy) { UnityEngine.Object.Destroy(base.gameObject); } } } private void Start() { waitStartTime = Run.FixedTimeStamp.now; consumed = false; UpdatePickupDisplay(); } private void OnEnable() { InstanceTracker.Add(this); } private void OnDisable() { InstanceTracker.Remove(this); } public Interactability GetInteractability(Interactor activator) { if (!base.enabled) { return Interactability.Disabled; } if (waitStartTime.timeSince < waitDuration || consumed) { return Interactability.Disabled; } CharacterBody component = activator.GetComponent(); if ((bool)component) { if (!BodyHasPickupPermission(component)) { return Interactability.Disabled; } return Interactability.Available; } return Interactability.Disabled; } public void OnInteractionBegin(Interactor activator) { AttemptGrant(activator.GetComponent()); } public bool ShouldShowOnScanner() { return true; } public bool ShouldProximityHighlight() { return true; } public string GetDisplayName() { return Language.GetString(PickupCatalog.GetPickupDef(pickupIndex)?.nameToken ?? PickupCatalog.invalidPickupToken); } public void SetPickupIndexFromString(string pickupString) { if (NetworkServer.active) { PickupIndex networkpickupIndex = PickupCatalog.FindPickupIndex(pickupString); NetworkpickupIndex = networkpickupIndex; } } [InitDuringStartup] private static void Init() { LegacyResourcesAPI.LoadAsyncCallback("Prefabs/NetworkedObjects/GenericPickup", delegate(GameObject operationResult) { pickupPrefab = operationResult; }); } public static GenericPickupController CreatePickup(in CreatePickupInfo createPickupInfo) { GameObject gameObject = UnityEngine.Object.Instantiate(createPickupInfo.prefabOverride ?? pickupPrefab, createPickupInfo.position, createPickupInfo.rotation); GenericPickupController component = gameObject.GetComponent(); if ((bool)component) { component.NetworkpickupIndex = createPickupInfo.pickupIndex; component.chestGeneratedFrom = createPickupInfo.chest; } else { PickupDisplay componentInChildren = gameObject.GetComponentInChildren(); if ((bool)componentInChildren) { GameObject modelObjectOverride = gameObject.GetComponentInChildren().gameObject; componentInChildren.RebuildModel(modelObjectOverride); } } PickupIndexNetworker component2 = gameObject.GetComponent(); if ((bool)component2) { component2.NetworkpickupIndex = createPickupInfo.pickupIndex; } PickupPickerController component3 = gameObject.GetComponent(); if ((bool)component3 && createPickupInfo.pickerOptions != null) { component3.SetOptionsServer(createPickupInfo.pickerOptions); } NetworkServer.Spawn(gameObject); return component; } [ContextMenu("Print Pickup Index")] private void PrintPickupIndex() { Debug.LogFormat("pickupIndex={0}", PickupCatalog.GetPickupDef(pickupIndex)?.internalName ?? "Invalid"); } public IInspectInfoProvider GetInspectInfoProvider() { return this; } public bool CanBeInspected() { if (pickupIndex != PickupIndex.none) { return pickupIndex.isValid; } return false; } public InspectInfo GetInfo() { return PickupCatalog.GetPickupDef(pickupIndex) ?? throw new InvalidOperationException("Attempted to get info for invalid pickup. Should be impossible. Investigate me."); } private void UNetVersion() { } public override bool OnSerialize(NetworkWriter writer, bool forceAll) { if (forceAll) { GeneratedNetworkCode._WritePickupIndex_None(writer, pickupIndex); writer.Write(Recycled); return true; } bool flag = false; if ((base.syncVarDirtyBits & (true ? 1u : 0u)) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } GeneratedNetworkCode._WritePickupIndex_None(writer, pickupIndex); } if ((base.syncVarDirtyBits & 2u) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } writer.Write(Recycled); } if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); } return flag; } public override void OnDeserialize(NetworkReader reader, bool initialState) { if (initialState) { pickupIndex = GeneratedNetworkCode._ReadPickupIndex_None(reader); Recycled = reader.ReadBoolean(); return; } int num = (int)reader.ReadPackedUInt32(); if (((uint)num & (true ? 1u : 0u)) != 0) { SyncPickupIndex(GeneratedNetworkCode._ReadPickupIndex_None(reader)); } if (((uint)num & 2u) != 0) { SyncRecycled(reader.ReadBoolean()); } } public override void PreStartClient() { } }