diff --git a/EXILED/Exiled.API/Features/Items/Firearm.cs b/EXILED/Exiled.API/Features/Items/Firearm.cs index 56215591a..ad49c4980 100644 --- a/EXILED/Exiled.API/Features/Items/Firearm.cs +++ b/EXILED/Exiled.API/Features/Items/Firearm.cs @@ -7,7 +7,6 @@ namespace Exiled.API.Features.Items { - using System; using System.Collections.Generic; using System.Linq; diff --git a/EXILED/Exiled.API/Features/Items/Item.cs b/EXILED/Exiled.API/Features/Items/Item.cs index 89bb189c4..988859049 100644 --- a/EXILED/Exiled.API/Features/Items/Item.cs +++ b/EXILED/Exiled.API/Features/Items/Item.cs @@ -34,6 +34,7 @@ namespace Exiled.API.Features.Items using InventorySystem.Items.Usables.Scp1576; using InventorySystem.Items.Usables.Scp244; using InventorySystem.Items.Usables.Scp330; + using NetworkManagerUtils.Dummies; using UnityEngine; using BaseConsumable = InventorySystem.Items.Usables.Consumable; @@ -190,6 +191,23 @@ public ushort Serial /// public Player Owner => Player.Get(Base.Owner) ?? Server.Host; + /// + /// Gets the emulator for dummy actions if this item inherits . + /// + /// Returns null if this item does not inherit . + public DummyKeyEmulator DummyEmulator + { + get + { + if (Base is AutosyncItem item) + { + return item.DummyEmulator; + } + + return null; + } + } + /// /// Gets or sets a reason for adding this item to the inventory. /// diff --git a/EXILED/Exiled.API/Features/Npc.cs b/EXILED/Exiled.API/Features/Npc.cs index 779121804..444da0ab5 100644 --- a/EXILED/Exiled.API/Features/Npc.cs +++ b/EXILED/Exiled.API/Features/Npc.cs @@ -16,13 +16,20 @@ namespace Exiled.API.Features using CommandSystem.Commands.RemoteAdmin.Dummies; using Exiled.API.Enums; using Exiled.API.Features.CustomStats; + using Exiled.API.Features.Items; using Exiled.API.Features.Roles; using Footprinting; + using InventorySystem; + using InventorySystem.Items.Usables; + using InventorySystem.Items.Usables.Scp330; using MEC; using Mirror; using NetworkManagerUtils.Dummies; using PlayerRoles; + using PlayerRoles.FirstPersonControl; + using PlayerRoles.Subroutines; using PlayerStatsSystem; + using RelativePositioning; using UnityEngine; /// @@ -355,5 +362,309 @@ public void LateDestroy(float time) this?.Destroy(); }); } + + /// + /// Moves this Npc by a direction relative to where they are looking. + /// + /// Direction where Npc should move relative to where they are looking. + /// The distance that the Npc should move by. + /// True if successful. + public bool TryMoveRelative(Vector3 dir, float distance) + { + if (Role is not FpcRole) + return false; + + Vector3 vector = CameraTransform.TransformDirection(dir).NormalizeIgnoreY(); + Position += vector * distance; + return true; + } + + /// + /// Makes the Npc look more left or more right. + /// + /// Amount that will be added to horizontal (in degrees). + /// True if successful. + public bool TryAddHorizontalLook(float amount) + { + if (Role is not FpcRole fpcRole) + return false; + + fpcRole.FirstPersonController.FpcModule.MouseLook.CurrentHorizontal += amount; + return true; + } + + /// + /// Makes the Npc look more up or more down. + /// + /// Amount that will be added to vertical (in degrees). + /// True if successful. + public bool TryAddVerticalLook(float amount) + { + if (Role is not FpcRole fpcRole) + return false; + + fpcRole.FirstPersonController.FpcModule.MouseLook.CurrentVertical += amount; + return true; + } + + /// + /// Forces Npc to look at certain point. + /// + /// Position to look at. + /// The amount in percentage how much to look at the position, 1 is full and will immediately look at the point. + /// True if successful. + public bool TryLookAtPoint(Vector3 position, float lerp = 1) + { + if (Role is not FpcRole fpcRole) + return false; + + fpcRole.FirstPersonController.LookAtPoint(position, lerp); + return true; + } + + /// + /// Forces Npc to look at a certain direction. + /// + /// The direction to look at. + /// The amount in percentage how much to look at the direction, 1 is full and will immediately look at the target direction. + /// True if successful. + public bool TryLookAtDirection(Vector3 dir, float lerp = 1) + { + if (Role is not FpcRole fpcRole) + return false; + + fpcRole.FirstPersonController.LookAtDirection(dir, lerp); + return true; + } + + /// + /// Makes the Npc jump by amount of strength. + /// + /// The strength used to jump. Null will choose the default one. + /// True if successful. + public bool Jump(float? jumpStrength = null) + { + if (Role is not FpcRole fpcRole) + { + return false; + } + + fpcRole.Jump(jumpStrength); + return true; + } + + /// + /// Makes the Npc eat certain kind of candy. + /// + /// The kind of candy to eat. + /// True if successful. + public bool EatCandy(CandyKindID candyKind) + { + foreach(Item? item in Items) + { + if (item is not Scp330 scp330) + { + continue; + } + + return EatCandy(scp330, candyKind); + } + + return false; + } + + /// + /// Makes the Npc eat certain kind of candy. + /// + /// The bag. + /// The kind of candy to eat. + /// True if successful. + public bool EatCandy(Scp330 from, CandyKindID candyKind) + { + for (int i = 0; i < from.Candies.Count; i++) + { + if (from.Base.Candies[i] == candyKind) + { + from.Base.ServerSelectCandy(i); + return true; + } + } + + return false; + } + + /// + /// Gets actions that can be done by the Dummy. + /// + /// . + public IReadOnlyList GetActions() + { + return DummyActionCollector.ServerGetActions(ReferenceHub); + } + + /// + /// Equips Item. + /// . + /// + /// The to equip. + public void EquipItem(Item item) + { + Inventory?.ServerSelectItem(item.Serial); + } + + /// + /// Equips nothing. + /// . + /// + public void HolsterItem() + { + Inventory?.ServerSelectItem(0); + } + + /// + /// Forces the Npc to stop using item. + /// + /// The to cancel usage of. + public void CancelUseItem(Item item) + { + UsableItemsController.ServerEmulateMessage(item.Serial, StatusMessage.StatusType.Cancel); + } + + /// + /// Forces the Npc to shoot. + /// + /// Specifies if the shooting is to be held. + /// True if successful. + public bool Shoot(bool hold) => + RunItemAction(CurrentItem, ActionName.Shoot, hold); + + /// + /// Forces the Npc to reload. + /// + /// Specifies if the reloading is to be held. + /// True if successful. + public bool Reload(bool hold) => + RunItemAction(CurrentItem, ActionName.Reload, hold); + + /// + /// Forces the Npc to zoom. + /// + /// Specifies if the zooming is to be held. + /// True if successful. + public bool Zoom(bool hold) => + RunItemAction(CurrentItem, ActionName.Zoom, hold); + + /// + /// Forces the Npc to run generic action with item. + /// + /// The to run action for. + /// The name of action to force. + /// Specifies if the action is to be held. + /// True if successful. + public bool RunItemAction(Item item, ActionName name, bool hold = true) => + RunAction(item?.DummyEmulator, name, hold); + + /// + /// Forces the Npc to stop generic action with item. + /// + /// The to stop action for. + /// The name of action to stop. + /// True if successful. + public bool StopItemAction(Item item, ActionName name) => + StopAction(item?.DummyEmulator, name); + + /// + /// Checks if certain action is currently active. + /// + /// The to check action for. + /// The name of action to check. + /// True if action is actively executed and emulator is not null. + public bool IsBeingDone(Item item, ActionName name) => + IsBeingDone(item?.DummyEmulator, name); + + /// + /// Forces the Npc to run generic action with subroutine. + /// + /// The to run action for. + /// The name of action to force. + /// Specifies if the action is to be held. + /// . + /// True if successful. + public bool RunSubroutineAction(T subroutine, ActionName name, bool hold = true) + where T : SubroutineBase => + RunAction(subroutine?.DummyEmulator, name, hold); + + /// + /// Forces the Npc to stop generic action with subroutine. + /// + /// The to stop action for. + /// The name of action to stop. + /// . + /// True if successful. + public bool StopSubroutineAction(T subroutine, ActionName name) + where T : SubroutineBase => + StopAction(subroutine?.DummyEmulator, name); + + /// + /// Checks if certain action is currently active. + /// + /// The to check action for. + /// The name of action to check. + /// . + /// True if action is actively executed and emulator is not null. + public bool IsBeingDone(T subroutine, ActionName name) + where T : SubroutineBase => + IsBeingDone(subroutine?.DummyEmulator, name); + + /// + /// Forces the Npc to run generic action with emulator. + /// + /// The to run action for. + /// The name of action to force. + /// Specifies if the action is to be held. + /// True if successful. + public bool RunAction(DummyKeyEmulator? emulator, ActionName name, bool hold) + { + if (emulator == null) + { + return false; + } + + emulator.AddEntry(name, !hold); + return true; + } + + /// + /// Forces the Npc to stop generic action with emulator. + /// + /// The to stop action for. + /// The name of action to stop. + /// True if successful. + public bool StopAction(DummyKeyEmulator? emulator, ActionName name) + { + if (emulator == null) + { + return false; + } + + emulator.RemoveEntry(name); + return true; + } + + /// + /// Checks if certain action is currently active. + /// + /// The to check action for. + /// The name of action to check. + /// True if action is actively executed and emulator is not null. + public bool IsBeingDone(DummyKeyEmulator? emulator, ActionName name) + { + if (emulator == null) + { + return false; + } + + return emulator.GetAction(name, false); + } } }