diff --git a/EHR.csproj b/EHR.csproj
index f70dd895c..2add63ad8 100644
--- a/EHR.csproj
+++ b/EHR.csproj
@@ -74,7 +74,7 @@
-
+
diff --git a/FodyWeavers.xml b/FodyWeavers.xml
deleted file mode 100644
index 1b14c30b2..000000000
--- a/FodyWeavers.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
- Csv
-
-
-
\ No newline at end of file
diff --git a/FodyWeavers.xsd b/FodyWeavers.xsd
deleted file mode 100644
index 3169e5aab..000000000
--- a/FodyWeavers.xsd
+++ /dev/null
@@ -1,188 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- A list of assembly names to exclude from the default action of
- "embed all Copy Local references", delimited with line breaks
-
-
-
-
-
- A list of assembly names to include from the default action of
- "embed all Copy Local references", delimited with line breaks.
-
-
-
-
-
- A list of runtime assembly names to exclude from the default
- action of "embed all Copy Local references", delimited with line breaks
-
-
-
-
-
- A list of runtime assembly names to include from the default
- action of "embed all Copy Local references", delimited with line breaks.
-
-
-
-
-
- A list of unmanaged 32 bit assembly names to include, delimited
- with line breaks.
-
-
-
-
-
- A list of unmanaged 64 bit assembly names to include, delimited
- with line breaks.
-
-
-
-
-
- The order of preloaded assemblies, delimited with line breaks.
-
-
-
-
-
-
- This will copy embedded files to disk before loading them into memory.
- This is helpful for some scenarios that expected an assembly to be loaded from a
- physical file.
-
-
-
-
-
- Controls if .pdbs for reference assemblies are also embedded.
-
-
-
-
-
- Controls if runtime assemblies are also embedded.
-
-
-
-
- Controls whether the runtime assemblies are embedded with their full
- path or only with their assembly name.
-
-
-
-
-
- Embedded assemblies are compressed by default, and uncompressed when
- they are loaded. You can turn compression off with this option.
-
-
-
-
-
- As part of Costura, embedded assemblies are no longer included as part
- of the build. This cleanup can be turned off.
-
-
-
-
-
- Costura by default will load as part of the module initialization.
- This flag disables that behavior. Make sure you call CosturaUtility.Initialize()
- somewhere in your code.
-
-
-
-
-
- Costura will by default use assemblies with a name like
- 'resources.dll' as a satellite resource and prepend the output path. This flag
- disables that behavior.
-
-
-
-
-
- A list of assembly names to exclude from the default action of "embed
- all Copy Local references", delimited with |
-
-
-
-
-
- A list of assembly names to include from the default action of "embed
- all Copy Local references", delimited with |.
-
-
-
-
-
- A list of runtime assembly names to exclude from the default action of
- "embed all Copy Local references", delimited with |
-
-
-
-
-
- A list of runtime assembly names to include from the default action of
- "embed all Copy Local references", delimited with |.
-
-
-
-
-
- A list of unmanaged 32 bit assembly names to include, delimited with
- |.
-
-
-
-
-
- A list of unmanaged 64 bit assembly names to include, delimited with
- |.
-
-
-
-
-
- The order of preloaded assemblies, delimited with |.
-
-
-
-
-
-
-
-
- 'true' to run assembly verification (PEVerify) on the target assembly after all
- weavers have been executed.
-
-
-
-
-
- A comma-separated list of error codes that can be safely ignored in assembly
- verification.
-
-
-
-
-
- 'false' to turn off automatic generation of the XML Schema file.
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Main.cs b/Main.cs
index 6782844db..db11dcafb 100644
--- a/Main.cs
+++ b/Main.cs
@@ -980,12 +980,6 @@ public class Main : BaseUnityPlugin
handler.Info($"{nameof(ThisAssembly.Git.Sha)}: {ThisAssembly.Git.Sha}");
handler.Info($"{nameof(ThisAssembly.Git.Tag)}: {ThisAssembly.Git.Tag}");
- /*
- NormalGameOptionsV12.RecommendedImpostors = NormalGameOptionsV12.MaxImpostors = Enumerable.Repeat(128, 128).ToArray();
- NormalGameOptionsV12.MinPlayers = Enumerable.Repeat(4, 128).ToArray();
- HideNSeekGameOptionsV12.MinPlayers = Enumerable.Repeat(4, 128).ToArray();
- */
-
PrivateTagManager.LoadTagsFromFile();
Harmony.PatchAll(Assembly.GetExecutingAssembly());
diff --git a/Patches/ChatControlPatch.cs b/Patches/ChatControlPatch.cs
index 3af3c5f04..c61a3537c 100644
--- a/Patches/ChatControlPatch.cs
+++ b/Patches/ChatControlPatch.cs
@@ -58,7 +58,7 @@ static class ChatControllerAwakePatch
{
while (__instance)
{
- if (__instance.IsOpenOrOpening)
+ if (__instance.State is ChatControllerState.Open or ChatControllerState.Opening or ChatControllerState.Closing)
{
__instance.freeChatField.background.color = DarkBackgroundColor;
@@ -71,6 +71,12 @@ static class ChatControllerAwakePatch
if (!OpenKeyboardIcon) OpenKeyboardIcon = GameObject.Find("OpenKeyboardIcon")?.transform.GetComponent();
if (OpenKeyboardIcon) OpenKeyboardIcon.sprite = Utils.LoadSprite("EHR.Resources.Images.DarkKeyboard.png", 100f);
}
+
+ if (!Main.DarkTheme.Value)
+ {
+ __instance.freeChatField.textArea.outputText.color = Color.black;
+ yield break;
+ }
yield return null;
}
diff --git a/Patches/Crowded.cs b/Patches/Crowded.cs
index a1a0e6246..d1c84037c 100644
--- a/Patches/Crowded.cs
+++ b/Patches/Crowded.cs
@@ -20,6 +20,21 @@ internal static class Crowded
private static CreateOptionsPicker Instance;
public static readonly int MaxImpostors = GameOptionsManager.Instance.currentHostOptions.MaxPlayers / 2;
private static int MaxPlayers => CurrentServerType == ServerType.Vanilla ? 15 : 127;
+
+ [HarmonyPatch(typeof(CreateGameOptions), nameof(CreateGameOptions.Confirm))]
+ public static class CreateGameOptionsConfirm
+ {
+ public static bool Prefix(CreateGameOptions __instance)
+ {
+ if (!DestroyableSingleton.Instance.Connecting(__instance)) return false;
+ __instance.logger.Info("Hosting an online game");
+ // - skip player count and impostor count checks -
+ GameOptionsManager.Instance.GameHostOptions.TryGetInt(Int32OptionNames.NumImpostors, out int num);
+ if (num == 0) GameOptionsManager.Instance.GameHostOptions.SetInt(Int32OptionNames.NumImpostors, 1);
+ __instance.CoStartGame();
+ return false;
+ }
+ }
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.Awake))]
public static class CreateOptionsPickerAwake
diff --git a/README.md b/README.md
index aff9f86f0..0c0e579c0 100644
--- a/README.md
+++ b/README.md
@@ -273,35 +273,6 @@ Click this badge to view our translation project:
### Translation progress
[](https://crowdin.com/project/ehr)
-
-
-## Should I switch from TOHE to EHR?
-
-- In comparison, both mods are great, but EHR has more roles, features, game modes, and settings.
-- Moreover, EHR contains almost all the roles from TOHE, so you won't be missing out on anything.
-- Some roles were deleted or reworked to make them more balanced or to make them fit better with the new roles.
-- If you switch to EHR, you will have access to all features without paying for anything.
-- Test builds are available for free in our Discord server, and you can report bugs and suggest features there as well.
-- /up was reworked to /setrole, allowing you to set anyone's role for the next game, not only yours.
-- You can fully gain access to /setrole by just hosting EHR lobbies weekly or uploading EHR content to YouTube, but
- everyone can use /setrole to set their own role without any requirements (essentially /up).
-- EHR has a Custom Team Assigner application, which is a unique feature that no other mod has.
-- You can force or ban certain role/add-on combinations directly in the game using /combo.
-- Not sure how to use commands? Use /help to see a list of all available commands.
-- EHR helps the host use commands by directly displaying their short descriptions, arguments, descriptions for all
- arguments, and checking whether an argument is valid or not.
-- You can also press TAB to autocomplete commands.
-- Some hints and tips are displayed during the loading screen and ejection animations, which can help you discover new
- features.
-- EHR has 16 unique custom game modes, which can be played with any number of players, some even alone.
-- You can make lobbies with more than 15 players on modded servers, just type /cs players 100 - for example - to set the
- player limit to 100.
-- I, the main developer of EHR, care about user feedback and suggestions, and I try to implement them as much as
- possible.
-- If this wasn't convincing enough, you can always try EHR and see if you like it better than TOHE.
-- In the end, it's your choice, and I respect it.
-
-#### There are more game changing features, which you can find on our website: [EHR Wiki](https://ehr-wiki.gurge44.eu/)
@@ -618,7 +589,7 @@ Click this badge to view our translation project:
# License for the Custom Team Assigner application (everything inside the `CTA` folder)
```
-Copyright (c) 2025, Gurge44
+Copyright (c) 2026, Gurge44
All rights reserved.
diff --git a/Roles/Standard/Impostor/Support/Perplexer.cs b/Roles/Standard/Impostor/Support/Perplexer.cs
index a8b2cc910..1e38425b7 100644
--- a/Roles/Standard/Impostor/Support/Perplexer.cs
+++ b/Roles/Standard/Impostor/Support/Perplexer.cs
@@ -52,7 +52,9 @@ public class Perplexer : RoleBase
public override bool OnShapeshift(PlayerControl shapeshifter, PlayerControl target, bool shapeshifting)
{
- if (shapeshifting && MarkedId == byte.MaxValue && target.IsAlive() && shapeshifter.GetAbilityUseLimit() >= 1f)
+ if (!shapeshifting) return true;
+
+ if (MarkedId == byte.MaxValue && target.IsAlive() && shapeshifter.GetAbilityUseLimit() >= 1f)
{
MarkedId = target.PlayerId;
Main.AllPlayerSpeed[MarkedId] *= -1;
@@ -66,6 +68,7 @@ public class Perplexer : RoleBase
if (target && target.IsAlive()) target.MarkDirtySettings();
}, AbilityDuration.GetFloat(), "Perplexer Revert Control Invert");
}
+
return false;
}
}
\ No newline at end of file
diff --git a/packaging/mono/BepInEx/patchers/ForceHideManagerPatcher.dll b/packaging/mono/BepInEx/patchers/ForceHideManagerPatcher.dll
new file mode 100644
index 000000000..72a60d5fb
Binary files /dev/null and b/packaging/mono/BepInEx/patchers/ForceHideManagerPatcher.dll differ