Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow ID_Dedicated/null for Npc.UserId Parameter. Additional functionality: - Npc.LookAt - Npc.ShootWeapon - Npc.SetAimDownSight - Player.UnloadWeapon - Player.ToggleWeaponFlashlight #2297

Merged
merged 16 commits into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 116 additions & 8 deletions Exiled.API/Features/Npc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,25 @@ namespace Exiled.API.Features
using System.Collections.Generic;
using System.Linq;

using CentralAuth;
using CommandSystem;

using Exiled.API.Enums;
using Exiled.API.Extensions;
using Exiled.API.Features.Components;

using Exiled.API.Features.Roles;
using Footprinting;

using InventorySystem.Items.Firearms.BasicMessages;
using InventorySystem.Items.Firearms.Modules;
using MEC;

using Mirror;

using PlayerRoles;

using PlayerRoles.FirstPersonControl;
using RelativePositioning;
using UnityEngine;

using Firearm = Items.Firearm;
using Object = UnityEngine.Object;

/// <summary>
Expand Down Expand Up @@ -128,15 +131,15 @@ public Npc(GameObject gameObject)
/// <param name="name">The name of the NPC.</param>
/// <param name="role">The RoleTypeId of the NPC.</param>
/// <param name="id">The player ID of the NPC.</param>
/// <param name="userId">The userID of the NPC.</param>
/// <param name="userId">The userID of the NPC. Use "ID_Dedicated" or <c>null</c> for VSR Compliant NPCs.</param>
/// <param name="position">The position to spawn the NPC.</param>
/// <returns>The <see cref="Npc"/> spawned.</returns>
public static Npc Spawn(string name, RoleTypeId role, int id = 0, string userId = "", Vector3? position = null)
{
GameObject newObject = Object.Instantiate(NetworkManager.singleton.playerPrefab);
Npc npc = new(newObject)
{
IsVerified = true,
IsVerified = userId != PlayerAuthenticationManager.DedicatedId && userId != null,
IsNPC = true,
};
try
Expand All @@ -161,7 +164,22 @@ public static Npc Spawn(string name, RoleTypeId role, int id = 0, string userId
NetworkServer.AddPlayerForConnection(fakeConnection, newObject);
try
{
npc.ReferenceHub.authManager.UserId = string.IsNullOrEmpty(userId) ? $"Dummy@localhost" : userId;
if (userId == PlayerAuthenticationManager.DedicatedId)
{
npc.ReferenceHub.authManager.SyncedUserId = userId;
try
{
npc.ReferenceHub.authManager.InstanceMode = ClientInstanceMode.DedicatedServer;
}
catch (Exception e)
{
Log.Debug($"Ignore: {e}");
}
}
else
{
npc.ReferenceHub.authManager.UserId = userId == string.Empty ? $"Dummy@localhost" : userId;
}
}
catch (Exception e)
{
Expand Down Expand Up @@ -196,5 +214,95 @@ public void Destroy()
Dictionary.Remove(GameObject);
Object.Destroy(GameObject);
}

/// <summary>
/// Forces the NPC to look in the specified rotation.
/// </summary>
/// <param name="position">The position to look at.</param>
public void LookAt(Vector3 position)
{
if (Role is FpcRole fpc)
{
Vector3 direction = position - Position;
Quaternion quat = Quaternion.LookRotation(direction, Vector3.up);
LookAt(quat);
}
}

/// <summary>
/// Forces the NPC to look in the specified rotation.
/// </summary>
/// <param name="rotation">The rotation to look towards.</param>
public void LookAt(Quaternion rotation)
{
if (Role is not FpcRole fpc)
return;

if (rotation.eulerAngles.z != 0f)
rotation = Quaternion.LookRotation(rotation * Vector3.forward, Vector3.up);

Vector2 angles = new Vector2(-rotation.eulerAngles.x, rotation.eulerAngles.y);

ushort hor = (ushort)Mathf.RoundToInt(Mathf.Repeat(angles.y, 360f) * (ushort.MaxValue / 360f));
ushort vert = (ushort)Mathf.RoundToInt(Mathf.Clamp(Mathf.Repeat(angles.x + 90f, 360f) - 2f, 0f, 176f) * (ushort.MaxValue / 176f));

fpc.FirstPersonController.FpcModule.MouseLook.ApplySyncValues(hor, vert);
}

/// <summary>
/// Forces the NPC to shoot their current <see cref="Firearm"></see>.
/// </summary>
/// <returns><see langword="true"/> if the weapon shot request is received. Returns <see langword="false"/> otherwise, or if the player is not an <see cref="IFpcRole"/> or is not holding a <see cref="Firearm"/>.</returns>
public bool ShootWeapon()
{
if (CurrentItem is not Firearm firearm)
return false;

if (!firearm.Base.ActionModule.ServerAuthorizeShot())
return false;

ShotMessage message = new ShotMessage()
{
ShooterCameraRotation = CameraTransform.rotation,
ShooterPosition = new RelativePosition(Transform.position),
ShooterWeaponSerial = CurrentItem.Serial,
TargetNetId = 0,
TargetPosition = default,
TargetRotation = Quaternion.identity,
};

Physics.Raycast(CameraTransform.position, CameraTransform.forward, out RaycastHit hit, firearm.Base.BaseStats.MaxDistance(), StandardHitregBase.HitregMask);

if (hit.transform && hit.collider.TryGetComponent(out IDestructible destructible) && destructible != null)
{
message.TargetNetId = destructible.NetworkId;
message.TargetPosition = new RelativePosition(hit.transform.position);
message.TargetRotation = hit.transform.rotation;
}
else if (hit.transform)
{
message.TargetPosition = new RelativePosition(hit.transform.position);
message.TargetRotation = hit.transform.rotation;
}

FirearmBasicMessagesHandler.ServerShotReceived(ReferenceHub.connectionToClient, message);
return true;
}

/// <summary>
/// Sets the NPC's current <see cref="Firearm"></see> status for Aiming Down Sights.
/// </summary>
/// <param name="shouldADS">Should the player be aiming down sights.</param>
/// <returns><see langword="true"/> if the weapon Aim Down Sights request is received. Returns <see langword="false"/> otherwise, or if the player is not an <see cref="IFpcRole"/> or is not holding a <see cref="Firearm"/>.</returns>
public bool SetAimDownSight(bool shouldADS)
{
if (CurrentItem is Firearm firearm)
{
FirearmBasicMessagesHandler.ServerRequestReceived(ReferenceHub.connectionToClient, new RequestMessage(firearm.Serial, shouldADS ? RequestType.AdsIn : RequestType.AdsOut));
return true;
}

return false;
}
}
}
}
36 changes: 34 additions & 2 deletions Exiled.API/Features/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1733,21 +1733,53 @@ public void TrySetCustomRoleFriendlyFire(string roleTypeId, Dictionary<RoleTypeI
public bool TryRemoveCustomeRoleFriendlyFire(string role) => CustomRoleFriendlyFireMultiplier.Remove(role);

/// <summary>
/// Forces the player to reload their current weapon.
/// Forces the player to reload their current <see cref="Firearm"></see>.
/// </summary>
/// <returns><see langword="true"/> if firearm was successfully reloaded. Otherwise, <see langword="false"/>.</returns>
public bool ReloadWeapon()
{
if (CurrentItem is Firearm firearm)
{
bool result = firearm.Base.AmmoManagerModule.ServerTryReload();
Connection.Send(new RequestMessage(firearm.Serial, RequestType.Reload));
if (result)
Connection.Send(new RequestMessage(firearm.Serial, RequestType.Reload));
return result;
}

return false;
}

/// <summary>
/// Forces the player to unload their current <see cref="Firearm"></see>.
/// </summary>
/// <returns><see langword="true"/> if the weapon unload request is received. Returns <see langword="false"/> otherwise, or if the player is not an <see cref="IFpcRole"/> or is not holding a <see cref="Firearm"/>.</returns>
public bool UnloadWeapon()
{
if (CurrentItem is Firearm firearm)
{
bool result = firearm.Base.AmmoManagerModule.ServerTryUnload();
if (result)
Connection.Send(new RequestMessage(firearm.Serial, RequestType.Unload));
return result;
}

return true;
}

/// <summary>
/// Forces the player to toggle the Flashlight Attachment on their current <see cref="Firearm"></see>.
/// </summary>
/// <returns><see langword="true"/> if the weapon flashlight toggle request is received. Returns <see langword="false"/> otherwise, or if the player is not an <see cref="IFpcRole"/> or is not holding a <see cref="Firearm"/>.</returns>
public bool ToggleWeaponFlashlight()
{
if (RoleManager.CurrentRole is not IFpcRole fpc || CurrentItem is not Firearm firearm)
return false;

bool oldCheck = firearm.FlashlightEnabled; // Temporary Solution
FirearmBasicMessagesHandler.ServerRequestReceived(ReferenceHub.connectionToClient, new RequestMessage(firearm.Serial, RequestType.ToggleFlashlight));
return oldCheck != firearm.FlashlightEnabled;
}

/// <summary>
/// Tries to get an item from a player's inventory.
/// </summary>
Expand Down
Loading