Compare commits

...
6 Commits
11 changed files with 10377 additions and 33 deletions

No files matched your search

+5 -8
View File
@@ -4,15 +4,12 @@
> InnerSloth added a way to use chat commands without needing to spam the chat after it to hide who typed it. In the next few weeks, please adapt to using commands with a `/cmd ` prefix. So if you were to type `/bt 4 snitch`, you would now type `/cmd bt 4 snitch`. This is the last major version of EHR that allows the old way of typing commands. Starting v7.3.0, the mod will NOT spam the chat even if you don't use /cmd.
## Bug Fixes
- Fixed Mingle end summary for non-host modded clients
- Fixed `Chat During Game` causing black screen for vanilla clients in Mingle, Quiz and Natural Disasters
- Fixed color tags not being replaced to letter-only ones when they had an alpha channel
- Fixed Romantic, Follower, and Lawyer not always winning when their target/partner wins
- Bloodlust Bodyguard now works correctly
- Fixed Accumulator not being able to kill impostors
- **Fixed chat commands sometimes not working when using the 'cmd' prefix**
- Fixed an error that caused CNOs to not re-appear after meetings
- Fixed some roles appearing in draft choices when they shouldn't
## Changes
- Updated and improved the command auto-completion system and helper
- Quarry's target's kill cooldown gets set to 0 when they get selected as the target
- New welcome template message
- Regular numbers in chat messages are now replaced to full-width unicode numbers to bypass InnerSloth's digit limit (they could've just used char.IsDigit but oh well)
---
+2 -2
View File
@@ -42,8 +42,8 @@ public class Main : BasePlugin
private const string DebugKeyHash = "c0fd562955ba56af3ae20d7ec9e64c664f0facecef4b3e366e109306adeae29d";
private const string DebugKeySalt = "59687b";
private const string PluginGuid = "com.gurge44.endlesshostroles";
public const string PluginVersion = "7.2.1";
public const string PluginDisplayVersion = "7.2.1";
public const string PluginVersion = "7.2.2";
public const string PluginDisplayVersion = "7.2.2";
public const bool TestBuild = false;
public const string NeutralColor = "#ffab1b";
+7 -2
View File
@@ -377,8 +377,13 @@ namespace EHR
try
{
AmongUsClient.Instance.RemoveNetObject(playerControl);
Object.Destroy(playerControl.gameObject);
try
{
AmongUsClient.Instance.RemoveNetObject(playerControl);
Object.Destroy(playerControl.gameObject);
}
catch (Exception e) { Utils.ThrowException(e); }
playerControl = Object.Instantiate(AmongUsClient.Instance.PlayerPrefab, Vector2.zero, Quaternion.identity);
playerControl.PlayerId = 254;
playerControl.isNew = false;
+3 -1
View File
@@ -81,8 +81,10 @@ public static class TemplateManager
}
else
{
const string oldWelcomeTemplate = "welcome:<b><size=2.5>Welcome!</size></b>\\nThis is a <color=#ff0000>Modded</color> Lobby.\\n<size=90%>The mod is <b><color=#00ffff>Endless Host Roles</color> <color=#00ffa5>(EHR)</color></b> <b><color=#902efd>v{{ModVersion}}</b></color>.\\nThe mod was made by <color=#ffff00>Gurge44</color>.</size>\\n\\n<size=70%><b>/r</b> → Show all active roles\\n<b>/r <color=#ff0000>[role name]</color></b> → Show description & settings for that role</size>";
const string newWelcomeTemplate = "welcome:<b>Welcome!</b>\\n<size=80%>This is a <color=red>Modded</color> Lobby.\\nYou're playing <b><color=blue>Endless Host Roles</color> <color=purple>(EHR)</color> <color=orange>v{{ModVersion}}</b></color>.\\nThe mod was made by <color=yellow>Gurge44</color>.\\n\\n<b>/r</b> → Show all active roles\\n<b>/r <color=red>[role name]</color></b> → Show description & settings for that role";
string text = File.ReadAllText(TemplateFilePath, Encoding.GetEncoding("UTF-8"));
File.WriteAllText(TemplateFilePath, text.Replace("5PNwUaN5", "hkk2p9ggv4"));
File.WriteAllText(TemplateFilePath, text.Replace("5PNwUaN5", "hkk2p9ggv4").Replace(oldWelcomeTemplate, newWelcomeTemplate));
}
}
+35 -1
View File
@@ -1865,7 +1865,10 @@ public static class Utils
title = title.Replace("color=", string.Empty);
if (GameStates.CurrentServerType == GameStates.ServerType.Vanilla)
{
text = ReplaceHexColorsWithSafeColors(text);
text = ReplaceDigitsOutsideRichText(text);
}
PlayerControl sender = !addToHistory || GameStates.CurrentServerType == GameStates.ServerType.Vanilla ? PlayerControl.LocalPlayer : Main.AllAlivePlayerControls.MinBy(x => x.PlayerId) ?? Main.AllPlayerControls.MinBy(x => x.PlayerId) ?? PlayerControl.LocalPlayer;
@@ -2182,7 +2185,7 @@ public static class Utils
foreach (char c in text)
{
if (char.IsDigit(c) && digitCount == 5)
if (c is >= '0' and <= '9' && digitCount == 5)
{
int lastNewline = sb.ToString().LastIndexOf('\n');
@@ -2319,6 +2322,37 @@ public static class Utils
return CachedLetterOnlyHexColors;
}
static string ReplaceDigitsOutsideRichText(string text)
{
if (string.IsNullOrWhiteSpace(text)) return text;
StringBuilder sb = new(text.Length);
bool insideTag = false;
foreach (char c in text)
{
switch (c)
{
case '<':
insideTag = true;
sb.Append(c);
continue;
case '>':
insideTag = false;
sb.Append(c);
continue;
case >= '0' and <= '9' when !insideTag:
sb.Append((char)('0' + (c - '0')));
break;
default:
sb.Append(c);
break;
}
}
return sb.ToString();
}
}
public static HashSet<byte> DirtyName = [];
+18 -6
View File
@@ -382,7 +382,6 @@ internal static class ChatCommands
ChatControllerUpdatePatch.CurrentHistorySelection = ChatHistory.Count;
string[] args = text.Split(' ');
var canceled = false;
Main.IsChatCommand = true;
@@ -403,8 +402,8 @@ internal static class ChatCommands
if (text.StartsWith('/'))
{
if (AmongUsClient.Instance.AmHost && text.StartsWith("/cmd"))
text = "/" + text[4..].TrimStart();
Utils.CheckServerCommand(ref text, out _);
string[] args = text.Split(' ');
foreach ((string key, Command command) in Command.AllCommands)
{
@@ -1429,7 +1428,7 @@ internal static class ChatCommands
byte[] allPlayerIds = Main.AllPlayerControls.Select(x => x.PlayerId).ToArray();
bool rollSpawnChance = Options.DraftAffectedByRoleSpawnChances.GetBool();
List<CustomRoles> allRoles = Enum.GetValues<CustomRoles>().Where(x => x < CustomRoles.NotAssigned && x.IsEnable() && !x.IsForOtherGameMode() && !CustomHnS.AllHnSRoles.Contains(x) && !x.IsVanilla() && x is not CustomRoles.GM && (!rollSpawnChance || IRandom.Instance.Next(100) < x.GetMode())).Shuffle();
List<CustomRoles> allRoles = Enum.GetValues<CustomRoles>().Where(x => x < CustomRoles.NotAssigned && x.IsEnable() && !x.IsForOtherGameMode() && !CustomHnS.AllHnSRoles.Contains(x) && !x.IsVanilla() && x is not CustomRoles.GM && !ShouldNotSpawn(x) && (!rollSpawnChance || IRandom.Instance.Next(100) < x.GetMode())).Shuffle();
if (allRoles.Count < allPlayerIds.Length)
{
@@ -1479,6 +1478,20 @@ internal static class ChatCommands
if (DraftResult.Count >= DraftRoles.Count || !GameStates.IsLobby || GameStates.InGame) yield break;
}
}
static bool ShouldNotSpawn(CustomRoles role)
{
return role switch
{
CustomRoles.Ventriloquist when GameStates.CurrentServerType == GameStates.ServerType.Vanilla => true,
CustomRoles.Weatherman when Main.LIMap || GameStates.CurrentServerType == GameStates.ServerType.Vanilla => true,
CustomRoles.RoomRusher when Main.LIMap => true,
CustomRoles.Doctor when Options.EveryoneSeesDeathReasons.GetBool() => true,
CustomRoles.Commander when Main.NormalOptions.NumImpostors <= 1 && Commander.CannotSpawnAsSoloImp.GetBool() => true,
CustomRoles.Changeling when Changeling.GetAvailableRoles(true).Count == 0 => true,
_ => false
};
}
}
private static void DraftDescriptionCommand(PlayerControl player, string commandKey, string text, string[] args)
@@ -3637,8 +3650,6 @@ internal static class ChatCommands
}
}
string[] args = text.Split(' ');
if (!Starspawn.IsDayBreak)
{
if (GuessManager.GuesserMsg(player, text) ||
@@ -3664,6 +3675,7 @@ internal static class ChatCommands
if (text.StartsWith('/') && !player.IsModdedClient() && (!GameStates.IsMeeting || MeetingHud.Instance.state is not MeetingHud.VoteStates.Results and not MeetingHud.VoteStates.Proceeding))
{
Utils.CheckServerCommand(ref text, out bool spamRequired);
string[] args = text.Split(' ');
foreach ((string key, Command command) in Command.AllCommands)
{
+7 -7
View File
@@ -982,17 +982,17 @@ internal static class MeetingHudStartPatch
if (AmongUsClient.Instance.AmHost)
{
if (Options.SyncButtonMode.GetBool())
{
Utils.SendMessage(string.Format(GetString("Message.SyncButtonLeft"), Options.SyncedButtonCount.GetFloat() - Options.UsedButtonCount));
Logger.Info("The ship has " + (Options.SyncedButtonCount.GetFloat() - Options.UsedButtonCount) + " buttons left", "SyncButtonMode");
}
LateTask.New(() =>
{
if (Options.SyncButtonMode.GetBool())
{
Utils.SendMessage(string.Format(GetString("Message.SyncButtonLeft"), Options.SyncedButtonCount.GetFloat() - Options.UsedButtonCount));
Logger.Info("The ship has " + (Options.SyncedButtonCount.GetFloat() - Options.UsedButtonCount) + " buttons left", "SyncButtonMode");
}
TemplateManager.SendTemplate("OnMeeting", noErr: true, sendOption: SendOption.None);
if (MeetingStates.FirstMeeting) TemplateManager.SendTemplate("OnFirstMeeting", noErr: true, sendOption: SendOption.None);
}, 6f, log: false);
}, 6.5f, log: false);
NotifyRoleSkillOnMeetingStart();
+1 -1
View File
@@ -1,3 +1,3 @@
welcome:<b>Welcome!</b>\nThis is a <color=red>Modded</color> Lobby.\nThe mod is <b><color=blue>Endless Host Roles</color> <color=purple>(EHR)</color></b> <b><color=orange>v{{ModVersion}}</b></color>.\nThe mod was made by <color=yellow>Gurge44</color>.</size>\n\n<size=70%><b>/r</b> → Show all active roles\n<b>/r <color=red>[role name]</color></b> → Show description & settings for that role</size>
welcome:<b>Welcome!</b>\n<size=80%>This is a <color=red>Modded</color> Lobby.\nYou're playing <b><color=blue>Endless Host Roles</color> <color=purple>(EHR)</color> <color=orange>v{{ModVersion}}</b></color>.\nThe mod was made by <color=yellow>Gurge44</color>.\n\n<b>/r</b> → Show all active roles\n<b>/r <color=red>[role name]</color></b> → Show description & settings for that role
OnMeeting:<size=70%><b>/m</b> → See what your role does\n<b>/r</b> → Show all active roles\n<b>/r <color=#ff0000>[role name]</color></b> → Show description & settings for that role\n<b>/bt <color=#0000ff>[ID]</color> <color=#ff0000>[role name]</color></b> → Guess that player's role</size>
help:<size=110%><b><color=#ffc0cb>ENDLESS HOST ROLES</color>\nQUICK GUIDE<size=50%>\n(Guide by <color=#b700ff>@fluxwux</color> on Discord)\n<size=30%>Take notes, class.\n\n<size=80%>EHR is a pretty complicated mod, but don't worry! I'm here to teach you all the basics of the game so you can become the best player there is! First off, let's start with the most integral part of the mod...\n\n<size=100%><color=#4dff00>ROLES</color>\n<size=80%></b>Each player will be assigned a role at the start of the game. There are 4 role types. <b><color=#ff1919>Impostor</color>, <color=#8cffff>Crewmate</color>, <color=#ffab1b>Neutral</color>, & <color=#ff9ace>Add-Ons</color></b><size=70%>.\n\n<size=80%><b><color=#ff1919>Impostors</color> & <color=#8cffff>Crewmates</b>\n<size=60%></color>Self explanatory. These roles are given to the impostors or crewmates.\n\n<b><size=80%><color=#ffab1b>Neutrals</color></b>\n<size=60%><b>This isn't a team. These roles are for any roles that don't side with the impostors or crewmates.</b> Usually, they work alone toward a goal, depending on what their role does. that goal can range from getting their target voted out to win alone, killing everyone in the game, simply staying alive until the end to win with the winning team, and more.\n\n<size=80%><b><color=#ff9ace>Add-Ons</color><size=60%>\nAdd-Ons are roles that you can get on top of your team role.</b>Usually, they provide minor benefits, such as your killer automatically self-reporting your body, giving you a red screen flash when a body dies, and more. Some are even major additions, like being able to kill people after your death. <b>But be weary, as you may be assigned a harmful Add-On!</b>These roles may give you reduced vision, an inability to report bodies, an inability to fix sabotages, and more. <b>Remember that most (not all) Add-Ons are not team specific</b>, and can be assigned to anyone unless the host individually choses which roles go on what.\n\n<size=85%><b>It is important to remember that nobody should reveal their role unless they have a reason to <size=75%>(eg. Snitch finishing their tasks to out the impostors)\n\n<size=100%><color=#204d42>CHAT COMMANDS\n<size=80%></b>Chat commands</color> are a very useful asset, and each one has its own very useful effect. Examples of some meeting commands can be found below <b>vvvv\n\n<size=73%>/m:</b> Explains your role and Add-On(s) in detail. <b>A MUST USE</b> if you don't know what your role does.\n<b>/r [role name]:</b> Shows you what any role in the game does. Just type in the role. Typing /r on its own shows every role enabled in the host's settings.\n<b>/kcount (can be disabled by host):</b> Displays the amount of impostors and neutral killers remaining.\n<b>/l (not i, L):</b> Used in the lobby to show the results of the previous game. Displays who died to whom, everyone's roles, and who won the last game. Usually, hosts will make this message appear automatically after the round ends.\n\n<size=100%><b><color=#f8cd46>GUESSING<size=80%></b>\nGuessing</color>, or variants of guessing, are abilities that can be used by a select few roles. Usually, they involve <b>guessing a certain player's role</b> in order to kill them during a meeting, or achieve other effects. Sometimes, hosts will make it so entire teams or even all players can guess.\n\n<size=90%><b>HOW TO <color=#f8cd46>GUESS</color>\n<size=60%></b>This guide will show the most common way to guess using the /bt command.\n\n<size=80%><b>#1. Figure out the player's role\n<size=60%></b>Before you can guess, you need to find out what their role is. If you've found out what their role is, you can guess them in the next meeting. For example, I know blue is the Snitch, so moving on to step 2...\n\n<size=80%><b>#2. Type /id\n<size=60%></b>Typing /id will show a list of everyone and their respective ID, represented by a number from 1-15. I type /id and blue's ID is 7. Sometimes, you may already see the IDs of all players without having to type this command. Now all that's left is...\n\n<b><size=80%>#3. Type /bt [player ID] [role}\n<size=60%></b>In my case, it would be /bt 7 snitch. Now, if correct, I have killed blue, and now blue will have to wallow in their shame as a ghost. How sad. But if I misguess, I have to wallow in my shame as I die if I guess wrong. How embarrasing.\n\n<size=80%><b>Now, you should be caught up on all things EHR related. Now go out there and win some games!
+2 -2
View File
@@ -111,7 +111,7 @@
"RoleSubCategoryLimitOptions.Crewmate_Power.Max": "Maximum Crewmate Power Roles",
"RoleSubCategoryLimitOptions.Crewmate_Killing.Min": "Minimum Crewmate Killing Roles",
"RoleSubCategoryLimitOptions.Crewmate_Killing.Max": "Maximum Crewmate Killing Roles",
"RoleSubCategoryLimitOptions.Crewmate_Chaos.Min": "Minimum Crewmate Chaos Roles",
"RoleSubCategoryLimitOptions.Crewmate_Chaos.Min": "أدنى حد من أدوار الطاقم الفوضوي",
"RoleSubCategoryLimitOptions.Crewmate_Chaos.Max": "Maximum Crewmate Chaos Roles",
"RoleSubCategoryLimitOptions.Crewmate_Miscellaneous.Min": "Minimum Crewmate Miscellaneous Roles",
"RoleSubCategoryLimitOptions.Crewmate_Miscellaneous.Max": "Maximum Crewmate Miscellaneous Roles",
@@ -130,7 +130,7 @@
"FactionLimits.Coven.Min": "<#00ffa5>Minimum</color> <#ffffff>amount of</color> Coven Roles",
"FactionLimits.Coven.Max": "<#00a5ff>Maximum</color> <#ffffff>amount of</color> Coven Roles",
"MinMadmateRoles": "<#00ffa5>Minimum</color> amount of <#ff1919>Madmate</color> Roles",
"MaxMadmateRoles": "<#00a5ff>Maximum</color> amount of <#ff1919>Madmate</color> Roles",
"MaxMadmateRoles": "<#00a5ff>اقصى</color> عدد من أدوار <#ff1919>الطاقم الغاضب </color>",
"MinNNKs": "<#00ffa5>Minimum</color> amount of <#ffff00>Non-Killing</color> <#ffab1b>Neutral Roles</color> <#888888>(Benign, Evil, Pariah)</color>",
"MaxNNKs": "<#00a5ff>Maximum</color> amount of <#ffff00>Non-Killing</color> <#ffab1b>Neutral Roles</color> <#888888>(Benign, Evil, Pariah)</color>",
"NeutralNonKillingRoles": "<#ffff00>Non-Killing</color> Roles",
File diff suppressed because it is too large. Load diff
+2 -3
View File
@@ -100,7 +100,7 @@ internal class Merchant : RoleBase
public override void OnTaskComplete(PlayerControl player, int completedTaskCount, int totalTaskCount)
{
if (!player.IsAlive() || !player.Is(CustomRoles.Merchant) || AddonsSold[player.PlayerId] >= OptionMaxSell.GetInt()) return;
if (!player.IsAlive() || AddonsSold[player.PlayerId] >= OptionMaxSell.GetInt()) return;
if (Addons.Count == 0)
{
@@ -114,10 +114,9 @@ internal class Merchant : RoleBase
List<PlayerControl> availableTargets =
Main.AllAlivePlayerControls.Where(x =>
x.PlayerId != player.PlayerId
&& !Pelican.IsEaten(x.PlayerId)
&& !x.Is(addon)
&& CustomRolesHelper.CheckAddonConflict(addon, x)
&& (Cleanser.CleansedCanGetAddon.GetBool() || (!Cleanser.CleansedCanGetAddon.GetBool() && !x.Is(CustomRoles.Cleansed)))
&& (!x.Is(CustomRoles.Cleansed) || Cleanser.CleansedCanGetAddon.GetBool())
&& ((OptionCanTargetCrew.GetBool() && x.IsCrewmate()) ||
(OptionCanTargetImpostor.GetBool() && x.GetCustomRole().IsImpostor()) ||
(OptionCanTargetNeutral.GetBool() && (x.GetCustomRole().IsNeutral() || x.IsNeutralKiller())) ||