diff --git a/EXILED/Exiled.CustomItems/API/Features/CustomKeycard.cs b/EXILED/Exiled.CustomItems/API/Features/CustomKeycard.cs index ca47de2989..d53846cbb0 100644 --- a/EXILED/Exiled.CustomItems/API/Features/CustomKeycard.cs +++ b/EXILED/Exiled.CustomItems/API/Features/CustomKeycard.cs @@ -8,6 +8,7 @@ namespace Exiled.CustomItems.API.Features { using System; + using System.Linq; using Exiled.API.Enums; using Exiled.API.Extensions; @@ -18,6 +19,8 @@ namespace Exiled.CustomItems.API.Features using Exiled.API.Features.Pickups; using Exiled.Events.EventArgs.Item; using Exiled.Events.EventArgs.Player; + using Interactables.Interobjects.DoorUtils; + using InventorySystem.Items.Keycards; using UnityEngine; /// @@ -33,40 +36,108 @@ public override ItemType Type set { if (!value.IsKeycard()) - throw new ArgumentOutOfRangeException("Type", value, "Invalid keycard type."); + throw new ArgumentOutOfRangeException(nameof(Type), value, "Invalid keycard type."); base.Type = value; } } + /// + /// Gets or sets name of keycard holder. + /// + public virtual string KeycardName { get; set; } = string.Empty; + + /// + /// Gets or sets a label for keycard. + /// + public virtual string KeycardLabel { get; set; } = string.Empty; + + /// + /// Gets or sets a color of keycard label. + /// + public virtual Color32? KeycardLabelColor { get; set; } + + /// + /// Gets or sets a tint color. + /// + public virtual Color32? TintColor { get; set; } + /// /// Gets or sets the permissions for custom keycard. /// - public virtual KeycardPermissions Permissions { get; set; } + public virtual KeycardPermissions Permissions { get; set; } = KeycardPermissions.None; + + /// + /// Gets or sets a color of keycard permissions. + /// + public virtual Color32? KeycardPermissionsColor { get; set; } /// public override void Give(Player player, Item item, bool displayMessage = true) { base.Give(player, item, displayMessage); - // TODO: Rework custom keycard - /* if (item.Is(out Keycard card)) - card.Permissions = Permissions; - */ + SetupKeycard(card); } /// public override Pickup? Spawn(Vector3 position, Item item, Player? previousOwner = null) { - /* if (item.Is(out Keycard card)) - card.Permissions = Permissions; - */ + SetupKeycard(card); return base.Spawn(position, item, previousOwner); } + /// + /// Setups keycard according to this class. + /// + /// Item instance. + protected virtual void SetupKeycard(Keycard keycard) + { + if (!keycard.Base.Customizable) + return; + + DetailBase[] details = keycard.Base.Details; + + NametagDetail? nameDetail = details.OfType().FirstOrDefault(); + + if (nameDetail != null && !string.IsNullOrEmpty(KeycardName)) + NametagDetail._customNametag = KeycardName; + + CustomItemNameDetail? raNameDetail = details.OfType().FirstOrDefault(); + + if (raNameDetail != null) + raNameDetail.Name = Name; + + CustomLabelDetail? labelDetail = details.OfType().FirstOrDefault(); + + if (labelDetail != null) + { + if (!string.IsNullOrEmpty(KeycardLabel)) + CustomLabelDetail._customText = KeycardLabel; + + if (KeycardLabelColor.HasValue) + CustomLabelDetail._customColor = KeycardLabelColor.Value; + } + + CustomPermsDetail? permsDetail = details.OfType().FirstOrDefault(); + + if (permsDetail != null) + { + CustomPermsDetail._customLevels = new((DoorPermissionFlags)Permissions); + CustomPermsDetail._customColor = KeycardPermissionsColor; + } + + CustomTintDetail? tintDetail = details.OfType().FirstOrDefault(); + + if (tintDetail != null && TintColor.HasValue) + { + CustomTintDetail._customColor = TintColor.Value; + } + } + /// /// Called when custom keycard interacts with a door. /// diff --git a/EXILED/Exiled.Events/EventArgs/Player/ShotEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ShotEventArgs.cs index 0eb6a5c69d..18d2303353 100644 --- a/EXILED/Exiled.Events/EventArgs/Player/ShotEventArgs.cs +++ b/EXILED/Exiled.Events/EventArgs/Player/ShotEventArgs.cs @@ -25,15 +25,16 @@ public class ShotEventArgs : IPlayerEvent, IFirearmEvent /// Raycast hit info. /// The firearm used. /// The IDestructible that was hit. Can be null. - public ShotEventArgs(HitscanHitregModuleBase hitregModule, RaycastHit hitInfo, InventorySystem.Items.Firearms.Firearm firearm, IDestructible destructible) + /// + public ShotEventArgs(HitscanHitregModuleBase hitregModule, RaycastHit hitInfo, InventorySystem.Items.Firearms.Firearm firearm, IDestructible destructible, float damage) { HitregModule = hitregModule; RaycastHit = hitInfo; Destructible = destructible; Firearm = Item.Get(firearm); + Damage = damage; Player = Firearm.Owner; - Damage = Destructible is not null ? HitregModule.DamageAtDistance(hitInfo.distance) : 0f; if (Destructible is HitboxIdentity hitboxIdentity) { diff --git a/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs b/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs index edc296bf1c..522fbb1c97 100644 --- a/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs +++ b/EXILED/Exiled.Events/Patches/Events/Player/Shot.cs @@ -7,6 +7,8 @@ namespace Exiled.Events.Patches.Events.Player { +#pragma warning disable SA1402 +#pragma warning disable SA1649 using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; @@ -22,92 +24,123 @@ namespace Exiled.Events.Patches.Events.Player using static HarmonyLib.AccessTools; /// - /// Patches . + /// Patches . /// Adds the event. /// [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.Shot))] - [HarmonyPatch(typeof(HitscanHitregModuleBase), nameof(HitscanHitregModuleBase.ServerAppendPrescan))] - internal static class Shot + [HarmonyPatch(typeof(HitscanHitregModuleBase), nameof(HitscanHitregModuleBase.ServerApplyDestructibleDamage))] + internal static class ShotTarget { - private static void ProcessRaycastMiss(HitscanHitregModuleBase hitregModule, Ray ray, float maxDistance) - { - RaycastHit hit = new() - { - distance = maxDistance, - point = ray.GetPoint(maxDistance), - normal = -ray.direction, - }; - - ShotEventArgs ev = new(hitregModule, hit, hitregModule.Firearm, null); - Handlers.Player.OnShot(ev); - } - private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) { List newInstructions = ListPool.Pool.Get(instructions); - MethodInfo raycastMethod = Method(typeof(Physics), nameof(Physics.Raycast), new[] { typeof(Ray), typeof(RaycastHit).MakeByRefType(), typeof(float), typeof(int) }); - int raycastFailIndex = newInstructions.FindIndex(i => i.Calls(raycastMethod)) + 2; - - newInstructions.InsertRange( - raycastFailIndex, - new CodeInstruction[] - { - // ProcessRaycastMiss(this, targetRay, maxDistance); - new(OpCodes.Ldarg_0), - new(OpCodes.Ldarg_1), - new(OpCodes.Ldloc_0), - new(OpCodes.Call, Method(typeof(Shot), nameof(ProcessRaycastMiss))), - }); - - int destructibleGetIndex = newInstructions.FindIndex(i => i.operand is MethodInfo { Name: nameof(Component.TryGetComponent) }) + 1; + int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Ldloc_2); Label continueLabel = generator.DefineLabel(); LocalBuilder ev = generator.DeclareLocal(typeof(ShotEventArgs)); newInstructions.InsertRange( - destructibleGetIndex, + index, new[] { - // var ev = new ShotEventArgs(this, hitInfo, firearm, component); - new(OpCodes.Ldarg_0), // this - new(OpCodes.Ldloc_1), // hitInfo - new(OpCodes.Ldarg_0), // this.Firearm + // this + new(OpCodes.Ldarg_0), + + // target.Raycast.Hit + new(OpCodes.Ldarg_1), + new(OpCodes.Ldfld, Field(typeof(DestructibleHitPair), nameof(DestructibleHitPair.Raycast))), + new(OpCodes.Ldfld, Field(typeof(HitRayPair), nameof(HitRayPair.Hit))), + + // this.Firearm + new(OpCodes.Ldarg_0), new(OpCodes.Callvirt, PropertyGetter(typeof(HitscanHitregModuleBase), nameof(HitscanHitregModuleBase.Firearm))), - new(OpCodes.Ldloc_2), // component + + // destructible + new(OpCodes.Ldloc_2), + + // damage + new(OpCodes.Ldloc_0), + + // ShotEventArgs ev = new ShotEventArgs(this, hitInfo, firearm, component, damage); new(OpCodes.Newobj, GetDeclaredConstructors(typeof(ShotEventArgs))[0]), - new(OpCodes.Dup), // Leave ShotEventArgs on the stack + new(OpCodes.Dup), + new(OpCodes.Dup), new(OpCodes.Stloc_S, ev.LocalIndex), - new(OpCodes.Dup), // Leave ShotEventArgs on the stack new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnShot))), - // if (!ev.CanHurt) hitInfo.distance = maxDistance; + // if (!ev.CanHurt) num = 0; new(OpCodes.Callvirt, PropertyGetter(typeof(ShotEventArgs), nameof(ShotEventArgs.CanHurt))), new(OpCodes.Brtrue, continueLabel), - new(OpCodes.Ldloca_S, 1), // hitInfo address - new(OpCodes.Ldloc_0), // maxDistance - new(OpCodes.Call, PropertySetter(typeof(RaycastHit), nameof(RaycastHit.distance))), // hitInfo.distance = maxDistance + new(OpCodes.Ldc_I4_0), + new(OpCodes.Stloc_0), new CodeInstruction(OpCodes.Nop).WithLabels(continueLabel), }); - int impactEffectsIndex = newInstructions.FindIndex(i => i.LoadsField(Field(typeof(HitscanResult), nameof(HitscanResult.Obstacles)))) - 1; + index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Ldarg_0); Label returnLabel = generator.DefineLabel(); newInstructions.InsertRange( - impactEffectsIndex, - new[] + index, + new CodeInstruction[] { // if (!ev.CanSpawnImpactEffects) return; - new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex).MoveLabelsFrom(newInstructions[impactEffectsIndex]), + new(OpCodes.Ldloc_S, ev.LocalIndex), new(OpCodes.Callvirt, PropertyGetter(typeof(ShotEventArgs), nameof(ShotEventArgs.CanSpawnImpactEffects))), - new(OpCodes.Brfalse, returnLabel), + new(OpCodes.Brfalse_S, returnLabel), }); - newInstructions[newInstructions.Count - 1].WithLabels(returnLabel); + newInstructions[newInstructions.Count - 1].labels.Add(returnLabel); + + for (int z = 0; z < newInstructions.Count; z++) + yield return newInstructions[z]; + + ListPool.Pool.Return(newInstructions); + } + } + + + /// + /// Patches . + /// Adds the event. + /// + [HarmonyPatch(typeof(HitscanHitregModuleBase), nameof(HitscanHitregModuleBase.ServerAppendPrescan))] + internal static class ShotMiss + { + private static IEnumerable Transpiler(IEnumerable instructions) + { + List newInstructions = ListPool.Pool.Get(instructions); + + int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Brtrue_S) + 1; + + newInstructions.InsertRange(index, new CodeInstruction[] + { + // this + new(OpCodes.Ldarg_0), + + // hitInfo + new(OpCodes.Ldloc_1), + + // this.Firearm + new(OpCodes.Ldarg_0), + new(OpCodes.Callvirt, PropertyGetter(typeof(HitscanHitregModuleBase), nameof(HitscanHitregModuleBase.Firearm))), + + // (IDestructible)null + new(OpCodes.Ldnull), + + // 0f + new(OpCodes.Ldc_R4, 0f), + + // ShotEventArgs = new(this, hitInfo, this.Firearm, (IDestructible)null, 0f) + new(OpCodes.Newobj, GetDeclaredConstructors(typeof(ShotEventArgs))[0]), + + // Handlers.Player.OnShot(ev); + new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnShot))), + }); for (int z = 0; z < newInstructions.Count; z++) yield return newInstructions[z];