mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-20 01:46:25 +00:00
New game mode: Capture The Flag
This commit is contained in:
1 parent
7f740afa0d
commit
f4bca467c1
30 files changed
+573
-42
No files matched your search
@@ -363,6 +363,9 @@ public enum CustomRoles
|
||||
// Speedrun
|
||||
Runner,
|
||||
|
||||
// Capture The Flag
|
||||
CTFPlayer,
|
||||
|
||||
// GM
|
||||
GM,
|
||||
|
||||
|
||||
@@ -363,6 +363,9 @@ public enum CustomRoles
|
||||
// Speedrun
|
||||
Runner,
|
||||
|
||||
// Capture The Flag
|
||||
CTFPlayer,
|
||||
|
||||
// GM
|
||||
GM,
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AU/@EntryIndexedValue">AU</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CD/@EntryIndexedValue">CD</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CK/@EntryIndexedValue">CK</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CTF/@EntryIndexedValue">CTF</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DLL/@EntryIndexedValue">DLL</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DY/@EntryIndexedValue">DY</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EHR/@EntryIndexedValue">EHR</s:String>
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EHR
|
||||
{
|
||||
public static class CTFManager
|
||||
{
|
||||
private static OptionItem AlertTeamMembersOfFlagTaken;
|
||||
private static OptionItem TaggedPlayersGet;
|
||||
private static OptionItem WhenFlagCarrierGetsTagged;
|
||||
|
||||
private static readonly string[] TaggedPlayersGetOptions =
|
||||
[
|
||||
"CTF_TaggedPlayersGet.SentBackToBase",
|
||||
"CTF_TaggedPlayersGet.Killed"
|
||||
];
|
||||
|
||||
private static readonly string[] WhenFlagCarrierGetsTaggedOptions =
|
||||
[
|
||||
"CTF_WhenFlagCarrierGetsTagged.FlagIsDropped",
|
||||
"CTF_WhenFlagCarrierGetsTagged.FlagIsReturned"
|
||||
];
|
||||
|
||||
public static (Color Color, string Team) WinnerData = (Color.white, "No one wins");
|
||||
|
||||
private static Dictionary<byte, CTFTeam> PlayerTeams = [];
|
||||
private static Dictionary<CTFTeam, CTFTeamData> TeamData = [];
|
||||
private static Dictionary<byte, CTFPlayerData> PlayerData = [];
|
||||
private static bool ValidTag;
|
||||
|
||||
private static NetworkedPlayerInfo.PlayerOutfit YellowOutfit => new NetworkedPlayerInfo.PlayerOutfit().Set("", 5, "", "", "", "pet_coaltonpet", "");
|
||||
private static NetworkedPlayerInfo.PlayerOutfit BlueOutfit => new NetworkedPlayerInfo.PlayerOutfit().Set("", 1, "", "", "", "pet_coaltonpet", "");
|
||||
|
||||
private static (Vector2 Position, string RoomName) BlueFlagBase => Main.CurrentMap switch
|
||||
{
|
||||
MapNames.Skeld => (new(16.5f, -4.8f), Translator.GetString(SystemTypes.Nav.ToString())),
|
||||
MapNames.Mira => (new(-4.5f, 2.0f), Translator.GetString(SystemTypes.Launchpad.ToString())),
|
||||
MapNames.Dleks => (new(-16.5f, -4.8f), Translator.GetString(SystemTypes.Nav.ToString())),
|
||||
MapNames.Polus => (new(9.5f, -12.5f), Translator.GetString(SystemTypes.Electrical.ToString())),
|
||||
MapNames.Airship => (new(-23.5f, -1.6f), Translator.GetString(SystemTypes.Cockpit.ToString())),
|
||||
MapNames.Fungle => (new(-15.5f, -7.5f), Translator.GetString(SystemTypes.Kitchen.ToString())),
|
||||
_ => (Vector2.zero, string.Empty)
|
||||
};
|
||||
|
||||
private static (Vector2 Position, string RoomName) YellowFlagBase => Main.CurrentMap switch
|
||||
{
|
||||
MapNames.Skeld => (new(-20.5f, -5.5f), Translator.GetString(SystemTypes.Reactor.ToString())),
|
||||
MapNames.Mira => (new(17.8f, 23.0f), Translator.GetString(SystemTypes.Greenhouse.ToString())),
|
||||
MapNames.Dleks => (new(20.5f, -5.5f), Translator.GetString(SystemTypes.Reactor.ToString())),
|
||||
MapNames.Polus => (new(36.5f, -7.5f), Translator.GetString(SystemTypes.Laboratory.ToString())),
|
||||
MapNames.Airship => (new(33.5f, -1.5f), Translator.GetString(SystemTypes.CargoBay.ToString())),
|
||||
MapNames.Fungle => (new(22.2f, 13.7f), Translator.GetString(SystemTypes.Comms.ToString())),
|
||||
_ => (Vector2.zero, string.Empty)
|
||||
};
|
||||
|
||||
public static void SetupCustomOption()
|
||||
{
|
||||
const int id = 69_215_001;
|
||||
Color color = Utils.GetRoleColor(CustomRoles.CTFPlayer);
|
||||
|
||||
AlertTeamMembersOfFlagTaken = new BooleanOptionItem(id, "CTF_AlertTeamMembersOfFlagTaken", true, TabGroup.GameSettings)
|
||||
.SetGameMode(CustomGameMode.CaptureTheFlag)
|
||||
.SetColor(color);
|
||||
|
||||
TaggedPlayersGet = new StringOptionItem(id + 1, "CTF_TaggedPlayersGet", TaggedPlayersGetOptions, 0, TabGroup.GameSettings)
|
||||
.SetGameMode(CustomGameMode.CaptureTheFlag)
|
||||
.SetColor(color);
|
||||
|
||||
WhenFlagCarrierGetsTagged = new StringOptionItem(id + 2, "CTF_WhenFlagCarrierGetsTagged", WhenFlagCarrierGetsTaggedOptions, 0, TabGroup.GameSettings)
|
||||
.SetGameMode(CustomGameMode.CaptureTheFlag)
|
||||
.SetColor(color);
|
||||
}
|
||||
|
||||
public static bool KnowTargetRoleColor(PlayerControl seer, PlayerControl target, ref string color)
|
||||
{
|
||||
Color32 teamColor = PlayerTeams[target.PlayerId].GetTeamColor();
|
||||
color = $"#{teamColor.r:x2}{teamColor.g:x2}{teamColor.b:x2}{teamColor.a:x2}";
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string GetSuffixText(PlayerControl seer, PlayerControl target)
|
||||
{
|
||||
if (seer.PlayerId != target.PlayerId) return string.Empty;
|
||||
return $"<size=1.4>{GetStatistics(target.PlayerId).Replace(" - ", "\n")}</size>";
|
||||
}
|
||||
|
||||
public static string GetStatistics(byte id)
|
||||
{
|
||||
if (!PlayerData.TryGetValue(id, out var stats)) return string.Empty;
|
||||
return string.Format(Translator.GetString("CTF_PlayerStats"), Math.Round(stats.FlagTime, 1), stats.TagCount);
|
||||
}
|
||||
|
||||
public static int GetFlagTime(byte id)
|
||||
{
|
||||
return (int)Math.Round(PlayerData[id].FlagTime);
|
||||
}
|
||||
|
||||
public static int GetTagCount(byte id)
|
||||
{
|
||||
return PlayerData[id].TagCount;
|
||||
}
|
||||
|
||||
public static bool CheckForGameEnd(out GameOverReason reason)
|
||||
{
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
var aapc = Main.AllAlivePlayerControls;
|
||||
|
||||
switch (aapc.Length)
|
||||
{
|
||||
case 0:
|
||||
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Draw);
|
||||
CustomWinnerHolder.WinnerIds = Main.PlayerStates.Keys.ToHashSet();
|
||||
reason = GameOverReason.HumansDisconnect;
|
||||
return true;
|
||||
case 1:
|
||||
TeamData[PlayerTeams[aapc[0].PlayerId]].SetAsWinner();
|
||||
return true;
|
||||
default:
|
||||
// If WinnerData is already set, end the game
|
||||
if (WinnerData.Team != "No one wins") return true;
|
||||
|
||||
// If all players are on the same team, end the game
|
||||
if (aapc.All(x => PlayerTeams[x.PlayerId] == CTFTeam.Blue) || aapc.All(x => PlayerTeams[x.PlayerId] == CTFTeam.Yellow))
|
||||
{
|
||||
TeamData[PlayerTeams[aapc[0].PlayerId]].SetAsWinner();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnGameStart()
|
||||
{
|
||||
// Reset all data
|
||||
PlayerTeams = [];
|
||||
TeamData = [];
|
||||
WinnerData = (Color.white, "No one wins");
|
||||
PlayerData = Main.PlayerStates.Keys.ToDictionary(x => x, _ => new CTFPlayerData());
|
||||
ValidTag = false;
|
||||
|
||||
// Check if the current game mode is Capture The Flag
|
||||
if (Options.CurrentGameMode != CustomGameMode.CaptureTheFlag) return;
|
||||
|
||||
// Assign players to teams
|
||||
List<PlayerControl> players = Main.AllAlivePlayerControls.ToList();
|
||||
int blueCount = players.Count / 2;
|
||||
int yellowCount = players.Count - blueCount;
|
||||
HashSet<byte> bluePlayers = [];
|
||||
HashSet<byte> yellowPlayers = [];
|
||||
|
||||
for (int i = 0; i < blueCount; i++)
|
||||
{
|
||||
PlayerControl player = players.RandomElement();
|
||||
players.Remove(player);
|
||||
PlayerTeams[player.PlayerId] = CTFTeam.Blue;
|
||||
bluePlayers.Add(player.PlayerId);
|
||||
Utils.RpcChangeSkin(player, BlueOutfit);
|
||||
}
|
||||
|
||||
foreach (PlayerControl player in players)
|
||||
{
|
||||
PlayerTeams[player.PlayerId] = CTFTeam.Yellow;
|
||||
yellowPlayers.Add(player.PlayerId);
|
||||
Utils.RpcChangeSkin(player, YellowOutfit);
|
||||
}
|
||||
|
||||
// Create flags
|
||||
var blueFlagBase = BlueFlagBase;
|
||||
var yellowFlagBase = YellowFlagBase;
|
||||
|
||||
CustomNetObject blueFlag = new BlueFlag(blueFlagBase.Position);
|
||||
CustomNetObject yellowFlag = new YellowFlag(yellowFlagBase.Position);
|
||||
|
||||
// Create team data
|
||||
TeamData[CTFTeam.Blue] = new(CTFTeam.Blue, blueFlag, bluePlayers, byte.MaxValue);
|
||||
TeamData[CTFTeam.Yellow] = new(CTFTeam.Yellow, yellowFlag, yellowPlayers, byte.MaxValue);
|
||||
|
||||
// Teleport players to their respective bases
|
||||
LateTask.New(() =>
|
||||
{
|
||||
foreach (var pc in Main.AllAlivePlayerControls)
|
||||
{
|
||||
if (PlayerTeams.TryGetValue(pc.PlayerId, out var team))
|
||||
{
|
||||
switch (team)
|
||||
{
|
||||
case CTFTeam.Blue:
|
||||
pc.TP(blueFlagBase.Position);
|
||||
pc.Notify(string.Format(Translator.GetString("CTF_Notify_EnemyTeamRoom"), yellowFlagBase.RoomName));
|
||||
break;
|
||||
case CTFTeam.Yellow:
|
||||
pc.TP(yellowFlagBase.Position);
|
||||
pc.Notify(string.Format(Translator.GetString("CTF_Notify_EnemyTeamRoom"), blueFlagBase.RoomName));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ValidTag = true;
|
||||
}, 10f, "CTFManager.OnGameStart");
|
||||
}
|
||||
|
||||
public static void OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (!ValidTag) return;
|
||||
|
||||
if (TeamData.FindFirst(x => x.Value.FlagCarrier == target.PlayerId, out var kvp))
|
||||
{
|
||||
kvp.Value.DropFlag();
|
||||
if (WhenFlagCarrierGetsTagged.GetValue() == 1)
|
||||
{
|
||||
kvp.Value.Flag.TP(kvp.Key.GetOppositeTeam().GetFlagBase().Position);
|
||||
}
|
||||
}
|
||||
|
||||
switch (TaggedPlayersGet.GetValue())
|
||||
{
|
||||
case 0:
|
||||
target.TP(PlayerTeams[target.PlayerId].GetFlagBase().Position);
|
||||
break;
|
||||
case 1:
|
||||
target.Suicide();
|
||||
break;
|
||||
}
|
||||
|
||||
PlayerData[killer.PlayerId].TagCount++;
|
||||
Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: killer);
|
||||
}
|
||||
|
||||
public static void OnPet(PlayerControl pc)
|
||||
{
|
||||
Logger.Info($"{pc.GetRealName()} petted their pet", "CTF.OnPet");
|
||||
// If the player is near the enemy's flag, pick it up
|
||||
var pos = pc.Pos();
|
||||
var enemy = TeamData[PlayerTeams[pc.PlayerId].GetOppositeTeam()];
|
||||
if (enemy.IsNearFlag(pos)) enemy.PickUpFlag(pc.PlayerId);
|
||||
}
|
||||
|
||||
static Color GetTeamColor(this CTFTeam team)
|
||||
{
|
||||
return team switch
|
||||
{
|
||||
CTFTeam.Blue => Color.blue,
|
||||
CTFTeam.Yellow => Color.yellow,
|
||||
_ => Color.white
|
||||
};
|
||||
}
|
||||
|
||||
static string GetTeamName(this CTFTeam team)
|
||||
{
|
||||
return team switch
|
||||
{
|
||||
CTFTeam.Blue => Translator.GetString("CTF_BlueTeamWins"),
|
||||
CTFTeam.Yellow => Translator.GetString("CTF_YellowTeamWins"),
|
||||
_ => string.Empty
|
||||
};
|
||||
}
|
||||
|
||||
static CTFTeam GetOppositeTeam(this CTFTeam team)
|
||||
{
|
||||
return team switch
|
||||
{
|
||||
CTFTeam.Blue => CTFTeam.Yellow,
|
||||
CTFTeam.Yellow => CTFTeam.Blue,
|
||||
_ => CTFTeam.Blue
|
||||
};
|
||||
}
|
||||
|
||||
static (Vector2 Position, string RoomName) GetFlagBase(this CTFTeam team)
|
||||
{
|
||||
return team switch
|
||||
{
|
||||
CTFTeam.Blue => BlueFlagBase,
|
||||
CTFTeam.Yellow => YellowFlagBase,
|
||||
_ => (Vector2.zero, string.Empty)
|
||||
};
|
||||
}
|
||||
|
||||
enum CTFTeam
|
||||
{
|
||||
Blue,
|
||||
Yellow
|
||||
}
|
||||
|
||||
class CTFTeamData(CTFTeam team, CustomNetObject flag, HashSet<byte> players, byte flagCarrier)
|
||||
{
|
||||
public CustomNetObject Flag { get; } = flag;
|
||||
private HashSet<byte> Players { get; } = players;
|
||||
public byte FlagCarrier { get; private set; } = flagCarrier;
|
||||
|
||||
public void SetAsWinner()
|
||||
{
|
||||
WinnerData = (team.GetTeamColor(), team.GetTeamName());
|
||||
CustomWinnerHolder.WinnerIds = Players;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (FlagCarrier == byte.MaxValue) return;
|
||||
var flagCarrierPc = FlagCarrier.GetPlayer();
|
||||
if (flagCarrierPc == null || !flagCarrierPc.IsAlive())
|
||||
{
|
||||
DropFlag();
|
||||
return;
|
||||
}
|
||||
|
||||
Flag.TP(flagCarrierPc.Pos());
|
||||
PlayerData[FlagCarrier].FlagTime += Time.fixedDeltaTime;
|
||||
Utils.NotifyRoles(SpecifySeer: flagCarrierPc, SpecifyTarget: flagCarrierPc);
|
||||
|
||||
CTFTeam enemy = team.GetOppositeTeam();
|
||||
if (Translator.GetString(Flag.playerControl.GetPlainShipRoom().RoomId.ToString()) == enemy.GetFlagBase().RoomName)
|
||||
TeamData[enemy].SetAsWinner();
|
||||
}
|
||||
|
||||
public void PickUpFlag(byte id)
|
||||
{
|
||||
if (FlagCarrier == id) return;
|
||||
|
||||
FlagCarrier = id;
|
||||
Update();
|
||||
|
||||
if (AlertTeamMembersOfFlagTaken.GetBool())
|
||||
{
|
||||
var enemyPlayers = TeamData[team.GetOppositeTeam()].Players;
|
||||
enemyPlayers.Select(x => x.GetPlayer()).DoIf(x => x != null, x => x.Notify("CTF_FlagTaken"));
|
||||
}
|
||||
}
|
||||
|
||||
public void DropFlag() => FlagCarrier = byte.MaxValue;
|
||||
|
||||
public bool IsNearFlag(Vector2 pos) => Vector2.Distance(Flag.Position, pos) < 1f;
|
||||
}
|
||||
|
||||
class CTFPlayerData
|
||||
{
|
||||
public int TagCount { get; set; }
|
||||
public float FlagTime { get; set; }
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.FixedUpdate))]
|
||||
class FixedUpdatePatch
|
||||
{
|
||||
public static void Postfix(PlayerControl __instance)
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost || !GameStates.IsInTask || Options.CurrentGameMode != CustomGameMode.Speedrun || Main.HasJustStarted || !__instance.IsHost()) return;
|
||||
|
||||
TeamData.Values.Do(x => x.Update());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,9 @@ namespace EHR
|
||||
public static readonly List<CustomNetObject> AllObjects = [];
|
||||
private static int MaxId = -1;
|
||||
protected int Id;
|
||||
private PlayerControl playerControl;
|
||||
public PlayerControl playerControl;
|
||||
private float PlayerControlTimer;
|
||||
private Vector2 Position;
|
||||
public Vector2 Position;
|
||||
private string Sprite;
|
||||
|
||||
protected void RpcChangeSprite(string sprite)
|
||||
@@ -219,7 +219,7 @@ namespace EHR
|
||||
{
|
||||
foreach (var pc in PlayerControl.AllPlayerControls)
|
||||
{
|
||||
if (!trapArea.VisibleList.Contains(pc.PlayerId))
|
||||
if (trapArea.VisibleList.Contains(pc.PlayerId))
|
||||
Show(pc);
|
||||
}
|
||||
}
|
||||
@@ -312,6 +312,7 @@ namespace EHR
|
||||
Sprite = sprite;
|
||||
++MaxId;
|
||||
Id = MaxId;
|
||||
if (MaxId > 10000) MaxId = 0;
|
||||
AllObjects.Add(this);
|
||||
foreach (var pc in PlayerControl.AllPlayerControls)
|
||||
{
|
||||
@@ -548,4 +549,22 @@ namespace EHR
|
||||
Show(catcher);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class YellowFlag : CustomNetObject
|
||||
{
|
||||
public YellowFlag(Vector2 position)
|
||||
{
|
||||
CreateNetObject("<size=100%><font=\"VCR SDF\"><line-height=72%><#000000>\u2588<#ffff00>\u2588<#ffff00>\u2588<#ffff00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<br><#000000>\u2588<#ffff00>\u2588<#ffff00>\u2588<#ffff00>\u2588<#ffff00>\u2588<#ffff00>\u2588<br><#000000>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<#ffff00>\u2588<#ffff00>\u2588<#ffff00>\u2588<br><#000000>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<br><#000000>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<br><#000000>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<br></line-height></size>", position);
|
||||
Main.AllPlayerControls.Do(Show);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class BlueFlag : CustomNetObject
|
||||
{
|
||||
public BlueFlag(Vector2 position)
|
||||
{
|
||||
CreateNetObject("<size=100%><font=\"VCR SDF\"><line-height=72%><#000000>\u2588<#0000ff>\u2588<#0000ff>\u2588<#0000ff>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<br><#000000>\u2588<#0000ff>\u2588<#0000ff>\u2588<#0000ff>\u2588<#0000ff>\u2588<#0000ff>\u2588<br><#000000>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<#0000ff>\u2588<#0000ff>\u2588<#0000ff>\u2588<br><#000000>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<br><#000000>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<br><#000000>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<alpha=#00>\u2588<br></line-height></size>", position);
|
||||
Main.AllPlayerControls.Do(Show);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,9 @@ internal static class CustomRoleSelector
|
||||
case CustomGameMode.Speedrun:
|
||||
AssignRoleToEveryone(CustomRoles.Runner);
|
||||
return;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
AssignRoleToEveryone(CustomRoles.CTFPlayer);
|
||||
return;
|
||||
case CustomGameMode.HideAndSeek:
|
||||
HnSManager.AssignRoles();
|
||||
RoleResult = HnSManager.PlayerRoles.ToDictionary(x => Utils.GetPlayerById(x.Key), x => x.Value.Role);
|
||||
|
||||
@@ -53,7 +53,8 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Killer or
|
||||
CustomRoles.Tasker or
|
||||
CustomRoles.Potato or
|
||||
CustomRoles.Runner;
|
||||
CustomRoles.Runner or
|
||||
CustomRoles.CTFPlayer;
|
||||
|
||||
public static RoleBase GetRoleClass(this CustomRoles role)
|
||||
{
|
||||
@@ -366,6 +367,8 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Potato => RoleTypes.Crewmate,
|
||||
// Speedrun
|
||||
CustomRoles.Runner => RoleTypes.Crewmate,
|
||||
// Capture The Flag
|
||||
CustomRoles.CTFPlayer => RoleTypes.Impostor,
|
||||
// Standard
|
||||
CustomRoles.Executioner => Executioner.CRoleChangeRoles[Executioner.ChangeRolesAfterTargetKilled.GetValue()].GetDYRole(),
|
||||
CustomRoles.Sheriff => UsePets && Sheriff.UsePet.GetBool() ? RoleTypes.GuardianAngel : RoleTypes.Impostor,
|
||||
@@ -659,6 +662,7 @@ internal static class CustomRolesHelper
|
||||
|
||||
public static bool PetActivatedAbility(this CustomRoles role)
|
||||
{
|
||||
if (Options.CurrentGameMode == CustomGameMode.CaptureTheFlag) return true;
|
||||
if (!Options.UsePets.GetBool()) return false;
|
||||
if (role.UsesPetInsteadOfKill()) return true;
|
||||
|
||||
@@ -1037,7 +1041,8 @@ internal static class CustomRolesHelper
|
||||
public static CountTypes GetCountTypes(this CustomRoles role) => role switch
|
||||
{
|
||||
CustomRoles.GM => CountTypes.OutOfGame,
|
||||
CustomRoles.Sidekick => CountTypes.Jackal,
|
||||
CustomRoles.Sidekick when Jackal.SidekickCountMode.GetValue() == 0 => CountTypes.Jackal,
|
||||
CustomRoles.Sidekick when Jackal.SidekickCountMode.GetValue() == 1 => CountTypes.OutOfGame,
|
||||
CustomRoles.Deathknight => CountTypes.Necromancer,
|
||||
CustomRoles.Parasite => CountTypes.Impostor,
|
||||
CustomRoles.Crewpostor => CountTypes.Impostor,
|
||||
|
||||
@@ -546,7 +546,7 @@ static class ExtendedPlayerControl
|
||||
|
||||
public static string GetNameWithRole(this PlayerControl player, bool forUser = false)
|
||||
{
|
||||
return $"{player?.Data?.PlayerName}" + (GameStates.IsInGame && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun ? $" ({player?.GetAllRoleName(forUser).RemoveHtmlTags().Replace('\n', ' ')})" : string.Empty);
|
||||
return $"{player?.Data?.PlayerName}" + (GameStates.IsInGame && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag ? $" ({player?.GetAllRoleName(forUser).RemoveHtmlTags().Replace('\n', ' ')})" : string.Empty);
|
||||
}
|
||||
|
||||
public static string GetRoleColorCode(this PlayerControl player)
|
||||
@@ -656,16 +656,18 @@ static class ExtendedPlayerControl
|
||||
public static bool CanUseKillButton(this PlayerControl pc)
|
||||
{
|
||||
if (!pc.IsAlive()) return false;
|
||||
if (Mastermind.ManipulatedPlayers.ContainsKey(pc.PlayerId)) return true;
|
||||
if (Penguin.IsVictim(pc)) return false;
|
||||
|
||||
switch (Options.CurrentGameMode)
|
||||
{
|
||||
case CustomGameMode.HotPotato or CustomGameMode.MoveAndStop:
|
||||
case CustomGameMode.Speedrun when !SpeedrunManager.CanKill.Contains(pc.PlayerId):
|
||||
return false;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Mastermind.ManipulatedPlayers.ContainsKey(pc.PlayerId)) return true;
|
||||
if (Penguin.IsVictim(pc)) return false;
|
||||
if (Pelican.IsEaten(pc.PlayerId)) return false;
|
||||
if (pc.Data.Role.Role == RoleTypes.GuardianAngel) return false;
|
||||
if (pc.Is(CustomRoles.Bloodlust)) return true;
|
||||
@@ -721,6 +723,8 @@ static class ExtendedPlayerControl
|
||||
CustomRoles.Potato => false,
|
||||
// Speedrun
|
||||
CustomRoles.Runner => false,
|
||||
// Capture The Flag
|
||||
CustomRoles.CTFPlayer => false,
|
||||
|
||||
_ => Main.PlayerStates.TryGetValue(pc.PlayerId, out var state) && state.Role.CanUseImpostorVentButton(pc)
|
||||
};
|
||||
|
||||
@@ -167,6 +167,7 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS
|
||||
}
|
||||
|
||||
break;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
case CustomGameMode.Speedrun:
|
||||
case CustomGameMode.HotPotato:
|
||||
case CustomGameMode.MoveAndStop:
|
||||
|
||||
@@ -124,6 +124,7 @@ public class PlayerState(byte playerId)
|
||||
}
|
||||
|
||||
if (!role.Is(Team.Impostor)) SubRoles.ToArray().DoIf(x => x.IsImpOnlyAddon(), RemoveSubRole);
|
||||
if (role is CustomRoles.Sidekick or CustomRoles.Necromancer or CustomRoles.Deathknight) RemoveSubRole(CustomRoles.Nimble);
|
||||
}
|
||||
|
||||
CheckMurderPatch.TimeSinceLastKill.Remove(PlayerId);
|
||||
|
||||
+14
-5
@@ -376,6 +376,13 @@ public static class GuessManager
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Markseeker.PlayerIdList.Any(x => Main.PlayerStates[x].Role is Markseeker { TargetRevealed: true } ms && ms.MarkedId == target.PlayerId))
|
||||
{
|
||||
if (!isUI) Utils.SendMessage(GetString("GuessMarkseekerTarget"), pc.PlayerId);
|
||||
else pc.ShowPopUp(GetString("GuessMarkseekerTarget"));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check whether add-on guessing is allowed
|
||||
if (!forceAllowGuess)
|
||||
{
|
||||
@@ -447,15 +454,17 @@ public static class GuessManager
|
||||
{
|
||||
if (DoubleShot.CheckGuess(pc, isUI)) return true;
|
||||
|
||||
guesserSuicide = true;
|
||||
Logger.Msg($"{guesserSuicide}", "guesserSuicide1");
|
||||
if (!isUI) Utils.SendMessage(GetString("GuessCrewRole"), pc.PlayerId, Utils.ColorString(Color.cyan, GetString("MessageFromGurge44")));
|
||||
else pc.ShowPopUp(Utils.ColorString(Color.cyan, GetString("MessageFromGurge44")) + "\n" + GetString("GuessCrewRole"));
|
||||
return true;
|
||||
}
|
||||
else if (pc.Is(CustomRoles.EvilGuesser) && target.Is(CustomRoleTypes.Impostor) && !Options.EGCanGuessImp.GetBool())
|
||||
{
|
||||
if (DoubleShot.CheckGuess(pc, isUI)) return true;
|
||||
|
||||
guesserSuicide = true;
|
||||
Logger.Msg($"{guesserSuicide}", "guesserSuicide2");
|
||||
if (!isUI) Utils.SendMessage(GetString("GuessImpRole"), pc.PlayerId, Utils.ColorString(Color.cyan, GetString("MessageFromGurge44")));
|
||||
else pc.ShowPopUp(Utils.ColorString(Color.cyan, GetString("MessageFromGurge44")) + "\n" + GetString("GuessImpRole"));
|
||||
return true;
|
||||
}
|
||||
else if (!target.Is(role))
|
||||
{
|
||||
@@ -465,7 +474,7 @@ public static class GuessManager
|
||||
Logger.Msg($"{guesserSuicide}", "guesserSuicide3");
|
||||
}
|
||||
|
||||
if (Options.GuesserDoesntDieOnMisguess.GetBool())
|
||||
if (guesserSuicide && Options.GuesserDoesntDieOnMisguess.GetBool())
|
||||
{
|
||||
if (!isUI) Utils.SendMessage(GetString("MisguessButNoSuicide"), pc.PlayerId, Utils.ColorString(Color.yellow, GetString("MessageFromGurge44")));
|
||||
else pc.ShowPopUp(Utils.ColorString(Color.yellow, GetString("MessageFromGurge44")) + "\n" + GetString("MisguessButNoSuicide"));
|
||||
|
||||
@@ -56,6 +56,8 @@ public static class NameColorManager
|
||||
case CustomGameMode.Speedrun when SpeedrunManager.CanKill.Contains(target.PlayerId):
|
||||
color = Main.ImpostorColor;
|
||||
return true;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
return CTFManager.KnowTargetRoleColor(seer, target, ref color);
|
||||
}
|
||||
|
||||
var seerRoleClass = Main.PlayerStates[seer.PlayerId].Role;
|
||||
|
||||
@@ -23,6 +23,7 @@ public enum CustomGameMode
|
||||
HotPotato = 0x05,
|
||||
HideAndSeek = 0x06,
|
||||
Speedrun = 0x07,
|
||||
CaptureTheFlag = 0x08,
|
||||
All = int.MaxValue
|
||||
}
|
||||
|
||||
@@ -55,7 +56,8 @@ public static class Options
|
||||
"MoveAndStop",
|
||||
"HotPotato",
|
||||
"HideAndSeek",
|
||||
"Speedrun"
|
||||
"Speedrun",
|
||||
"CaptureTheFlag"
|
||||
];
|
||||
|
||||
private static Dictionary<CustomRoles, int> roleCounts;
|
||||
@@ -773,6 +775,7 @@ public static class Options
|
||||
4 => CustomGameMode.HotPotato,
|
||||
5 => CustomGameMode.HideAndSeek,
|
||||
6 => CustomGameMode.Speedrun,
|
||||
7 => CustomGameMode.CaptureTheFlag,
|
||||
_ => CustomGameMode.Standard
|
||||
};
|
||||
|
||||
@@ -802,7 +805,7 @@ public static class Options
|
||||
var sb = new System.Text.StringBuilder();
|
||||
var grouped = Enum.GetValues<CustomRoles>().GroupBy(x =>
|
||||
{
|
||||
if (x is CustomRoles.GM or CustomRoles.Philantropist or CustomRoles.Konan or CustomRoles.NotAssigned or CustomRoles.LovingCrewmate or CustomRoles.LovingImpostor or CustomRoles.Convict || x.IsForOtherGameMode() || x.IsVanilla() || x.ToString().Contains("EHR")) return 4;
|
||||
if (x is CustomRoles.GM or CustomRoles.Philantropist or CustomRoles.Konan or CustomRoles.NotAssigned or CustomRoles.LovingCrewmate or CustomRoles.LovingImpostor or CustomRoles.Convict || x.IsForOtherGameMode() || x.IsVanilla() || x.ToString().Contains("EHR") || HnSManager.AllHnSRoles.Contains(x)) return 4;
|
||||
if (x.IsAdditionRole()) return 3;
|
||||
if (x.IsImpostor() || x.IsMadmate()) return 0;
|
||||
if (x.IsNeutral()) return 1;
|
||||
@@ -827,8 +830,8 @@ public static class Options
|
||||
}
|
||||
|
||||
const string path = "./roles.txt";
|
||||
if (!File.Exists(path)) File.Create(path).Close();
|
||||
File.WriteAllText(path, sb.ToString());
|
||||
if (!System.IO.File.Exists(path)) System.IO.File.Create(path).Close();
|
||||
System.IO.File.WriteAllText(path, sb.ToString());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -1259,6 +1262,8 @@ public static class Options
|
||||
SpeedrunManager.SetupCustomOption();
|
||||
// Hide And Seek
|
||||
HnSManager.SetupCustomOption();
|
||||
// Capture The Flag
|
||||
CTFManager.SetupCustomOption();
|
||||
|
||||
yield return null;
|
||||
|
||||
|
||||
+17
-2
@@ -26,7 +26,6 @@ using Newtonsoft.Json;
|
||||
using UnityEngine;
|
||||
using static EHR.Translator;
|
||||
|
||||
|
||||
namespace EHR;
|
||||
|
||||
/*
|
||||
@@ -638,6 +637,7 @@ public static class Utils
|
||||
case CustomGameMode.MoveAndStop: return true;
|
||||
case CustomGameMode.HotPotato: return false;
|
||||
case CustomGameMode.Speedrun: return true;
|
||||
case CustomGameMode.CaptureTheFlag: return false;
|
||||
case CustomGameMode.HideAndSeek: return HnSManager.HasTasks(p);
|
||||
}
|
||||
|
||||
@@ -828,6 +828,7 @@ public static class Utils
|
||||
|
||||
public static bool IsRoleTextEnabled(PlayerControl __instance)
|
||||
{
|
||||
if (Options.CurrentGameMode == CustomGameMode.CaptureTheFlag) return false;
|
||||
if (__instance.PlayerId == PlayerControl.LocalPlayer.PlayerId || Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.SoloKombat or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato or CustomGameMode.Speedrun || (Options.CurrentGameMode == CustomGameMode.HideAndSeek && HnSManager.IsRoleTextEnabled(PlayerControl.LocalPlayer, __instance)) || Main.VisibleTasksCount && PlayerControl.LocalPlayer.Data.IsDead && Options.GhostCanSeeOtherRoles.GetBool() || PlayerControl.LocalPlayer.Is(CustomRoles.Mimic) && Main.VisibleTasksCount && __instance.Data.IsDead && Options.MimicCanSeeDeadRoles.GetBool()) return true;
|
||||
|
||||
switch (__instance.GetCustomRole())
|
||||
@@ -1328,6 +1329,7 @@ public static class Utils
|
||||
}
|
||||
|
||||
break;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
case CustomGameMode.Speedrun:
|
||||
case CustomGameMode.HotPotato:
|
||||
case CustomGameMode.HideAndSeek:
|
||||
@@ -1668,6 +1670,7 @@ public static class Utils
|
||||
CustomGameMode.MoveAndStop => $"<color=#00ffa5><size=1.7>{modeText}</size></color>\r\n{name}",
|
||||
CustomGameMode.HotPotato => $"<color=#e8cd46><size=1.7>{modeText}</size></color>\r\n{name}",
|
||||
CustomGameMode.HideAndSeek => $"<color=#345eeb><size=1.7>{modeText}</size></color>\r\n{name}",
|
||||
CustomGameMode.CaptureTheFlag => $"<color=#1313c2><size=1.7>{modeText}</size></color>\r\n{name}",
|
||||
CustomGameMode.Speedrun => ColorString(GetRoleColor(CustomRoles.Speedrunner), $"<size=1.7>{modeText}</size>\r\n") + name,
|
||||
_ => name
|
||||
};
|
||||
@@ -1932,6 +1935,9 @@ public static class Utils
|
||||
case CustomGameMode.HideAndSeek:
|
||||
SelfSuffix.Append(HnSManager.GetSuffixText(seer, seer));
|
||||
break;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
SelfSuffix.Append(CTFManager.GetSuffixText(seer, seer));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1942,7 +1948,7 @@ public static class Utils
|
||||
if (Options.CurrentGameMode == CustomGameMode.FFA && FFAManager.FFATeamMode.GetBool())
|
||||
SeerRealName = SeerRealName.ApplyNameColorData(seer, seer, isForMeeting);
|
||||
|
||||
if (!isForMeeting && MeetingStates.FirstMeeting && Options.ChangeNameToRoleInfo.GetBool() && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun)
|
||||
if (!isForMeeting && MeetingStates.FirstMeeting && Options.ChangeNameToRoleInfo.GetBool() && Options.CurrentGameMode is not CustomGameMode.FFA and not CustomGameMode.MoveAndStop and not CustomGameMode.HotPotato and not CustomGameMode.Speedrun and not CustomGameMode.CaptureTheFlag)
|
||||
{
|
||||
var team = CustomTeamManager.GetCustomTeam(seer.PlayerId);
|
||||
if (team != null)
|
||||
@@ -2164,6 +2170,8 @@ public static class Utils
|
||||
? $"<size={fontSize}>{target.GetDisplayRoleName(seer.PlayerId != target.PlayerId && !seer.Data.IsDead)}{GetProgressText(target)}</size>\r\n"
|
||||
: string.Empty;
|
||||
|
||||
if (Options.CurrentGameMode == CustomGameMode.CaptureTheFlag) TargetRoleText = string.Empty;
|
||||
|
||||
if (!GameStates.IsLobby)
|
||||
{
|
||||
if (!seer.Data.IsDead && seer.IsRevealedPlayer(target) && target.Is(CustomRoles.Trickster))
|
||||
@@ -2248,6 +2256,9 @@ public static class Utils
|
||||
case CustomGameMode.HideAndSeek:
|
||||
TargetSuffix.Append(HnSManager.GetSuffixText(seer, target));
|
||||
break;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
TargetSuffix.Append(CTFManager.GetSuffixText(seer, target));
|
||||
break;
|
||||
}
|
||||
|
||||
Main.PlayerStates.Values.Do(x => TargetSuffix.Append(x.Role.GetSuffix(seer, target, isMeeting: isForMeeting)));
|
||||
@@ -2448,6 +2459,7 @@ public static class Utils
|
||||
CustomRoles.QuickShooter => QuickShooter.ShapeshiftCooldown.GetInt(),
|
||||
CustomRoles.Disperser => Disperser.DisperserShapeshiftCooldown.GetInt(),
|
||||
CustomRoles.Twister => Twister.ShapeshiftCooldown.GetInt(),
|
||||
CustomRoles.Abyssbringer => Abyssbringer.BlackHolePlaceCooldown.GetInt(),
|
||||
CustomRoles.Warlock => Warlock.IsCursed ? -1 : Warlock.ShapeshiftCooldown.GetInt(),
|
||||
CustomRoles.Swiftclaw => Swiftclaw.DashCD.GetInt() + (includeDuration ? Swiftclaw.DashDuration.GetInt() : 0),
|
||||
CustomRoles.Parasite => (int)Parasite.SSCD + (includeDuration ? (int)Parasite.SSDur : 0),
|
||||
@@ -2846,6 +2858,9 @@ public static class Utils
|
||||
int time = HotPotatoManager.GetSurvivalTime(id);
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} - <#e8cd46>Survived: <#ffffff>{(time == 0 ? "Until The End</color>" : $"{time}</color>s")}</color> ({GetVitalText(id, true)})";
|
||||
break;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
summary = $"{ColorString(Main.PlayerColors[id], name)} - {CTFManager.GetStatistics(id)} ({GetVitalText(id, true)})";
|
||||
break;
|
||||
}
|
||||
|
||||
return check && GetDisplayRoleName(id, true).RemoveHtmlTags().Contains("INVALID:NotAssigned")
|
||||
|
||||
@@ -368,6 +368,7 @@ class GameEndChecker
|
||||
public static void SetPredicateToHotPotato() => Predicate = new HotPotatoGameEndPredicate();
|
||||
public static void SetPredicateToSpeedrun() => Predicate = new SpeedrunGameEndPredicate();
|
||||
public static void SetPredicateToHideAndSeek() => Predicate = new HideAndSeekGameEndPredicate();
|
||||
public static void SetPredicateToCaptureTheFlag() => Predicate = new CaptureTheFlagGameEndPredicate();
|
||||
|
||||
class NormalGameEndPredicate : GameEndPredicate
|
||||
{
|
||||
@@ -713,6 +714,20 @@ class GameEndChecker
|
||||
}
|
||||
}
|
||||
|
||||
class CaptureTheFlagGameEndPredicate : GameEndPredicate
|
||||
{
|
||||
public override bool CheckForEndGame(out GameOverReason reason)
|
||||
{
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
return WinnerIds.Count <= 0 && CheckGameEndByLivingPlayers(out reason);
|
||||
}
|
||||
|
||||
private static bool CheckGameEndByLivingPlayers(out GameOverReason reason)
|
||||
{
|
||||
return CTFManager.CheckForGameEnd(out reason);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class GameEndPredicate
|
||||
{
|
||||
/// <summary>Checks the game ending condition and stores the value in CustomWinnerHolder. </summary>
|
||||
|
||||
@@ -20,6 +20,9 @@ public static class OptionsMenuBehaviourStartPatch
|
||||
private static ClientOptionItem LongMode;
|
||||
private static ClientOptionItem ShowPlayerInfoInLobby;
|
||||
private static ClientOptionItem LobbyMusic;
|
||||
#if DEBUG
|
||||
private static ClientOptionItem GodMode;
|
||||
#endif
|
||||
|
||||
public static void Postfix(OptionsMenuBehaviour __instance)
|
||||
{
|
||||
@@ -44,7 +47,7 @@ public static class OptionsMenuBehaviourStartPatch
|
||||
|
||||
static void UnlockFPSButtonToggle()
|
||||
{
|
||||
Application.targetFrameRate = Main.UnlockFps.Value ? 165 : 60;
|
||||
Application.targetFrameRate = Main.UnlockFps.Value ? 120 : 60;
|
||||
Logger.SendInGame(string.Format(Translator.GetString("FPSSetTo"), Application.targetFrameRate));
|
||||
}
|
||||
}
|
||||
@@ -170,9 +173,6 @@ public static class OptionsMenuBehaviourStartPatch
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if DEBUG
|
||||
private static ClientOptionItem GodMode;
|
||||
#endif
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(OptionsMenuBehaviour), nameof(OptionsMenuBehaviour.Close))]
|
||||
|
||||
@@ -932,11 +932,13 @@ public class GameSettingMenuPatch
|
||||
|
||||
var GameSettingsLabel = __instance.GameSettingsButton.transform.parent.parent.FindChild("GameSettingsLabel").GetComponent<TextMeshPro>();
|
||||
GameSettingsLabel.DestroyTranslator();
|
||||
GameSettingsLabel.text = Translator.GetString($"Mode{Options.CurrentGameMode}").Split(':').Last().TrimStart(' ');
|
||||
var gameModeText = Translator.GetString($"Mode{Options.CurrentGameMode}").Split(':').Last().TrimStart(' ');
|
||||
if (gameModeText.Length >= 15) gameModeText = $"<size=70%>{gameModeText}</size>";
|
||||
GameSettingsLabel.text = gameModeText;
|
||||
if (IsRussian)
|
||||
{
|
||||
GameSettingsLabel.transform.localScale = new(0.7f, 0.7f, 1f);
|
||||
GameSettingsLabel.transform.localPosition = new Vector3(-3.77f, 1.62f, -4);
|
||||
GameSettingsLabel.transform.localPosition = new(-3.77f, 1.62f, -4);
|
||||
}
|
||||
|
||||
var GameSettingsLabelPos = GameSettingsLabel.transform.localPosition;
|
||||
|
||||
@@ -58,6 +58,7 @@ class SetRecommendationsPatch
|
||||
case CustomGameMode.SoloKombat:
|
||||
case CustomGameMode.FFA:
|
||||
case CustomGameMode.HotPotato:
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
__instance.CrewLightMod = __instance.ImpostorLightMod = 1.25f;
|
||||
__instance.NumImpostors = 3;
|
||||
__instance.NumCommonTasks = 0;
|
||||
@@ -65,6 +66,7 @@ class SetRecommendationsPatch
|
||||
__instance.NumShortTasks = 0;
|
||||
__instance.KillCooldown = 0f;
|
||||
__instance.NumEmergencyMeetings = 0;
|
||||
__instance.KillDistance = 0;
|
||||
break;
|
||||
case CustomGameMode.Speedrun:
|
||||
case CustomGameMode.MoveAndStop:
|
||||
|
||||
@@ -422,6 +422,7 @@ class SetHudActivePatch
|
||||
__instance.ImpostorVentButton?.ToggleVisible(false);
|
||||
__instance.AbilityButton?.ToggleVisible(false);
|
||||
return;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
case CustomGameMode.FFA:
|
||||
__instance.ReportButton?.ToggleVisible(false);
|
||||
__instance.SabotageButton?.ToggleVisible(false);
|
||||
|
||||
+24
-4
@@ -77,6 +77,16 @@ class SetUpRoleTextPatch
|
||||
__instance.RoleBlurbText.text = GetString("RunnerInfo");
|
||||
break;
|
||||
}
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
{
|
||||
var color = ColorUtility.TryParseHtmlString("#1313c2", out var c) ? c : new(255, 255, 255, 255);
|
||||
__instance.YouAreText.transform.gameObject.SetActive(false);
|
||||
__instance.RoleText.text = GetString("CTFPlayer");
|
||||
__instance.RoleText.color = color;
|
||||
__instance.RoleBlurbText.color = color;
|
||||
__instance.RoleBlurbText.text = GetString("CTFPlayerInfo");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
CustomRoles role = lp.GetCustomRole();
|
||||
@@ -181,7 +191,8 @@ class BeginCrewmatePatch
|
||||
{
|
||||
public static bool Prefix(IntroCutscene __instance, ref Il2CppSystem.Collections.Generic.List<PlayerControl> teamToDisplay)
|
||||
{
|
||||
if (PlayerControl.LocalPlayer.Is(CustomRoleTypes.Neutral) && !PlayerControl.LocalPlayer.GetCustomRole().IsMadmate())
|
||||
CustomRoles role = PlayerControl.LocalPlayer.GetCustomRole();
|
||||
if (PlayerControl.LocalPlayer.Is(CustomRoleTypes.Neutral) && !role.IsMadmate())
|
||||
{
|
||||
teamToDisplay = new();
|
||||
teamToDisplay.Add(PlayerControl.LocalPlayer);
|
||||
@@ -197,7 +208,7 @@ class BeginCrewmatePatch
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (PlayerControl.LocalPlayer.Is(CustomRoles.Madmate) || PlayerControl.LocalPlayer.GetCustomRole().IsMadmate())
|
||||
else if (PlayerControl.LocalPlayer.Is(CustomRoles.Madmate) || role.IsMadmate())
|
||||
{
|
||||
teamToDisplay = new();
|
||||
teamToDisplay.Add(PlayerControl.LocalPlayer);
|
||||
@@ -206,13 +217,13 @@ class BeginCrewmatePatch
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PlayerControl.LocalPlayer.GetCustomRole() == CustomRoles.LovingCrewmate)
|
||||
if (role == CustomRoles.LovingCrewmate || PlayerControl.LocalPlayer.Is(CustomRoles.Lovers))
|
||||
{
|
||||
teamToDisplay = new();
|
||||
teamToDisplay.Add(PlayerControl.LocalPlayer);
|
||||
teamToDisplay.Add(Main.LoversPlayers.FirstOrDefault(x => x.PlayerId != PlayerControl.LocalPlayer.PlayerId));
|
||||
}
|
||||
else if (PlayerControl.LocalPlayer.GetCustomRole() == CustomRoles.LovingImpostor)
|
||||
else if (role == CustomRoles.LovingImpostor)
|
||||
{
|
||||
teamToDisplay.Add(Main.LoversPlayers.FirstOrDefault(x => x.PlayerId != PlayerControl.LocalPlayer.PlayerId));
|
||||
}
|
||||
@@ -555,6 +566,15 @@ class BeginCrewmatePatch
|
||||
__instance.ImpostorText.text = GetString("SubText.HideAndSeek");
|
||||
break;
|
||||
}
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
{
|
||||
__instance.TeamTitle.text = $"<size=70%>{GetString("CTFPlayer")}</size>";
|
||||
__instance.TeamTitle.color = __instance.BackgroundBar.material.color = new Color32(19, 19, 194, byte.MaxValue);
|
||||
PlayerControl.LocalPlayer.Data.Role.IntroSound = GetIntroSound(RoleTypes.Engineer);
|
||||
__instance.ImpostorText.gameObject.SetActive(true);
|
||||
__instance.ImpostorText.text = GetString("CTFPlayerInfo");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if (Input.GetKey(KeyCode.RightShift))
|
||||
|
||||
@@ -70,7 +70,7 @@ public static class MainMenuManagerPatch
|
||||
|
||||
UpdateButton.gameObject.SetActive(ModUpdater.HasUpdate);
|
||||
|
||||
Application.targetFrameRate = Main.UnlockFps.Value ? 9999 : 60;
|
||||
Application.targetFrameRate = Main.UnlockFps.Value ? 120 : 60;
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MainMenuManager), nameof(MainMenuManager.LateUpdate)), HarmonyPostfix]
|
||||
@@ -152,7 +152,7 @@ public static class MainMenuManagerPatch
|
||||
|
||||
WebsiteButton.gameObject.SetActive(true);
|
||||
|
||||
Application.targetFrameRate = Main.UnlockFps.Value ? 9999 : 60;
|
||||
Application.targetFrameRate = Main.UnlockFps.Value ? 120 : 60;
|
||||
}
|
||||
|
||||
private static PassiveButton CreateButton(string name, Vector3 localPosition, Color32 normalColor, Color32 hoverColor, Action action, string label, Vector2? scale = null)
|
||||
|
||||
@@ -815,6 +815,9 @@ internal class SelectRolesPatch
|
||||
case CustomGameMode.HideAndSeek:
|
||||
HnSManager.StartSeekerBlindTime();
|
||||
break;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
CTFManager.OnGameStart();
|
||||
break;
|
||||
}
|
||||
|
||||
HudManager.Instance.SetHudActive(true);
|
||||
@@ -856,6 +859,9 @@ internal class SelectRolesPatch
|
||||
case CustomGameMode.HideAndSeek:
|
||||
GameEndChecker.SetPredicateToHideAndSeek();
|
||||
break;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
GameEndChecker.SetPredicateToCaptureTheFlag();
|
||||
break;
|
||||
}
|
||||
|
||||
GameOptionsSender.AllSenders.Clear();
|
||||
|
||||
+16
-1
@@ -62,7 +62,7 @@ class EndGamePatch
|
||||
if (date == DateTime.MinValue) continue;
|
||||
var killerId = value.GetRealKiller();
|
||||
var gmIsFM = Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.MoveAndStop;
|
||||
var gmIsFMHH = gmIsFM || Options.CurrentGameMode is CustomGameMode.HotPotato or CustomGameMode.HideAndSeek or CustomGameMode.Speedrun;
|
||||
var gmIsFMHH = gmIsFM || Options.CurrentGameMode is CustomGameMode.HotPotato or CustomGameMode.HideAndSeek or CustomGameMode.Speedrun or CustomGameMode.CaptureTheFlag;
|
||||
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))})");
|
||||
@@ -261,6 +261,14 @@ class SetEverythingUpPatch
|
||||
WinnerText.color = Main.PlayerColors[winnerId];
|
||||
goto EndOfText;
|
||||
}
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
{
|
||||
var winnerData = CTFManager.WinnerData;
|
||||
__instance.BackgroundBar.material.color = winnerData.Color;
|
||||
WinnerText.text = winnerData.Team;
|
||||
WinnerText.color = winnerData.Color;
|
||||
goto EndOfText;
|
||||
}
|
||||
}
|
||||
|
||||
if (CustomWinnerHolder.WinnerTeam == CustomWinner.CustomTeam)
|
||||
@@ -426,6 +434,13 @@ class SetEverythingUpPatch
|
||||
sb.Append("\n\u3000 ").Append(EndGamePatch.SummaryText[id]);
|
||||
break;
|
||||
}
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
{
|
||||
var list = cloneRoles.OrderByDescending(CTFManager.GetFlagTime);
|
||||
foreach (var id in list.Where(EndGamePatch.SummaryText.ContainsKey))
|
||||
sb.Append("\n\u3000 ").Append(EndGamePatch.SummaryText[id]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
sb.Append("</b>\n");
|
||||
|
||||
@@ -19,16 +19,20 @@ class LocalPetPatch
|
||||
|
||||
public static bool Prefix(PlayerControl __instance)
|
||||
{
|
||||
if (!Options.UsePets.GetBool()) return true;
|
||||
if (!Options.UsePets.GetBool() && Options.CurrentGameMode != CustomGameMode.CaptureTheFlag) return true;
|
||||
if (!(AmongUsClient.Instance.AmHost && AmongUsClient.Instance.AmClient)) return true;
|
||||
if (GameStates.IsLobby || !__instance.IsAlive()) return true;
|
||||
|
||||
if (__instance.petting) return true;
|
||||
__instance.petting = true;
|
||||
|
||||
if (Options.CurrentGameMode == CustomGameMode.CaptureTheFlag) goto Skip;
|
||||
|
||||
if (!LastProcess.ContainsKey(__instance.PlayerId)) LastProcess.TryAdd(__instance.PlayerId, Utils.TimeStamp - 2);
|
||||
if (LastProcess[__instance.PlayerId] + 1 >= Utils.TimeStamp) return true;
|
||||
|
||||
Skip:
|
||||
|
||||
ExternalRpcPetPatch.Prefix(__instance.MyPhysics, (byte)RpcCalls.Pet);
|
||||
|
||||
LastProcess[__instance.PlayerId] = Utils.TimeStamp;
|
||||
@@ -50,7 +54,7 @@ class ExternalRpcPetPatch
|
||||
|
||||
public static void Prefix(PlayerPhysics __instance, [HarmonyArgument(0)] byte callID)
|
||||
{
|
||||
if (GameStates.IsLobby || !Options.UsePets.GetBool() || !AmongUsClient.Instance.AmHost || (RpcCalls)callID != RpcCalls.Pet) return;
|
||||
if (GameStates.IsLobby || (!Options.UsePets.GetBool() && Options.CurrentGameMode != CustomGameMode.CaptureTheFlag) || !AmongUsClient.Instance.AmHost || (RpcCalls)callID != RpcCalls.Pet) return;
|
||||
|
||||
var pc = __instance.myPlayer;
|
||||
var physics = __instance;
|
||||
@@ -69,6 +73,12 @@ class ExternalRpcPetPatch
|
||||
&& pc.GetCustomRole().PetActivatedAbility())
|
||||
physics.CancelPet();
|
||||
|
||||
if (Options.CurrentGameMode == CustomGameMode.CaptureTheFlag)
|
||||
{
|
||||
CTFManager.OnPet(pc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LastProcess.ContainsKey(pc.PlayerId)) LastProcess.TryAdd(pc.PlayerId, Utils.TimeStamp - 2);
|
||||
if (LastProcess[pc.PlayerId] + 1 >= Utils.TimeStamp) return;
|
||||
LastProcess[pc.PlayerId] = Utils.TimeStamp;
|
||||
|
||||
@@ -2,7 +2,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using AmongUs.GameOptions;
|
||||
using EHR.AddOns.Common;
|
||||
using EHR.AddOns.Crewmate;
|
||||
@@ -225,6 +224,9 @@ class CheckMurderPatch
|
||||
case CustomGameMode.HideAndSeek:
|
||||
HnSManager.OnCheckMurder(killer, target);
|
||||
return false;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
CTFManager.OnCheckMurder(killer, target);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ToiletMaster.OnAnyoneCheckMurderStart(killer, target)) return false;
|
||||
@@ -1218,7 +1220,7 @@ class FixedUpdatePatch
|
||||
}
|
||||
}
|
||||
|
||||
public static Task DoPostfix(PlayerControl __instance)
|
||||
public static void DoPostfix(PlayerControl __instance)
|
||||
{
|
||||
var player = __instance;
|
||||
var playerId = player.PlayerId;
|
||||
@@ -1471,6 +1473,13 @@ class FixedUpdatePatch
|
||||
var seer = PlayerControl.LocalPlayer;
|
||||
var target = __instance;
|
||||
|
||||
if (target.Is(CustomRoles.Car))
|
||||
{
|
||||
target.cosmetics.nameText.text = Car.Name;
|
||||
RoleText.enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
bool self = seer.PlayerId == target.PlayerId;
|
||||
|
||||
Mark.Clear();
|
||||
@@ -1675,6 +1684,9 @@ class FixedUpdatePatch
|
||||
case CustomGameMode.HideAndSeek:
|
||||
Suffix.Append(HnSManager.GetSuffixText(seer, target));
|
||||
break;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
Suffix.Append(CTFManager.GetSuffixText(seer, target));
|
||||
break;
|
||||
}
|
||||
|
||||
if (Options.CurrentGameMode == CustomGameMode.SoloKombat)
|
||||
@@ -1732,8 +1744,6 @@ class FixedUpdatePatch
|
||||
RoleText.transform.SetLocalY(0.2f);
|
||||
}
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public static void AddExtraAbilityUsesOnFinishedTasks(PlayerControl player)
|
||||
|
||||
@@ -1906,6 +1906,7 @@
|
||||
"GuessAdtRole": "Unfortunately, the Host's settings do not allow you to guess add-ons",
|
||||
"GuessImpRole": "Unfortunately, the Host's settings do not allow Impostors to guess Impostor roles.",
|
||||
"GuessCrewRole": "Unfortunately, the Host's settings do not allow crewmates to guess crewmate roles.",
|
||||
"GuessMarkseekerTarget": "You can't guess the target of the Markseeker",
|
||||
"GuessKill": "<b>{0}</b> was guessed",
|
||||
"GuessNull": "Please select an ID of a living player to guess their role",
|
||||
"GuessHelp": "Instructions: /bt [Player ID] [Role Name] \\nExample: /bt 3 Bait \\nYou can see the player IDs before everyone's names \\n or use the /id command to list the player IDs",
|
||||
@@ -3914,6 +3915,24 @@
|
||||
"Runner": "Speedrunner",
|
||||
"RunnerInfo": "COMPLETE TASKS AND KILL EVERYONE FIRST TO WIN",
|
||||
"RunnerInfoLong": "COMPLETE TASKS AND KILL EVERYONE FIRST TO WIN",
|
||||
"CaptureTheFlag": "CAPTURE THE FLAG",
|
||||
"ModeCaptureTheFlag": "Gamemode: Capture The Flag",
|
||||
"ModeDescribe.CaptureTheFlag": "In the Capture The Flag gamemode, there are two teams: Yellow and Blue. Each team has a flag in their base. The goal is to capture the enemy's flag and bring it back to your base. The first team to capture the enemy's flag wins!\\n\\nYou can grab the flag by petting your pet near it.\\n\\nPlayers can be tagged by the enemy team, which will send them back to their base or kill them, depending on the settings. Players who are carrying a flag cannot tag other players.",
|
||||
"CTF_AlertTeamMembersOfFlagTaken": "Alert Team Members When their Flag is Taken",
|
||||
"CTF_TaggedPlayersGet": "Tagged Players get",
|
||||
"CTF_TaggedPlayersGet.SentBackToBase": "Sent Back to Base",
|
||||
"CTF_TaggedPlayersGet.Killed": "Killed",
|
||||
"CTF_WhenFlagCarrierGetsTagged": "When the Flag Carrier Gets Tagged, the Flag is",
|
||||
"CTF_WhenFlagCarrierGetsTagged.FlagIsDropped": "Dropped",
|
||||
"CTF_WhenFlagCarrierGetsTagged.FlagIsReturned": "Returned",
|
||||
"CTFPlayer": "Capture The Flag",
|
||||
"CTFPlayerInfo": "CAPTURE THE ENEMY'S FLAG TO WIN",
|
||||
"CTFPlayerInfoLong": "CAPTURE THE ENEMY'S FLAG TO WIN",
|
||||
"CTF_FlagTaken": "The enemy took your flag!",
|
||||
"CTF_BlueTeamWins": "The Blue Team wins!",
|
||||
"CTF_YellowTeamWins": "The Yellow Team wins!",
|
||||
"CTF_PlayerStats": "<#00ffa5>Total Flag Carrying Time: <#ffffff>{0}s</color> - Total Tags: <#ffffff>{1}</color></color>",
|
||||
"CTF_Notify_EnemyTeamRoom": "The enemy team is in <#00ffa5>{0}</color>",
|
||||
"HideAndSeek": "HIDE AND SEEK",
|
||||
"ModeHideAndSeek": "Gamemode: Hide And Seek",
|
||||
"ModeDescribe.HideAndSeek": "In the Hide And Seek gamemode, seekers have to kill all hiders, while hiders have to complete all tasks without being killed in order to win.\\nIf the time limit reaches 0, hiders win.\\n\\nRoles: Hider, Seeker, Fox, Troll, Jumper, Detector, Jet, Dasher, Locator, Venter, Agent, Taskinator",
|
||||
@@ -4344,8 +4363,8 @@
|
||||
"BlackHolePlaceCooldown": "Black Hole Place Cooldown",
|
||||
"BlackHoleDespawnMode": "Black Hole Despawn Mode",
|
||||
"BlackHoleDespawnTime": "Time After Black Hole Despawns",
|
||||
"Abyssbringer.Suffix": "<size=80%><#00ffa5>Number of players consumed by</color> {0} <#00ffa5>active black holes:</color></size><size=70%>\\n",
|
||||
"Abyssbringer.Suffix.BlackHole": "<#00ffa5>{0}:</color> {1}</size>",
|
||||
"Abyssbringer.Suffix": "<size=80%><#00ffa5>Number of players consumed by</color> {0} <#00ffa5>active black holes:</color></size><size=70%>\\n{1}",
|
||||
"Abyssbringer.Suffix.BlackHole": "<#00ffa5>{0}:</color> {1}",
|
||||
"After1PlayerEaten": "After 1 Player Was Eaten",
|
||||
"BlackHoleMovesTowardsNearestPlayer": "Black Hole Moves Towards Nearest Player",
|
||||
"BlackHoleMoveSpeed": "Black Hole Moving Speed",
|
||||
|
||||
@@ -93,8 +93,9 @@ namespace EHR.Crewmate
|
||||
|
||||
var distance = PropelDistance.GetFloat();
|
||||
var collider = target.Collider;
|
||||
for (var newPos = target.Pos(); Vector2.Distance(pos, newPos) < distance && !PhysicsHelpers.AnythingBetween(collider, collider.bounds.center, newPos, Constants.ShipOnlyMask, false) && GameStates.IsInTask; newPos += addVector)
|
||||
for (var newPos = target.Pos(); Vector2.Distance(pos, newPos) < distance && GameStates.IsInTask; newPos += addVector)
|
||||
{
|
||||
if (PhysicsHelpers.AnythingBetween(collider, collider.bounds.center, newPos, Constants.ShipOnlyMask, false)) break;
|
||||
target.TP(newPos, log: false);
|
||||
yield return new WaitForSeconds(0.05f);
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ namespace EHR.Crewmate
|
||||
if (!OptionCanSellHarmful.GetBool()) GroupedAddons.Remove(AddonTypes.Harmful);
|
||||
if (!OptionCanSellHelpful.GetBool()) GroupedAddons.Remove(AddonTypes.Helpful);
|
||||
if (!OptionCanSellMixed.GetBool()) GroupedAddons.Remove(AddonTypes.Mixed);
|
||||
GroupedAddons.Remove(AddonTypes.ImpOnly);
|
||||
|
||||
Addons = GroupedAddons.Values.Flatten().ToList();
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace EHR.Impostor
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
private static OptionItem BlackHolePlaceCooldown;
|
||||
public static OptionItem BlackHolePlaceCooldown;
|
||||
private static OptionItem BlackHoleDespawnMode;
|
||||
private static OptionItem BlackHoleDespawnTime;
|
||||
private static OptionItem BlackHoleMovesTowardsNearestPlayer;
|
||||
|
||||
Reference in new issue
Block a user