mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-20 01:46:15 +00:00
Implement custom murders & extension method
This commit is contained in:
1 parent
9fc5044085
commit
7af85d9fbb
4 files changed
+271
-50
No files matched your search
@@ -0,0 +1,186 @@
|
||||
using AmongUs.GameOptions;
|
||||
using Assets.CoreScripts;
|
||||
using Reactor.Networking.Attributes;
|
||||
using Reactor.Utilities;
|
||||
using Reactor.Utilities.Extensions;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Networking;
|
||||
public static class CustomMurderRpcs
|
||||
{
|
||||
[MethodRpc((uint)MiraRpc.CustomMurder)]
|
||||
public static void RpcCustomMurder(this PlayerControl source, PlayerControl target, bool didSucceed = true,
|
||||
bool resetKillTimer = true, bool createDeadBody = true, bool teleportMurderer = true, bool showKillAnim = true, bool playKillSound = true)
|
||||
{
|
||||
MurderResultFlags murderResultFlags = didSucceed ? MurderResultFlags.Succeeded : MurderResultFlags.FailedError;
|
||||
MurderResultFlags murderResultFlags2 = MurderResultFlags.DecisionByHost | murderResultFlags;
|
||||
|
||||
source.CustomMurder(target, murderResultFlags2, resetKillTimer, createDeadBody, teleportMurderer, showKillAnim, playKillSound);
|
||||
}
|
||||
|
||||
public static void CustomMurder(this PlayerControl source, PlayerControl target, MurderResultFlags resultFlags,
|
||||
bool resetKillTimer = true, bool createDeadBody = true, bool teleportMurderer = true, bool showKillAnim = true, bool playKillSound = true)
|
||||
{
|
||||
source.isKilling = false;
|
||||
source.logger.Debug(string.Format("{0} trying to murder {1}", source.PlayerId, target.PlayerId), null);
|
||||
NetworkedPlayerInfo data = target.Data;
|
||||
if (resultFlags.HasFlag(MurderResultFlags.FailedError))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (resultFlags.HasFlag(MurderResultFlags.FailedProtected) || (resultFlags.HasFlag(MurderResultFlags.DecisionByHost) && target.protectedByGuardianId > -1))
|
||||
{
|
||||
target.protectedByGuardianThisRound = true;
|
||||
bool flag = PlayerControl.LocalPlayer.Data.Role.Role == RoleTypes.GuardianAngel;
|
||||
if (flag && PlayerControl.LocalPlayer.Data.PlayerId == target.protectedByGuardianId)
|
||||
{
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsGuardianAngelCrewmatesProtected);
|
||||
DestroyableSingleton<AchievementManager>.Instance.OnProtectACrewmate();
|
||||
}
|
||||
if (source.AmOwner || flag)
|
||||
{
|
||||
target.ShowFailedMurder();
|
||||
|
||||
if (resetKillTimer)
|
||||
{
|
||||
source.SetKillTimer(GameOptionsManager.Instance.CurrentGameOptions.GetFloat(FloatOptionNames.KillCooldown) / 2f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target.RemoveProtection();
|
||||
}
|
||||
source.logger.Debug(string.Format("{0} failed to murder {1} due to guardian angel protection", source.PlayerId, target.PlayerId), null);
|
||||
return;
|
||||
}
|
||||
if (resultFlags.HasFlag(MurderResultFlags.Succeeded) || resultFlags.HasFlag(MurderResultFlags.DecisionByHost))
|
||||
{
|
||||
DestroyableSingleton<DebugAnalytics>.Instance.Analytics.Kill(target.Data, source.Data);
|
||||
if (source.AmOwner)
|
||||
{
|
||||
if (GameManager.Instance.IsHideAndSeek())
|
||||
{
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsImpostorKills_HideAndSeek);
|
||||
}
|
||||
else
|
||||
{
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsImpostorKills);
|
||||
}
|
||||
if (source.CurrentOutfitType == PlayerOutfitType.Shapeshifted)
|
||||
{
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsShapeshifterShiftedKills);
|
||||
}
|
||||
if (Constants.ShouldPlaySfx() && playKillSound)
|
||||
{
|
||||
SoundManager.Instance.PlaySound(source.KillSfx, false, 0.8f, null);
|
||||
}
|
||||
|
||||
if (resetKillTimer)
|
||||
{
|
||||
source.SetKillTimer(GameOptionsManager.Instance.CurrentGameOptions.GetFloat(FloatOptionNames.KillCooldown));
|
||||
}
|
||||
}
|
||||
DestroyableSingleton<UnityTelemetry>.Instance.WriteMurder();
|
||||
target.gameObject.layer = LayerMask.NameToLayer("Ghost");
|
||||
if (target.AmOwner)
|
||||
{
|
||||
StatsManager.Instance.IncrementStat(StringNames.StatsTimesMurdered);
|
||||
if (Minigame.Instance)
|
||||
{
|
||||
try
|
||||
{
|
||||
Minigame.Instance.Close();
|
||||
Minigame.Instance.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
if (showKillAnim)
|
||||
{
|
||||
DestroyableSingleton<HudManager>.Instance.KillOverlay.ShowKillAnimation(source.Data, data);
|
||||
}
|
||||
|
||||
target.cosmetics.SetNameMask(false);
|
||||
target.RpcSetScanner(false);
|
||||
}
|
||||
DestroyableSingleton<AchievementManager>.Instance.OnMurder(source.AmOwner, target.AmOwner, source.CurrentOutfitType == PlayerOutfitType.Shapeshifted, source.shapeshiftTargetPlayerId, (int)target.PlayerId);
|
||||
Coroutines.Start(source.KillAnimations.Random().CoPerformCustomKill(source, target, createDeadBody, teleportMurderer));
|
||||
source.logger.Debug(string.Format("{0} succeeded in murdering {1}", source.PlayerId, target.PlayerId), null);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerator CoPerformCustomKill(this KillAnimation anim, PlayerControl source, PlayerControl target,
|
||||
bool createDeadBody = true, bool teleportMurderer = true)
|
||||
{
|
||||
FollowerCamera cam = Camera.main.GetComponent<FollowerCamera>();
|
||||
bool isParticipant = PlayerControl.LocalPlayer == source || PlayerControl.LocalPlayer == target;
|
||||
PlayerPhysics sourcePhys = source.MyPhysics;
|
||||
|
||||
if (teleportMurderer)
|
||||
{
|
||||
KillAnimation.SetMovement(source, false);
|
||||
}
|
||||
|
||||
KillAnimation.SetMovement(target, false);
|
||||
|
||||
if (isParticipant)
|
||||
{
|
||||
PlayerControl.LocalPlayer.isKilling = true;
|
||||
source.isKilling = true;
|
||||
}
|
||||
|
||||
DeadBody deadBody = null;
|
||||
|
||||
if (createDeadBody)
|
||||
{
|
||||
deadBody = Object.Instantiate(GameManager.Instance.DeadBodyPrefab);
|
||||
deadBody.enabled = false;
|
||||
deadBody.ParentId = target.PlayerId;
|
||||
deadBody.bodyRenderers.ToArray().ToList().ForEach(target.SetPlayerMaterialColors);
|
||||
|
||||
target.SetPlayerMaterialColors(deadBody.bloodSplatter);
|
||||
Vector3 vector = target.transform.position + anim.BodyOffset;
|
||||
vector.z = vector.y / 1000f;
|
||||
deadBody.transform.position = vector;
|
||||
}
|
||||
|
||||
if (isParticipant)
|
||||
{
|
||||
cam.Locked = true;
|
||||
ConsoleJoystick.SetMode_Task();
|
||||
if (PlayerControl.LocalPlayer.AmOwner)
|
||||
{
|
||||
PlayerControl.LocalPlayer.MyPhysics.inputHandler.enabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
target.Die(DeathReason.Kill, true);
|
||||
yield return source.MyPhysics.Animations.CoPlayCustomAnimation(anim.BlurAnim);
|
||||
sourcePhys.Animations.PlayIdleAnimation();
|
||||
|
||||
if (teleportMurderer)
|
||||
{
|
||||
source.NetTransform.SnapTo(target.transform.position);
|
||||
KillAnimation.SetMovement(source, true);
|
||||
}
|
||||
KillAnimation.SetMovement(target, true);
|
||||
|
||||
if (deadBody)
|
||||
{
|
||||
deadBody.enabled = true;
|
||||
}
|
||||
|
||||
if (isParticipant)
|
||||
{
|
||||
cam.Locked = false;
|
||||
PlayerControl.LocalPlayer.isKilling = false;
|
||||
source.isKilling = false;
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,6 @@ public enum MiraRpc : uint
|
||||
SyncRoleOptions,
|
||||
AddModifier,
|
||||
RemoveModifier,
|
||||
SyncModifiers
|
||||
SyncModifiers,
|
||||
CustomMurder,
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.Modifiers;
|
||||
using Reactor.Utilities;
|
||||
using System.Linq;
|
||||
using MiraAPI.Networking;
|
||||
using Reactor.Networking.Attributes;
|
||||
using Reactor.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.Utilities;
|
||||
@@ -17,47 +17,47 @@ public static class Extensions
|
||||
{
|
||||
return type is { IsClass: true, IsAbstract: true, IsSealed: true };
|
||||
}
|
||||
|
||||
|
||||
public static Color32 GetShadowColor(this Color32 color, byte darknessAmount)
|
||||
{
|
||||
return
|
||||
new Color32((byte)Mathf.Clamp(color.r - darknessAmount, 0, 255), (byte)Mathf.Clamp(color.g - darknessAmount, 0, 255),
|
||||
(byte)Mathf.Clamp(color.b - darknessAmount, 0, 255), byte.MaxValue);
|
||||
}
|
||||
|
||||
|
||||
public static string? Truncate(this string? value, int maxLength, string truncationSuffix = "…")
|
||||
{
|
||||
return value?.Length > maxLength
|
||||
? value[..maxLength] + truncationSuffix
|
||||
: value;
|
||||
}
|
||||
|
||||
|
||||
public static Queue<NetData[]> ChunkNetData(this IEnumerable<NetData> dataCollection, int chunkSize)
|
||||
{
|
||||
Queue<NetData[]> chunks = [];
|
||||
List<NetData> current = [];
|
||||
|
||||
|
||||
var count = 0;
|
||||
foreach (var netData in dataCollection)
|
||||
{
|
||||
var length = netData.GetLength();
|
||||
|
||||
|
||||
if (length > chunkSize)
|
||||
{
|
||||
Logger<MiraApiPlugin>.Error($"NetData length is greater than chunk size: {length} > {chunkSize}");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (count + length > chunkSize)
|
||||
{
|
||||
chunks.Enqueue(current.ToArray());
|
||||
current.Clear();
|
||||
count = 0;
|
||||
}
|
||||
|
||||
|
||||
current.Add(netData);
|
||||
}
|
||||
|
||||
|
||||
if (current.Count > 0)
|
||||
{
|
||||
chunks.Enqueue(current.ToArray());
|
||||
@@ -65,22 +65,22 @@ public static class Extensions
|
||||
|
||||
return chunks;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static bool IsCustom(this OptionBehaviour optionBehaviour)
|
||||
{
|
||||
return ModdedOptionsManager.ModdedOptions.Values.Any(opt => opt.OptionBehaviour && opt.OptionBehaviour.Equals(optionBehaviour));
|
||||
}
|
||||
|
||||
public static readonly Dictionary<PlayerControl, ModifierComponent> ModifierComponents = new();
|
||||
|
||||
|
||||
public static ModifierComponent? GetModifierComponent(this PlayerControl player)
|
||||
{
|
||||
if (ModifierComponents.TryGetValue(player, out var component))
|
||||
{
|
||||
return component;
|
||||
}
|
||||
|
||||
|
||||
component = player.GetComponent<ModifierComponent>();
|
||||
if (component)
|
||||
{
|
||||
@@ -102,17 +102,17 @@ public static class Extensions
|
||||
}
|
||||
return randomizedList;
|
||||
}
|
||||
|
||||
|
||||
public static T? GetModifier<T>(this PlayerControl? player) where T : BaseModifier
|
||||
{
|
||||
return player?.GetModifierComponent()?.ActiveModifiers.Find(x => x is T) as T;
|
||||
}
|
||||
|
||||
|
||||
public static bool HasModifier<T>(this PlayerControl? player) where T : BaseModifier
|
||||
{
|
||||
return player?.GetModifierComponent() != null && player.GetModifierComponent()!.ActiveModifiers.Exists(x => x is T);
|
||||
}
|
||||
|
||||
|
||||
public static bool HasModifier(this PlayerControl? player, uint id)
|
||||
{
|
||||
return player?.GetModifierComponent() != null && player.GetModifierComponent()!.ActiveModifiers.Exists(x => x.ModifierId == id);
|
||||
@@ -143,7 +143,7 @@ public static class Extensions
|
||||
Logger<MiraApiPlugin>.Error($"Cannot add modifier {typeof(T).Name} because it is not registered.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
player.RpcAddModifier(id);
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ public static class Extensions
|
||||
.FirstOrDefault(component => component && !component.Reported);
|
||||
}
|
||||
|
||||
public static T? GetNearestObjectOfType<T>(this PlayerControl playerControl, float radius, string? colliderTag=null, Func<T, bool>? predicate=null) where T : Component
|
||||
public static T? GetNearestObjectOfType<T>(this PlayerControl playerControl, float radius, string? colliderTag = null, Func<T, bool>? predicate = null) where T : Component
|
||||
{
|
||||
var results = new Il2CppSystem.Collections.Generic.List<Collider2D>();
|
||||
Physics2D.OverlapCircle(playerControl.GetTruePosition(), radius, Helpers.Filter, results);
|
||||
@@ -197,40 +197,17 @@ public static class Extensions
|
||||
.FirstOrDefault(predicate ?? (component => component));
|
||||
}
|
||||
|
||||
public static PlayerControl? GetClosestPlayer(this PlayerControl playerControl, bool includeImpostors, float distance)
|
||||
public static PlayerControl? GetClosestPlayer(this PlayerControl playerControl, bool includeImpostors, float distance, bool ignoreColliders = false)
|
||||
{
|
||||
PlayerControl? result = null;
|
||||
if (!ShipStatus.Instance)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var truePosition = playerControl.GetTruePosition();
|
||||
var filteredPlayers = Helpers.GetClosestPlayers(playerControl, distance, ignoreColliders)
|
||||
.Where(playerInfo => !playerInfo.Data.Disconnected && playerInfo.PlayerId != playerControl.PlayerId && !playerInfo.Data.IsDead &&
|
||||
(includeImpostors || !playerInfo.Data.Role.IsImpostor));
|
||||
|
||||
var filteredPlayers = GameData.Instance.AllPlayers.ToArray()
|
||||
.Where(playerInfo => !playerInfo.Disconnected && playerInfo.PlayerId != playerControl.PlayerId && !playerInfo.IsDead &&
|
||||
(includeImpostors || !playerInfo.Role.IsImpostor));
|
||||
|
||||
foreach (var playerInfo in filteredPlayers)
|
||||
{
|
||||
var @object = playerInfo.Object;
|
||||
if (!@object)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var vector = @object.GetTruePosition() - truePosition;
|
||||
var magnitude = vector.magnitude;
|
||||
if (!(magnitude <= distance) || PhysicsHelpers.AnyNonTriggersBetween(truePosition,
|
||||
vector.normalized,
|
||||
magnitude, LayerMask.GetMask("Ship", "Objects")))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result = @object;
|
||||
distance = magnitude;
|
||||
}
|
||||
return result;
|
||||
return filteredPlayers.First();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using MiraAPI.Roles;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using TMPro;
|
||||
@@ -14,6 +15,62 @@ public static class Helpers
|
||||
return ShipStatus.Instance.AllRooms.ToList().Find(room => room.roomArea.OverlapPoint(pos));
|
||||
}
|
||||
|
||||
public static List<PlayerControl> GetClosestPlayers(PlayerControl source, float distance = 2f, bool ignoreColliders = true, bool ignoreSource = true)
|
||||
{
|
||||
if (!ShipStatus.Instance)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Vector2 myPos = source.GetTruePosition();
|
||||
List<PlayerControl> players = GetClosestPlayers(myPos, distance, ignoreColliders);
|
||||
|
||||
return ignoreSource ? players.Where(plr => plr.PlayerId != source.PlayerId).ToList() : players;
|
||||
}
|
||||
|
||||
public static List<PlayerControl> GetClosestPlayers(Vector2 source, float distance = 2f, bool ignoreColliders = true)
|
||||
{
|
||||
if (!ShipStatus.Instance)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
List<PlayerControl> outputList = new List<PlayerControl>();
|
||||
outputList.Clear();
|
||||
List<NetworkedPlayerInfo> allPlayers = GameData.Instance.AllPlayers.ToArray().ToList();
|
||||
|
||||
for (int i = 0; i < allPlayers.Count; i++)
|
||||
{
|
||||
NetworkedPlayerInfo networkedPlayerInfo = allPlayers[i];
|
||||
|
||||
PlayerControl @object = networkedPlayerInfo.Object;
|
||||
if (@object && @object.Collider.enabled)
|
||||
{
|
||||
Vector2 vector = @object.GetTruePosition() - source;
|
||||
float magnitude = vector.magnitude;
|
||||
if (magnitude <= distance && (ignoreColliders || !PhysicsHelpers.AnyNonTriggersBetween(source, vector.normalized, magnitude, Constants.ShipAndObjectsMask)))
|
||||
{
|
||||
outputList.Add(@object);
|
||||
}
|
||||
}
|
||||
}
|
||||
outputList.Sort(delegate (PlayerControl a, PlayerControl b)
|
||||
{
|
||||
float magnitude2 = (a.GetTruePosition() - source).magnitude;
|
||||
float magnitude3 = (b.GetTruePosition() - source).magnitude;
|
||||
if (magnitude2 > magnitude3)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (magnitude2 < magnitude3)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
return outputList;
|
||||
}
|
||||
|
||||
public static TextMeshPro CreateTextLabel(string name, Transform parent,
|
||||
AspectPosition.EdgeAlignments alignment, Vector3 distance, float fontSize = 2f,
|
||||
TextAlignmentOptions textAlignment = TextAlignmentOptions.Center)
|
||||
|
||||
Reference in new issue
Block a user