Android & Submerged compatibility

This commit is contained in:
Gurge44 committed 2025-07-11 21:01:36 +02:00
1 parent eb0cc841e9
commit 470acb6239
57 files changed
+696 -116

No files matched your search

+1 -1
View File
@@ -235,7 +235,7 @@ public static class CaptureTheFlag
if (!ValidTag) return false;
switch (aapc.Length)
switch (aapc.Length + ExtendedPlayerControl.TempExiled.Count)
{
case 0:
ResetSkins();
+2 -1
View File
@@ -236,6 +236,7 @@ public static class KingOfTheZones
AllRooms.Remove(SystemTypes.Outside);
AllRooms.Remove(SystemTypes.Ventilation);
AllRooms.RemoveWhere(x => x.ToString().Contains("Decontamination"));
if (SubmergedCompatibility.IsSubmerged()) AllRooms.RemoveWhere(x => (byte)x > 135);
Zones = DefaultZones[Main.CurrentMap][NumZones.GetInt() - 1];
@@ -582,7 +583,7 @@ public static class KingOfTheZones
if (!Main.IntroDestroyed) return false;
switch (aapc.Length)
switch (aapc.Length + ExtendedPlayerControl.TempExiled.Count)
{
case 0:
{
+18 -16
View File
@@ -2,7 +2,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using AmongUs.GameOptions;
using EHR.Modules;
using HarmonyLib;
using UnityEngine;
@@ -162,6 +161,8 @@ public static class Quiz
if (FFAEndTS == 0)
{
int numPlayers = Main.AllAlivePlayerControls.Length;
if (QuestionTimeLimitEndTS != 0)
{
string answers = string.Empty;
@@ -190,7 +191,8 @@ public static class Quiz
if (index != -1) question = question.Insert(index + 1, "\n");
}
return $"<#ffff44>{question}</color>\n{answers}<#CF2472>{QuestionTimeLimitEndTS - Utils.TimeStamp}</color>";
string invis = numPlayers <= 3 && CurrentDifficulty > Difficulty.Test ? $"\n<#888888>{GetString("Quiz.Suffix.Invisible")}</color>" : string.Empty;
return $"<#ffff44>{question}</color>\n{answers}<#CF2472>{QuestionTimeLimitEndTS - Utils.TimeStamp}</color>{invis}";
}
string correctAnswer = CurrentQuestion.Answers[CurrentQuestion.CorrectAnswerIndex];
@@ -199,7 +201,6 @@ public static class Quiz
string color = DyingPlayers.Contains(seer.PlayerId) ? "#FF0000" : "#00FF00";
string str = string.Format(GetString("Quiz.Notify.CorrectAnswer"), color, correctAnswerLetter, correctAnswer, correctRoom);
int numPlayers = Main.AllAlivePlayerControls.Length;
if (DyingPlayers.Count == numPlayers) str += "\n" + GetString(numPlayers <= 3 ? "Quiz.Notify.AllWrongShort" : "Quiz.Notify.AllWrong");
if (CurrentDifficulty > Difficulty.Test && Settings[CurrentDifficulty].QuestionsAsked.GetInt() <= QuestionsAsked)
@@ -348,6 +349,11 @@ public static class Quiz
if (Main.CurrentMap is MapNames.Skeld or MapNames.Dleks) time -= 3;
QuestionTimeLimitEndTS = Utils.TimeStamp + time;
PlayerControl[] aapc = Main.AllAlivePlayerControls;
if (aapc.Length is 3 or 2 && CurrentDifficulty > Difficulty.Test)
aapc.Do(x => x.RpcMakeInvisible());
Logger.Info($"New question: {CurrentQuestion.Question} | {string.Join(", ", CurrentQuestion.Answers)} | {CurrentQuestion.Answers[CurrentQuestion.CorrectAnswerIndex]}", "Quiz");
Logger.Info($"Time limit: {time}", "Quiz");
}
@@ -439,7 +445,7 @@ public static class Quiz
Logger.Info($"Number of correct answers for everyone currently: {string.Join(", ", NumCorrectAnswers.Select(x => $"{Main.AllPlayerNames.GetValueOrDefault(x.Key, string.Empty)}: {x.Value[CurrentDifficulty][Round]}"))}", "Quiz");
if (everyoneWasWrong) QuestionsAsked--;
else Utils.NotifyRoles();
Utils.NotifyRoles();
yield return new WaitForSeconds(everyoneWasWrong ? aapc.Length <= 3 ? 4f : 6f : 3f);
if (GameStates.IsMeeting || ExileController.Instance || !GameStates.InGame || GameStates.IsLobby) yield break;
@@ -468,27 +474,22 @@ public static class Quiz
yield break;
case 1:
var pc = dyingPlayers[0].GetPlayer();
if (pc != null) pc.Suicide();
if (pc == null) goto case 0;
pc.RpcMakeVisible();
pc.Suicide();
goto case 0;
case var x when x == aapc.Length:
Round--;
NumCorrectAnswers.Values.Do(d => d[CurrentDifficulty][Round] = 0);
goto case 0;
default:
if (aapc.Length is 3 or 2) aapc.Do(x => x.RpcMakeVisible());
yield return new WaitForSeconds(2f);
AllowKills = true;
FFAEndTS = Utils.TimeStamp + FFAEventLength.GetInt();
var spectators = aapc.ExceptBy(dyingPlayers, x => x.PlayerId).ToArray();
foreach (PlayerControl spectator in spectators)
{
spectator.RpcExileV2();
spectator.RpcSetRoleDesync(RoleTypes.GuardianAngel, spectator.OwnerId);
}
spectators.Do(x => x.ExileTemporarily());
yield return new WaitForSeconds(0.2f);
spectators.Do(x => x.RpcResetAbilityCooldown());
Main.PlayerStates.Values.IntersectBy(spectators.Select(x => x.PlayerId), x => x.Player.PlayerId).Do(x => x.SetDead());
var stillLiving = dyingPlayers.ToValidPlayers().FindAll(x => x.IsAlive());
stillLiving.ForEach(x => x.RpcChangeRoleBasis(CustomRoles.NSerialKiller));
Utils.SendRPC(CustomRPC.QuizSync, AllowKills);
@@ -507,13 +508,13 @@ public static class Quiz
Utils.SendRPC(CustomRPC.QuizSync, AllowKills);
var location = RandomSpawn.SpawnMap.GetSpawnMap().Positions.IntersectBy(UsedRooms[Main.CurrentMap].Values, x => x.Key).RandomElement().Value;
PlayerControl ffaSurvivor = stillLiving[0];
switch (stillLiving.Count)
{
case 0:
break;
case 1:
PlayerControl ffaSurvivor = stillLiving[0];
ffaSurvivor.RpcChangeRoleBasis(CustomRoles.QuizPlayer);
ffaSurvivor.TP(location);
break;
@@ -579,6 +580,7 @@ public static class Quiz
SystemTypes correctRoom = UsedRooms[Main.CurrentMap][(char)('A' + CurrentQuestion.CorrectAnswerIndex)];
DyingPlayers = Main.AllAlivePlayerControls.Select(x => (ID: x.PlayerId, Room: x.GetPlainShipRoom())).Where(x => correctRoom == SystemTypes.Outside ? x.Room != null : x.Room == null || x.Room.RoomId != correctRoom).Select(x => x.ID).ToList();
QuestionTimeLimitEndTS = 0;
Utils.NotifyRoles();
}
else
Main.Instance.StartCoroutine(EndQuestion());
+11
View File
@@ -103,6 +103,13 @@ public static class RoomRush
[(SystemTypes.MiningPit, SystemTypes.Storage)] = 2,
[(SystemTypes.MiningPit, SystemTypes.Dropship)] = 2,
[(SystemTypes.MiningPit, SystemTypes.Comms)] = 2
},
[(MapNames)6] = new()
{
[(SystemTypes.Admin, SystemTypes.Lounge)] = 2,
[(SystemTypes.Comms, SystemTypes.Medical)] = 2,
[((SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.Research, SystemTypes.Medical)] = 3,
[((SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.Ballast, SystemTypes.Security)] = 2
}
};
@@ -188,6 +195,7 @@ public static class RoomRush
AllRooms.Remove(SystemTypes.Outside);
AllRooms.Remove(SystemTypes.Ventilation);
AllRooms.RemoveWhere(x => x.ToString().Contains("Decontamination"));
if (SubmergedCompatibility.IsSubmerged()) AllRooms.RemoveWhere(x => (byte)x > 135);
DonePlayers = [];
Points = [];
@@ -313,6 +321,7 @@ public static class RoomRush
MapNames.Polus => SystemTypes.Dropship,
MapNames.Airship => SystemTypes.MainHall,
MapNames.Fungle => SystemTypes.Dropship,
(MapNames)6 => (SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.UpperCentral,
_ => throw new ArgumentOutOfRangeException(map.ToString(), "Invalid map")
};
@@ -330,6 +339,7 @@ public static class RoomRush
{
MapNames.MiraHQ => previous is SystemTypes.Laboratory or SystemTypes.Reactor ^ RoomGoal is SystemTypes.Laboratory or SystemTypes.Reactor,
MapNames.Polus => previous == SystemTypes.Specimens || RoomGoal == SystemTypes.Specimens,
(MapNames)6 => (previous == (SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.Ballast) ^ (RoomGoal == (SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.Ballast),
_ => false
};
@@ -341,6 +351,7 @@ public static class RoomRush
: Options.DecontaminationTimeOnMiraHQ.GetInt() + Options.DecontaminationDoorOpenTimeOnMiraHQ.GetInt()
: 6;
if (SubmergedCompatibility.IsSubmerged()) decontaminationTime = 3;
time += decontaminationTime + 3;
}
+1
View File
@@ -58,6 +58,7 @@ public static class Speedrun
{
int timer = TimeLimit.GetInt();
if (Main.CurrentMap is MapNames.Airship or MapNames.Fungle) timer += 5;
if (SubmergedCompatibility.IsSubmerged()) timer *= 2;
if (TimeStacksUp.GetBool())
Timers[pc.PlayerId] += timer;
+1
View File
@@ -266,6 +266,7 @@ public static class TheMindGame
if (currentMap is MapNames.Fungle) AllRooms.Remove(SystemTypes.FishingDock);
if (currentMap is MapNames.Fungle) AllRooms.Remove(SystemTypes.Greenhouse);
AllRooms.RemoveAll(x => x.ToString().Contains("Decontamination"));
if (SubmergedCompatibility.IsSubmerged()) AllRooms.RemoveAll(x => (byte)x > 135);
PlayerControl[] aapc = Main.AllAlivePlayerControls;
Points = aapc.ToDictionary(x => x.PlayerId, _ => 0);
+5 -1
View File
@@ -30,6 +30,7 @@ namespace EHR;
[BepInIncompatibility("MalumMenu")]
[BepInIncompatibility("com.ten.thebetterroles")]
[BepInIncompatibility("xyz.crowdedmods.crowdedmod")]
[BepInDependency(SubmergedCompatibility.SubmergedGuid, BepInDependency.DependencyFlags.SoftDependency)]
[BepInProcess("Among Us.exe")]
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")]
public class Main : BasePlugin
@@ -55,7 +56,7 @@ public class Main : BasePlugin
public const string ForkId = "EHR";
public const string SupportedAUVersion = "2025.4.15";
public const string DataPath =
public static readonly string DataPath =
#if ANDROID
Application.persistentDataPath;
#else
@@ -811,6 +812,9 @@ public class Main : BasePlugin
try { DevManager.StartFetchingTags(); }
catch (Exception e) { Utils.ThrowException(e); }
try { SubmergedCompatibility.Initialize(); }
catch (Exception e) { Utils.ThrowException(e); }
Logger.Msg("========= EHR loaded! =========", "Plugin Load");
Logger.Msg($"EHR Version: {PluginVersion}, Test Build: {TestBuild}", "Plugin Load");
};
+1
View File
@@ -91,6 +91,7 @@ public static class AFKDetector
case Data.Phase.Detection:
data.CurrentPhase = Data.Phase.Warning;
data.Timer = 15f;
Utils.NotifyRoles(SpecifyTarget: pc);
if (pc.IsAlive() && !MeetingStates.FirstMeeting) pc.FixBlackScreen();
break;
case Data.Phase.Warning:
+1 -1
View File
@@ -109,7 +109,7 @@ public static class Achievements
ItsJustAPrankBro // As Bomber, Kill half the lobby in 1 bomb
}
private const string SaveFilePath = $"{Main.DataPath}/EHR_DATA/Achievements.json";
private static readonly string SaveFilePath = $"{Main.DataPath}/EHR_DATA/Achievements.json";
private const string ApiBaseUrl = "https://gurge44.pythonanywhere.com/achievements";
private const string ApiSaveEndpoint = $"{ApiBaseUrl}/save";
+4 -4
View File
@@ -15,10 +15,10 @@ namespace EHR;
public static class BanManager
{
private const string DenyNameListPath = $"{Main.DataPath}/EHR_DATA/DenyName.txt";
private const string BanListPath = $"{Main.DataPath}/EHR_DATA/BanList.txt";
private const string ModeratorListPath = $"{Main.DataPath}/EHR_DATA/Moderators.txt";
private const string WhiteListListPath = $"{Main.DataPath}/EHR_DATA/WhiteList.txt";
private static readonly string DenyNameListPath = $"{Main.DataPath}/EHR_DATA/DenyName.txt";
private static readonly string BanListPath = $"{Main.DataPath}/EHR_DATA/BanList.txt";
private static readonly string ModeratorListPath = $"{Main.DataPath}/EHR_DATA/Moderators.txt";
private static readonly string WhiteListListPath = $"{Main.DataPath}/EHR_DATA/WhiteList.txt";
private static readonly List<string> EACList = [];
public static readonly List<string> TempBanWhiteList = []; // To prevent writing to the banlist
+6 -4
View File
@@ -4,6 +4,7 @@ using System.Linq;
using AmongUs.InnerNet.GameDataMessages;
using EHR;
using EHR.Crewmate;
using EHR.Impostor;
using EHR.Modules;
using HarmonyLib;
using Hazel;
@@ -44,7 +45,7 @@ namespace EHR
string skinId = PlayerControl.LocalPlayer.Data.Outfits[PlayerOutfitType.Default].SkinId;
string petId = PlayerControl.LocalPlayer.Data.Outfits[PlayerOutfitType.Default].PetId;
string visorId = PlayerControl.LocalPlayer.Data.Outfits[PlayerOutfitType.Default].VisorId;
var sender = CustomRpcSender.Create("SetFakeData", this is BedWarsItemGenerator ? SendOption.None : SendOption.Reliable, log: false);
var sender = CustomRpcSender.Create("CustomNetObject.RpcChangeSprite", this is BedWarsItemGenerator ? SendOption.None : SendOption.Reliable, log: false);
MessageWriter writer = sender.stream;
sender.StartMessage();
PlayerControl.LocalPlayer.Data.Outfits[PlayerOutfitType.Default].PlayerName = "<size=14><br></size>" + sprite;
@@ -218,7 +219,7 @@ namespace EHR
string skinId = PlayerControl.LocalPlayer.Data.Outfits[PlayerOutfitType.Default].SkinId;
string petId = PlayerControl.LocalPlayer.Data.Outfits[PlayerOutfitType.Default].PetId;
string visorId = PlayerControl.LocalPlayer.Data.Outfits[PlayerOutfitType.Default].VisorId;
var sender = CustomRpcSender.Create("SetFakeData", SendOption.Reliable, log: false);
var sender = CustomRpcSender.Create("CustomNetObject.CreateNetObject", SendOption.Reliable, log: false);
MessageWriter writer = sender.stream;
sender.StartMessage();
PlayerControl.LocalPlayer.Data.Outfits[PlayerOutfitType.Default].PlayerName = "<size=14><br></size>" + sprite;
@@ -270,7 +271,7 @@ namespace EHR
LateTask.New(() =>
{
var sender = CustomRpcSender.Create("SetFakeData", SendOption.Reliable, log: false);
var sender = CustomRpcSender.Create("CustomNetObject.CreateNetObject (2)", SendOption.Reliable, log: false);
MessageWriter writer = sender.stream;
sender.StartMessage(pc.OwnerId);
writer.StartMessage(1);
@@ -326,7 +327,8 @@ namespace EHR
{
if (!(Options.IntegrateNaturalDisasters.GetBool() && Options.CurrentGameMode != CustomGameMode.NaturalDisasters))
TempDespawnedObjects = AllObjects.ToList();
if (Abyssbringer.ShouldDespawnCNOOnMeeting) TempDespawnedObjects.RemoveAll(x => x is BlackHole);
Reset();
}
+1
View File
@@ -112,6 +112,7 @@ public class GrabOxygenMaskSabotage : CustomSabotage
MapNames.Polus => SystemTypes.Specimens,
MapNames.Airship => SystemTypes.Medical,
MapNames.Fungle => SystemTypes.Kitchen,
(MapNames)6 => (SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.Filtration,
_ => SystemTypes.Outside
};
+4
View File
@@ -230,6 +230,9 @@ public class CustomLogger
""";
Builder.Append(logEntry);
#if DEBUG
Finish(false);
#endif
}
private IEnumerator InactivityCheck()
@@ -250,5 +253,6 @@ public class CustomLogger
if (dump) append += HtmlFooter;
File.AppendAllText(LOGFilePath, append);
PrivateInstance = null;
if (dump) Main.Instance.StopCoroutine(InactivityCheck());
}
}
+1 -1
View File
@@ -45,7 +45,7 @@ internal static class DisableDevice
MapNames.Dleks => 1.5f,
MapNames.Airship => 1.8f,
MapNames.Fungle => 1.8f,
_ => 0.0f
_ => 2f
};
public static void FixedUpdate()
+1 -1
View File
@@ -197,7 +197,7 @@ public enum ErrorCode
// 002 Support related
UnsupportedVersion = 002_000_3, // 002-000-1 AmongUs version is outdated
UnsupportedMap = 002_000_2, // 002-000-2 Unsupported Map
UnsupportedMap = 002_000_1, // 002-000-1 Unsupported Map
// ==========
// 000 Test
+6
View File
@@ -261,8 +261,12 @@ internal static class ExtendedPlayerControl
if (init) Main.PlayerStates[player.PlayerId].InitTask(player);
}
public static HashSet<byte> TempExiled = [];
public static void ExileTemporarily(this PlayerControl pc)
{
if (!TempExiled.Add(pc.PlayerId)) return;
pc.Exiled();
Main.PlayerStates[pc.PlayerId].SetDead();
@@ -341,6 +345,7 @@ internal static class ExtendedPlayerControl
return;
}
TempExiled.Remove(player.PlayerId);
GhostRolesManager.RemoveGhostRole(player.PlayerId);
Main.PlayerStates[player.PlayerId].IsDead = false;
Main.PlayerStates[player.PlayerId].deathReason = PlayerState.DeathReason.etc;
@@ -652,6 +657,7 @@ internal static class ExtendedPlayerControl
MapNames.Skeld or MapNames.Dleks when room != null => true,
MapNames.Polus when overlapPointNonAlloc >= 1 => true,
MapNames.Polus when pos.y is >= -26.11f and <= -6.41f && pos.x is >= 3.56f and <= 32.68f => true,
(MapNames)6 => true,
_ => false
};
}
+7 -1
View File
@@ -93,6 +93,12 @@ public static class GuessManager
return true;
}
if (!Options.CanGuessDuringDiscussionTime.GetBool() && MeetingHud.Instance && MeetingHud.Instance.state is MeetingHud.VoteStates.Discussion or MeetingHud.VoteStates.Animating)
{
ShowMessage("GuessDuringDiscussion");
return true;
}
if (pc.Is(CustomRoles.Decryptor) && Decryptor.GuessMode.GetValue() == 2) goto SkipCheck;
if ((pc.IsCrewmate() && !Options.CrewmatesCanGuess.GetBool()) ||
@@ -1069,7 +1075,7 @@ public static class GuessManager
if (AmongUsClient.Instance.AmHost)
{
HashSet<byte> guessers = Main.AllPlayerControls.Where(x => CanGuess(x, false)).Select(x => x.PlayerId).ToHashSet();
HashSet<byte> guessers = Main.AllAlivePlayerControls.Where(x => CanGuess(x, false)).Select(x => x.PlayerId).ToHashSet();
LateTask.New(() => guessers.Do(x => Utils.SendMessage(GetString("YouCanGuess"), x, GetString("YouCanGuessTitle"))), 12f, log: false);
}
}
+4
View File
@@ -337,6 +337,7 @@ public static class Options
public static OptionItem ReportBaitAtAllCost;
public static OptionItem GuesserDoesntDieOnMisguess;
public static OptionItem CanGuessDuringDiscussionTime;
public static OptionItem GuesserMaxKillsPerMeeting;
public static OptionItem GuesserMaxKillsPerGame;
@@ -2658,6 +2659,9 @@ public static class Options
GuesserDoesntDieOnMisguess = new BooleanOptionItem(19718, "GuesserDoesntDieOnMisguess", false, TabGroup.TaskSettings)
.SetGameMode(CustomGameMode.Standard);
CanGuessDuringDiscussionTime = new BooleanOptionItem(19799, "CanGuessDuringDiscussionTime", true, TabGroup.TaskSettings)
.SetGameMode(CustomGameMode.Standard);
LoadingPercentage = 92;
MainLoadingText = "Building game settings";
+1 -1
View File
@@ -6,7 +6,7 @@ namespace EHR.Modules;
public static class PrivateTagManager
{
private const string TagFile = $"{Main.DataPath}/EHR_DATA/tags.txt";
private static readonly string TagFile = $"{Main.DataPath}/EHR_DATA/tags.txt";
public static Dictionary<string, string> Tags = [];
public static void AddTag(string friendcode, string tag)
+13 -2
View File
@@ -136,8 +136,18 @@ public enum CustomRPC
SyncRomanticTarget,
SyncVengefulRomanticTarget,
SetRevealedPlayer,
SetCurrentRevealTarget,
SetDoomsayerProgress,
SetCurrentRevealTarget = 209,
/*
* SUBMERGED RPCs
* 210 - SetCustomData
* 211 - RequestChangeFloor
* 212 - AcknowledgeChangeFloor
* 213 - EngineVent
* 214 - OxygenDeath
*/
SetDoomsayerProgress = 215,
SetTrackerTarget,
RpcPassBomb,
SetAlchemistTimer,
@@ -229,6 +239,7 @@ internal static class RPCHandlerPatch
private static bool TrustedRpc(byte id)
{
if (SubmergedCompatibility.IsSubmerged() && id is >= 120 and <= 124) return true;
return (CustomRPC)id is CustomRPC.VersionCheck or CustomRPC.RequestRetryVersionCheck or CustomRPC.AntiBlackout or CustomRPC.SyncNameNotify or CustomRPC.RequestSendMessage or CustomRPC.RequestCommandProcessing or CustomRPC.Judge or CustomRPC.SetNiceSwapperVotes or CustomRPC.MeetingKill or CustomRPC.Guess or CustomRPC.MafiaRevenge or CustomRPC.BAU or CustomRPC.FFAKill or CustomRPC.TMGSync or CustomRPC.ParityCopCommand;
}
+1 -1
View File
@@ -12,7 +12,7 @@ namespace EHR;
public static class SpamManager
{
private const string BannedWordsFilePath = $"{Main.DataPath}/EHR_DATA/BanWords.txt";
private static readonly string BannedWordsFilePath = $"{Main.DataPath}/EHR_DATA/BanWords.txt";
private static List<string> BanWords = [];
public static void Init()
+1 -1
View File
@@ -16,7 +16,7 @@ namespace EHR;
public static class TemplateManager
{
private const string TemplateFilePath = $"{Main.DataPath}/EHR_DATA/template.txt";
private static readonly string TemplateFilePath = $"{Main.DataPath}/EHR_DATA/template.txt";
private static readonly Dictionary<string, Func<string>> ReplaceDictionary = new()
{
+5 -1
View File
@@ -120,6 +120,9 @@ public static class Translator
public static string GetString(string s, Dictionary<string, string> replacementDic = null, bool console = false)
{
if (SubmergedCompatibility.IsSubmerged() && int.TryParse(s, out int roomNumber) && roomNumber is >= 128 and <= 135)
s = $"SubmergedRoomName.{roomNumber}";
SupportedLangs langId = TranslationController.InstanceExists ? TranslationController.Instance.currentLanguage.languageID : SupportedLangs.English;
if (console) langId = SupportedLangs.English;
@@ -167,8 +170,9 @@ public static class Translator
{
#if ANDROID
return TranslationController.Instance.GetString(stringName);
#endif
#else
return FastDestroyableSingleton<TranslationController>.Instance.GetString(stringName, new Il2CppReferenceArray<Il2CppSystem.Object>(0));
#endif
}
public static string GetRoleString(string str, bool forUser = true)
+10 -1
View File
@@ -153,12 +153,13 @@ public static class Utils
{
PlayerControl pc = nt.myPlayer;
var sendOption = SendOption.Reliable;
bool submerged = SubmergedCompatibility.IsSubmerged();
if (!noCheckState)
{
if (pc.Is(CustomRoles.AntiTP)) return false;
if (pc.inVent || pc.inMovingPlat || pc.onLadder || !pc.IsAlive() || pc.MyPhysics.Animations.IsPlayingAnyLadderAnimation() || pc.MyPhysics.Animations.IsPlayingEnterVentAnimation())
if (pc.inVent || pc.inMovingPlat || pc.onLadder || !pc.IsAlive() || pc.MyPhysics.Animations.IsPlayingAnyLadderAnimation() || pc.MyPhysics.Animations.IsPlayingEnterVentAnimation() || (submerged && pc.IsLocalPlayer() && SubmergedCompatibility.GetInTransition()))
{
if (log) Logger.Warn($"Target ({pc.GetNameWithRole().RemoveHtmlTags()}) is in an un-teleportable state - Teleporting canceled", "TP");
return false;
@@ -209,6 +210,14 @@ public static class Utils
CheckInvalidMovementPatch.LastPosition[pc.PlayerId] = location;
CheckInvalidMovementPatch.ExemptedPlayers.Add(pc.PlayerId);
AFKDetector.TempIgnoredPlayers.Add(pc.PlayerId);
LateTask.New(() => AFKDetector.TempIgnoredPlayers.Remove(pc.PlayerId), 0.2f + CalculatePingDelay(), log: false);
if (submerged)
{
SubmergedCompatibility.ChangeFloor(pc.PlayerId, pc.transform.position.y > -7);
SubmergedCompatibility.CheckOutOfBoundsElevator(pc);
}
if (sendOption == SendOption.Reliable) NumSnapToCallsThisRound++;
return true;
+2 -2
View File
@@ -237,7 +237,7 @@ internal static class ChatCommands
if (LastModFileUpdate + 5 > now) { friendCodes = ModsFileCache; }
else
{
const string friendCodesFilePath = $"{Main.DataPath}/EHR_DATA/Moderators.txt";
var friendCodesFilePath = $"{Main.DataPath}/EHR_DATA/Moderators.txt";
if (!File.Exists(friendCodesFilePath))
{
@@ -265,7 +265,7 @@ internal static class ChatCommands
if (LastVIPFileUpdate + 5 > now) { friendCodes = VIPsFileCache; }
else
{
const string friendCodesFilePath = $"{Main.DataPath}/EHR_DATA/VIPs.txt";
var friendCodesFilePath = $"{Main.DataPath}/EHR_DATA/VIPs.txt";
if (!File.Exists(friendCodesFilePath))
{
+18
View File
@@ -173,6 +173,23 @@ internal static class AuthTimeoutPatch
// If you don't patch this, you still need to wait for 5 s.
// I have no idea why this is happening
#if ANDROID
[HarmonyPatch(typeof(AmongUsClient._CoJoinOnlinePublicGame_d__50), nameof(AmongUsClient._CoJoinOnlinePublicGame_d__50.MoveNext))]
[HarmonyPrefix]
public static void EnableUdpMatchmakingPrefix(AmongUsClient._CoJoinOnlinePublicGame_d__50 __instance)
{
// Skip to state 1, which just calls CoJoinOnlineGameDirect
if (__instance.__1__state == 0 && !ServerManager.Instance.IsHttp)
{
__instance.__1__state = 1;
__instance.__8__1 = new()
{
matchmakerToken = string.Empty
};
}
}
#else
[HarmonyPatch(typeof(AmongUsClient._CoJoinOnlinePublicGame_d__49), nameof(AmongUsClient._CoJoinOnlinePublicGame_d__49.MoveNext))]
[HarmonyPrefix]
public static void EnableUdpMatchmakingPrefix(AmongUsClient._CoJoinOnlinePublicGame_d__49 __instance)
@@ -188,4 +205,5 @@ internal static class AuthTimeoutPatch
};
}
}
#endif
}
+1 -1
View File
@@ -7,7 +7,7 @@ public static class DeconSystemUpdateSystemPatch
{
public static void Prefix(DeconSystem __instance)
{
if (!AmongUsClient.Instance.AmHost) return;
if (!AmongUsClient.Instance.AmHost || SubmergedCompatibility.IsSubmerged()) return;
if (Options.ChangeDecontaminationTime.GetBool())
{
+12 -3
View File
@@ -32,6 +32,15 @@ internal static class AllMapIconsPatch
dleksIcon.MapImage = Utils.LoadSprite("EHR.Resources.Images.DleksBanner.png", 100f);
dleksIcon.NameImage = Utils.LoadSprite("EHR.Resources.Images.DleksBanner-Wordart.png", 100f);
__instance.AllMapIcons.Add(dleksIcon);
if (SubmergedCompatibility.Loaded)
{
MapIconByName submergedIcon = Object.Instantiate(__instance, __instance.gameObject.transform).AllMapIcons[0];
submergedIcon.Name = (MapNames)6;
submergedIcon.MapImage = Utils.LoadSprite("EHR.Resources.Images.SubmergedBanner.png", 100f);
submergedIcon.NameImage = Utils.LoadSprite("EHR.Resources.Images.Submerged-Wordart.png", 100f);
__instance.AllMapIcons.Add(submergedIcon);
}
}
catch (Exception e) { Utils.ThrowException(e); }
}
@@ -56,7 +65,7 @@ public static class DleksPatch
}
// removed dleks check as it's always false
int num2 = Mathf.Clamp(GameOptionsManager.Instance.CurrentGameOptions.MapId, 0, Constants.MapNames.Length - 1);
int num2 = GameOptionsManager.Instance.CurrentGameOptions.MapId == 6 && SubmergedCompatibility.Loaded && SubmergedCompatibility.IsSupported(Options.CurrentGameMode) ? 6 : Mathf.Clamp(GameOptionsManager.Instance.CurrentGameOptions.MapId, 0, Constants.MapNames.Length - 1);
__instance.__2__current = __instance.__4__this.ShipLoadingAsyncHandle = __instance.__4__this.ShipPrefabs[num2].InstantiateAsync();
__instance.__1__state = 1;
@@ -118,7 +127,7 @@ public static class VentSetButtonsPatch
}
[HarmonyPatch(typeof(Vent), nameof(Vent.TryMoveToVent))]
internal class VentTryMoveToVentPatch
internal static class VentTryMoveToVentPatch
{
// Update arrows buttons when player move to vents
private static void Postfix(Vent __instance, [HarmonyArgument(0)] Vent otherVent)
@@ -132,7 +141,7 @@ internal class VentTryMoveToVentPatch
}
[HarmonyPatch(typeof(Vent), nameof(Vent.UpdateArrows))]
internal class VentUpdateArrowsPatch
internal static class VentUpdateArrowsPatch
{
// Fixes "Index was outside the bounds of the array" errors when arrows updates in vent on Dleks map
private static bool Prefix()
+2 -2
View File
@@ -15,7 +15,7 @@ internal static class ExileControllerWrapUpPatch
{
public static NetworkedPlayerInfo LastExiled;
private static void WrapUpPostfix(NetworkedPlayerInfo exiled)
public static void WrapUpPostfix(NetworkedPlayerInfo exiled)
{
var decidedWinner = false;
if (!AmongUsClient.Instance.AmHost) return;
@@ -112,7 +112,7 @@ internal static class ExileControllerWrapUpPatch
}, 2.5f, "AfterPlayerDeathTasks For Exiled Player");
}
private static void WrapUpFinalizer()
public static void WrapUpFinalizer()
{
// Even if an exception occurs in WrapUpPostfix, this part will be executed reliably.
+1
View File
@@ -1124,6 +1124,7 @@ public static class GameSettingMenuPatch
Vector3 gameSettingsLabelPos = gameSettingsLabel.transform.localPosition;
CustomGameMode[] gms = Enum.GetValues<CustomGameMode>()[..^1];
if (Main.NormalOptions.MapId == 6 && SubmergedCompatibility.Loaded) gms = gms.Where(SubmergedCompatibility.IsSupported).ToArray();
int totalCols = Mathf.Max(1, Mathf.CeilToInt(gms.Length / 5f));
GMButtons = [];
+4 -2
View File
@@ -194,8 +194,9 @@ public static class GameStartManagerPatch
CreateOptionsPickerPatch.SetDleks = Main.CurrentMap == MapNames.Dleks;
}
else if (CreateOptionsPickerPatch.SetDleks) Main.NormalOptions.MapId = 3;
else if (CreateOptionsPickerPatch.SetSubmerged) Main.NormalOptions.MapId = 6;
if (Main.CurrentMap == MapNames.Dleks)
if (Main.CurrentMap == MapNames.Dleks || Main.NormalOptions.MapId == 6)
{
var opt = Main.NormalOptions.CastFast<IGameOptions>();
@@ -233,7 +234,7 @@ public static class GameStartManagerPatch
try { instance.UpdateMapImage((MapNames)GameManager.Instance.LogicOptions.MapId); }
catch (Exception e)
{
if (GameManager.Instance.LogicOptions.MapId >= Enum.GetValues<MapNames>().Length)
if (GameManager.Instance.LogicOptions.MapId >= Enum.GetValues<MapNames>().Length && !(GameManager.Instance.LogicOptions.MapId == 6 && SubmergedCompatibility.Loaded && SubmergedCompatibility.IsSupported(Options.CurrentGameMode)))
ErrorText.Instance.AddError(ErrorCode.UnsupportedMap);
Utils.ThrowException(e);
@@ -541,6 +542,7 @@ public static class GameStartRandomMap
CreateOptionsPickerPatch.SetDleks = Main.CurrentMap == MapNames.Dleks;
}
else if (CreateOptionsPickerPatch.SetDleks) Main.NormalOptions.MapId = 3;
else if (CreateOptionsPickerPatch.SetSubmerged) Main.NormalOptions.MapId = 6;
}
public static byte SelectRandomMap()
+4 -2
View File
@@ -667,6 +667,8 @@ internal static class SabotageMapPatch
public static void Postfix(InfectedOverlay __instance)
{
if (SubmergedCompatibility.IsSubmerged()) return;
float perc = __instance.sabSystem.PercentCool;
int total = __instance.sabSystem.initialCooldown ? 10 : 30;
if (SabotageSystemTypeRepairDamagePatch.IsCooldownModificationEnabled) total = (int)SabotageSystemTypeRepairDamagePatch.ModifiedCooldownSec;
@@ -709,7 +711,7 @@ internal static class MapRoomDoorsUpdatePatch
public static bool Prefix(MapRoom __instance)
{
if (!__instance.door || !ShipStatus.Instance) return false;
if (!__instance.door || !ShipStatus.Instance || SubmergedCompatibility.IsSubmerged()) return false;
SystemTypes room = __instance.room;
@@ -958,7 +960,7 @@ internal static class TaskPanelBehaviourPatch
else
{
finalText += Main.AllPlayerControls
.Select(x => (pc: x, points_string: RoomRush.GetPoints(x.PlayerId), points_int: int.Parse(RoomRush.GetPoints(x.PlayerId).Split('/')[0])))
.Select(x => (pc: x, points_string: RoomRush.GetPoints(x.PlayerId), points_int: int.TryParse(RoomRush.GetPoints(x.PlayerId).Split('/')[0], out int points) ? points : 0))
.OrderByDescending(x => x.points_int)
.Aggregate("<size=70%>", (s, x) => $"{s}\r\n{x.pc.PlayerId.ColoredPlayerName()} - {x.points_string}");
}
+14
View File
@@ -14,6 +14,19 @@ namespace EHR;
// Patch for non-host modded clients to ensure that the intro cutscene is shown correctly
// and GameStates.InGame is set to true
#if ANDROID
[HarmonyPatch(typeof(IntroCutscene._ShowRole_d__40), nameof(IntroCutscene._ShowRole_d__40.MoveNext))]
static class ShowRoleMoveNextPatchAndroid
{
public static void Postfix(IntroCutscene._ShowRole_d__40 __instance, ref bool __result)
{
if (AmongUsClient.Instance.AmHost || __instance.__1__state != 1 || !__result) return;
GameStates.InGame = true;
SetUpRoleTextPatch.Postfix(__instance.__4__this);
}
}
#else
[HarmonyPatch(typeof(IntroCutscene._ShowRole_d__41), nameof(IntroCutscene._ShowRole_d__41.MoveNext))]
static class ShowRoleMoveNextPatch
{
@@ -25,6 +38,7 @@ static class ShowRoleMoveNextPatch
SetUpRoleTextPatch.Postfix(__instance.__4__this);
}
}
#endif
// For some reason, IntroCutScene.ShowRole is not called in the base game with this exact same code,
// so we need to patch HudManager.CoShowIntro for the host entirely to ensure that the intro is shown correctly
+2
View File
@@ -14,6 +14,7 @@ internal static class LightSourceUpdatePatch
public static bool Prefix(LightSource __instance)
{
if (SubmergedCompatibility.IsSubmerged()) return true;
Vector3 position = __instance.transform.position;
position.z -= 7f;
__instance.UpdateFlashlightAngle();
@@ -40,6 +41,7 @@ internal static class LightSourceGpuRendererPatch
public static bool Prefix(LightSourceGpuRenderer __instance, [HarmonyArgument(0)] float effectiveRadius)
{
if (SubmergedCompatibility.IsSubmerged()) return true;
if (!__instance.shadowTexture || !__instance.shadowCasterMaterial) return false;
float width = __instance.shadowTexture.width;
__instance.shadowCasterMaterial.SetFloat(DepthCompressionValue, effectiveRadius);
+94 -38
View File
@@ -9,6 +9,9 @@ namespace EHR.Patches;
// Thanks: https://github.com/SubmergedAmongUs/Submerged/blob/4a5a6b47cbed526670ae4b7eae76acd7c42e35de/Submerged/UI/Patches/MapSelectButtonPatches.cs#L49
internal static class CreateOptionsPickerPatch
{
public static bool SetSubmerged;
private static MapSelectButton SubmergedButton;
public static bool SetDleks;
private static MapSelectButton DleksButton;
@@ -22,58 +25,108 @@ internal static class CreateOptionsPickerPatch
if (SceneManager.GetActiveScene().name == "FindAGame") return;
const int dleksPos = 3;
const int submergedPos = 6;
MapSelectButton[] AllMapButton = __instance.transform.GetComponentsInChildren<MapSelectButton>();
if (AllMapButton != null)
{
GameObject dlekS_ehT = Object.Instantiate(AllMapButton[0].gameObject, __instance.transform);
dlekS_ehT.transform.position = AllMapButton[dleksPos].transform.position;
dlekS_ehT.transform.SetSiblingIndex(dleksPos + 2);
var dlekS_ehT_MapButton = dlekS_ehT.GetComponent<MapSelectButton>();
DleksButton = dlekS_ehT_MapButton;
foreach (SpriteRenderer icon in dlekS_ehT_MapButton.MapIcon)
// Dleks Button
{
if (icon == null || icon.transform == null) continue;
icon.flipX = true;
}
GameObject dlekS_ehT = Object.Instantiate(AllMapButton[0].gameObject, __instance.transform);
dlekS_ehT.transform.position = AllMapButton[dleksPos].transform.position;
dlekS_ehT.transform.SetSiblingIndex(dleksPos + 2);
var dlekS_ehT_MapButton = dlekS_ehT.GetComponent<MapSelectButton>();
DleksButton = dlekS_ehT_MapButton;
dlekS_ehT_MapButton.Button.OnClick.RemoveAllListeners();
dlekS_ehT_MapButton.Button.OnClick.AddListener((Action)(() =>
{
__instance.SelectMap(__instance.AllMapIcons[0]);
if (__instance.selectedButton) __instance.selectedButton.Button.SelectButton(false);
__instance.selectedButton = dlekS_ehT_MapButton;
__instance.selectedButton.Button.SelectButton(true);
__instance.selectedMapId = 3;
Main.NormalOptions.MapId = 0;
__instance.MapImage.sprite = Utils.LoadSprite("EHR.Resources.Images.DleksBanner.png", 100f);
__instance.MapName.sprite = Utils.LoadSprite("EHR.Resources.Images.DleksBanner-Wordart.png", 100f);
}));
for (int i = dleksPos; i < AllMapButton.Length; i++) AllMapButton[i].transform.localPosition += new Vector3(0.625f, 0f, 0f);
if (DleksButton != null)
{
if (SetDleks)
foreach (SpriteRenderer icon in dlekS_ehT_MapButton.MapIcon)
{
if (icon == null || icon.transform == null) continue;
icon.flipX = true;
}
dlekS_ehT_MapButton.Button.OnClick.RemoveAllListeners();
dlekS_ehT_MapButton.Button.OnClick.AddListener((Action)(() =>
{
__instance.SelectMap(__instance.AllMapIcons[0]);
if (__instance.selectedButton) __instance.selectedButton.Button.SelectButton(false);
DleksButton.Button.SelectButton(true);
__instance.selectedButton = DleksButton;
__instance.selectedButton = dlekS_ehT_MapButton;
__instance.selectedButton.Button.SelectButton(true);
__instance.selectedMapId = 3;
Main.NormalOptions.MapId = 0;
__instance.MapImage.sprite = Utils.LoadSprite("EHR.Resources.Images.DleksBanner.png", 100f);
__instance.MapName.sprite = Utils.LoadSprite("EHR.Resources.Images.DleksBanner-Wordart.png", 100f);
}));
for (int i = dleksPos; i < AllMapButton.Length; i++) AllMapButton[i].transform.localPosition += new Vector3(0.625f, 0f, 0f);
if (DleksButton != null)
{
if (SetDleks)
{
if (__instance.selectedButton) __instance.selectedButton.Button.SelectButton(false);
DleksButton.Button.SelectButton(true);
__instance.selectedButton = DleksButton;
__instance.selectedMapId = 3;
__instance.MapImage.sprite = Utils.LoadSprite("EHR.Resources.Images.DleksBanner.png", 100f);
__instance.MapName.sprite = Utils.LoadSprite("EHR.Resources.Images.DleksBanner-Wordart.png", 100f);
}
else
DleksButton.Button.SelectButton(false);
}
}
// Submerged Button
if (SubmergedCompatibility.Loaded)
{
GameObject submergedButton = Object.Instantiate(AllMapButton[0].gameObject, __instance.transform);
submergedButton.transform.position = AllMapButton[submergedPos].transform.position;
submergedButton.transform.SetSiblingIndex(submergedPos + 2);
var submergedButton_MapButton = submergedButton.GetComponent<MapSelectButton>();
SubmergedButton = submergedButton_MapButton;
submergedButton_MapButton.Button.OnClick.RemoveAllListeners();
submergedButton_MapButton.Button.OnClick.AddListener((Action)(() =>
{
__instance.SelectMap(__instance.AllMapIcons[6]);
if (__instance.selectedButton) __instance.selectedButton.Button.SelectButton(false);
__instance.selectedButton = submergedButton_MapButton;
__instance.selectedMapId = 6;
Main.NormalOptions.MapId = 6;
__instance.MapImage.sprite = Utils.LoadSprite("EHR.Resources.Images.SubmergedBanner.png", 100f);
__instance.MapName.sprite = Utils.LoadSprite("EHR.Resources.Images.SubmergedBanner-Wordart.png", 100f);
}));
for (var i = 0; i < submergedPos; i++) AllMapButton[i].transform.localPosition += new Vector3(-0.625f, 0f, 0f);
if (SubmergedButton != null)
{
if (SetSubmerged)
{
if (__instance.selectedButton) __instance.selectedButton.Button.SelectButton(false);
SubmergedButton.Button.SelectButton(true);
__instance.selectedButton = SubmergedButton;
__instance.selectedMapId = 6;
__instance.MapImage.sprite = Utils.LoadSprite("EHR.Resources.Images.SubmergedBanner.png", 100f);
__instance.MapName.sprite = Utils.LoadSprite("EHR.Resources.Images.SubmergedBanner-Wordart.png", 100f);
}
else
SubmergedButton.Button.SelectButton(false);
}
else
DleksButton.Button.SelectButton(false);
}
}
}
@@ -86,6 +139,7 @@ internal static class CreateOptionsPickerPatch
if (__instance.MapName == null) return false;
if (DleksButton != null) SetDleks = __instance.selectedMapId == 3;
if (SubmergedButton != null) SetSubmerged = __instance.selectedMapId == 6;
if (__instance.selectedMapId == 3)
{
@@ -103,10 +157,12 @@ internal static class CreateOptionsPickerPatch
}
[HarmonyPatch(typeof(CreateOptionsPicker), nameof(CreateOptionsPicker.Awake))]
private class MenuMapPickerPatch
private static class MenuMapPickerPatch
{
public static void Postfix(CreateOptionsPicker __instance)
{
if (SubmergedCompatibility.Loaded) return;
Transform mapPickerTransform = __instance.transform.Find("MapPicker");
var mapPickerMenu = mapPickerTransform.Find("Map Picker Menu").GetComponent<MapPickerMenu>();
+5 -2
View File
@@ -173,7 +173,7 @@ internal static class ChangeRoleSettings
RoleTypes.Tracker
}.Do(x => Main.NormalOptions.roleOptions.SetRoleRate(x, 0, 0));
if (Main.NormalOptions.MapId > 5)
if (Main.NormalOptions.MapId > 5 && !(Main.NormalOptions.MapId == 6 && SubmergedCompatibility.Loaded))
{
Logger.SendInGame(GetString("UnsupportedMap"), Color.red);
ErrorText.Instance.AddError(ErrorCode.UnsupportedMap);
@@ -545,7 +545,7 @@ internal static class StartGameHostPatch
if (!ShipStatus.Instance)
{
int index = Mathf.Clamp(GameOptionsManager.Instance.CurrentGameOptions.MapId, 0, Constants.MapNames.Length - 1);
int index = GameOptionsManager.Instance.CurrentGameOptions.MapId == 6 && SubmergedCompatibility.Loaded && SubmergedCompatibility.IsSupported(Options.CurrentGameMode) ? 6 : Mathf.Clamp(GameOptionsManager.Instance.CurrentGameOptions.MapId, 0, Constants.MapNames.Length - 1);
AUClient.ShipLoadingAsyncHandle = AUClient.ShipPrefabs[index].InstantiateAsync();
while (!AUClient.ShipLoadingAsyncHandle.IsDone)
@@ -560,6 +560,7 @@ internal static class StartGameHostPatch
GameObject result = AUClient.ShipLoadingAsyncHandle.Result;
ShipStatus.Instance = result.GetComponent<ShipStatus>();
AUClient.Spawn(ShipStatus.Instance);
Logger.Info($"Successfully spawned ShipStatus for map {GameOptionsManager.Instance.CurrentGameOptions.MapId} ({Constants.MapNames[GameOptionsManager.Instance.CurrentGameOptions.MapId]})", "StartGameHost");
}
try
@@ -1048,6 +1049,8 @@ internal static class StartGameHostPatch
pc.Data.SendGameData();
}
Logger.Info("Successfully set everyone's data as Disconnected", "StartGameHost");
LoadingBarManager loadingBarManager = FastDestroyableSingleton<LoadingBarManager>.Instance;
yield return loadingBarManager.WaitAndSmoothlyUpdate(90f, 100f, 1f, GetString("LoadingBarText.1"));
loadingBarManager.ToggleLoadingBar(false);
+6
View File
@@ -1074,6 +1074,12 @@ internal static class ReportDeadBodyPatch
Notify("TargetDisregarded");
return false;
}
if (__instance.Is(CustomRoles.Oblivious))
{
Notify("AmOblivious");
return false;
}
}
if (Options.SyncButtonMode.GetBool() && target == null)
+31
View File
@@ -114,6 +114,7 @@ internal abstract class RandomSpawn
MapNames.Dleks => new DleksSpawnMap(),
MapNames.Airship => new AirshipSpawnMap(),
MapNames.Fungle => new FungleSpawnMap(),
(MapNames)6 => new SubmergedSpawnMap(),
_ => throw new ArgumentOutOfRangeException(nameof(Main.CurrentMap), Main.CurrentMap, "Invalid map")
};
}
@@ -282,4 +283,34 @@ internal abstract class RandomSpawn
return positions.ToArray().OrderBy(_ => Guid.NewGuid()).Take(1).FirstOrDefault();
}
}
public class SubmergedSpawnMap : SpawnMap
{
public readonly Dictionary<SystemTypes, Vector2> positions = new()
{
[(SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.Filtration] = new(8.51f, -21.13f),
[SystemTypes.Electrical] = new(10.8f, -27.15f),
[SystemTypes.Storage] = new(2.55f, -34.73f),
[(SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.LowerLobby] = new(6.53f, -39.44f),
[(SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.Ballast] = new(-8.26f, -39.84f),
[SystemTypes.Security] = new(-4.23f, -33.38f),
[SystemTypes.Engine] = new(-12.58f, -27.83f),
[SystemTypes.Admin] = new(-9.96f, 10.95f),
[(SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.Observatory] = new(-12.39f, 17.78f),
[SystemTypes.Lounge] = new(-6.43f, 14.11f),
[SystemTypes.MeetingRoom] = new(-1.85f, 12.32f),
[SystemTypes.Cafeteria] = new(-8.57f, 25.47f),
[(SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.Research] = new(0.96f, 29.84f),
[SystemTypes.Medical] = new(6.17f, 31.38f),
[SystemTypes.Comms] = new(11.03f, 23.53f),
[(SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.UpperLobby] = new(7.72f, 8.66f)
};
public override Dictionary<SystemTypes, Vector2> Positions => positions;
protected override KeyValuePair<SystemTypes, Vector2> GetLocation()
{
return positions.ToArray().OrderBy(_ => Guid.NewGuid()).Take(1).FirstOrDefault();
}
}
}
+3 -1
View File
@@ -378,6 +378,8 @@ public static class SabotageSystemTypeRepairDamagePatch
newReader.Recycle();
}
if (SubmergedCompatibility.IsSubmerged()) return CheckSabotage(__instance, player, systemTypes);
if (Options.EnableCustomSabotages.GetBool())
{
if (Options.EnableGrabOxygenMaskCustomSabotage.GetBool() && systemTypes == SystemTypes.Comms)
@@ -529,7 +531,7 @@ public static class SecurityCameraPatch
[HarmonyPatch(typeof(ShipStatus), nameof(ShipStatus.RepairCriticalSabotages))]
internal static class ShipStatusRepairCriticalSabotagesPatch
{
public static void Postfix(ShipStatus __instance)
public static void Postfix()
{
CustomSabotage.Reset();
}
+5 -1
View File
@@ -94,6 +94,8 @@ internal static class RepairSystemPatch
if ((Options.CurrentGameMode != CustomGameMode.Standard || 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;
switch (player.GetCustomRole())
{
case CustomRoles.SabotageMaster:
@@ -171,7 +173,8 @@ internal static class RepairSystemPatch
break;
}
case SystemTypes.Sabotage when AmongUsClient.Instance.NetworkMode != NetworkModes.FreePlay: { return SabotageSystemTypeRepairDamagePatch.CheckSabotage(null, player, systemType); }
case SystemTypes.Sabotage when AmongUsClient.Instance.NetworkMode != NetworkModes.FreePlay:
return SabotageSystemTypeRepairDamagePatch.CheckSabotage(null, player, systemType);
case SystemTypes.Security when amount == 1:
{
bool camerasDisabled = (MapNames)Main.NormalOptions.MapId switch
@@ -468,6 +471,7 @@ internal static class ShipStatusSerializePatch
{
if (!AmongUsClient.Instance.AmHost) return;
if (initialState) return;
if (SubmergedCompatibility.IsSubmerged()) return;
var cancel = false;
+2
View File
@@ -287,6 +287,7 @@ public static class TextBoxPatch
catch { }
}
#if !ANDROID
[HarmonyPatch(nameof(TextBoxTMP.Update))]
[HarmonyPrefix]
public static bool UpdatePatch(TextBoxTMP __instance)
@@ -310,6 +311,7 @@ public static class TextBoxPatch
return false;
}
#endif
[HarmonyPatch(nameof(TextBoxTMP.IsCharAllowed))]
[HarmonyPrefix]
+36
View File
@@ -52,6 +52,42 @@ internal static class CanUseVentPatch
// always true for engineer-based roles
couldUse = playerControl.CanUseImpostorVentButton() || (pc.Role.Role == RoleTypes.Engineer && pc.Role.CanUse(__instance.CastFast<IUsable>()));
if (SubmergedCompatibility.IsSubmerged()) // From TheOtherRoles
{
// As submerged does, only change stuff for vents 9 and 14 of submerged. Code partially provided by AlexejheroYTB
if (SubmergedCompatibility.GetInTransition())
{
__result = float.MaxValue;
return canUse = couldUse = false;
}
switch (__instance.Id)
{
case 9: // Cannot enter vent 9 (Engine Room Exit Only Vent)!
{
if (playerControl.inVent) break;
__result = float.MaxValue;
return canUse = couldUse = false;
}
case 14: // Lower Central
{
__result = float.MaxValue;
couldUse = couldUse && !pc.IsDead && (playerControl.CanMove || playerControl.inVent);
canUse = couldUse;
if (canUse)
{
Vector3 center = playerControl.Collider.bounds.center;
Vector3 position = __instance.transform.position;
__result = Vector2.Distance(center, position);
canUse &= __result <= __instance.UsableDistance;
}
return false;
}
}
}
canUse = couldUse;
// Not available if custom roles are not available
if (!canUse) return false;
Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

+15 -1
View File
@@ -929,6 +929,7 @@
"LadderDeath": "Fall From Ladders",
"LadderDeathChance": "Fall To Death Chance",
"GuesserDoesntDieOnMisguess": "<color=#f3e304>Guesser Doesn't Die on Misguess</color>",
"CanGuessDuringDiscussionTime": "<color=#f3e304>Guessers Can Guess During Discussion Time</color>",
"YouCanGuessTitle": "<#00ffff>You can guess!</color>",
"YouCanGuess": "Use the <b>/bt [player id] [role]</b> command to guess someone's role. You can see everyone's IDs next to their name, or type <b>/id</b>.",
"DisableTasks": "Disable Tasks",
@@ -1819,6 +1820,7 @@
"PlayerWasRevived": "The player whose body you tried to report was revived",
"HypnosisNoMeeting": "Everyone is in hypnosis, no meeting calls are allowed currently",
"TargetDisregarded": "The player whose body you tried to report is <#FF6347>Disregarded</color>",
"AmOblivious": "You are <color=#424242>Oblivious</color>, meaning you cannot report bodies",
"ShipHasNoMoreMeetingsLeft": "The ship has no more emergency meetings left",
"UnreportableBody": "This body is unreportable due to a role-specific action",
"CheckMurderFail": "<#ff0000>The murder was canceled due to the following reason:\\n</color>",
@@ -2110,6 +2112,7 @@
"GodNoticeAlive": "During the meeting, each player felt a revelation from heaven, and it turned out that God is still alive!",
"WorkaholicAdviceAlive": "It's not recommended to kill or vote <b>{0}</b> out. Doing so will help them finish their tasks quicker.",
"GuessDead": "Sorry, but it's impossible to guess roles after your death",
"GuessDuringDiscussion": "According to the host's settings, you cannot guess during discussion time.",
"GuessSuperStar": "The Super Star can't be guessed.... you thought it would be that easy, right?",
"GuessNotifiedBait": "Bait can't be guessed because it was announced, you thought it would be that easy, right?",
"GuessGM": "Guessing the GM is impossible because they're already dead.... And why would you do that to the poor Host?",
@@ -2884,7 +2887,7 @@
"ERR-000-804-1": "EHR does not support the Vanilla HnS, so unloaded.",
"ERR-001-000-3": "Main dictionary has duplicated keys.",
"ERR-002-000-3": "Your Among Us version is outdated.",
"ERR-002-000-2": "You're using an unsupported map, i.e. Submerged.",
"ERR-002-000-1": "This game mode is incompatible with the current map.",
"FixBlackScreenWaitForDead": "Everyone is alive, and the black screen fix requires a ghost, so it will wait until someone dies.",
"DraftNotEnoughRoles": "There are not enough roles enabled to choose from, so drafting cannot be started. Please enable more roles and try again.",
"DraftStart": "<size=90%>Drafting has started! You now have a chance to pick a role from the list below if you'd like to get one of them for the next game. The roles you get to choose from, with their indexes, are:</size>\\n\\n{0}\\n\\n<size=90%>Chat commands to use:</size>\\n- Pick role: <b>/draft [index]</b>\\n- View description of role: <b>/dd [index]</b>\\nReplace [index] with the number of the role you want to pick. Example: <b>/draft 4</b>\\n<size=70%>If you don't pick a role, you get a random one like you normally would.</size>",
@@ -5153,6 +5156,7 @@
"Bedwars.BedStatus.Broken": "<u><#ff0000>BED DESTROYED!</color></u>\\n<size=80%>You will no longer respawn!</size>",
"Bedwars.BedStatus.EnemyBroken": "{0}'s bed has been destroyed!",
"Bedwars.HudText.GracePeriodLeft": "Grace period ends\\nin <#ffff44>{0}</color> seconds",
"BedWars.InventorySlotsOption": "Number of Inventory Slots",
"BedWars.SpeedPotionDurationOption": "Speed Potion Duration",
"BedWars.InvisPotionDurationOption": "Invisibility Potion Duration",
"BedWars.ReviveTimeOption": "Revive Time",
@@ -5216,6 +5220,7 @@
"Quiz.Notify.FFAOngoing": "<size=80%><#ffff00><u>Kill everyone</u> and survive</color>\\nHint: Someone is in {0}\\n<#ffa500>{1}</color></size>",
"Quiz.Notify.FFASpectating": "<size=80%><#00ffff>Spectate the chaos!</color>\\n<#00a5ff>{1}</color></size>",
"Quiz.Notify.CorrectAnswerNum": "<size=80%>You got {0}/{1} questions correct! The required amount was {2}.</size>",
"Quiz.Suffix.Invisible": "You cannot see where other people go\\nOther people cannot see where you go",
"Quiz.TaskBarText": "---- GAME DATA ----\\n\\nDifficulty: {0}\\nRound: {1}\\nQuestion: {2}\\n\\nNumber of correct answers\\nfrom everyone so far:\\n\\n{3}",
"Quiz.EndResults.CorrectAnswerNum": "Correct Answers: ",
"Quiz.Questions.Test.0": "Which map are we on?",
@@ -9063,6 +9068,7 @@
"RememberedLover": "You remembered you were a Lover!",
"RememberButtonText": "Remember",
"CanRememberCrewPower": "Can Remember <#8cffff>Crewmate</color> <#fbff00>Power</color> Roles",
"ReportBodyAfterRemember": "After remembering a role by reporting, report the body",
"IncompatibleNeutralMode": "If neutral is incompatible, turn into",
"ImmuneToAttacksWhenTasksDone": "Immune to attacks on task completion",
"TwisterCooldown": "Twist Cooldown",
@@ -9686,6 +9692,14 @@
"AutoGMRotationStatusText.NextGM": "Next",
"AutoGMRotationStatusText.GMPoll": "Player Vote (/gmpoll)",
"AutoGMRotationStatusText.CooldownTimer": "Jumping to the next game mode on the list in <b>{0}</b> seconds",
"SubmergedRoomName.128": "Research",
"SubmergedRoomName.129": "Observatory",
"SubmergedRoomName.130": "Upper Central",
"SubmergedRoomName.131": "Upper Lobby",
"SubmergedRoomName.132": "Filtration",
"SubmergedRoomName.133": "Ballast",
"SubmergedRoomName.134": "Lower Central",
"SubmergedRoomName.135": "Lower Lobby",
"ModLanguage": "Mod Language",
"UseGameLanguage": "Use Game Language",
"Hungarian": "Hungarian",
+1 -1
View File
@@ -49,7 +49,7 @@ public class Allergic : IAddon
public static void OnFixedUpdate(PlayerControl pc)
{
if (Main.HasJustStarted || ExileController.Instance) return;
if (Main.HasJustStarted || !Main.IntroDestroyed || ExileController.Instance || AntiBlackout.SkipTasks) return;
if (!AllergicPlayers.TryGetValue(pc.PlayerId, out byte targetId)) return;
+1
View File
@@ -59,6 +59,7 @@ public class CameraMan : RoleBase
MapNames.Dleks => new(-13.5f, -5.5f),
MapNames.Airship => new(5.8f, -10.8f),
MapNames.Fungle => new(9.5f, 1.2f),
(MapNames)6 => new(-4.23f, -33.38f),
_ => throw new NotImplementedException()
};
}
+2 -1
View File
@@ -25,6 +25,7 @@ public class Abyssbringer : RoleBase
private int Count;
public override bool IsEnable => On;
public static bool ShouldDespawnCNOOnMeeting => (DespawnMode)BlackHoleDespawnMode.GetValue() == DespawnMode.AfterMeeting;
public override void SetupCustomOption()
{
@@ -117,7 +118,7 @@ public class Abyssbringer : RoleBase
public override void OnReportDeadBody()
{
if ((DespawnMode)BlackHoleDespawnMode.GetValue() == DespawnMode.AfterMeeting)
if (ShouldDespawnCNOOnMeeting)
{
Loop.Times(BlackHoles.Count, i => Utils.SendRPC(CustomRPC.SyncRoleData, AbyssbringerId, 3, i));
+9 -6
View File
@@ -75,9 +75,9 @@ public class Wiper : RoleBase
switch (Main.CurrentMap)
{
case MapNames.Skeld or MapNames.Dleks when room.RoomId == SystemTypes.Cafeteria: { return true; }
case MapNames.Skeld or MapNames.Dleks when room.RoomId == SystemTypes.Cafeteria:
return true;
case MapNames.MiraHQ:
{
switch (MeetingStates.FirstMeeting, room.RoomId)
{
case (true, SystemTypes.Launchpad):
@@ -86,9 +86,7 @@ public class Wiper : RoleBase
}
break;
}
case MapNames.Polus:
{
switch (MeetingStates.FirstMeeting, room.RoomId)
{
case (true, SystemTypes.Dropship):
@@ -97,8 +95,13 @@ public class Wiper : RoleBase
}
break;
}
case MapNames.Fungle when !MeetingStates.FirstMeeting && room.RoomId == SystemTypes.MeetingRoom: { return true; }
case MapNames.Fungle when !MeetingStates.FirstMeeting && room.RoomId == SystemTypes.MeetingRoom:
return true;
default:
if (SubmergedCompatibility.IsSubmerged() && room.RoomId is (SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.LowerCentral or (SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.UpperCentral)
return true;
break;
}
return room.RoomId is SystemTypes.Outside or SystemTypes.Hallway or SystemTypes.Ventilation || room.RoomId.ToString().Contains("Decontamination");
+5 -1
View File
@@ -21,6 +21,7 @@ public class Amnesiac : RoleBase
public static OptionItem CanVent;
private static OptionItem VentCooldown;
private static OptionItem VentDuration;
private static OptionItem ReportBodyAfterRemember;
private static readonly CustomRoles[] AmnesiacIncompatibleNeutralMode =
[
@@ -69,6 +70,9 @@ public class Amnesiac : RoleBase
VentDuration = new FloatOptionItem(Id + 15, "MaxInVentTime", new(0f, 180f, 0.5f), 5f, TabGroup.NeutralRoles)
.SetParent(CanVent)
.SetValueFormat(OptionFormat.Seconds);
ReportBodyAfterRemember = new BooleanOptionItem(Id + 16, "ReportBodyAfterRemember", true, TabGroup.NeutralRoles)
.SetParent(CustomRoleSpawnChances[CustomRoles.Amnesiac]);
}
public override void Init()
@@ -123,7 +127,7 @@ public class Amnesiac : RoleBase
if (RememberMode.GetValue() == 0)
{
RememberRole(reporter, target.Object);
return false;
return ReportBodyAfterRemember.GetBool();
}
return true;
+1
View File
@@ -78,6 +78,7 @@ internal class Bargainer : RoleBase
get
{
var mapName = Main.CurrentMap.ToString();
if (SubmergedCompatibility.IsSubmerged()) mapName = "Submerged";
IEnumerable<KeyValuePair<string, Vector2>> devices = DisableDevice.DevicePos.SkipWhile(x => !x.Key.StartsWith(mapName)).TakeWhile(x => x.Key.StartsWith(mapName));
return devices.Select(x => x.Value);
}
+2 -1
View File
@@ -37,7 +37,8 @@ internal class Chemist : RoleBase
[MapNames.Polus] = SystemTypes.Dropship,
[MapNames.Dleks] = SystemTypes.Reactor,
[MapNames.Airship] = SystemTypes.GapRoom,
[MapNames.Fungle] = SystemTypes.Reactor
[MapNames.Fungle] = SystemTypes.Reactor,
[(MapNames)6] = (SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.Ballast
};
private static readonly Dictionary<Factory, Dictionary<string, (List<(int Count, Item Item)> Ingredients, List<(int Count, Item Item)> Results)>> Processes = new()
+1 -1
View File
@@ -38,7 +38,7 @@ internal class Mario : RoleBase
{
On = true;
MarioVentCount[playerId] = 0;
MarioVentNumWin = MapWinCounts[Main.CurrentMap].GetInt();
MarioVentNumWin = MapWinCounts[SubmergedCompatibility.IsSubmerged() ? MapNames.Airship : Main.CurrentMap].GetInt();
}
public override void Init()
+2 -3
View File
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using AmongUs.GameOptions;
using EHR.Crewmate;
using EHR.Modules;
@@ -142,7 +141,7 @@ public class Pelican : RoleBase
3 => new(27f, 3.3f), // dlekS ehT
4 => new(-16.8f, -6.2f), // Airship
5 => new(9.6f, 23.2f), // The Fungle
_ => throw new ArgumentOutOfRangeException(Main.NormalOptions.MapId.ToString(), "Unsupported map")
_ => new(50f, 50f) // Default position if the map is not recognized
};
}
+21 -9
View File
@@ -66,6 +66,7 @@ public class RoomRusher : RoleBase
AllRooms.Remove(SystemTypes.Outside);
AllRooms.Remove(SystemTypes.Ventilation);
AllRooms.RemoveWhere(x => x.ToString().Contains("Decontamination"));
if (SubmergedCompatibility.IsSubmerged()) AllRooms.RemoveWhere(x => (byte)x > 135);
Map = RandomSpawn.SpawnMap.GetSpawnMap();
@@ -94,6 +95,7 @@ public class RoomRusher : RoleBase
MapNames.Polus => afterMeeting ? SystemTypes.Office : SystemTypes.Dropship,
MapNames.Airship => SystemTypes.MainHall,
MapNames.Fungle => SystemTypes.Dropship,
(MapNames)6 => (SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.UpperCentral,
_ => throw new ArgumentOutOfRangeException(map.ToString(), "Invalid map")
};
@@ -112,20 +114,20 @@ public class RoomRusher : RoleBase
{
MapNames.MiraHQ => previous is SystemTypes.Laboratory or SystemTypes.Reactor ^ RoomGoal is SystemTypes.Laboratory or SystemTypes.Reactor,
MapNames.Polus => previous == SystemTypes.Specimens || RoomGoal == SystemTypes.Specimens,
(MapNames)6 => (previous == (SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.Ballast) ^ (RoomGoal == (SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.Ballast),
_ => false
};
if (involvesDecontamination)
{
bool polus = map == MapNames.Polus;
int decontaminationTime = Options.ChangeDecontaminationTime.GetBool()
? polus
? Options.DecontaminationTimeOnPolus.GetInt()
: Options.DecontaminationTimeOnMiraHQ.GetInt()
: 3;
? map == MapNames.Polus
? Options.DecontaminationTimeOnPolus.GetInt() + Options.DecontaminationDoorOpenTimeOnPolus.GetInt()
: Options.DecontaminationTimeOnMiraHQ.GetInt() + Options.DecontaminationDoorOpenTimeOnMiraHQ.GetInt()
: 6;
time += decontaminationTime * (polus ? 2 : 4);
if (SubmergedCompatibility.IsSubmerged()) decontaminationTime = 3;
time += decontaminationTime + 3;
}
switch (map)
@@ -133,12 +135,22 @@ public class RoomRusher : RoleBase
case MapNames.Fungle when RoomGoal == SystemTypes.Laboratory || previous == SystemTypes.Laboratory:
time += (int)(8 / speed);
break;
case MapNames.Polus when (RoomGoal == SystemTypes.Laboratory && previous is not SystemTypes.Storage and not SystemTypes.Specimens) || (previous == SystemTypes.Laboratory && RoomGoal is not SystemTypes.Office and not SystemTypes.Storage and not SystemTypes.Electrical):
case MapNames.Polus when (RoomGoal == SystemTypes.Laboratory && previous is not SystemTypes.Storage and not SystemTypes.Specimens and not SystemTypes.Office) || (previous == SystemTypes.Laboratory && RoomGoal is not SystemTypes.Office and not SystemTypes.Storage and not SystemTypes.Electrical and not SystemTypes.Specimens):
time -= (int)(5 * speed);
break;
case MapNames.Airship when previous == SystemTypes.GapRoom:
time *= RoomGoal switch
{
SystemTypes.MeetingRoom => 6,
SystemTypes.Brig or SystemTypes.VaultRoom or SystemTypes.Records or SystemTypes.Showers or SystemTypes.Lounge => 3,
SystemTypes.Engine or SystemTypes.CargoBay or SystemTypes.Medical => 2,
_ => 1
};
break;
}
TimeLeft = Math.Max((int)Math.Round(time * GlobalTimeMultiplier.GetFloat()), 5);
var maxTime = (int)Math.Ceiling(32 / speed);
TimeLeft = Math.Clamp((int)Math.Round(time * GlobalTimeMultiplier.GetFloat()), 6, maxTime);
Logger.Info($"Goal = from: {Translator.GetString(previous.ToString())} ({previous}), to: {Translator.GetString(RoomGoal.ToString())} ({RoomGoal}) - Time: {TimeLeft} ({map})", "Room Rusher");
LocateArrow.RemoveAllTarget(RoomRusherId);
LocateArrow.Add(RoomRusherId, goalPos);
+287
View File
@@ -0,0 +1,287 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using BepInEx;
using BepInEx.Unity.IL2CPP;
using EHR.Patches;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes;
namespace EHR;
// https://github.com/eDonnes124/Town-Of-Us-R/tree/master/source/Patches/SubmergedCompatibility.cs
public static class SubmergedCompatibility
{
/*
public static class Classes
{
public const string ElevatorMover = "ElevatorMover";
}
*/
public const string SubmergedGuid = "Submerged";
private const ShipStatus.MapType SubmergedMapType = (ShipStatus.MapType)6;
// public static SemanticVersioning.Version Version { get; private set; }
public static bool Loaded { get; set; }
private static BasePlugin Plugin { get; set; }
private static Assembly Assembly { get; set; }
private static Type[] Types { get; set; }
// public static Dictionary<string, Type> InjectedTypes { get; private set; }
/*
private static MonoBehaviour _submarineStatus;
public static MonoBehaviour SubmarineStatus
{
get
{
if (!Loaded) return null;
if (_submarineStatus is null || _submarineStatus.WasCollected || !_submarineStatus || _submarineStatus == null)
{
if (ShipStatus.Instance is null || ShipStatus.Instance == null || !ShipStatus.Instance || ShipStatus.Instance.WasCollected)
return _submarineStatus = null;
if (ShipStatus.Instance.Type == SUBMERGED_MAP_TYPE)
return _submarineStatus = ShipStatus.Instance.GetComponent(Il2CppType.From(SubmarineStatusType))?.TryCast(SubmarineStatusType) as MonoBehaviour;
return _submarineStatus = null;
}
return _submarineStatus;
}
}
*/
private static Type SubmarineStatusType;
// private static MethodInfo CalculateLightRadiusMethod;
// private static MethodInfo RpcRequestChangeFloorMethod;
// private static Type FloorHandlerType;
// private static MethodInfo GetFloorHandlerMethod;
private static Type SubmarinePlayerFloorSystemType;
private static MethodInfo SubmarinePlayerFloorSystemInstanceGetter;
private static MethodInfo ChangePlayerFloorStateMethod;
private static Type VentPatchDataType;
private static PropertyInfo InTransitionField;
// private static Type CustomTaskTypesType;
// private static FieldInfo RetrieveOxigenMaskField;
// public static TaskTypes RetrieveOxygenMask;
// private static Type SubmarineOxygenSystemType;
// private static PropertyInfo SubmarineOxygenSystemInstanceField;
// private static MethodInfo RepairDamageMethod;
private static Type SubmergedExileController;
private static MethodInfo SubmergedExileWrapUpMethod;
private static Type SubmarineElevator;
private static MethodInfo GetInElevator;
// private static MethodInfo GetMovementStageFromTime;
private static FieldInfo GetSubElevatorSystem;
// private static Type ElevatorConsole;
// private static MethodInfo CanUse;
private static Type SubmarineElevatorSystem;
private static FieldInfo UpperDeckIsTargetFloor;
private static FieldInfo SubmergedInstance;
private static FieldInfo SubmergedElevators;
public static void Initialize()
{
Loaded = IL2CPPChainloader.Instance.Plugins.TryGetValue(SubmergedGuid, out PluginInfo plugin);
if (!Loaded) return;
Plugin = plugin!.Instance as BasePlugin;
// Version = plugin.Metadata.Version;
Assembly = Plugin!.GetType().Assembly;
Types = AccessTools.GetTypesFromAssembly(Assembly);
AccessTools.PropertyGetter(Types.FirstOrDefault(t => t.Name == "ComponentExtensions"), "RegisteredTypes")
.Invoke(null, []);
SubmarineStatusType = Types.First(t => t.Name == "SubmarineStatus");
SubmergedInstance = AccessTools.Field(SubmarineStatusType, "instance");
SubmergedElevators = AccessTools.Field(SubmarineStatusType, "elevators");
// CalculateLightRadiusMethod = AccessTools.Method(SubmarineStatusType, "CalculateLightRadius");
// FloorHandlerType = Types.First(t => t.Name == "FloorHandler");
// GetFloorHandlerMethod = AccessTools.Method(FloorHandlerType, "GetFloorHandler", [typeof(PlayerControl)]);
// RpcRequestChangeFloorMethod = AccessTools.Method(FloorHandlerType, "RpcRequestChangeFloor");
SubmarinePlayerFloorSystemType = Types.First(t => t.Name == "SubmarinePlayerFloorSystem");
SubmarinePlayerFloorSystemInstanceGetter = AccessTools.PropertyGetter(SubmarinePlayerFloorSystemType, "Instance");
ChangePlayerFloorStateMethod = AccessTools.Method(SubmarinePlayerFloorSystemType, "ChangePlayerFloorState");
VentPatchDataType = Types.First(t => t.Name == "VentPatchData");
InTransitionField = AccessTools.Property(VentPatchDataType, "InTransition");
// CustomTaskTypesType = Types.First(t => t.Name == "CustomTaskTypes");
// RetrieveOxigenMaskField = AccessTools.Field(CustomTaskTypesType, "RetrieveOxygenMask");
// var retTaskType = AccessTools.Field(CustomTaskTypesType, "taskType");
// RetrieveOxygenMask = (TaskTypes)retTaskType.GetValue(RetrieveOxigenMaskField.GetValue(null))!;
// SubmarineOxygenSystemType = Types.First(t => t.Name == "SubmarineOxygenSystem");
// SubmarineOxygenSystemInstanceField = AccessTools.Property(SubmarineOxygenSystemType, "Instance");
// RepairDamageMethod = AccessTools.Method(SubmarineOxygenSystemType, "RepairDamage");
SubmergedExileController = Types.First(t => t.Name == "SubmergedExileController");
SubmergedExileWrapUpMethod = AccessTools.Method(SubmergedExileController, "WrapUpAndSpawn");
SubmarineElevator = Types.First(t => t.Name == "SubmarineElevator");
GetInElevator = AccessTools.Method(SubmarineElevator, "GetInElevator", [typeof(PlayerControl)]);
// GetMovementStageFromTime = AccessTools.Method(SubmarineElevator, "GetMovementStageFromTime");
GetSubElevatorSystem = AccessTools.Field(SubmarineElevator, "system");
// ElevatorConsole = Types.First(t => t.Name == "ElevatorConsole");
// CanUse = AccessTools.Method(ElevatorConsole, "CanUse");
SubmarineElevatorSystem = Types.First(t => t.Name == "SubmarineElevatorSystem");
UpperDeckIsTargetFloor = AccessTools.Field(SubmarineElevatorSystem, "upperDeckIsTargetFloor");
var harmony = new Harmony("ehr.submerged.patch");
MethodInfo exilerolechangePostfix = SymbolExtensions.GetMethodInfo(() => WrapUpPatch());
harmony.Patch(SubmergedExileWrapUpMethod, null, new HarmonyMethod(exilerolechangePostfix));
/*
var canusePrefix = SymbolExtensions.GetMethodInfo(() => CanUsePrefix());
var canusePostfix = SymbolExtensions.GetMethodInfo(() => CanUsePostfix());
_harmony.Patch(CanUse, new HarmonyMethod(canusePrefix), new HarmonyMethod(canusePostfix));
*/
}
private static void WrapUpPatch()
{
try { ExileControllerWrapUpPatch.WrapUpPostfix(ExileControllerWrapUpPatch.LastExiled); }
finally { ExileControllerWrapUpPatch.WrapUpFinalizer(); }
}
public static void CheckOutOfBoundsElevator(PlayerControl player)
{
if (!Loaded) return;
if (!IsSubmerged()) return;
Tuple<bool, object> elevator = GetPlayerElevator(player);
if (!elevator.Item1) return;
var currentFloor = (bool)UpperDeckIsTargetFloor.GetValue(GetSubElevatorSystem.GetValue(elevator.Item2))!; //true is top, false is bottom
bool playerFloor = player.transform.position.y > -7f; //true is top, false is bottom
if (currentFloor != playerFloor)
ChangeFloor(player.PlayerId, currentFloor);
}
/*
public static void MoveDeadPlayerElevator(PlayerControl player)
{
if (!isSubmerged()) return;
Tuple<bool, object> elevator = GetPlayerElevator(player);
if (!elevator.Item1) return;
int MovementStage = (int)GetMovementStageFromTime.Invoke(elevator.Item2, null)!;
if (MovementStage >= 5)
{
//Fade to clear
bool topfloortarget = (bool)UpperDeckIsTargetFloor.GetValue(getSubElevatorSystem.GetValue(elevator.Item2))!; //true is top, false is bottom
bool topintendedtarget = player.transform.position.y > -7f; //true is top, false is bottom
if (topfloortarget != topintendedtarget)
{
ChangeFloor(!topintendedtarget);
}
}
}
*/
private static Tuple<bool, object> GetPlayerElevator(PlayerControl player)
{
if (!IsSubmerged()) return Tuple.Create(false, (object)null);
// ReSharper disable once RedundantAssignment
IList elevatorlist = CreateList(SubmarineElevator);
elevatorlist = (IList)SubmergedElevators.GetValue(SubmergedInstance.GetValue(null))!;
foreach (object elevator in elevatorlist)
if ((bool)GetInElevator.Invoke(elevator, [player])!)
return Tuple.Create(true, elevator);
return Tuple.Create(false, (object)null);
}
public static void ChangeFloor(byte playerId, bool toUpper)
{
if (!Loaded) return;
ChangePlayerFloorStateMethod.Invoke(SubmarinePlayerFloorSystemInstanceGetter.Invoke(null, null), [playerId, toUpper]);
}
public static bool GetInTransition()
{
if (!Loaded) return false;
return (bool)InTransitionField.GetValue(null)!;
}
public static bool IsSupported(CustomGameMode gameMode)
{
return gameMode is CustomGameMode.Standard or CustomGameMode.SoloKombat or CustomGameMode.FFA or CustomGameMode.MoveAndStop or CustomGameMode.HotPotato or CustomGameMode.HideAndSeek or CustomGameMode.Speedrun or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush;
}
/*
public static void RepairOxygen()
{
if (!Loaded) return;
try
{
ShipStatus.Instance.RpcUpdateSystem((SystemTypes)130, 64);
RepairDamageMethod.Invoke(SubmarineOxygenSystemInstanceField.GetValue(null), new object[] { PlayerControl.LocalPlayer, 64 });
}
catch (System.NullReferenceException)
{
}
}
*/
public static bool IsSubmerged()
{
return Loaded && ShipStatus.Instance && ShipStatus.Instance.Type == SubmergedMapType;
}
private static object TryCast(this Il2CppObjectBase self, Type type)
{
return AccessTools.Method(self.GetType(), nameof(Il2CppObjectBase.TryCast)).MakeGenericMethod(type).Invoke(self, []);
}
private static IList CreateList(Type myType)
{
Type genericListType = typeof(List<>).MakeGenericType(myType);
return (IList)Activator.CreateInstance(genericListType);
}
public enum SubmergedSystemTypes : byte
{
Research = 0x80, // 128
Observatory = 0x81, // 129
UpperCentral = 0x82, // 130 (SubmarineOxygenSystem)
UpperLobby = 0x83, // 131
Filtration = 0x84, // 132
Ballast = 0x85, // 133
LowerCentral = 0x86, // 134
LowerLobby = 0x87, // 135
ElevatorWestLeft = 0x88, // 136 (SubmarineElevatorSystem (HallwayLeft))
ElevatorWestRight = 0x89, // 137 (SubmarineElevatorSystem (HallwayRight))
ElevatorEastLeft = 0x8a, // 138 (SubmarineElevatorSystem (LobbyLeft))
ElevatorEastRight = 0x8b, // 139 (SubmarineElevatorSystem (LobbyRight))
ElevatorService = 0x8c, // 140 (SubmarineElevatorSystem (Service))
SubmarinePlayerFloorSystem = 0x8d, // 141
SubmarineSecuritySabotageSystem = 0x8e, // 142
SubmarineSpawnInSystem = 0x8f, // 143
SubmarineBoxCatSystem = 0x90 // 144
}
}
+1
View File
@@ -4,5 +4,6 @@
<add key="nuget" value="https://api.nuget.org/v3/index.json"/>
<add key="BepInEx" value="https://nuget.bepinex.dev/v3/index.json"/>
<add key="Samboy" value="https://nuget.samboy.dev/v3/index.json"/>
<add key="LocalPackages" value="D:\Coding\Packages"/>
</packageSources>
</configuration>