Implement custom murders & extension method

This commit is contained in:
ang-xd committed 2024-08-28 15:18:36 -04:00
1 parent 9fc5044085
commit 7af85d9fbb
4 files changed
+271 -50

No files matched your search

+186
View File
@@ -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;
}
}
+2 -1
View File
@@ -6,5 +6,6 @@ public enum MiraRpc : uint
SyncRoleOptions,
AddModifier,
RemoveModifier,
SyncModifiers
SyncModifiers,
CustomMurder,
}
+26 -49
View File
@@ -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();
}
}
+57
View File
@@ -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)