Snowdown game mode + bug fixes

This commit is contained in:
Gurge44 committed 2025-12-04 10:43:51 +01:00
1 parent 13e184be32
commit 1e6cbd62c0
29 files changed
+735 -81

No files matched your search

+3
View File
@@ -478,6 +478,9 @@ public enum CustomRoles
// Mingle
MinglePlayer,
// Snowdown
SnowdownPlayer,
// GM
GM,
+3
View File
@@ -478,6 +478,9 @@ public enum CustomRoles
// Mingle
MinglePlayer,
// Snowdown
SnowdownPlayer,
// GM
GM,
+481
View File
@@ -0,0 +1,481 @@
using System;
using System.Collections.Generic;
using System.Linq;
using AmongUs.GameOptions;
using UnityEngine;
namespace EHR;
public static class Snowdown
{
// Kill: Give the target a snowball
// Vanish: Throw snowball in movement direction / Cycle to next powerup while in shop
// Sabotage: Enter/Exit shop
// Pet: Buy the selected powerup in the shop
public static float SnowballThrowSpeed = 4f;
private static int SnowballGainFrequency = 5;
private static int MaxSnowballsInHand = 5;
private static int MaxCoinsInHand = 10;
private static bool GameEndsAfterTime = true;
private static int GameEndTime = 300;
private static bool GameEndsWhenPointsReached = true;
private static int PointsToReach = 5;
private static Dictionary<PowerUp, int> PowerUpPrices = new()
{
[PowerUp.FillUp] = 3,
[PowerUp.IncreaseCapacity] = 1,
[PowerUp.FasterPickup] = 2
};
private static OptionItem SnowballThrowSpeedOption;
private static OptionItem SnowballGainFrequencyOption;
private static OptionItem MaxSnowballsInHandOption;
private static OptionItem MaxCoinsInHandOption;
private static OptionItem GameEndsAfterTimeOption;
private static OptionItem GameEndTimeOption;
private static OptionItem GameEndsWhenPointsReachedOption;
private static OptionItem PointsToReachOption;
private static Dictionary<PowerUp, OptionItem> PowerUpPriceOptions = [];
public static Dictionary<byte, PlayerData> Data = [];
private static List<Snowball> Snowballs = [];
public static ((float Left, float Right) X, (float Bottom, float Top) Y) MapBounds;
private static long GameStartTS;
private static readonly char[] Nums1 = "⓿❶❷❸❹❺❻❼❽❾❿".ToCharArray();
private static readonly char[] Nums2 = "⓪①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳".ToCharArray();
private static readonly char[] Nums3 = "⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇".ToCharArray();
public static void SetupCustomOption()
{
var id = 69_225_001;
Color color = Utils.GetRoleColor(CustomRoles.SnowdownPlayer);
const CustomGameMode gameMode = CustomGameMode.Snowdown;
const TabGroup tab = TabGroup.GameSettings;
SnowballThrowSpeedOption = new FloatOptionItem(id++, "Snowdown.SnowballThrowSpeedOption", new(0.25f, 10f, 0.25f), 4f, tab)
.SetHeader(true)
.SetColor(color)
.SetGameMode(gameMode)
.SetValueFormat(OptionFormat.Multiplier);
SnowballGainFrequencyOption = new IntegerOptionItem(id++, "Snowdown.SnowballGainFrequencyOption", new(1, 30, 1), 5, tab)
.SetColor(color)
.SetGameMode(gameMode)
.SetValueFormat(OptionFormat.Seconds);
MaxSnowballsInHandOption = new IntegerOptionItem(id++, "Snowdown.MaxSnowballsInHandOption", new(1, 10, 1), 5, tab)
.SetColor(color)
.SetGameMode(gameMode)
.SetValueFormat(OptionFormat.Pieces);
MaxCoinsInHandOption = new IntegerOptionItem(id++, "Snowdown.MaxCoinsInHandOption", new(1, 20, 1), 10, tab)
.SetColor(color)
.SetGameMode(gameMode)
.SetValueFormat(OptionFormat.Pieces);
GameEndsAfterTimeOption = new BooleanOptionItem(id++, "Snowdown.GameEndsAfterTimeOption", true, tab)
.SetColor(color)
.SetGameMode(gameMode);
GameEndTimeOption = new IntegerOptionItem(id++, "Snowdown.GameEndTimeOption", new(15, 1800, 15), 300, tab)
.SetColor(color)
.SetGameMode(gameMode)
.SetValueFormat(OptionFormat.Seconds);
GameEndsWhenPointsReachedOption = new BooleanOptionItem(id++, "Snowdown.GameEndsWhenPointsReachedOption", true, tab)
.SetColor(color)
.SetGameMode(gameMode);
PointsToReachOption = new IntegerOptionItem(id++, "Snowdown.PointsToReachOption", new(1, 100, 1), 5, tab)
.SetColor(color)
.SetGameMode(gameMode)
.SetValueFormat(OptionFormat.Pieces);
PowerUpPriceOptions = Enum.GetValues<PowerUp>().ToDictionary(x => x, x => new IntegerOptionItem(id++, "Snowdown.PowerUpPriceOption", new(1, 20, 1), PowerUpPrices[x], tab)
.SetColor(color)
.SetGameMode(gameMode)
.SetValueFormat(OptionFormat.Pieces)
.AddReplacement(("{powerup}", Translator.GetString($"Snowdown.PowerUp.{x}"))));
}
public static void ApplyGameOptions(IGameOptions opt, PlayerControl player)
{
AURoleOptions.PhantomCooldown = 0.1f;
AURoleOptions.PhantomDuration = 0.1f;
}
public static string GetSuffix(PlayerControl seer, PlayerControl target)
{
if (!Data.TryGetValue(seer.PlayerId, out PlayerData seerData) || !Data.TryGetValue(target.PlayerId, out PlayerData targetData)) return string.Empty;
if (seer.PlayerId == target.PlayerId)
return seerData.GetSelfSuffix();
StringBuilder sb = new("<#ffffff>");
AddStats(sb, targetData);
sb.Append("</color>");
return sb.ToString();
}
private static void AddStats(StringBuilder sb, PlayerData targetData)
{
sb.Append("<#e4fdff>");
sb.Append(Nums1[targetData.SnowballsReady]);
sb.Append("</color>");
sb.Append(' ');
sb.Append("<#dfc57b>");
sb.Append(Nums2[targetData.Coins]);
sb.Append("</color>");
if (targetData.Points > 0)
{
sb.Append(' ');
sb.Append("<#e5acff>");
sb.Append(Nums3[targetData.Points - 1]);
sb.Append("</color>");
}
}
public static string GetStatistics(byte id)
{
if (!Data.TryGetValue(id, out PlayerData data)) return string.Empty;
StringBuilder sb = new("<#ffffff>");
AddStats(sb, data);
sb.Append("</color>");
return sb.ToString();
}
public static string GetHudText()
{
if (!GameEndsAfterTime) return string.Empty;
long timeLeft = GameEndTime - (Utils.TimeStamp - GameStartTS);
return $"{timeLeft / 60:00}:{timeLeft % 60:00}";
}
public static bool CheckGameEnd(out GameOverReason reason)
{
reason = GameOverReason.ImpostorsByKill;
if (GameStates.IsEnded || !Main.IntroDestroyed) return false;
PlayerControl[] aapc = Main.AllAlivePlayerControls;
switch (aapc.Length)
{
case 1:
{
PlayerControl winner = aapc[0];
Logger.Info($"Winner: {winner.GetRealName().RemoveHtmlTags()}", "Snowdown");
CustomWinnerHolder.WinnerIds = [winner.PlayerId];
Main.DoBlockNameChange = true;
return true;
}
case 0:
{
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.None);
Logger.Warn("No players alive. Force ending the game", "Snowdown");
return true;
}
default:
{
if (GameEndsAfterTime && Utils.TimeStamp - GameStartTS >= GameEndTime)
{
int max = Data.Values.Max(x => x.Points);
CustomWinnerHolder.WinnerIds = Data.Where(x => x.Value.Points == max && x.Key.GetPlayer() != null).Select(x => x.Key).ToHashSet();
Logger.Info($"Winners: {(string.Join(", ", CustomWinnerHolder.WinnerIds.Select(x => Main.AllPlayerNames.GetValueOrDefault(x, "[Unknown player]"))))}", "Snowdown");
Main.DoBlockNameChange = true;
return true;
}
if (GameEndsWhenPointsReached && Data.IntersectBy(Main.AllAlivePlayerControls.Select(p => p.PlayerId), x => x.Key).FindFirst(x => x.Value.Points >= PointsToReach, out var winnerData))
{
CustomWinnerHolder.WinnerIds = [winnerData.Key];
Logger.Info($"Winners: {Main.AllPlayerNames.GetValueOrDefault(winnerData.Key, "[Unknown player]")}", "Snowdown");
Main.DoBlockNameChange = true;
return true;
}
return false;
}
}
}
public static void Init()
{
SnowballThrowSpeed = SnowballThrowSpeedOption.GetFloat();
SnowballGainFrequency = SnowballGainFrequencyOption.GetInt();
MaxSnowballsInHand = MaxSnowballsInHandOption.GetInt();
MaxCoinsInHand = MaxCoinsInHandOption.GetInt();
GameEndsAfterTime = GameEndsAfterTimeOption.GetBool();
GameEndTime = GameEndTimeOption.GetInt();
GameEndsWhenPointsReached = GameEndsWhenPointsReachedOption.GetBool();
PointsToReach = PointsToReachOption.GetInt();
PowerUpPrices = PowerUpPriceOptions.ToDictionary(x => x.Key, x => x.Value.GetInt());
Data = Main.AllPlayerControls.ToDictionary(x => x.PlayerId, _ => new PlayerData());
Snowballs = [];
Dictionary<SystemTypes, Vector2>.ValueCollection rooms = RandomSpawn.SpawnMap.GetSpawnMap().Positions?.Values;
if (rooms == null) return;
float[] x = rooms.Select(r => r.x).ToArray();
float[] y = rooms.Select(r => r.y).ToArray();
const float extend = 5f;
MapBounds = ((x.Min() - extend, x.Max() + extend), (y.Min() - extend, y.Max() + extend));
}
public static void GameStart() // Called as non-host client too!
{
GameStartTS = Utils.TimeStamp;
}
public static void OnCheckMurder(PlayerControl killer, PlayerControl target)
{
if (!Data.TryGetValue(killer.PlayerId, out PlayerData killerData) || !Data.TryGetValue(target.PlayerId, out PlayerData targetData) || killerData.SnowballsReady < 1 || targetData.SnowballsReady >= targetData.MaxSnowballsReady) return;
killerData.SnowballsReady--;
targetData.SnowballsReady++;
}
//[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.FixedUpdate))]
public static class FixedUpdatePatch
{
public static void Postfix(PlayerControl __instance)
{
if (!AmongUsClient.Instance.AmHost || !GameStates.IsInTask || ExileController.Instance || AntiBlackout.SkipTasks || Options.CurrentGameMode != CustomGameMode.Snowdown || !Main.IntroDestroyed) return;
if (!__instance.IsAlive() || !Data.TryGetValue(__instance.PlayerId, out PlayerData data)) return;
var now = Utils.TimeStamp;
var pos = __instance.Pos();
var touchingSnowball = Snowballs.Find(x => x.Thrower != __instance && Vector2.Distance(x.Position, pos) < 1.5f);
if (touchingSnowball != null)
{
touchingSnowball.Despawn();
if (touchingSnowball.Thrower != null && Data.TryGetValue(touchingSnowball.Thrower.PlayerId, out PlayerData throwerData) && throwerData.Coins < throwerData.MaxCoins)
throwerData.Coins++;
}
if (data.SnowballsReady < data.MaxSnowballsReady && now - data.LastSnowballGainTS >= data.SnowballGainInterval)
{
data.SnowballsReady++;
data.LastSnowballGainTS = now;
}
if (Vector2.Distance(pos, data.LastPosition) > 0.01f)
{
data.LastLastPosition = data.LastPosition;
data.LastPosition = pos;
}
foreach (PlayerControl target in Main.AllAlivePlayerControls)
{
string suffix = GetSuffix(__instance, target);
if (!data.LastSuffix.TryGetValue(target.PlayerId, out string lastSuffix))
goto Skip;
if (lastSuffix != suffix)
Utils.NotifyRoles(SpecifySeer: __instance, SpecifyTarget: target);
Skip:
data.LastSuffix[target.PlayerId] = suffix;
}
}
}
public class PlayerData
{
public Vector2 LastPosition = new(0f, 0f);
public Vector2 LastLastPosition = new(0f, 0f);
public int SnowballsReady;
public int MaxSnowballsReady = MaxSnowballsInHand;
public long LastSnowballGainTS;
public int SnowballGainInterval = SnowballGainFrequency;
public int Coins;
public int MaxCoins = MaxCoinsInHand;
public bool InShop;
private int ShopSelectedIndex;
public int Points;
private bool ShowHelp;
public readonly Dictionary<byte, string> LastSuffix = [];
public void OnSabotage(PlayerControl pc)
{
InShop = (SnowballsReady != MaxSnowballsReady || MaxSnowballsReady < 20 || MaxCoins < 10 || SnowballGainInterval > 1) && !InShop;
ShowHelp = !Main.HasPlayedGM[CustomGameMode.Snowdown].Contains(pc.FriendCode);
if (pc.AmOwner) HudSpritePatch.ForceUpdate = true;
}
public void OnPet(PlayerControl pc)
{
if (!InShop)
{
if (Coins >= 20)
{
Points++;
Coins -= 20;
}
return;
}
PowerUp powerUp = Enum.GetValues<PowerUp>()[ShopSelectedIndex];
int cost = PowerUpPrices[powerUp];
if (Coins < cost) return;
switch (powerUp)
{
case PowerUp.FillUp:
{
if (SnowballsReady == MaxSnowballsReady) return;
SnowballsReady = MaxSnowballsReady;
break;
}
case PowerUp.IncreaseCapacity:
{
switch (MaxSnowballsReady, MaxCoins)
{
case (>= 10, >= 20):
return;
case (>= 10, < 20):
MaxCoins++;
break;
case (< 10, >= 20):
MaxSnowballsReady++;
break;
case (< 10, < 20):
MaxSnowballsReady++;
MaxCoins++;
break;
}
break;
}
case PowerUp.FasterPickup:
{
if (SnowballGainInterval <= 1) return;
SnowballGainInterval--;
break;
}
}
Coins -= cost;
if (SnowballsReady == MaxSnowballsReady && MaxSnowballsReady >= 20 && MaxCoins >= 10 && SnowballGainInterval <= 1)
InShop = false;
}
public void OnVanish(PlayerControl pc)
{
if (InShop)
{
ShopSelectedIndex++;
if (ShopSelectedIndex >= Enum.GetValues<PowerUp>().Length)
ShopSelectedIndex = 0;
}
else
{
if (SnowballsReady <= 0) return;
SnowballsReady--;
Snowballs.Add(new Snowball(pc.Pos(), (LastPosition - LastLastPosition).normalized, pc));
}
}
public string GetSelfSuffix()
{
StringBuilder sb = new("<#ffffff><size=140%>");
AddStats(sb, this);
sb.Append("</size>");
if (InShop)
{
PowerUp powerUp = Enum.GetValues<PowerUp>()[ShopSelectedIndex];
sb.Append('\n');
sb.Append('\n');
sb.Append(Translator.GetString($"Snowdown.PowerUp.{powerUp}"));
sb.Append(' ');
sb.Append("<#dfc57b>");
sb.Append(Nums2[PowerUpPrices[powerUp]]);
sb.Append("</color>");
sb.Append('\n');
sb.Append("<size=90%>");
sb.Append(Translator.GetString($"Snowdown.PowerUp.{powerUp}.Description"));
sb.Append("</size>");
if (ShowHelp)
{
sb.Append('\n');
sb.Append('\n');
sb.Append("<size=80%>");
sb.Append(Translator.GetString("Snowdown.ShopHelp"));
sb.Append("</size>");
}
}
sb.Append("</color>");
return sb.ToString();
}
}
public enum PowerUp
{
FillUp,
IncreaseCapacity,
FasterPickup
}
}
internal class SnowdownPlayer : RoleBase
{
public static bool On;
public override bool IsEnable => On;
public override void SetupCustomOption() { }
public override void Init()
{
On = false;
}
public override void Add(byte playerId)
{
On = true;
}
public override bool CanUseSabotage(PlayerControl pc)
{
return pc.IsAlive();
}
public override bool OnSabotage(PlayerControl pc)
{
if (Snowdown.Data.TryGetValue(pc.PlayerId, out Snowdown.PlayerData data))
data.OnSabotage(pc);
return false;
}
public override bool OnVanish(PlayerControl pc)
{
if (Snowdown.Data.TryGetValue(pc.PlayerId, out Snowdown.PlayerData data))
data.OnVanish(pc);
return false;
}
public override void OnPet(PlayerControl pc)
{
if (Snowdown.Data.TryGetValue(pc.PlayerId, out Snowdown.PlayerData data))
data.OnPet(pc);
}
}
+7 -3
View File
@@ -137,7 +137,8 @@ public class Main : BasePlugin
[CustomGameMode.HideAndSeek] = [],
[CustomGameMode.Speedrun] = [],
[CustomGameMode.CaptureTheFlag] = [],
[CustomGameMode.NaturalDisasters] = []
[CustomGameMode.NaturalDisasters] = [],
[CustomGameMode.Snowdown] = []
};
public static Dictionary<CustomGameMode, Color> GameModeColors = [];
@@ -258,7 +259,7 @@ public class Main : BasePlugin
foreach (PlayerControl pc in PlayerControl.AllPlayerControls)
{
if (pc == null || pc.PlayerId >= 254 || !pc.IsAlive() || pc.Data == null || (pc.Data.Disconnected && IntroDestroyed) || Pelican.IsEaten(pc.PlayerId)) continue;
if (pc == null || pc.PlayerId >= 254 || !pc.IsAlive() || pc.Data == null || (pc.Data.Disconnected && IntroDestroyed) || Pelican.IsEaten(pc.PlayerId) || pc.Is(CustomRoles.GM)) continue;
result[i++] = pc;
}
@@ -776,6 +777,8 @@ public class Main : BasePlugin
{ CustomRoles.Racer, "#AFAFAF" },
// Mingle
{ CustomRoles.MinglePlayer, "#FE9900" },
// Snowdown
{ CustomRoles.SnowdownPlayer, "#e4fdff" },
// Hide And Seek
{ CustomRoles.Seeker, "#ff1919" },
{ CustomRoles.Hider, "#345eeb" },
@@ -858,7 +861,8 @@ public class Main : BasePlugin
[CustomGameMode.TheMindGame] = Color.yellow,
[CustomGameMode.BedWars] = Utils.GetRoleColor(CustomRoles.BedWarsPlayer),
[CustomGameMode.Deathrace] = Utils.GetRoleColor(CustomRoles.Racer),
[CustomGameMode.Mingle] = Utils.GetRoleColor(CustomRoles.MinglePlayer)
[CustomGameMode.Mingle] = Utils.GetRoleColor(CustomRoles.MinglePlayer),
[CustomGameMode.Snowdown] = Utils.GetRoleColor(CustomRoles.SnowdownPlayer)
};
IL2CPPChainloader.Instance.Finished += () =>
+1
View File
@@ -26,6 +26,7 @@ public static class AutoHaunt
CustomGameMode.RoomRush => RoomRush.PointsSystem ? validPCs.MaxBy(x => RoomRush.GetPoints(x.PlayerId)) : validPCs.RandomElement(),
CustomGameMode.KingOfTheZones => validPCs.MaxBy(x => KingOfTheZones.GetZoneTime(x.PlayerId)),
CustomGameMode.Deathrace => validPCs.MaxBy(x => Deathrace.Data.TryGetValue(x.PlayerId, out var drData) ? drData.Lap : 0),
CustomGameMode.Snowdown => validPCs.Where(x => Snowdown.Data.ContainsKey(x.PlayerId)).Select(x => (pc: x, data: Snowdown.Data[x.PlayerId])).OrderByDescending(x => x.data.Points).ThenBy(x => x.data.SnowballGainInterval).ThenByDescending(x => x.data.Coins).ThenByDescending(x => x.data.SnowballsReady).Select(x => x.pc).FirstOrDefault(),
_ => validPCs.RandomElement()
};
}
+33 -2
View File
@@ -239,7 +239,9 @@ namespace EHR
catch (Exception exception) { Utils.ThrowException(exception); }
}
playerControl.RawSetName(sprite);
try { playerControl.RawSetName("<size=14><br></size>" + sprite); }
catch (Exception e) { Utils.ThrowException(e); }
string name = PlayerControl.LocalPlayer.Data.Outfits[PlayerOutfitType.Default].PlayerName;
int colorId = PlayerControl.LocalPlayer.Data.Outfits[PlayerOutfitType.Default].ColorId;
string hatId = PlayerControl.LocalPlayer.Data.Outfits[PlayerOutfitType.Default].HatId;
@@ -285,6 +287,8 @@ namespace EHR
}, 0.6f);
}
playerControl.cosmetics.currentBodySprite.BodySprite.color = Color.clear;
playerControl.cosmetics.colorBlindText.color = Color.clear;
Position = position;
Sprite = sprite;
++MaxId;
@@ -868,7 +872,7 @@ namespace EHR
public sealed class DeathracePowerUp : CustomNetObject
{
public Deathrace.PowerUp PowerUp;
public readonly Deathrace.PowerUp PowerUp;
public DeathracePowerUp(Vector2 position, Deathrace.PowerUp powerUp)
{
@@ -897,6 +901,33 @@ namespace EHR
CreateNetObject(Utils.ColorString(color, $"{icon}\n<size=80%>{Translator.GetString($"Deathrace.PowerUpDisplay.{powerUp}").ToUpper()}</size>"), position);
}
}
internal sealed class Snowball : CustomNetObject
{
public PlayerControl Thrower;
public Vector2 Direction;
public Snowball(Vector2 from, Vector2 direction, PlayerControl thrower)
{
Thrower = thrower;
Direction = direction;
CreateNetObject("<line-height=97%><cspace=0.16em><#0000>W</color><mark=#e4fdff>WWWW</mark><#0000>W</color>\n<mark=#e4fdff>WWWWWW</mark>\n<mark=#e4fdff>WWWWWW</mark>\n<mark=#e4fdff>WWWWWW</mark>\n<mark=#e4fdff>WWWWWW</mark>\n<#0000>W</color><mark=#e4fdff>WWWW</mark><#0000>W", from);
}
protected override void OnFixedUpdate()
{
Vector2 newPos = Position + Direction * Time.fixedDeltaTime * Snowdown.SnowballThrowSpeed;
if ((PhysicsHelpers.AnythingBetween(Position, newPos, Constants.ShipOnlyMask, false)) ||
newPos.x < Snowdown.MapBounds.X.Left || newPos.x > Snowdown.MapBounds.X.Right || newPos.y < Snowdown.MapBounds.Y.Bottom || newPos.y > Snowdown.MapBounds.Y.Top)
{
Despawn();
return;
}
TP(newPos);
}
}
}
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.RawSetName))]
+2 -1
View File
@@ -36,7 +36,8 @@ internal static class CustomRoleSelector
{ CustomGameMode.TheMindGame, CustomRoles.TMGPlayer },
{ CustomGameMode.BedWars, CustomRoles.BedWarsPlayer },
{ CustomGameMode.Deathrace, CustomRoles.Racer },
{ CustomGameMode.Mingle, CustomRoles.MinglePlayer }
{ CustomGameMode.Mingle, CustomRoles.MinglePlayer },
{ CustomGameMode.Snowdown, CustomRoles.SnowdownPlayer }
};
public static void SelectCustomRoles()
+5 -1
View File
@@ -73,7 +73,8 @@ internal static class CustomRolesHelper
CustomRoles.TMGPlayer or
CustomRoles.BedWarsPlayer or
CustomRoles.Racer or
CustomRoles.MinglePlayer;
CustomRoles.MinglePlayer or
CustomRoles.SnowdownPlayer;
}
public static RoleBase GetRoleClass(this CustomRoles role)
@@ -470,6 +471,8 @@ internal static class CustomRolesHelper
CustomRoles.BedWarsPlayer => RoleTypes.Phantom,
// Deathrace
CustomRoles.Racer => RoleTypes.Phantom,
// Snowdown
CustomRoles.SnowdownPlayer => RoleTypes.Phantom,
// Standard
CustomRoles.Sheriff => UsePets && Sheriff.UsePet.GetBool() ? RoleTypes.GuardianAngel : RoleTypes.Impostor,
CustomRoles.Crusader => UsePets && Crusader.UsePet.GetBool() ? RoleTypes.GuardianAngel : RoleTypes.Impostor,
@@ -1052,6 +1055,7 @@ internal static class CustomRolesHelper
or CustomRoles.CTFPlayer
or CustomRoles.BedWarsPlayer
or CustomRoles.Racer
or CustomRoles.SnowdownPlayer
or CustomRoles.Wizard
or CustomRoles.AntiAdminer
or CustomRoles.Stasis
+5 -2
View File
@@ -355,7 +355,7 @@ internal static class ExtendedPlayerControl
RoleTypes newRoleType = state.MainRole.GetRoleTypes();
if (Options.CurrentGameMode is CustomGameMode.SoloPVP or CustomGameMode.FFA or CustomGameMode.CaptureTheFlag or CustomGameMode.KingOfTheZones or CustomGameMode.BedWars)
if (Options.CurrentGameMode is CustomGameMode.SoloPVP or CustomGameMode.FFA or CustomGameMode.CaptureTheFlag or CustomGameMode.KingOfTheZones or CustomGameMode.BedWars or CustomGameMode.Snowdown)
hasValue |= sender.RpcSetRole(player, newRoleType, player.OwnerId);
player.ResetKillCooldown();
@@ -1081,7 +1081,7 @@ internal static class ExtendedPlayerControl
{
try
{
bool addRoleName = GameStates.IsInGame && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.StopAndGo and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag and not CustomGameMode.NaturalDisasters and not CustomGameMode.RoomRush and not CustomGameMode.Quiz and not CustomGameMode.TheMindGame and not CustomGameMode.BedWars and not CustomGameMode.Deathrace and not CustomGameMode.Mingle;
bool addRoleName = GameStates.IsInGame && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.StopAndGo and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag and not CustomGameMode.NaturalDisasters and not CustomGameMode.RoomRush and not CustomGameMode.Quiz and not CustomGameMode.TheMindGame and not CustomGameMode.BedWars and not CustomGameMode.Deathrace and not CustomGameMode.Mingle and not CustomGameMode.Snowdown;
return $"{player?.Data?.PlayerName}" + (addRoleName ? $" ({player?.GetAllRoleName(forUser).RemoveHtmlTags().Replace('\n', ' ')})" : string.Empty);
}
catch (Exception e)
@@ -1365,6 +1365,7 @@ internal static class ExtendedPlayerControl
case CustomGameMode.CaptureTheFlag:
case CustomGameMode.BedWars:
case CustomGameMode.Deathrace:
case CustomGameMode.Snowdown:
return true;
}
@@ -1425,6 +1426,7 @@ internal static class ExtendedPlayerControl
CustomGameMode.Quiz => false,
CustomGameMode.TheMindGame => false,
CustomGameMode.Mingle => false,
CustomGameMode.Snowdown => true,
CustomGameMode.Deathrace => Deathrace.CanUseVent(pc, pc.GetClosestVent().Id),
CustomGameMode.Standard when CopyCat.Instances.Any(x => x.CopyCatPC.PlayerId == pc.PlayerId) => true,
@@ -1735,6 +1737,7 @@ internal static class ExtendedPlayerControl
CustomRoles.QuizPlayer => 3f,
CustomRoles.BedWarsPlayer => 1f,
CustomRoles.Racer => 3f,
CustomRoles.SnowdownPlayer => 1f,
_ when player.Is(CustomRoles.Underdog) => Main.AllAlivePlayerControls.Length <= Underdog.UnderdogMaximumPlayersNeededToKill.GetInt() ? Underdog.UnderdogKillCooldownWithLessPlayersAlive.GetInt() : Underdog.UnderdogKillCooldownWithMorePlayersAlive.GetInt(),
_ => Main.AllPlayerKillCooldown[player.PlayerId]
};
+3
View File
@@ -175,6 +175,9 @@ public static class FixedUpdateCaller
case CustomGameMode.BedWars:
BedWars.FixedUpdatePatch.Postfix(pc);
break;
case CustomGameMode.Snowdown:
Snowdown.FixedUpdatePatch.Postfix(pc);
break;
}
CheckInvalidMovementPatch.Postfix(pc);
@@ -171,6 +171,11 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS
CaptureTheFlag.ApplyGameOptions();
goto case CustomGameMode.RoomRush;
}
case CustomGameMode.Snowdown:
{
Snowdown.ApplyGameOptions(opt, player);
goto case CustomGameMode.RoomRush;
}
case CustomGameMode.NaturalDisasters:
{
SetMaxVision();
+1
View File
@@ -46,6 +46,7 @@ public static class NameColorManager
if (FreeForAll.PlayerTeams.TryGetValue(target.PlayerId, out int team))
color = FreeForAll.TeamColors.GetValueOrDefault(team, "#00ffff");
return true;
case CustomGameMode.Snowdown:
case CustomGameMode.Mingle:
case CustomGameMode.RoomRush:
case CustomGameMode.NaturalDisasters:
+10 -3
View File
@@ -35,6 +35,7 @@ public enum CustomGameMode
BedWars = 0x0E,
Deathrace = 0x0F,
Mingle = 0x10,
Snowdown = 0x11,
All = int.MaxValue
}
@@ -88,7 +89,8 @@ public static class Options
"TheMindGame",
"BedWars",
"Deathrace",
"Mingle"
"Mingle",
"Snowdown"
];
private static Dictionary<CustomRoles, int> roleCounts;
@@ -919,6 +921,7 @@ public static class Options
13 => CustomGameMode.BedWars,
14 => CustomGameMode.Deathrace,
15 => CustomGameMode.Mingle,
16 => CustomGameMode.Snowdown,
_ => CustomGameMode.Standard
};
@@ -1841,6 +1844,8 @@ public static class Options
Deathrace.SetupCustomOption();
// Mingle
Mingle.SetupCustomOption();
// Snowdown
Snowdown.SetupCustomOption();
yield return null;
@@ -3205,10 +3210,11 @@ public static class Options
foreach (CustomGameMode customGameMode in Enum.GetValues<CustomGameMode>()[..^1])
{
OptionItem chanceToSelectGMInGroup = new IntegerOptionItem(id++, $"AGMR.RandomGroup.GMChance.{customGameMode}", new(0, 100, 5), 50, TabGroup.SystemSettings)
OptionItem chanceToSelectGMInGroup = new IntegerOptionItem(id++, $"AGMR.RandomGroup.GMChance", new(0, 100, 5), 50, TabGroup.SystemSettings)
.SetParent(EnableAutoGMRotation)
.SetValueFormat(OptionFormat.Percent)
.SetColor(Main.GameModeColors[customGameMode]);
.SetColor(Main.GameModeColors[customGameMode])
.AddReplacement(("{gm}", Translator.GetString($"{customGameMode}")));
dict[customGameMode] = chanceToSelectGMInGroup;
}
@@ -3291,6 +3297,7 @@ public static class Options
13 => CustomGameMode.BedWars,
14 => CustomGameMode.Deathrace,
15 => CustomGameMode.Mingle,
16 => CustomGameMode.Snowdown,
_ => CustomGameMode.Standard
};
AutoGMRotationCompiled.AddRange(Enumerable.Repeat(gm, times));
+20 -8
View File
@@ -822,6 +822,7 @@ public static class Utils
case CustomGameMode.BedWars:
case CustomGameMode.Deathrace:
case CustomGameMode.Mingle:
case CustomGameMode.Snowdown:
return false;
case CustomGameMode.HideAndSeek:
return CustomHnS.HasTasks(p);
@@ -1046,7 +1047,7 @@ public static class Utils
{
switch (Options.CurrentGameMode)
{
case CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle:
case CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle or CustomGameMode.Snowdown:
case CustomGameMode.Standard when IsRevivingRoleAlive() && Main.DiedThisRound.Contains(PlayerControl.LocalPlayer.PlayerId):
return PlayerControl.LocalPlayer.Is(CustomRoles.GM);
case CustomGameMode.FFA or CustomGameMode.SoloPVP or CustomGameMode.StopAndGo or CustomGameMode.HotPotato or CustomGameMode.Speedrun:
@@ -1551,6 +1552,7 @@ public static class Utils
}
break;
case CustomGameMode.Snowdown:
case CustomGameMode.Mingle:
case CustomGameMode.Deathrace:
case CustomGameMode.BedWars:
@@ -2176,6 +2178,7 @@ public static class Utils
CustomGameMode.BedWars => ColorString(GetRoleColor(CustomRoles.BedWarsPlayer), $"{modeText}\r\n") + name,
CustomGameMode.Deathrace => ColorString(GetRoleColor(CustomRoles.Racer), $"{modeText}\r\n") + name,
CustomGameMode.Mingle => ColorString(GetRoleColor(CustomRoles.MinglePlayer), $"{modeText}\r\n") + name,
CustomGameMode.Snowdown => ColorString(GetRoleColor(CustomRoles.SnowdownPlayer), $"{modeText}\r\n") + name,
_ => name
};
}
@@ -2641,13 +2644,16 @@ public static class Utils
case CustomGameMode.Mingle:
additionalSuffixes.Add(Mingle.GetSuffix(seer));
break;
case CustomGameMode.Snowdown:
additionalSuffixes.Add(Snowdown.GetSuffix(seer, seer));
break;
}
List<string> addSuff = additionalSuffixes.ConvertAll(x => x.Trim()).FindAll(x => !string.IsNullOrEmpty(x));
if (addSuff.Count > 0)
{
if (SelfSuffix.Length > 0 && SelfSuffix[^1] != '\n')
if (SelfSuffix.ToString().RemoveHtmlTags().Length > 0 && SelfSuffix[^1] != '\n')
SelfSuffix.Append('\n');
SelfSuffix.Append(string.Join('\n', addSuff));
@@ -2664,7 +2670,7 @@ public static class Utils
if ((Options.CurrentGameMode == CustomGameMode.FFA && FreeForAll.FFATeamMode.GetBool()) || Options.CurrentGameMode == CustomGameMode.HotPotato)
seerRealName = seerRealName.ApplyNameColorData(seer, seer, forMeeting);
if (!forMeeting && MeetingStates.FirstMeeting && Options.ChangeNameToRoleInfo.GetBool() && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.StopAndGo and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag and not CustomGameMode.NaturalDisasters and not CustomGameMode.RoomRush and not CustomGameMode.KingOfTheZones and not CustomGameMode.Quiz and not CustomGameMode.TheMindGame and not CustomGameMode.BedWars and not CustomGameMode.Deathrace and not CustomGameMode.Mingle)
if (!forMeeting && MeetingStates.FirstMeeting && Options.ChangeNameToRoleInfo.GetBool() && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.StopAndGo and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag and not CustomGameMode.NaturalDisasters and not CustomGameMode.RoomRush and not CustomGameMode.KingOfTheZones and not CustomGameMode.Quiz and not CustomGameMode.TheMindGame and not CustomGameMode.BedWars and not CustomGameMode.Deathrace and not CustomGameMode.Mingle and not CustomGameMode.Snowdown)
{
CustomTeamManager.CustomTeam team = CustomTeamManager.GetCustomTeam(seer.PlayerId);
@@ -2702,7 +2708,7 @@ public static class Utils
SelfSuffix.Append($"\n\n<#ffffff>{GetString($"GameModeTutorial.{Options.CurrentGameMode}")}</color>\n");
}
bool noRoleText = GameStates.IsLobby || Options.CurrentGameMode is CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle;
bool noRoleText = GameStates.IsLobby || Options.CurrentGameMode is CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle or CustomGameMode.Snowdown;
// Combine the seer's job title and SelfTaskText with the seer's player name and SelfMark
string selfRoleName = noRoleText ? string.Empty : $"<size={fontSize}>{seer.GetDisplayRoleName()}{selfTaskText}</size>";
@@ -2756,7 +2762,7 @@ public static class Utils
break;
}
selfName += SelfSuffix.ToString() == string.Empty ? string.Empty : $"\r\n{SelfSuffix}";
selfName += SelfSuffix.ToString() == string.Empty ? string.Empty : $"\r\n{SelfSuffix.ToString().Trim()}";
if (!forMeeting) selfName += "\r\n";
}
@@ -2879,7 +2885,7 @@ public static class Utils
if (IsRevivingRoleAlive() && Main.DiedThisRound.Contains(seer.PlayerId))
targetRoleText = string.Empty;
if (Options.CurrentGameMode is CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle)
if (Options.CurrentGameMode is CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle or CustomGameMode.Snowdown)
targetRoleText = string.Empty;
if (!GameStates.IsLobby)
@@ -2991,6 +2997,9 @@ public static class Utils
case CustomGameMode.Deathrace:
additionalSuffixes.Add(Deathrace.GetSuffix(seer, target, false));
break;
case CustomGameMode.Snowdown:
additionalSuffixes.Add(Snowdown.GetSuffix(seer, target));
break;
}
if (MeetingStates.FirstMeeting && Main.ShieldPlayer == target.FriendCode && !string.IsNullOrEmpty(target.FriendCode) && Options.CurrentGameMode is CustomGameMode.Standard or CustomGameMode.FFA or CustomGameMode.Speedrun)
@@ -3008,7 +3017,7 @@ public static class Utils
if (addSuff.Count > 0)
{
if (TargetSuffix.Length > 0 && TargetSuffix[^1] != '\n')
if (TargetSuffix.ToString().RemoveHtmlTags().Length > 0 && TargetSuffix[^1] != '\n')
TargetSuffix.Append('\n');
TargetSuffix.Append(string.Join('\n', addSuff));
@@ -3027,7 +3036,7 @@ public static class Utils
targetPlayerName = EmptyMessage;
var targetName = $"{targetRoleText}{targetPlayerName}{targetDeathReason}{TargetMark}";
targetName += GameStates.IsLobby || TargetSuffix.ToString() == string.Empty ? string.Empty : $"{newLineBeforeSuffix}{TargetSuffix}";
targetName += GameStates.IsLobby || TargetSuffix.ToString() == string.Empty ? string.Empty : $"{newLineBeforeSuffix}{TargetSuffix.ToString().Trim()}";
// Camouflage
if (Camouflage.IsCamouflage && !camouflageIsForMeeting) targetName = $"<size=0>{targetName}</size>";
@@ -3989,6 +3998,9 @@ public static class Utils
int time3 = Mingle.GetSurvivalTime(id);
summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>{GetString("SurvivedTimePrefix")}: <#ffffff>{(time3 == 0 ? $"{GetString("SurvivedUntilTheEnd")}</color>" : $"{time3}</color>s")}</color> ({GetVitalText(id, true)})";
break;
case CustomGameMode.Snowdown:
summary = $"{ColorString(Main.PlayerColors[id], name)} - {Snowdown.GetStatistics(id)}";
break;
}
return check && GetDisplayRoleName(id, true).RemoveHtmlTags().Contains("INVALID:NotAssigned")
+19
View File
@@ -560,6 +560,11 @@ internal static class GameEndChecker
Predicate = new MingleGameEndPredicate();
}
public static void SetPredicateToSnowdown()
{
Predicate = new SnowdownGameEndPredicate();
}
private class NormalGameEndPredicate : GameEndPredicate
{
public override bool CheckForGameEnd(out GameOverReason reason)
@@ -1133,6 +1138,20 @@ internal static class GameEndChecker
return Mingle.CheckGameEnd(out reason);
}
}
private class SnowdownGameEndPredicate : GameEndPredicate
{
public override bool CheckForGameEnd(out GameOverReason reason)
{
reason = GameOverReason.ImpostorsByKill;
return WinnerIds.Count <= 0 && CheckGameEndByLivingPlayers(out reason);
}
private static bool CheckGameEndByLivingPlayers(out GameOverReason reason)
{
return Snowdown.CheckGameEnd(out reason);
}
}
public abstract class GameEndPredicate
{
+1
View File
@@ -1167,6 +1167,7 @@ public static class GameSettingMenuPatch
Vector3 gameSettingsLabelPos = gameSettingsLabel.transform.localPosition;
CustomGameMode[] gms = Enum.GetValues<CustomGameMode>()[..^1];
gms = gms.Without(CustomGameMode.TheMindGame).ToArray();
if (SubmergedCompatibility.Loaded && Main.NormalOptions.MapId == 6) gms = gms.Where(SubmergedCompatibility.IsSupported).ToArray();
int totalCols = Mathf.Max(1, Mathf.CeilToInt(gms.Length / 7f));
+12
View File
@@ -250,6 +250,9 @@ internal static class HudManagerPatch
case CustomRoles.RRPlayer when __instance.AbilityButton != null && RoomRush.VentLimit.TryGetValue(PlayerControl.LocalPlayer.PlayerId, out int ventLimit):
__instance.AbilityButton.SetUsesRemaining(ventLimit);
break;
case CustomRoles.SnowdownPlayer:
__instance.AbilityButton?.OverrideText(GetString("SnowdownButtonText"));
break;
}
if (role.PetActivatedAbility() && Options.CurrentGameMode == CustomGameMode.Standard && player.GetRoleTypes() != RoleTypes.Engineer && !role.OnlySpawnsWithPets() && !role.AlwaysUsesPhantomBase() && !player.GetCustomSubRoles().Any(StartGameHostPatch.BasisChangingAddons.ContainsKey) && role is not CustomRoles.Changeling and not CustomRoles.Ninja and not CustomRoles.Duality && (!role.SimpleAbilityTrigger() || !Options.UsePhantomBasis.GetBool() || !(player.IsNeutralKiller() && Options.UsePhantomBasisForNKs.GetBool())) && !(Options.UseMeetingShapeshift.GetBool() && player.UsesMeetingShapeshift()))
@@ -275,6 +278,7 @@ internal static class HudManagerPatch
CustomGameMode.HideAndSeek when player.IsHost() => CustomHnS.GetSuffixText(player, player, true),
CustomGameMode.NaturalDisasters => NaturalDisasters.SuffixText(),
CustomGameMode.Deathrace => Deathrace.GetSuffix(player, player, true),
CustomGameMode.Snowdown => Snowdown.GetHudText(),
CustomGameMode.Standard => state.Role.GetSuffix(player, player, true, GameStates.IsMeeting) + GetAddonSuffixes(),
_ => string.Empty
};
@@ -575,6 +579,14 @@ internal static class SetHudActivePatch
switch (Options.CurrentGameMode)
{
case CustomGameMode.Snowdown:
__instance.AbilityButton?.ToggleVisible(true);
__instance.ReportButton?.ToggleVisible(false);
__instance.KillButton?.ToggleVisible(true);
__instance.ImpostorVentButton?.ToggleVisible(true);
__instance.SabotageButton?.ToggleVisible(true);
__instance.PetButton?.ToggleVisible(true);
return;
case CustomGameMode.BedWars:
__instance.AbilityButton?.ToggleVisible(true);
__instance.ReportButton?.ToggleVisible(false);
+5
View File
@@ -49,6 +49,11 @@ public static class HudSpritePatch
switch (player.GetCustomRole())
{
case CustomRoles.SnowdownPlayer when Snowdown.Data.TryGetValue(player.PlayerId, out Snowdown.PlayerData snowdownData) && !snowdownData.InShop:
{
newAbilityButton = CustomButton.Get("Snowdown");
break;
}
case CustomRoles.CTFPlayer:
{
newAbilityButton = CustomButton.Get("Tag");
+36 -12
View File
@@ -301,6 +301,16 @@ internal static class SetUpRoleTextPatch
__instance.RoleBlurbText.text = GetString("MinglePlayerInfo");
break;
}
case CustomGameMode.Snowdown:
{
Color color = Utils.GetRoleColor(CustomRoles.SnowdownPlayer);
__instance.YouAreText.transform.gameObject.SetActive(false);
__instance.RoleText.text = GetString("SnowdownPlayer");
__instance.RoleText.color = color;
__instance.RoleBlurbText.color = color;
__instance.RoleBlurbText.text = GetString("SnowdownPlayerInfo");
break;
}
default:
{
CustomRoles role = lp.GetCustomRole();
@@ -992,6 +1002,15 @@ internal static class BeginCrewmatePatch
__instance.ImpostorText.text = GetString("MinglePlayerInfo");
break;
}
case CustomGameMode.Snowdown:
{
__instance.TeamTitle.text = GetString("SnowdownPlayer");
__instance.TeamTitle.color = __instance.BackgroundBar.material.color = Utils.GetRoleColor(CustomRoles.SnowdownPlayer);
PlayerControl.LocalPlayer.Data.Role.IntroSound = GetIntroSound(RoleTypes.Detective);
__instance.ImpostorText.gameObject.SetActive(true);
__instance.ImpostorText.text = GetString("SnowdownPlayerInfo");
break;
}
}
return;
@@ -1116,16 +1135,6 @@ internal static class IntroCutsceneDestroyPatch
if (Main.NormalOptions.MapId != 4)
{
foreach (PlayerControl pc in aapc)
{
pc.RpcResetAbilityCooldown();
if (pc.GetCustomRole().UsesPetInsteadOfKill())
pc.AddAbilityCD(10);
else
pc.AddAbilityCD(false);
}
if (Options.CurrentGameMode == CustomGameMode.Standard)
{
if (Options.FixFirstKillCooldown.GetBool())
@@ -1167,8 +1176,17 @@ internal static class IntroCutsceneDestroyPatch
LateTask.New(() =>
{
lp.RpcResetAbilityCooldown();
lp.SetKillCooldown(10f);
foreach (PlayerControl pc in aapc)
{
pc.RpcResetAbilityCooldown();
if (pc.GetCustomRole().UsesPetInsteadOfKill())
pc.AddAbilityCD(10);
else
pc.AddAbilityCD(false);
}
}, 0.8f, log: false);
StartGameHostPatch.RpcSetRoleReplacer.SetActualSelfRolesAfterOverride();
@@ -1186,7 +1204,7 @@ internal static class IntroCutsceneDestroyPatch
}
}, 0.1f, log: false);
if (Options.UsePets.GetBool() && Options.CurrentGameMode is CustomGameMode.Standard or CustomGameMode.HideAndSeek or CustomGameMode.CaptureTheFlag or CustomGameMode.BedWars)
if (Options.UsePets.GetBool() && Options.CurrentGameMode is CustomGameMode.Standard or CustomGameMode.HideAndSeek or CustomGameMode.CaptureTheFlag or CustomGameMode.BedWars or CustomGameMode.Snowdown)
{
void GrantPetForEveryone()
{
@@ -1309,6 +1327,9 @@ internal static class IntroCutsceneDestroyPatch
case CustomGameMode.Mingle:
Main.Instance.StartCoroutine(Mingle.GameStart());
break;
case CustomGameMode.Snowdown:
Snowdown.GameStart();
break;
}
Utils.CheckAndSetVentInteractions();
@@ -1322,6 +1343,9 @@ internal static class IntroCutsceneDestroyPatch
{
foreach (PlayerControl player in Main.AllPlayerControls)
Main.PlayerStates[player.PlayerId].InitTask(player);
if (Options.CurrentGameMode == CustomGameMode.Snowdown)
Snowdown.GameStart();
}
Logger.Info("OnDestroy", "IntroCutscene");
+6
View File
@@ -1047,6 +1047,9 @@ internal static class StartGameHostPatch
case CustomGameMode.Deathrace:
Deathrace.Init();
goto default;
case CustomGameMode.Snowdown:
Snowdown.Init();
goto default;
default:
if (Options.IntegrateNaturalDisasters.GetBool()) goto case CustomGameMode.NaturalDisasters;
break;
@@ -1116,6 +1119,9 @@ internal static class StartGameHostPatch
case CustomGameMode.Mingle:
GameEndChecker.SetPredicateToMingle();
break;
case CustomGameMode.Snowdown:
GameEndChecker.SetPredicateToSnowdown();
break;
}
// Add players with unclassified roles to the list of players who require ResetCam.
+10 -1
View File
@@ -66,7 +66,7 @@ internal static class EndGamePatch
byte killerId = value.GetRealKiller();
bool gmIsFm = Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.StopAndGo;
bool gmIsFmhh = gmIsFm || Options.CurrentGameMode is CustomGameMode.HotPotato or CustomGameMode.HideAndSeek or CustomGameMode.Speedrun or CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle;
bool gmIsFmhh = gmIsFm || Options.CurrentGameMode is CustomGameMode.HotPotato or CustomGameMode.HideAndSeek or CustomGameMode.Speedrun or CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle or CustomGameMode.Snowdown;
sb.Append($"\n{date:T} {Main.AllPlayerNames[key]} ({(gmIsFmhh ? string.Empty : Utils.GetDisplayRoleName(key, true))}{(gmIsFm ? string.Empty : Utils.GetSubRolesText(key, summary: true))}) [{Utils.GetVitalText(key)}]");
if (killerId != byte.MaxValue && killerId != key) sb.Append($"\n\t⇐ {Main.AllPlayerNames[killerId]} ({(gmIsFmhh ? string.Empty : Utils.GetDisplayRoleName(killerId, true))}{(gmIsFm ? string.Empty : Utils.GetSubRolesText(killerId, summary: true))})");
}
@@ -342,6 +342,14 @@ internal static class SetEverythingUpPatch
goto EndOfText;
}
case CustomGameMode.Snowdown:
{
Color color = Utils.GetRoleColor(CustomRoles.SnowdownPlayer);
__instance.BackgroundBar.material.color = color;
winnerText.text = (CustomWinnerHolder.WinnerIds.Count <= 1 ? CustomWinnerHolder.WinnerIds.FirstOrDefault().ColoredPlayerName() : CustomWinnerHolder.WinnerIds.Select(x => x.ColoredPlayerName()).Join()) + GetString("Win");
winnerText.color = color;
goto EndOfText;
}
}
}
@@ -550,6 +558,7 @@ internal static class SetEverythingUpPatch
break;
}
case CustomGameMode.Snowdown:
case CustomGameMode.Mingle:
case CustomGameMode.Deathrace:
case CustomGameMode.BedWars:
+11 -4
View File
@@ -259,6 +259,9 @@ internal static class CheckMurderPatch
case CustomGameMode.Deathrace:
Deathrace.OnCheckMurder(killer, target);
return false;
case CustomGameMode.Snowdown:
Snowdown.OnCheckMurder(killer, target);
return false;
}
PlagueBearer.CheckAndSpreadInfection(killer, target);
@@ -1514,7 +1517,7 @@ internal static class FixedUpdatePatch
{
Camouflage.OnFixedUpdate(player);
if (localPlayer && Options.CurrentGameMode is CustomGameMode.Standard or CustomGameMode.FFA or CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters && GameStartTimeStamp + 44 == TimeStamp)
if (localPlayer && Options.CurrentGameMode is CustomGameMode.Standard or CustomGameMode.FFA or CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.Snowdown && GameStartTimeStamp + 44 == TimeStamp)
NotifyRoles();
}
}
@@ -1975,6 +1978,9 @@ internal static class FixedUpdatePatch
case CustomGameMode.Mingle when self:
additionalSuffixes.Add(Mingle.GetSuffix(seer));
break;
case CustomGameMode.Snowdown:
additionalSuffixes.Add(Snowdown.GetSuffix(seer, target));
break;
}
if (MeetingStates.FirstMeeting && Main.ShieldPlayer == target.FriendCode && !string.IsNullOrEmpty(target.FriendCode) && !self && Options.CurrentGameMode is CustomGameMode.Standard or CustomGameMode.SoloPVP or CustomGameMode.FFA)
@@ -1984,7 +1990,7 @@ internal static class FixedUpdatePatch
if (addSuff.Count > 0)
{
if (Suffix.Length > 0 && Suffix[^1] != '\n')
if (Suffix.ToString().RemoveHtmlTags().Length > 0 && Suffix[^1] != '\n')
Suffix.Append('\n');
Suffix.Append(string.Join('\n', addSuff));
@@ -2010,10 +2016,11 @@ internal static class FixedUpdatePatch
else
roleText = string.Empty;
string newLineBeforeSuffix = !(Options.CurrentGameMode == CustomGameMode.BedWars && !self && GameStates.InGame) ? "\r\n" : " - ";
string suffix = Suffix.ToString().Trim();
string newLineBeforeSuffix = !(Options.CurrentGameMode == CustomGameMode.BedWars && !self && GameStates.InGame) ? "\n" : " - ";
string deathReason = !seer.IsAlive() && seer.KnowDeathReason(target) ? $"{newLineBeforeSuffix}<size=1.5>『{ColorString(GetRoleColor(CustomRoles.Doctor), GetVitalText(target.PlayerId))}』</size>" : string.Empty;
target.cosmetics.nameText.text = $"{roleText}{realName}{deathReason}{Mark}{newLineBeforeSuffix}{Suffix}";
target.cosmetics.nameText.text = $"{roleText}{realName}{deathReason}{Mark}{newLineBeforeSuffix}{suffix}";
// Camouflage
if (Camouflage.IsCamouflage) target.cosmetics.nameText.text = $"<size=0>{target.cosmetics.nameText.text}</size>";
+1 -1
View File
@@ -372,7 +372,7 @@ public static class SabotageSystemTypeRepairDamagePatch
public static bool Prefix(SabotageSystemType __instance, [HarmonyArgument(0)] PlayerControl player, [HarmonyArgument(1)] MessageReader msgReader)
{
Instance = __instance;
if (Options.CurrentGameMode != CustomGameMode.Standard || __instance.Timer > 0f) return false;
if (Options.CurrentGameMode is not (CustomGameMode.Standard or CustomGameMode.Snowdown) || __instance.Timer > 0f) return false;
SystemTypes systemTypes;
{
+2 -2
View File
@@ -91,7 +91,7 @@ internal static class RepairSystemPatch
if (!AmongUsClient.Instance.AmHost) return true; // Execute the following only on the host
if ((Options.CurrentGameMode != CustomGameMode.Standard || Options.DisableSabotage.GetBool()) && systemType == SystemTypes.Sabotage) return false;
if ((Options.CurrentGameMode is not (CustomGameMode.Standard or CustomGameMode.Snowdown) || Options.DisableSabotage.GetBool()) && systemType == SystemTypes.Sabotage) return false;
if (player.Is(CustomRoles.Fool) && systemType is SystemTypes.Comms or SystemTypes.Electrical) return false;
if (SubmergedCompatibility.IsSubmerged() && systemType is not (SystemTypes.Electrical or SystemTypes.Comms)) return true;
@@ -253,7 +253,7 @@ internal static class CloseDoorsPatch
{
public static bool Prefix([HarmonyArgument(0)] SystemTypes room)
{
bool allow = !Options.DisableSabotage.GetBool() && !AntiBlackout.SkipTasks && Options.CurrentGameMode is not CustomGameMode.SoloPVP and not CustomGameMode.FFA and not CustomGameMode.StopAndGo and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag and not CustomGameMode.NaturalDisasters and not CustomGameMode.RoomRush and not CustomGameMode.KingOfTheZones and not CustomGameMode.Quiz and not CustomGameMode.TheMindGame and not CustomGameMode.BedWars and not CustomGameMode.Deathrace and not CustomGameMode.Mingle;
bool allow = !Options.DisableSabotage.GetBool() && !AntiBlackout.SkipTasks && Options.CurrentGameMode is not CustomGameMode.SoloPVP and not CustomGameMode.FFA and not CustomGameMode.StopAndGo and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag and not CustomGameMode.NaturalDisasters and not CustomGameMode.RoomRush and not CustomGameMode.KingOfTheZones and not CustomGameMode.Quiz and not CustomGameMode.TheMindGame and not CustomGameMode.BedWars and not CustomGameMode.Deathrace and not CustomGameMode.Mingle and not CustomGameMode.Snowdown;
if (Doorjammer.JammedRooms.Contains(room)) allow = false;
if (SecurityGuard.BlockSabo.Count > 0) allow = false;
+1 -1
View File
@@ -177,7 +177,7 @@ internal static class RpcSetTasksPatch
}
// GM and Lazy Guy have no tasks
if (pc.Is(CustomRoles.GM) || pc.Is(CustomRoles.LazyGuy) || Options.CurrentGameMode is CustomGameMode.SoloPVP or CustomGameMode.FFA or CustomGameMode.HotPotato or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.Quiz or CustomGameMode.CaptureTheFlag or CustomGameMode.KingOfTheZones or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle)
if (pc.Is(CustomRoles.GM) || pc.Is(CustomRoles.LazyGuy) || Options.CurrentGameMode is CustomGameMode.SoloPVP or CustomGameMode.FFA or CustomGameMode.HotPotato or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.Quiz or CustomGameMode.CaptureTheFlag or CustomGameMode.KingOfTheZones or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle or CustomGameMode.Snowdown)
{
hasCommonTasks = false;
numShortTasks = 0;
+25 -16
View File
@@ -4943,6 +4943,7 @@
"GameModeTutorial.Speedrun": "<size=90%>The current game mode is <#800080>Speedrun</color>.</size>\\n<size=80%>Complete all tasks to gain the kill button, then kill everyone to win.\\nYou must do a task or kill within your time limit, else you suicide.</size>",
"GameModeTutorial.CaptureTheFlag": "<size=90%>The current game mode is <#1313c2>Capture The Flag</color>.</size>\\n<size=80%>Get to the base of the enemy team and press Vanish to pick up their flag.\\nBring it back to your own base for your team to win.\\nPress Kill on someone to tag them, this will get rid of that player temporarily or permanently.</size>",
"GameModeTutorial.NaturalDisasters": "<size=90%>The current game mode is <#03fc4a>Natural Disasters</color>.</size>\\n<size=80%>Various disasters will constantly spawn on the map.\\nSome kill you instantly, some just debuff you temporarily.\\nBe the last player alive to win!</size>",
"GameModeTutorial.Snowdown": "<size=90%>The current game mode is <#e4fdff>Snowdown</color>.</size>\\n<size=80%>Vanish to throw a snowball in your movement direction.\\nIf it hits someone, you get a coin. Pet to turn 20 coins into 1 point.\\nCall a critical sabotage to enter/exit the shop. Highest points wins!</size>",
"GameModeListTitle": "Game Modes",
"ModeStandard": "Gamemode: Standard",
"FFA": "FREE FOR ALL",
@@ -5484,6 +5485,29 @@
"MinglePlayer": "MINGLE",
"MinglePlayerInfo": "SURVIVE BY BEING IN THE RIGHT ROOMS",
"MinglePlayerInfoLong": "SURVIVE BY BEING IN THE RIGHT ROOMS",
"Snowdown": "SNOWDOWN",
"ModeSnowdown": "Gamemode: Snowdown",
"ModeDescribe.Snowdown": "In the Snowdown gamemode, the player that successfully throws the most snowballs at other players wins! You get 1 coin for every hit. You can spend coins on powerups in the shop, or turn pet to turn 20 coins into 1 point. Highest <u>points</u> (not coins) wins! Triggers:\\n- Vanish: Throw a snowball in your movement direction / Cycle between powerups while in shop\\n- Calling a ciritical Sabotage: Enter/Exit shop\\n- Pet: Turn 20 coins into 1 point / Buy selected powerup in shop\\n- Kill: Give the target one of your snowballs",
"Snowdown.SnowballThrowSpeedOption": "Snowball Throwing Speed",
"Snowdown.SnowballGainFrequencyOption": "Initial Snowball Gaining Frequency",
"Snowdown.MaxSnowballsInHandOption": "Max Snowballs Everyone Can Hold Initially",
"Snowdown.MaxCoinsInHandOption": "Max Coins Everyone Can Hold Initially",
"Snowdown.GameEndsAfterTimeOption": "Game Ends After a Certain Time",
"Snowdown.GameEndTimeOption": "Game Ends After",
"Snowdown.GameEndsWhenPointsReachedOption": "Game Ends When Someone Reached a Specific Number of Points",
"Snowdown.PointsToReachOption": "Points to Reach to Win",
"Snowdown.PowerUpPriceOption": "{powerup} Powerup Price (Coins)",
"Snowdown.PowerUp.FillUp": "Fill-Up",
"Snowdown.PowerUp.FillUp.Description": "Sets your snowball counter to its max",
"Snowdown.PowerUp.IncreaseCapacity": "Capacity Increment",
"Snowdown.PowerUp.IncreaseCapacity.Description": "Increases how many snowballs and coins you can hold",
"Snowdown.PowerUp.FasterPickup": "Pickup Boost",
"Snowdown.PowerUp.FasterPickup.Description": "Increases your rate of getting snowballs",
"Snowdown.ShopHelp": "Vanish: Cycle between powerups | Pet: Buy selected powerup | Sabotage: Exit shop",
"SnowdownButtonText": "THROW",
"SnowdownPlayer": "SNOWDOWN",
"SnowdownPlayerInfo": "THROW SNOWBALLS AT PLAYERS",
"SnowdownPlayerInfoLong": "THROW SNOWBALLS AT PLAYERS",
"Quiz": "QUIZ",
"ModeQuiz": "Gamemode: Quiz",
"ModeDescribe.Quiz": "In the Quiz game mode, you must answer questions correctly to survive. There are various difficulties with varying time limits. Each question has 5 answer choices: A, B, C, D, E. Each choice has a room name next to the answer. To pick an answer, you must enter the room corresponding to the answer. If you answer correctly, you survive. If you don't answer enough questions correctly, you eventually die or compete in a Free For All (FFA) match with the other players who got the questions wrong.",
@@ -10195,22 +10219,7 @@
"AGMR.Slot.RandomGroupId": "Number/ID of the random group to use for choosing the game mode for this slot/game",
"AGMR.Slot.Count": "How many times should we repeat this?",
"MenuTitle.AGMR.RandomGroup": "Random Group {index}",
"AGMR.RandomGroup.GMChance.Standard": "Chance of selecting Standard",
"AGMR.RandomGroup.GMChance.SoloPVP": "Chance of selecting Solo PVP",
"AGMR.RandomGroup.GMChance.FFA": "Chance of selecting FFA",
"AGMR.RandomGroup.GMChance.StopAndGo": "Chance of selecting Stop And Go",
"AGMR.RandomGroup.GMChance.HotPotato": "Chance of selecting Hot Potato",
"AGMR.RandomGroup.GMChance.HideAndSeek": "Chance of selecting (Custom) Hide And Seek",
"AGMR.RandomGroup.GMChance.Speedrun": "Chance of selecting Speedrun",
"AGMR.RandomGroup.GMChance.CaptureTheFlag": "Chance of selecting Capture The Flag",
"AGMR.RandomGroup.GMChance.NaturalDisasters": "Chance of selecting Natural Disasters",
"AGMR.RandomGroup.GMChance.RoomRush": "Chance of selecting Room Rush",
"AGMR.RandomGroup.GMChance.KingOfTheZones": "Chance of selecting King Of The Zones",
"AGMR.RandomGroup.GMChance.Quiz": "Chance of selecting Quiz",
"AGMR.RandomGroup.GMChance.TheMindGame": "Chance of selecting The Mind Game",
"AGMR.RandomGroup.GMChance.BedWars": "Chance of selecting Bed Wars",
"AGMR.RandomGroup.GMChance.Deathrace": "Chance of selecting Deathrace",
"AGMR.RandomGroup.GMChance.Mingle": "Chance of selecting Mingle",
"AGMR.RandomGroup.GMChance": "Chance of selecting {gm}",
"AutoGMRotationStatusText": "<u>AUTOMATIC GAME MODE ROTATION IS ENABLED!</u>",
"AutoGMRotationStatusText.NextGM": "Next",
"AutoGMRotationStatusText.GMPoll": "Player Vote (/gmpoll)",
+2 -2
View File
@@ -122,10 +122,10 @@ public class Swooper : RoleBase
try
{
if (UsedRole == CustomRoles.Chameleon)
AURoleOptions.EngineerCooldown = Cooldown + 1f;
AURoleOptions.EngineerCooldown = Cooldown + 2f;
else if (UsePhantomBasis.GetBool() && (UsedRole == CustomRoles.Swooper || UsePhantomBasisForNKs.GetBool()))
{
AURoleOptions.PhantomCooldown = Cooldown + 1f;
AURoleOptions.PhantomCooldown = Cooldown + 2f;
AURoleOptions.PhantomDuration = 0.1f;
}
}
+23 -20
View File
@@ -62,29 +62,32 @@ public class Executioner : RoleBase
{
PlayerIdList.Add(playerId);
LateTask.New(() =>
if (AmongUsClient.Instance.AmHost)
{
try
LateTask.New(() =>
{
List<PlayerControl> targetList = [];
targetList.AddRange(from target in Main.AllPlayerControls where playerId != target.PlayerId where CanTargetImpostor.GetBool() || !target.Is(CustomRoleTypes.Impostor) where CanTargetNeutralKiller.GetBool() || !target.IsNeutralKiller() where CanTargetNeutralBenign.GetBool() || !target.IsNeutralBenign() where CanTargetNeutralEvil.GetBool() || (!target.IsNeutralEvil() && !target.IsNeutralPariah()) where CanTargetCoven.GetBool() || !target.Is(CustomRoleTypes.Coven) where target.GetCustomRole() is not (CustomRoles.GM or CustomRoles.SuperStar) where Main.LoversPlayers.TrueForAll(x => x.PlayerId != playerId) select target);
targetList.AddRange(Main.AllPlayerControls.Where(x => x.IsCrewmate() && x.GetCustomRole() is not (CustomRoles.Swapper or CustomRoles.Mayor or CustomRoles.NiceGuesser or CustomRoles.Dictator)));
targetList.RemoveAll(x => x.GetCustomRole() is CustomRoles.Swapper or CustomRoles.Mayor or CustomRoles.NiceGuesser or CustomRoles.Dictator or CustomRoles.Executioner or CustomRoles.Terrorist or CustomRoles.GM);
if (!CanTargetNeutralBenign.GetBool() && !CanTargetNeutralEvil.GetBool() && !CanTargetNeutralKiller.GetBool()) targetList.RemoveAll(x => x.GetCustomRole().IsNeutral() || x.Is(CustomRoles.Bloodlust));
if (targetList.Count == 0)
try
{
LateTask.New(() => ChangeRole(Utils.GetPlayerById(playerId)), 8f, log: false);
return;
}
List<PlayerControl> targetList = [];
targetList.AddRange(from target in Main.AllPlayerControls where playerId != target.PlayerId where CanTargetImpostor.GetBool() || !target.Is(CustomRoleTypes.Impostor) where CanTargetNeutralKiller.GetBool() || !target.IsNeutralKiller() where CanTargetNeutralBenign.GetBool() || !target.IsNeutralBenign() where CanTargetNeutralEvil.GetBool() || (!target.IsNeutralEvil() && !target.IsNeutralPariah()) where CanTargetCoven.GetBool() || !target.Is(CustomRoleTypes.Coven) where target.GetCustomRole() is not (CustomRoles.GM or CustomRoles.SuperStar) where Main.LoversPlayers.TrueForAll(x => x.PlayerId != playerId) select target);
targetList.AddRange(Main.AllPlayerControls.Where(x => x.IsCrewmate() && x.GetCustomRole() is not (CustomRoles.Swapper or CustomRoles.Mayor or CustomRoles.NiceGuesser or CustomRoles.Dictator)));
targetList.RemoveAll(x => x.GetCustomRole() is CustomRoles.Swapper or CustomRoles.Mayor or CustomRoles.NiceGuesser or CustomRoles.Dictator or CustomRoles.Executioner or CustomRoles.Terrorist or CustomRoles.GM);
if (!CanTargetNeutralBenign.GetBool() && !CanTargetNeutralEvil.GetBool() && !CanTargetNeutralKiller.GetBool()) targetList.RemoveAll(x => x.GetCustomRole().IsNeutral() || x.Is(CustomRoles.Bloodlust));
PlayerControl selectedTarget = targetList.RandomElement();
Target[playerId] = selectedTarget.PlayerId;
SendRPC(playerId, selectedTarget.PlayerId, "SetTarget");
Logger.Info($"{Utils.GetPlayerById(playerId)?.GetNameWithRole().RemoveHtmlTags()}'s target: {selectedTarget.GetNameWithRole().RemoveHtmlTags()}", "Executioner");
}
catch (Exception ex) { Logger.Error(ex.ToString(), "Executioner.Add"); }
}, 0.5f, log: false);
if (targetList.Count == 0)
{
LateTask.New(() => ChangeRole(Utils.GetPlayerById(playerId)), 8f, log: false);
return;
}
PlayerControl selectedTarget = targetList.RandomElement();
Target[playerId] = selectedTarget.PlayerId;
SendRPC(playerId, selectedTarget.PlayerId, "SetTarget");
Logger.Info($"{Utils.GetPlayerById(playerId)?.GetNameWithRole().RemoveHtmlTags()}'s target: {selectedTarget.GetNameWithRole().RemoveHtmlTags()}", "Executioner");
}
catch (Exception ex) { Logger.Error(ex.ToString(), "Executioner.Add"); }
}, 0.5f, log: false);
}
}
public override void Remove(byte playerId)
@@ -95,7 +98,7 @@ public class Executioner : RoleBase
public static void SendRPC(byte executionerId, byte targetId = 0x73, string Progress = "")
{
if (!AmongUsClient.Instance.AmHost || !Utils.DoRPC) return;
if (!AmongUsClient.Instance.AmHost) return;
MessageWriter writer;
+2 -2
View File
@@ -123,10 +123,10 @@ public class Hater : RoleBase
target.SetRealKiller(killer);
killer.Kill(target);
target.Data.IsDead = true;
Main.PlayerStates[target.PlayerId].deathReason = PlayerState.DeathReason.Misfire;
Main.PlayerStates[target.PlayerId].deathReason = PlayerState.DeathReason.Sacrifice;
}
killer.Suicide(PlayerState.DeathReason.Sacrifice);
killer.Suicide(PlayerState.DeathReason.Misfire);
Logger.Info($"{killer.GetRealName()} killed incorrect target => misfire", "Hater");
return false;
}