mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-20 01:46:25 +00:00
Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
c7cfd4ce28
36 files changed
+668
-136
No files matched your search
+62
-19
@@ -3,31 +3,74 @@ name: Build
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: github.head_ref != 'translation'
|
||||
# ---------------------------------------------------------
|
||||
# 1) Build EHR
|
||||
# ---------------------------------------------------------
|
||||
build-ehr:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 6.0.x
|
||||
|
||||
- name: Cache NuGet packages
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.nuget/packages
|
||||
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-nuget-
|
||||
- name: Build EHR
|
||||
run: dotnet build EHR.csproj -c Release -o build/
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
- name: Upload EHR artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ehr
|
||||
path: build/EHR.dll
|
||||
retention-days: 1
|
||||
|
||||
- name: Build
|
||||
run: dotnet build --configuration Release --no-restore
|
||||
# ---------------------------------------------------------
|
||||
# 2) Build Control Panel
|
||||
# ---------------------------------------------------------
|
||||
build-control-panel:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 6.0.x
|
||||
|
||||
- name: Setup MSBuild
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
|
||||
- name: Restore .NET packages
|
||||
run: dotnet restore -p:SelfContained=true CTA/CustomTeamAssigner/CustomTeamAssigner.csproj
|
||||
|
||||
- name: Build Control Panel (MSBuild)
|
||||
run: |
|
||||
msbuild CTA/CustomTeamAssigner/CustomTeamAssigner.csproj `
|
||||
/p:Configuration=Release `
|
||||
/p:Platform="x64" `
|
||||
/p:PublishSingleFile=true `
|
||||
/p:SelfContained=true `
|
||||
/p:RuntimeIdentifier=win-x64 `
|
||||
/p:IncludeAllContentForSelfExtract=true `
|
||||
/p:DebugType=None `
|
||||
/p:PublishDir=build\ `
|
||||
/t:publish
|
||||
|
||||
- name: Rename exe file
|
||||
run: |
|
||||
mkdir build/
|
||||
mv CTA/CustomTeamAssigner/build/CustomTeamAssigner.exe build/EHR_Control-Panel_BETA.exe
|
||||
|
||||
- name: Upload Control Panel artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: control-panel
|
||||
path: build/EHR_Control-Panel_BETA.exe
|
||||
retention-days: 1
|
||||
@@ -289,6 +289,7 @@ public enum CustomRoles
|
||||
|
||||
// Neutrals
|
||||
|
||||
Accumulator,
|
||||
Agitator,
|
||||
Amnesiac,
|
||||
Amogus,
|
||||
@@ -358,6 +359,7 @@ public enum CustomRoles
|
||||
Pulse,
|
||||
Pursuer,
|
||||
Pyromaniac,
|
||||
Quarry,
|
||||
QuizMaster,
|
||||
Reckless,
|
||||
Revolutionist,
|
||||
|
||||
@@ -327,12 +327,12 @@ public static class RoomRush
|
||||
MapNames.Airship => SystemTypes.MainHall,
|
||||
MapNames.Fungle => SystemTypes.Dropship,
|
||||
(MapNames)6 => (SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.UpperCentral,
|
||||
_ => throw new ArgumentOutOfRangeException(map.ToString(), "Invalid map")
|
||||
_ => AllRooms.RandomElement()
|
||||
};
|
||||
|
||||
DonePlayers.Clear();
|
||||
RoomGoal = AllRooms.Without(previous).RandomElement();
|
||||
Vector2 goalPos = Map.Positions.GetValueOrDefault(RoomGoal, RoomGoal.GetRoomClass().transform.position);
|
||||
Vector2 goalPos = RoomGoal.GetRoomClass().transform.position;
|
||||
float speed = Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod);
|
||||
int time;
|
||||
|
||||
@@ -579,7 +579,7 @@ public static class RoomRush
|
||||
|
||||
if (WinByPointsInsteadOfDeaths.GetBool())
|
||||
{
|
||||
Vector2 location = everyoneDies ? Map.Positions.GetValueOrDefault(RoomGoal, RoomGoal.GetRoomClass().transform.position) : DonePlayers.RandomElement().GetPlayer().Pos();
|
||||
Vector2 location = everyoneDies && !Main.LIMap ? Map.Positions.GetValueOrDefault(RoomGoal, RoomGoal.GetRoomClass().transform.position) : DonePlayers.RandomElement().GetPlayer().Pos();
|
||||
playersOutsideRoom.MassTP(location);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -279,7 +279,7 @@ public class Main : BasePlugin
|
||||
|
||||
public static MapNames CurrentMap => (MapNames)NormalOptions.MapId;
|
||||
|
||||
public static bool LIMap => NormalOptions?.MapId == 7;
|
||||
public static bool LIMap => NormalOptions is { MapId: 7 };
|
||||
|
||||
public override void Load()
|
||||
{
|
||||
@@ -561,6 +561,8 @@ public class Main : BasePlugin
|
||||
{ CustomRoles.Thanos, "#F9D401" },
|
||||
{ CustomRoles.Berserker, "#50538F" },
|
||||
{ CustomRoles.SerialKiller, "#233fcc" },
|
||||
{ CustomRoles.Quarry, "#c1fb2b" },
|
||||
{ CustomRoles.Accumulator, "#2bfbae" },
|
||||
{ CustomRoles.Spider, "#C9E44C" },
|
||||
{ CustomRoles.SoulCollector, "#6021A0" },
|
||||
{ CustomRoles.Sharpshooter, "#5901D4" },
|
||||
@@ -1066,6 +1068,8 @@ public enum CustomWinner
|
||||
Necromancer = CustomRoles.Necromancer,
|
||||
Wraith = CustomRoles.Wraith,
|
||||
SerialKiller = CustomRoles.SerialKiller,
|
||||
Quarry = CustomRoles.Quarry,
|
||||
Accumulator = CustomRoles.Accumulator,
|
||||
Spider = CustomRoles.Spider,
|
||||
SoulCollector = CustomRoles.SoulCollector,
|
||||
Berserker = CustomRoles.Berserker,
|
||||
|
||||
@@ -84,6 +84,9 @@ public static class Camouflage
|
||||
{
|
||||
if (Camouflager.On && Camouflager.IsActive) return true;
|
||||
|
||||
if (Options.CurrentGameMode != CustomGameMode.Standard) return false;
|
||||
if (MeetingStates.FirstMeeting && Options.CommsCamouflagePreventRound1.GetBool()) return false;
|
||||
|
||||
switch (Main.CurrentMap)
|
||||
{
|
||||
case MapNames.Fungle when Options.CommsCamouflageDisableOnFungle.GetBool():
|
||||
|
||||
@@ -368,6 +368,7 @@ namespace EHR
|
||||
|
||||
System.Collections.IEnumerator WaitForMeetingEnd()
|
||||
{
|
||||
yield return new WaitForSecondsRealtime(5f);
|
||||
while (ReportDeadBodyPatch.MeetingStarted || GameStates.IsMeeting || ExileController.Instance || AntiBlackout.SkipTasks) yield return null;
|
||||
yield return new WaitForSecondsRealtime(1f);
|
||||
while (ReportDeadBodyPatch.MeetingStarted || GameStates.IsMeeting || ExileController.Instance || AntiBlackout.SkipTasks) yield return null;
|
||||
|
||||
@@ -377,6 +377,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Clerk => CustomRoles.Crewmate,
|
||||
CustomRoles.CovenMember => CustomRoles.Crewmate,
|
||||
CustomRoles.Augur => Augur.CanVent.GetBool() ? CustomRoles.Engineer : CustomRoles.Crewmate,
|
||||
CustomRoles.Accumulator => Accumulator.CanVentBeforeKilling.GetBool() ? CustomRoles.Engineer : CustomRoles.Crewmate,
|
||||
|
||||
// Vanilla roles (just in case)
|
||||
CustomRoles.ImpostorEHR => CustomRoles.Impostor,
|
||||
@@ -516,6 +517,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Duality => RoleTypes.Impostor,
|
||||
CustomRoles.Berserker => RoleTypes.Impostor,
|
||||
CustomRoles.SerialKiller => RoleTypes.Impostor,
|
||||
CustomRoles.Quarry => RoleTypes.Shapeshifter,
|
||||
CustomRoles.SoulCollector => RoleTypes.Impostor,
|
||||
CustomRoles.Spider => UsePets ? RoleTypes.Impostor : RoleTypes.Shapeshifter,
|
||||
CustomRoles.Explosivist => UsePets ? RoleTypes.Impostor : RoleTypes.Shapeshifter,
|
||||
@@ -654,6 +656,8 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Parasite or
|
||||
CustomRoles.Berserker or
|
||||
CustomRoles.SerialKiller or
|
||||
CustomRoles.Quarry or
|
||||
CustomRoles.Accumulator or
|
||||
CustomRoles.Spider or
|
||||
CustomRoles.SoulCollector or
|
||||
CustomRoles.Sharpshooter or
|
||||
@@ -1964,6 +1968,8 @@ public enum CountTypes
|
||||
HexMaster,
|
||||
Wraith,
|
||||
SerialKiller,
|
||||
Quarry,
|
||||
Accumulator,
|
||||
Spider,
|
||||
SoulCollector,
|
||||
Berserker,
|
||||
|
||||
@@ -282,7 +282,7 @@ public class CustomRpcSender
|
||||
throw new InvalidOperationException(errorMsg);
|
||||
}
|
||||
|
||||
if (messages >= 10)
|
||||
if (messages >= AmongUsClient.Instance.GetMaxMessagePackingLimit())
|
||||
{
|
||||
EndMessage(startNew: true);
|
||||
StartMessage(currentRpcTarget);
|
||||
|
||||
@@ -237,7 +237,7 @@ public static class CollectionExtensions
|
||||
/// <typeparam name="T">The type of the elements in the collection</typeparam>
|
||||
/// <returns><c>true</c> if the collection contains any elements that satisfy the predicate, <c>false</c> otherwise</returns>
|
||||
[Annotations.CollectionAccess(Annotations.CollectionAccessType.Read)]
|
||||
public static bool FindFirst<T>(this IEnumerable<T> collection, Func<T, bool> predicate, out T element)
|
||||
public static bool FindFirst<T>(this IEnumerable<T> collection, [Annotations.InstantHandle] Func<T, bool> predicate, out T element)
|
||||
{
|
||||
if (collection is List<T> list)
|
||||
{
|
||||
|
||||
+13
-3
@@ -217,6 +217,7 @@ public static class GuessManager
|
||||
|
||||
switch (pc.GetCustomRole())
|
||||
{
|
||||
case CustomRoles.Augur when !((Augur)Main.PlayerStates[pc.PlayerId].Role).CanGuess:
|
||||
case CustomRoles.Augur when Main.GuesserGuessed[pc.PlayerId] >= Augur.MaxGuessesPerGame.GetInt():
|
||||
case CustomRoles.Augur when Main.GuesserGuessedMeeting[pc.PlayerId] >= Augur.MaxGuessesPerMeeting.GetInt():
|
||||
case CustomRoles.NiceGuesser when Main.GuesserGuessed[pc.PlayerId] >= Options.GGCanGuessTime.GetInt():
|
||||
@@ -485,7 +486,8 @@ public static class GuessManager
|
||||
|
||||
Doomsayer.GuessesCountPerMeeting++;
|
||||
|
||||
if (Doomsayer.GuessesCountPerMeeting >= Doomsayer.MaxNumberOfGuessesPerMeeting.GetInt()) Doomsayer.CantGuess = true;
|
||||
if (Doomsayer.GuessesCountPerMeeting >= Doomsayer.MaxNumberOfGuessesPerMeeting.GetInt())
|
||||
Doomsayer.CantGuess = true;
|
||||
|
||||
if (!Doomsayer.KillCorrectlyGuessedPlayers.GetBool() && pc.PlayerId != dp.PlayerId)
|
||||
{
|
||||
@@ -518,6 +520,13 @@ public static class GuessManager
|
||||
}
|
||||
}
|
||||
|
||||
if (pc.Is(CustomRoles.Augur) && pc.PlayerId == dp.PlayerId)
|
||||
{
|
||||
ShowMessage("Augur.IncorrectGuess");
|
||||
((Augur)Main.PlayerStates[pc.PlayerId].Role).CanGuess = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
string name = dp.GetRealName();
|
||||
if (!Options.DisableKillAnimationOnGuess.GetBool()) CustomSoundsManager.RPCPlayCustomSoundAll("Gunfire");
|
||||
|
||||
@@ -537,7 +546,8 @@ public static class GuessManager
|
||||
Doomsayer.GuessingToWin[pc.PlayerId]++;
|
||||
Doomsayer.SendRPC(pc);
|
||||
|
||||
if (!Doomsayer.GuessedRoles.Contains(role)) Doomsayer.GuessedRoles.Add(role);
|
||||
if (!Doomsayer.GuessedRoles.Contains(role))
|
||||
Doomsayer.GuessedRoles.Add(role);
|
||||
|
||||
Doomsayer.CheckCountGuess(pc);
|
||||
}
|
||||
@@ -1362,7 +1372,7 @@ public static class GuessManager
|
||||
int textIndex = 0;
|
||||
|
||||
int messages = 0;
|
||||
int packingLimit = 10;
|
||||
int packingLimit = AmongUsClient.Instance.GetMaxMessagePackingLimit();
|
||||
|
||||
var skipped = false;
|
||||
PlayerControl guesser = guesserId.GetPlayer();
|
||||
|
||||
@@ -209,6 +209,7 @@ public static class NameColorManager
|
||||
CustomRoles.Crewpostor when seer.Is(CustomRoleTypes.Impostor) && Options.AlliesKnowCrewpostor.GetBool() => Main.RoleColors[CustomRoles.Madmate],
|
||||
CustomRoles.Hypocrite when seer.Is(CustomRoleTypes.Impostor) && Hypocrite.AlliesKnowHypocrite.GetBool() => Main.RoleColors[CustomRoles.Madmate],
|
||||
CustomRoles.President when ((President)targetRoleClass).IsRevealed => Main.RoleColors[CustomRoles.President],
|
||||
CustomRoles.Quarry when ((Quarry)targetRoleClass).TargetId == seer.PlayerId => "000000",
|
||||
_ => color
|
||||
};
|
||||
|
||||
|
||||
@@ -614,6 +614,7 @@ public static class Options
|
||||
public static OptionItem CommsCamouflageLimitMaxTimesPerGame;
|
||||
public static OptionItem CommsCamouflageLimitMaxTimesPerRound;
|
||||
public static OptionItem CommsCamouflageSetSameSpeed;
|
||||
public static OptionItem CommsCamouflagePreventRound1;
|
||||
public static OptionItem DisableReportWhenCC;
|
||||
public static OptionItem SabotageTimeControl;
|
||||
public static OptionItem SkeldReactorTimeLimit;
|
||||
@@ -743,6 +744,7 @@ public static class Options
|
||||
public static OptionItem EnableAutoMessage;
|
||||
public static OptionItem AutoMessageSendInterval;
|
||||
public static OptionItem DraftMaxRolesPerPlayer;
|
||||
public static OptionItem DraftAffectedByRoleSpawnChances;
|
||||
public static OptionItem LargerRoleTextSize;
|
||||
public static OptionItem DynamicTaskCountColor;
|
||||
public static OptionItem ShowTaskCountWhenAlive;
|
||||
@@ -2116,6 +2118,10 @@ public static class Options
|
||||
CommsCamouflageSetSameSpeed = new BooleanOptionItem(22211, "CommsCamouflageSetSameSpeed", true, TabGroup.GameSettings)
|
||||
.SetGameMode(CustomGameMode.Standard)
|
||||
.SetColor(new Color32(243, 96, 96, byte.MaxValue));
|
||||
|
||||
CommsCamouflagePreventRound1 = new BooleanOptionItem(22212, "CommsCamouflagePreventRound1", true, TabGroup.GameSettings)
|
||||
.SetGameMode(CustomGameMode.Standard)
|
||||
.SetColor(new Color32(243, 96, 96, byte.MaxValue));
|
||||
|
||||
DisableReportWhenCC = new BooleanOptionItem(22300, "DisableReportWhenCC", false, TabGroup.GameSettings)
|
||||
.SetGameMode(CustomGameMode.Standard)
|
||||
@@ -3013,6 +3019,9 @@ public static class Options
|
||||
DraftMaxRolesPerPlayer = new IntegerOptionItem(19431, "DraftMaxRolesPerPlayer", new(1, 30, 1), 5, TabGroup.GameSettings)
|
||||
.SetGameMode(CustomGameMode.Standard)
|
||||
.SetColor(new Color32(193, 255, 209, byte.MaxValue));
|
||||
|
||||
DraftAffectedByRoleSpawnChances = new BooleanOptionItem(19435, "DraftAffectedByRoleSpawnChances", false, TabGroup.GameSettings)
|
||||
.SetColor(new Color32(193, 255, 209, byte.MaxValue));
|
||||
|
||||
LargerRoleTextSize = new BooleanOptionItem(24451, "LargerRoleTextSize", false, TabGroup.GameSettings)
|
||||
.SetColor(new Color32(193, 255, 209, byte.MaxValue));
|
||||
|
||||
+5
-16
@@ -50,7 +50,6 @@ public enum CustomRPC
|
||||
SyncAbilityCD,
|
||||
SyncGeneralOptions,
|
||||
SyncRoleData,
|
||||
RequestSendMessage,
|
||||
NotificationPopper,
|
||||
RequestCommandProcessing,
|
||||
|
||||
@@ -72,10 +71,10 @@ public enum CustomRPC
|
||||
SetDrawPlayer,
|
||||
SyncHeadHunter,
|
||||
SyncRabbit,
|
||||
SyncSoulHunter,
|
||||
|
||||
BAU = 150,
|
||||
|
||||
SyncSoulHunter,
|
||||
SyncMycologist,
|
||||
SyncBubble,
|
||||
AddTornado,
|
||||
@@ -88,10 +87,10 @@ public enum CustomRPC
|
||||
SyncMafiosoData,
|
||||
SyncMafiosoPistolCD,
|
||||
SyncDamoclesTimer,
|
||||
SyncChronomancer,
|
||||
|
||||
Sicko = 164,
|
||||
|
||||
SyncChronomancer,
|
||||
PenguinSync,
|
||||
SyncInfection,
|
||||
SetAlchemistPotion,
|
||||
@@ -135,6 +134,7 @@ public enum CustomRPC
|
||||
SyncVengefulRomanticTarget,
|
||||
SetRevealedPlayer,
|
||||
SetCurrentRevealTarget,
|
||||
RpcPassBomb,
|
||||
SetDoomsayerProgress = 209,
|
||||
|
||||
/*
|
||||
@@ -147,7 +147,6 @@ public enum CustomRPC
|
||||
*/
|
||||
|
||||
SetTrackerTarget = 215,
|
||||
RpcPassBomb,
|
||||
SetAlchemistTimer,
|
||||
SyncPostman,
|
||||
SyncChangeling,
|
||||
@@ -168,6 +167,7 @@ public enum CustomRPC
|
||||
SyncCamouflage,
|
||||
SetChatVisible,
|
||||
Exclusionary,
|
||||
Deadlined,
|
||||
|
||||
// Game Modes
|
||||
RoomRushDataSync,
|
||||
@@ -255,7 +255,7 @@ internal static class RPCHandlerPatch
|
||||
{
|
||||
if (id == 115) return true;
|
||||
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.SetSwapperVotes or CustomRPC.MeetingKill or CustomRPC.Guess or CustomRPC.NemesisRevenge or CustomRPC.BAU or CustomRPC.FFAKill or CustomRPC.TMGSync or CustomRPC.InspectorCommand or CustomRPC.ImitatorClick or CustomRPC.RetributionistClick or CustomRPC.StarspawnClick or CustomRPC.VentriloquistClick;
|
||||
return (CustomRPC)id is CustomRPC.VersionCheck or CustomRPC.RequestRetryVersionCheck or CustomRPC.AntiBlackout or CustomRPC.SyncNameNotify or CustomRPC.RequestCommandProcessing or CustomRPC.Judge or CustomRPC.SetSwapperVotes or CustomRPC.MeetingKill or CustomRPC.Guess or CustomRPC.NemesisRevenge or CustomRPC.BAU or CustomRPC.FFAKill or CustomRPC.TMGSync or CustomRPC.InspectorCommand or CustomRPC.ImitatorClick or CustomRPC.RetributionistClick or CustomRPC.StarspawnClick or CustomRPC.VentriloquistClick;
|
||||
}
|
||||
|
||||
private static bool CheckRateLimit(PlayerControl __instance, RpcCalls rpcType)
|
||||
@@ -631,17 +631,6 @@ internal static class RPCHandlerPatch
|
||||
Main.AllPlayerSpeed[id] = speed;
|
||||
break;
|
||||
}
|
||||
case CustomRPC.RequestSendMessage:
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost) break;
|
||||
|
||||
string text = reader.ReadString();
|
||||
byte sendTo = reader.ReadByte();
|
||||
string title = reader.ReadString();
|
||||
bool noSplit = reader.ReadBoolean();
|
||||
Utils.SendMessage(text, sendTo, title, noSplit);
|
||||
break;
|
||||
}
|
||||
case CustomRPC.NotificationPopper:
|
||||
{
|
||||
byte typeId = reader.ReadByte();
|
||||
|
||||
+55
-44
@@ -825,6 +825,7 @@ public static class Utils
|
||||
case CustomRoles.Eclipse:
|
||||
case CustomRoles.Pyromaniac:
|
||||
case CustomRoles.SerialKiller:
|
||||
case CustomRoles.Quarry:
|
||||
case CustomRoles.Spider:
|
||||
case CustomRoles.SoulCollector:
|
||||
case CustomRoles.Berserker:
|
||||
@@ -960,6 +961,7 @@ public static class Utils
|
||||
case CustomRoles.Cherokious:
|
||||
case CustomRoles.Crewpostor:
|
||||
case CustomRoles.Hypocrite:
|
||||
case CustomRoles.Accumulator:
|
||||
if (forRecompute && !p.IsDead) hasTasks = false;
|
||||
if (p.IsDead) hasTasks = false;
|
||||
break;
|
||||
@@ -1831,14 +1833,7 @@ public static class Utils
|
||||
if (!AmongUsClient.Instance.AmHost)
|
||||
{
|
||||
if (sendTo == PlayerControl.LocalPlayer.PlayerId && !multiple)
|
||||
{
|
||||
MessageWriter w = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.RequestSendMessage, SendOption.Reliable, AmongUsClient.Instance.HostId);
|
||||
w.Write(text);
|
||||
w.Write(sendTo);
|
||||
w.Write(title);
|
||||
w.Write(noSplit);
|
||||
AmongUsClient.Instance.FinishRpcImmediately(w);
|
||||
}
|
||||
SendLocally(PlayerControl.LocalPlayer);
|
||||
|
||||
return writer;
|
||||
}
|
||||
@@ -1860,21 +1855,7 @@ public static class Utils
|
||||
|
||||
if (sendTo != byte.MaxValue && receiver.AmOwner)
|
||||
{
|
||||
if (HudManager.InstanceExists)
|
||||
{
|
||||
string name = sender.Data.PlayerName;
|
||||
sender.SetName(title);
|
||||
HudManager.Instance.Chat.AddChat(sender, text);
|
||||
sender.SetName(name);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string pureText = text.RemoveHtmlTags();
|
||||
string pureTitle = title.RemoveHtmlTags();
|
||||
Logger.Info($" Message: {pureText[..(pureText.Length <= 300 ? pureText.Length : 300)]} - To: {GetPlayerById(sendTo)?.GetRealName()} - Title: {pureTitle[..(pureTitle.Length <= 300 ? pureTitle.Length : 300)]}", "SendMessage");
|
||||
}
|
||||
catch { Logger.Info(" Message sent", "SendMessage"); }
|
||||
SendLocally(sender);
|
||||
|
||||
if (addToHistory) ChatUpdatePatch.LastMessages.Add((text, sendTo, title, TimeStamp));
|
||||
return writer;
|
||||
@@ -2185,30 +2166,31 @@ public static class Utils
|
||||
|
||||
foreach (char c in text)
|
||||
{
|
||||
if (char.IsDigit(c))
|
||||
if (char.IsDigit(c) && digitCount == 5)
|
||||
{
|
||||
digitCount++;
|
||||
int lastNewline = sb.ToString().LastIndexOf('\n');
|
||||
|
||||
if (digitCount > 5)
|
||||
if (lastNewline >= 0)
|
||||
{
|
||||
int lastNewline = sb.ToString().LastIndexOf('\n');
|
||||
|
||||
if (lastNewline >= 0)
|
||||
{
|
||||
result.Add(sb.ToString(0, lastNewline + 1));
|
||||
sb.Remove(0, lastNewline + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(sb.ToString());
|
||||
sb.Clear();
|
||||
}
|
||||
|
||||
digitCount = 1;
|
||||
result.Add(sb.ToString(0, lastNewline + 1));
|
||||
sb.Remove(0, lastNewline + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(sb.ToString());
|
||||
sb.Clear();
|
||||
}
|
||||
|
||||
digitCount = 0;
|
||||
foreach (char r in sb.ToString())
|
||||
if (char.IsDigit(r))
|
||||
digitCount++;
|
||||
}
|
||||
|
||||
sb.Append(c);
|
||||
|
||||
if (char.IsDigit(c))
|
||||
digitCount++;
|
||||
}
|
||||
|
||||
if (sb.Length > 0)
|
||||
@@ -2216,6 +2198,25 @@ public static class Utils
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void SendLocally(PlayerControl sender)
|
||||
{
|
||||
if (HudManager.InstanceExists)
|
||||
{
|
||||
string name = sender.Data.PlayerName;
|
||||
sender.SetName(title);
|
||||
HudManager.Instance.Chat.AddChat(sender, text);
|
||||
sender.SetName(name);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string pureText = text.RemoveHtmlTags();
|
||||
string pureTitle = title.RemoveHtmlTags();
|
||||
Logger.Info($" Message: {pureText[..(pureText.Length <= 300 ? pureText.Length : 300)]} - To: {PlayerControl.LocalPlayer.GetRealName()} - Title: {pureTitle[..(pureTitle.Length <= 300 ? pureTitle.Length : 300)]}", "SendMessage");
|
||||
}
|
||||
catch { Logger.Info(" Message sent", "SendMessage"); }
|
||||
}
|
||||
}
|
||||
|
||||
public static HashSet<byte> DirtyName = [];
|
||||
@@ -3011,7 +3012,17 @@ public static class Utils
|
||||
if (Options.CurrentGameMode != CustomGameMode.Standard) goto BeforeEnd;
|
||||
|
||||
if (guesserIsForMeeting || forMeeting || (seerRole == CustomRoles.Nemesis && !seer.IsAlive() && Options.NemesisCanKillNum.GetInt() >= 1))
|
||||
targetPlayerName = $"{ColorString(GetRoleColor(seerRole), target.PlayerId.ToString())} {targetPlayerName}";
|
||||
{
|
||||
byte id = target.PlayerId;
|
||||
|
||||
if (Doppelganger.SwappedIDs.FindFirst(x => x.Item1 == id || x.Item2 == id, out var pair))
|
||||
{
|
||||
if (pair.Item1 == id) id = pair.Item2;
|
||||
else if (pair.Item2 == id) id = pair.Item1;
|
||||
}
|
||||
|
||||
targetPlayerName = $"{ColorString(GetRoleColor(seerRole), id.ToString())} {targetPlayerName}";
|
||||
}
|
||||
|
||||
switch (seerRole)
|
||||
{
|
||||
@@ -3327,7 +3338,7 @@ public static class Utils
|
||||
public static void SendGameData()
|
||||
{
|
||||
int messages = 0;
|
||||
int packingLimit = 10;
|
||||
int packingLimit = AmongUsClient.Instance.GetMaxMessagePackingLimit();
|
||||
|
||||
MessageWriter writer = MessageWriter.Get(SendOption.Reliable);
|
||||
writer.StartMessage(5);
|
||||
@@ -3361,7 +3372,7 @@ public static class Utils
|
||||
public static void SendGameDataTo(int targetClientId)
|
||||
{
|
||||
int messages = 0;
|
||||
int packingLimit = 10;
|
||||
int packingLimit = AmongUsClient.Instance.GetMaxMessagePackingLimit();
|
||||
|
||||
MessageWriter writer = MessageWriter.Get(SendOption.Reliable);
|
||||
writer.StartMessage(6);
|
||||
@@ -3509,8 +3520,8 @@ public static class Utils
|
||||
{
|
||||
CustomRoles.Farmer => 2,
|
||||
CustomRoles.Thanos => 5,
|
||||
CustomRoles.PortalMaker => 5,
|
||||
CustomRoles.Mole => Mole.CD.GetInt(),
|
||||
CustomRoles.PortalMaker => PortalMaker.AbilityCooldown.GetInt(),
|
||||
CustomRoles.Telecommunication => Telecommunication.VentCooldown.GetInt(),
|
||||
CustomRoles.Tether => Tether.VentCooldown.GetInt(),
|
||||
CustomRoles.Gardener => Gardener.AbilityCooldown.GetInt(),
|
||||
|
||||
@@ -585,6 +585,12 @@ internal static class ChatCommands
|
||||
|
||||
private static void ConfirmAuthCommand(PlayerControl player, string commandKey, string text, string[] args)
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost)
|
||||
{
|
||||
RequestCommandProcessingFromHost(text, commandKey);
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameStates.CurrentServerType != GameStates.ServerType.Vanilla)
|
||||
{
|
||||
Utils.SendMessage("\n", player.PlayerId, GetString("ConfirmAuth.ErrorNotVanilla"));
|
||||
@@ -1419,7 +1425,8 @@ internal static class ChatCommands
|
||||
DraftResult = [];
|
||||
|
||||
byte[] allPlayerIds = Main.AllPlayerControls.Select(x => x.PlayerId).ToArray();
|
||||
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).Shuffle();
|
||||
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();
|
||||
|
||||
if (allRoles.Count < allPlayerIds.Length)
|
||||
{
|
||||
|
||||
@@ -203,7 +203,7 @@ public static class ChatManager
|
||||
}
|
||||
|
||||
if (Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.SoloPVP or CustomGameMode.NaturalDisasters or CustomGameMode.Mingle or CustomGameMode.HideAndSeek && GameStates.InGame && !message.StartsWith('/'))
|
||||
Main.AllAlivePlayerControls.NotifyPlayers(string.Format(Translator.GetString("FFAChatMessageNotify"), Main.GameModeColors.GetValueOrDefault(Options.CurrentGameMode, new(1,1,1)), player.PlayerId.ColoredPlayerName(), message));
|
||||
Main.AllAlivePlayerControls.NotifyPlayers(string.Format(Utils.ColorString(Main.GameModeColors.GetValueOrDefault(Options.CurrentGameMode, new(1,1,1)), Translator.GetString("FFAChatMessageNotify")), player.PlayerId.ColoredPlayerName(), message));
|
||||
}
|
||||
|
||||
public static void AddChatHistory(PlayerControl player, string message)
|
||||
|
||||
@@ -28,16 +28,6 @@ internal static class MakePublicPatch
|
||||
}
|
||||
}*/
|
||||
|
||||
[HarmonyPatch(typeof(InnerNetClient), nameof(InnerNetClient.GetMaxMessagePackingLimit))]
|
||||
static class GetMaxMessagePackingLimitPatch
|
||||
{
|
||||
public static bool Prefix(ref int __result)
|
||||
{
|
||||
__result = 10;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(MMOnlineManager), nameof(MMOnlineManager.Start))]
|
||||
// ReSharper disable once InconsistentNaming
|
||||
internal static class MMOnlineManagerStartPatch
|
||||
|
||||
@@ -956,6 +956,14 @@ internal static class MeetingHudStartPatch
|
||||
Transform child = deathReasonTextMeeting.transform.FindChild("RoleTextMeeting");
|
||||
if (child != null) Object.Destroy(child.gameObject);
|
||||
|
||||
byte id = pva.TargetPlayerId;
|
||||
|
||||
if (Doppelganger.SwappedIDs.FindFirst(x => x.Item1 == id || x.Item2 == id, out var pair))
|
||||
{
|
||||
if (pair.Item1 == id) id = pair.Item2;
|
||||
else if (pair.Item2 == id) id = pair.Item1;
|
||||
}
|
||||
|
||||
// Thanks BAU (By D1GQ) - are you happy now?
|
||||
Transform playerLevel = pva.transform.Find("PlayerLevel");
|
||||
Transform levelDisplay = Object.Instantiate(playerLevel, pva.transform);
|
||||
@@ -967,7 +975,7 @@ internal static class MeetingHudStartPatch
|
||||
Transform idNumber = levelDisplay.transform.Find("LevelNumber");
|
||||
Object.Destroy(idLabel.GetComponent<TextTranslatorTMP>());
|
||||
idLabel.GetComponent<TextMeshPro>().text = "ID";
|
||||
idNumber.GetComponent<TextMeshPro>().text = pva.TargetPlayerId.ToString();
|
||||
idNumber.GetComponent<TextMeshPro>().text = id.ToString();
|
||||
idLabel.name = "IdLabel";
|
||||
idNumber.name = "IdNumber";
|
||||
}
|
||||
|
||||
@@ -63,10 +63,11 @@ internal static class EndGamePatch
|
||||
DateTime date = value.RealKiller.TimeStamp;
|
||||
if (date == DateTime.MinValue) continue;
|
||||
|
||||
long secondsIn = new DateTimeOffset(date.ToUniversalTime()).ToUnixTimeSeconds() - IntroCutsceneDestroyPatch.IntroDestroyTS;
|
||||
byte killerId = value.GetRealKiller();
|
||||
bool gmIsFm = Options.CurrentGameMode is CustomGameMode.FFA or CustomGameMode.StopAndGo;
|
||||
bool gmIsFmhh = gmIsFm || Options.CurrentGameMode is CustomGameMode.HotPotato or CustomGameMode.HideAndSeek or CustomGameMode.Speedrun or CustomGameMode.CaptureTheFlag or CustomGameMode.NaturalDisasters or CustomGameMode.RoomRush or CustomGameMode.KingOfTheZones or CustomGameMode.Quiz or CustomGameMode.TheMindGame or CustomGameMode.BedWars or CustomGameMode.Deathrace or CustomGameMode.Mingle or CustomGameMode.Snowdown;
|
||||
sb.Append($"\n{date:T} {Main.AllPlayerNames[key]} ({(gmIsFmhh ? string.Empty : Utils.GetDisplayRoleName(key, true))}{(gmIsFm ? string.Empty : Utils.GetSubRolesText(key, summary: true))}) [{Utils.GetVitalText(key)}]");
|
||||
sb.Append($"\n{secondsIn / 60:00}:{secondsIn % 60:00} {Main.AllPlayerNames[key]} ({(gmIsFmhh ? string.Empty : Utils.GetDisplayRoleName(key, true))}{(gmIsFm ? string.Empty : Utils.GetSubRolesText(key, summary: true))}) [{Utils.GetVitalText(key)}]");
|
||||
if (killerId != byte.MaxValue && killerId != key) sb.Append($"\n\t⇐ {Main.AllPlayerNames[killerId]} ({(gmIsFmhh ? string.Empty : Utils.GetDisplayRoleName(killerId, true))}{(gmIsFm ? string.Empty : Utils.GetSubRolesText(killerId, summary: true))})");
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ internal static class ExternalRpcPetPatch
|
||||
LateTask.New(() => OnPetUse(pc), 0.2f, $"OnPetUse: {pc.GetNameWithRole().RemoveHtmlTags()}", false);
|
||||
}
|
||||
|
||||
public static void OnPetUse(PlayerControl pc)
|
||||
private static void OnPetUse(PlayerControl pc)
|
||||
{
|
||||
if (pc == null ||
|
||||
pc.inVent ||
|
||||
@@ -191,7 +191,7 @@ internal static class ExternalRpcPetPatch
|
||||
public static PlayerControl SelectKillButtonTarget(PlayerControl pc)
|
||||
{
|
||||
Vector2 pos = pc.Pos();
|
||||
List<(PlayerControl pc, float distance)> players = Main.AllAlivePlayerControls.Without(pc).Select(x => (pc: x, distance: Vector2.Distance(pos, x.Pos()))).Where(x => x.distance < 2.5f).OrderBy(x => x.distance).ToList();
|
||||
List<(PlayerControl pc, float distance)> players = Main.AllAlivePlayerControls.Without(pc).Select(x => (pc: x, distance: Vector2.Distance(pos, x.Pos()))).Where(x => x.distance < 3.5f).OrderBy(x => x.distance).ToList();
|
||||
PlayerControl target = players.Count > 0 ? players[0].pc : null;
|
||||
|
||||
if (target != null && target.Is(CustomRoles.Detour))
|
||||
|
||||
@@ -264,6 +264,7 @@ internal static class CheckMurderPatch
|
||||
|
||||
if (ToiletMaster.OnAnyoneCheckMurderStart(killer, target)) return false;
|
||||
if (Dad.OnAnyoneCheckMurderStart(target)) return false;
|
||||
if (Quarry.OnAnyoneCheckMurderStart(killer, target)) return false;
|
||||
|
||||
Simon.RemoveTarget(killer, Simon.Instruction.Kill);
|
||||
|
||||
@@ -1069,6 +1070,12 @@ internal static class ReportDeadBodyPatch
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Quarry.OnAnyoneCheckReportDeadBody(__instance))
|
||||
{
|
||||
Notify("QuarryTarget");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Librarian.OnAnyoneReport(__instance)) return false; // Player dies, no notify needed
|
||||
if (!Trapster.OnAnyoneCheckReportDeadBody(__instance, target)) return false; // Player dies, no notify needed
|
||||
|
||||
|
||||
@@ -485,7 +485,7 @@ Click this badge to view our translation project:
|
||||
### :star: [PL](https://github.com/Lotus-AU/LotusContinued) :
|
||||
|
||||
>
|
||||
> - Ideas for some roles
|
||||
> - Ideas for some roles (Alchemist, Chameleon, Escapist, Necromancer, Deathknight, Romantic and its variants, Vengeance)
|
||||
> - Reference: Customize Main Menu
|
||||
> - Reference: Auto Play Again
|
||||
> - Reference: Some TabIcons
|
||||
|
||||
@@ -126,6 +126,7 @@ deltamalty#8755 | | Nggeereee | Toxic |
|
||||
polarsail#2468 | | hatenggers | Toxic |
|
||||
primesnow#4272 | 65b98a6de | HiverXM | Toxic | The n word
|
||||
todayside#9634 | 4e44cffe6 | | Racist | Joined a lobby with racial slur in name
|
||||
roundframe#4949 | 914830596 | PeaceMaker | Toxic | The n word
|
||||
|
||||
## Other
|
||||
tenthrope#3476 | | Oldishwire | Other |
|
||||
|
||||
@@ -10180,7 +10180,7 @@
|
||||
"WinnerRoleText.Bubble": "Bubble Wins!",
|
||||
"WinnerRoleText.Hookshot": "Hookshot Wins!",
|
||||
"WinnerRoleText.Sprayer": "Sprayer Wins!",
|
||||
"WinnerRoleText.SoulHunter": "Soul Hunter Wins!",
|
||||
"WinnerRoleText.SoulHunter": "صائد الروح يفوز!",
|
||||
"WinnerRoleText.Tiger": "Tiger Wins!",
|
||||
"WinnerRoleText.Beehive": "فوز خلية النحل!",
|
||||
"WinnerRoleText.Phantasm": "Phantasm Wins!",
|
||||
|
||||
@@ -1199,6 +1199,7 @@
|
||||
"CommsCamouflageLimitMaxTimesPerGame": "Maximum Times of Camouflaging Per Game",
|
||||
"CommsCamouflageLimitMaxTimesPerRound": "Maximum Times of Camouflaging Per Round",
|
||||
"CommsCamouflageSetSameSpeed": "All Players Have the Same Speed During Camouflage",
|
||||
"CommsCamouflagePreventRound1": "Don't Camouflage Round 1",
|
||||
"DisableReportWhenCC": "Disable body reporting while camouflaged",
|
||||
"EnableDebugMode": "Enable Debug Mode",
|
||||
"ChangeNameToRoleInfo": "Show Role Info to Unmodded Clients Round 1",
|
||||
@@ -3024,6 +3025,7 @@
|
||||
"DraftTitle": "<#00a5ff>➡</color> <#00ffa5>Drafting</color> <#00a5ff>⬅</color>",
|
||||
"DraftChosen": "You have chosen the <b>{0}</b> role for the next game.\\n<size=70%>You can still change your choice by typing /draft [index] again.</size>",
|
||||
"DraftMaxRolesPerPlayer": "Max Roles For Each Player To Choose From When Drafting",
|
||||
"DraftAffectedByRoleSpawnChances": "Take Role Spawn Chances Into Account When Picking Draft Choices",
|
||||
"EveryoneDrafted": "<#00ffa5>✓</color> <#00ff00>Everyone has drafted their role!</color>",
|
||||
"DeathCommand": "<#ffffff>You were killed by </color>{0}<#ffffff>\\nTheir role is </color>{1}",
|
||||
"DeathCommandFail": "<#ff0000>The game could not determine your real killer.</color>\\nYour death reason is <#00ffa5>{0}</color>.",
|
||||
@@ -4455,6 +4457,7 @@
|
||||
"PortalMaker": "Portal Maker",
|
||||
"PortalMakerInfo": "Make Portals & Teleport Between Them",
|
||||
"PortalMakerInfoLong": "(Crewmates):\\nAs the Portal Maker, you can place 2 permanent portals by petting. After placing both portals, if anyone goes near one of them, they will be teleported to the other portal. Portal usage has a 5 second cooldown.",
|
||||
"PortalMaker.PetToRemovePortals": "Can Pet To Remove Both Portals After Placing Them",
|
||||
"RiftMaker": "Rift Maker",
|
||||
"RiftMakerInfo": "Teleport between 2 places",
|
||||
"RiftMakerInfoLong": "(Impostors):\\nAs the Rift Maker, you may shapeshift twice/pet twice to mark two points on the ship. After you've marked both places, whenever you go near a marked place, you will automatically teleport to the other mark point.\\nVenting discards both marked places.\\n\\nYou cannot vent normally.",
|
||||
@@ -4973,7 +4976,7 @@
|
||||
"FFA_ShieldIsOneTimeUse": "Shields break after 1 kill attempt",
|
||||
"FFA_ShieldBroken": "<color=#ffff00>Someone tried to kill you, your shield is now broken!</color>",
|
||||
"FFA_ChatDuringGame": "Chat During Game",
|
||||
"FFAChatMessageNotify": "<#ffffff><#00ffff>[Chat]</color> {0}: {1}",
|
||||
"FFAChatMessageNotify": "[Chat] {0}: <#ffffff>{1}",
|
||||
"Killer": "FREE FOR ALL",
|
||||
"KillerInfo": "KILL EVERYONE TO WIN",
|
||||
"KillerInfoLong": "KILL EVERYONE TO WIN",
|
||||
@@ -9247,6 +9250,15 @@
|
||||
"Telekinetic.Mode.Shield": "Shield",
|
||||
"Telekinetic.Mode.Speed": "Speed",
|
||||
"Telekinetic.Mode.Doors": "Doors",
|
||||
"Quarry": "Quarry",
|
||||
"QuarryInfo": "Hide From Players",
|
||||
"QuarryInfoLong": "(Neutrals):\\nAs the Quarry, you may shapeshift to select your target and swap places with them, they then have to kill you in a specified time or they die. If they find and kill you, they live. You can also kill normally using the kill button.\\nKill everyone to win!",
|
||||
"Quarry.TargetHasArrowToQuarry": "Target has an arrow to the <#c1fb2b>Quarry</color>",
|
||||
"Quarry.CanVentDuringSeekTime": "Can Vent During Seek Time",
|
||||
"Quarry.TargetDiesOnMeetingCall": "Target Dies on Meeting Call",
|
||||
"Quarry.SeekTime": "Seek Time",
|
||||
"Quarry.TargetSeekBeginNotify": "<#ff0000>You were swapped with the {0}! They have a black name!",
|
||||
"Quarry.TimeLeftSuffix": "<#ffff00>You have <#ffffff>{0}s</color> to kill the {1} <#ffa500>or you die!",
|
||||
"Nonplus": "Nonplus",
|
||||
"NonplusInfo": "Blind Others",
|
||||
"NonplusInfoLong": "(Neutrals):\\nAs the Nonplus, you can blind others by venting or petting. Blinded players can't see anything for a set amount of time.\\nKill everyone to win.",
|
||||
@@ -9516,6 +9528,14 @@
|
||||
"ImmuneToAttacksWhenTasksDone": "Immune to attacks on task completion",
|
||||
"TwisterCooldown": "Twist Cooldown",
|
||||
"TwisterButtonText": "Twist",
|
||||
"Accumulator": "Accumulator",
|
||||
"AccumulatorInfo": "Do Tasks For Low Kill Cooldown",
|
||||
"AccumulatorInfoLong": "(Neutrals):\\nAs the Accumulator, you start with a crewmate basis, and you can do tasks. Each task you complete lowers your kill cooldown (that you'll eventually have). You can pet or finish all tasks to turn into a killing neutral with the kill cooldown you accumulated.\\nKill everyone to win!",
|
||||
"Accumulator.KillCooldownDecrementWithEachTask": "Kill Cooldown Decrement With Each Task",
|
||||
"Accumulator.CanVentBeforeKilling": "Can Vent Before Killing",
|
||||
"Accumulator.CanVentAfterKilling": "Can Vent After Killing",
|
||||
"Accumulator.ImpostorVisionBeforeKilling": "Has Impostor Vision Before Killing",
|
||||
"Accumulator.ImpostorVisionAfterKilling": "Has Impostor Vision After Killing",
|
||||
"MainMenu.SettingsButton": "Settings",
|
||||
"MainMenu.InventoryButton": "Inventory",
|
||||
"MainMenu.CreditsButton": "Credits",
|
||||
@@ -10028,9 +10048,10 @@
|
||||
"Goddess.Suffix": "<{1}>Stone Gazing</color> <#888888>-</color> <b>{0}</b>",
|
||||
"Augur": "Augur",
|
||||
"AugurInfo": "Guess roles",
|
||||
"AugurInfoLong": "(Coven):\\nYou can guess players' roles during meetings. You cannot gain the Necronomicon, similar to a Reaper.",
|
||||
"AugurInfoLong": "(Coven):\\nYou can guess players' roles during meetings. If you misguess, you don't die, but you can't guess again until the next meeting. You cannot gain the Necronomicon, similar to a Reaper.",
|
||||
"Augur.MaxGuessesPerMeeting": "Max number of guesses per meeting",
|
||||
"Augur.MaxGuessesPerGame": "Max number of guesses per game",
|
||||
"Augur.IncorrectGuess": "<#ff0000>Incorrect!</color> You can't guess in this meeting anymore.",
|
||||
"Dreamweaver": "Dreamweaver",
|
||||
"DreamweaverInfo": "Drive players insane with weaving",
|
||||
"DreamweaverInfoLong": "(Coven):\\nWeave a player; woven players will become insane after the next meeting. Once insane, when they go near someone, they will immediately use their ability if their cooldown is up. Upon your death, all insane players will be freed. When enhanced by the Necronomicon, insane players will suicide after making contact with another player.",
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using EHR.Modules;
|
||||
using Hazel;
|
||||
|
||||
namespace EHR.Roles;
|
||||
|
||||
public class Deadlined : IAddon
|
||||
@@ -21,12 +24,14 @@ public class Deadlined : IAddon
|
||||
{
|
||||
DidTask.Add(pc.PlayerId);
|
||||
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
|
||||
Utils.SendRPC(CustomRPC.Deadlined, 1, pc.PlayerId);
|
||||
}
|
||||
|
||||
public static void AfterMeetingTasks()
|
||||
{
|
||||
DidTask = [];
|
||||
MeetingEndTS = Utils.TimeStamp;
|
||||
Utils.SendRPC(CustomRPC.Deadlined, 2);
|
||||
|
||||
foreach (PlayerControl pc in Main.AllPlayerControls)
|
||||
{
|
||||
@@ -40,13 +45,28 @@ public class Deadlined : IAddon
|
||||
{
|
||||
if (MeetingStates.FirstMeeting) return;
|
||||
|
||||
if (MeetingEndTS + InactiveTime.GetInt() > Utils.TimeStamp) return;
|
||||
if (MeetingEndTS + InactiveTime.GetInt() >= Utils.TimeStamp) return;
|
||||
|
||||
foreach (PlayerControl pc in Main.AllAlivePlayerControls)
|
||||
{
|
||||
if (!pc.Is(CustomRoles.Deadlined)) continue;
|
||||
|
||||
if (!DidTask.Contains(pc.PlayerId)) pc.Suicide();
|
||||
if (!DidTask.Contains(pc.PlayerId))
|
||||
pc.Suicide();
|
||||
}
|
||||
}
|
||||
|
||||
public static void ReceiveRPC(MessageReader reader)
|
||||
{
|
||||
switch (reader.ReadPackedInt32())
|
||||
{
|
||||
case 1:
|
||||
DidTask.Add(reader.ReadByte());
|
||||
break;
|
||||
case 2:
|
||||
DidTask = [];
|
||||
MeetingEndTS = Utils.TimeStamp - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ public class Augur : CovenBase
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public bool CanGuess;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(650070)
|
||||
@@ -34,6 +36,7 @@ public class Augur : CovenBase
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
CanGuess = true;
|
||||
}
|
||||
|
||||
public override void ApplyGameOptions(IGameOptions opt, byte playerId)
|
||||
@@ -44,4 +47,14 @@ public class Augur : CovenBase
|
||||
AURoleOptions.EngineerInVentMaxTime = MaxInVentTime.GetFloat();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnReportDeadBody()
|
||||
{
|
||||
CanGuess = true;
|
||||
}
|
||||
|
||||
public override void AfterMeetingTasks()
|
||||
{
|
||||
CanGuess = true;
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,14 @@ public class PortalMaker : RoleBase
|
||||
private List<Vector2> Marks;
|
||||
private Dictionary<byte, long> LastTP;
|
||||
|
||||
public static OptionItem AbilityCooldown;
|
||||
private static OptionItem PetToRemovePortals;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(652800);
|
||||
StartSetup(652800)
|
||||
.AutoSetupOption(ref AbilityCooldown, 5, new IntegerValueRule(1, 120, 1), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref PetToRemovePortals, true);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
@@ -36,7 +41,14 @@ public class PortalMaker : RoleBase
|
||||
|
||||
public override void OnPet(PlayerControl pc)
|
||||
{
|
||||
if (Marks.Count == 2) return;
|
||||
if (Marks.Count == 2)
|
||||
{
|
||||
if (!PetToRemovePortals.GetBool()) return;
|
||||
Marks.Clear();
|
||||
CustomNetObject.AllObjects.OfType<Portal>().Do(x => x.Despawn());
|
||||
pc.Notify(Translator.GetString("MarksCleared"));
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 pos = pc.Pos();
|
||||
if (Marks.Count == 1 && Vector2.Distance(Marks[0], pos) < 4f) return;
|
||||
|
||||
@@ -127,6 +127,7 @@ public class Alchemist : RoleBase
|
||||
public override void OnTaskComplete(PlayerControl pc, int completedTaskCount, int totalTaskCount)
|
||||
{
|
||||
PotionID = (byte)IRandom.Instance.Next(1, 8);
|
||||
SendRPCData();
|
||||
|
||||
switch (PotionID)
|
||||
{
|
||||
@@ -233,10 +234,9 @@ public class Alchemist : RoleBase
|
||||
player.Notify(GetString("AlchemistNoPotion"));
|
||||
break;
|
||||
}
|
||||
|
||||
am.SendRPCData();
|
||||
|
||||
|
||||
am.PotionID = 10;
|
||||
am.SendRPCData();
|
||||
}
|
||||
|
||||
private void SendRPC()
|
||||
|
||||
@@ -57,35 +57,30 @@ internal class Beacon : RoleBase
|
||||
|
||||
public override void OnCheckPlayerPosition(PlayerControl pc)
|
||||
{
|
||||
if (!GameStates.IsInTask || pc == null) return;
|
||||
|
||||
long now = Utils.TimeStamp;
|
||||
if (LastChange.TryGetValue(pc.PlayerId, out long ts) && ts == now) return;
|
||||
|
||||
Vector2 pos = pc.Pos();
|
||||
float radius = Radius.GetFloat();
|
||||
bool beaconNearby = Beacons.Any(x => Vector2.Distance(x.Pos(), pos) <= radius);
|
||||
bool affectedPlayer = AffectedPlayers.Contains(pc.PlayerId);
|
||||
bool lightsOff = Utils.IsActive(SystemTypes.Electrical);
|
||||
|
||||
switch (affectedPlayer)
|
||||
switch (affectedPlayer: AffectedPlayers.Contains(pc.PlayerId), beaconNearby: Utils.IsActive(SystemTypes.Electrical) && Beacons.Any(x => Vector2.Distance(x.Pos(), pos) <= radius))
|
||||
{
|
||||
case true when !beaconNearby:
|
||||
case (affectedPlayer: true, beaconNearby: false):
|
||||
{
|
||||
AffectedPlayers.Remove(pc.PlayerId);
|
||||
if (lightsOff) pc.MarkDirtySettings();
|
||||
pc.MarkDirtySettings();
|
||||
|
||||
LastChange[pc.PlayerId] = now;
|
||||
break;
|
||||
}
|
||||
case false when beaconNearby:
|
||||
case (affectedPlayer: false, beaconNearby: true):
|
||||
{
|
||||
AffectedPlayers.Add(pc.PlayerId);
|
||||
if (lightsOff) pc.MarkDirtySettings();
|
||||
pc.MarkDirtySettings();
|
||||
|
||||
LastChange[pc.PlayerId] = now;
|
||||
|
||||
if (pc.AmOwner && lightsOff)
|
||||
if (pc.AmOwner)
|
||||
Achievements.Type.ALightInTheShadows.CompleteAfterGameEnd();
|
||||
|
||||
break;
|
||||
|
||||
@@ -85,10 +85,16 @@ public class Mastermind : RoleBase
|
||||
});
|
||||
}
|
||||
|
||||
private long LastUpdate;
|
||||
|
||||
public override void OnFixedUpdate(PlayerControl _)
|
||||
{
|
||||
if (!IsEnable || GameStates.IsMeeting || (ManipulatedPlayers.Count == 0 && ManipulateDelays.Count == 0)) return;
|
||||
|
||||
long now = TimeStamp;
|
||||
if (now == LastUpdate) return;
|
||||
LastUpdate = now;
|
||||
|
||||
foreach (KeyValuePair<byte, long> x in ManipulateDelays)
|
||||
{
|
||||
PlayerControl pc = GetPlayerById(x.Key);
|
||||
@@ -99,10 +105,10 @@ public class Mastermind : RoleBase
|
||||
continue;
|
||||
}
|
||||
|
||||
if (x.Value + Delay.GetInt() < TimeStamp)
|
||||
if (x.Value + Delay.GetInt() < now)
|
||||
{
|
||||
ManipulateDelays.Remove(x.Key);
|
||||
ManipulatedPlayers.TryAdd(x.Key, TimeStamp);
|
||||
ManipulatedPlayers.TryAdd(x.Key, now);
|
||||
|
||||
if (pc.HasKillButton()) TempKCDs.TryAdd(pc.PlayerId, Main.KillTimers[pc.PlayerId]);
|
||||
else pc.RpcChangeRoleBasis(CustomRoles.SerialKiller);
|
||||
@@ -123,7 +129,7 @@ public class Mastermind : RoleBase
|
||||
continue;
|
||||
}
|
||||
|
||||
if (x.Value + TimeLimit.GetInt() < TimeStamp)
|
||||
if (x.Value + TimeLimit.GetInt() < now)
|
||||
{
|
||||
ManipulatedPlayers.Remove(x.Key);
|
||||
if (!TempKCDs.Remove(x.Key)) player.RpcChangeRoleBasis(player.GetCustomRole());
|
||||
@@ -134,7 +140,7 @@ public class Mastermind : RoleBase
|
||||
Achievements.Type.OutOfTime.Complete();
|
||||
}
|
||||
|
||||
long time = TimeLimit.GetInt() - (TimeStamp - x.Value);
|
||||
long time = TimeLimit.GetInt() - (now - x.Value);
|
||||
|
||||
player.Notify(string.Format(GetString("ManipulateNotify"), time), 3f, true);
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ public class Swooper : RoleBase
|
||||
{
|
||||
long cooldown = lastTime + (long)Cooldown - now;
|
||||
|
||||
if ((int)cooldown != CD && !(UsedRole != CustomRoles.Chameleon && UsePhantomBasis.GetBool() && (UsedRole != CustomRoles.Wraith || UsePhantomBasisForNKs.GetBool())))
|
||||
if ((int)cooldown != CD && !(UsePhantomBasis.GetBool() && (UsedRole != CustomRoles.Wraith || UsePhantomBasisForNKs.GetBool())))
|
||||
player.Notify(string.Format(GetString("CDPT"), cooldown + 1), 3f, true);
|
||||
|
||||
CD = (int)cooldown;
|
||||
@@ -212,7 +212,7 @@ public class Swooper : RoleBase
|
||||
case < 0:
|
||||
lastTime = now;
|
||||
|
||||
if (UsedRole == CustomRoles.Chameleon)
|
||||
if (UsedRole == CustomRoles.Chameleon && !UsePets.GetBool())
|
||||
{
|
||||
int ventId = ventedId == -10 ? Main.LastEnteredVent[player.PlayerId].Id : ventedId;
|
||||
Main.AllPlayerControls.Without(player).Do(x => player.MyPhysics.RpcExitVentDesync(ventId, x));
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using AmongUs.GameOptions;
|
||||
using EHR.Modules;
|
||||
using Hazel;
|
||||
|
||||
namespace EHR.Roles;
|
||||
|
||||
public class Accumulator : RoleBase
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
private static OptionItem StartingKillCooldown;
|
||||
private static OptionItem KillCooldownDecrementWithEachTask;
|
||||
public static OptionItem CanVentBeforeKilling;
|
||||
private static OptionItem VentCooldown;
|
||||
private static OptionItem MaxInVentTime;
|
||||
private static OptionItem CanVentAfterKilling;
|
||||
private static OptionItem ImpostorVisionBeforeKilling;
|
||||
private static OptionItem ImpostorVisionAfterKilling;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
private bool Killing;
|
||||
private float KCD;
|
||||
private byte AccumulatorId;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(657700)
|
||||
.AutoSetupOption(ref StartingKillCooldown, 30f, new FloatValueRule(0f, 180f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref KillCooldownDecrementWithEachTask, 3f, new FloatValueRule(0f, 30f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref CanVentBeforeKilling, false)
|
||||
.AutoSetupOption(ref VentCooldown, 0f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds, overrideParent: CanVentBeforeKilling)
|
||||
.AutoSetupOption(ref MaxInVentTime, 0f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds, overrideParent: CanVentBeforeKilling)
|
||||
.AutoSetupOption(ref CanVentAfterKilling, true)
|
||||
.AutoSetupOption(ref ImpostorVisionBeforeKilling, false)
|
||||
.AutoSetupOption(ref ImpostorVisionAfterKilling, true)
|
||||
.CreateOverrideTasksData();
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
Killing = false;
|
||||
KCD = StartingKillCooldown.GetFloat();
|
||||
AccumulatorId = playerId;
|
||||
}
|
||||
|
||||
public override void SetKillCooldown(byte id)
|
||||
{
|
||||
Main.AllPlayerKillCooldown[id] = KCD;
|
||||
}
|
||||
|
||||
public override bool CanUseKillButton(PlayerControl pc)
|
||||
{
|
||||
return Killing;
|
||||
}
|
||||
|
||||
public override void ApplyGameOptions(IGameOptions opt, byte id)
|
||||
{
|
||||
opt.SetVision(Killing ? ImpostorVisionAfterKilling.GetBool() : ImpostorVisionBeforeKilling.GetBool());
|
||||
|
||||
if (!Killing && CanVentBeforeKilling.GetBool())
|
||||
{
|
||||
AURoleOptions.EngineerCooldown = VentCooldown.GetFloat();
|
||||
AURoleOptions.EngineerInVentMaxTime = MaxInVentTime.GetFloat();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanUseImpostorVentButton(PlayerControl pc)
|
||||
{
|
||||
return Killing && CanVentAfterKilling.GetBool();
|
||||
}
|
||||
|
||||
public override void OnTaskComplete(PlayerControl pc, int completedTaskCount, int totalTaskCount)
|
||||
{
|
||||
KCD -= KillCooldownDecrementWithEachTask.GetFloat();
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, pc.PlayerId, KCD, Killing);
|
||||
|
||||
if (completedTaskCount + 1 >= totalTaskCount)
|
||||
OnPet(pc);
|
||||
}
|
||||
|
||||
public override void OnPet(PlayerControl pc)
|
||||
{
|
||||
if (Killing) return;
|
||||
Killing = true;
|
||||
pc.RpcSetRoleDesync(RoleTypes.Impostor, pc.OwnerId, setRoleMap: true);
|
||||
LateTask.New(() => pc.SetKillCooldown(KCD), 0.2f);
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, pc.PlayerId, KCD, Killing);
|
||||
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
|
||||
}
|
||||
|
||||
public override void AfterMeetingTasks()
|
||||
{
|
||||
if (!Killing) return;
|
||||
PlayerControl pc = AccumulatorId.GetPlayer();
|
||||
if (pc == null || !pc.IsAlive()) return;
|
||||
pc.RpcSetRoleDesync(RoleTypes.Impostor, pc.OwnerId, setRoleMap: true);
|
||||
}
|
||||
|
||||
public void ReceiveRPC(MessageReader reader)
|
||||
{
|
||||
KCD = reader.ReadSingle();
|
||||
Killing = reader.ReadBoolean();
|
||||
}
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
if (seer.PlayerId != AccumulatorId || seer.PlayerId != target.PlayerId || (seer.IsModdedClient() && !hud) || meeting || Killing) return string.Empty;
|
||||
return string.Format(Translator.GetString("KCD"), Math.Round(Math.Max(0, KCD), 1));
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@ public class Doppelganger : RoleBase
|
||||
|
||||
public static int LocalPlayerChangeSkinTimes;
|
||||
|
||||
public static HashSet<(byte, byte)> SwappedIDs = [];
|
||||
public static Dictionary<byte, string> DoppelVictim = [];
|
||||
public static Dictionary<byte, NetworkedPlayerInfo.PlayerOutfit> DoppelPresentSkin = [];
|
||||
private static Dictionary<byte, int> TotalSteals = [];
|
||||
@@ -68,6 +69,7 @@ public class Doppelganger : RoleBase
|
||||
public override void Init()
|
||||
{
|
||||
PlayerIdList = [];
|
||||
SwappedIDs = [];
|
||||
DoppelVictim = [];
|
||||
TotalSteals = [];
|
||||
DoppelPresentSkin = [];
|
||||
@@ -255,6 +257,9 @@ public class Doppelganger : RoleBase
|
||||
|
||||
DoppelVictim[target.PlayerId] = tname;
|
||||
|
||||
SwappedIDs.RemoveWhere(x => x.Item1 == killer.PlayerId || x.Item2 == killer.PlayerId);
|
||||
SwappedIDs.Add((killer.PlayerId, target.PlayerId));
|
||||
|
||||
|
||||
RpcChangeSkin(target, killerSkin);
|
||||
Logger.Info("Changed target skin", "Doppelganger");
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using AmongUs.GameOptions;
|
||||
using EHR.Modules;
|
||||
using EHR.Modules.Extensions;
|
||||
using Hazel;
|
||||
|
||||
namespace EHR.Roles;
|
||||
|
||||
public class Quarry : RoleBase
|
||||
{
|
||||
public static bool On;
|
||||
private static List<Quarry> Instances = [];
|
||||
|
||||
private static OptionItem TargetHasArrowToQuarry;
|
||||
private static OptionItem CanVentDuringSeekTime;
|
||||
private static OptionItem TargetDiesOnMeetingCall;
|
||||
private static OptionItem SeekTime;
|
||||
private static OptionItem ShapeshiftCooldown;
|
||||
private static OptionItem KillCooldown;
|
||||
private static OptionItem CanVent;
|
||||
private static OptionItem ImpostorVision;
|
||||
private static OptionItem AbilityUseLimit;
|
||||
private static OptionItem AbilityUseGainWithEachKill;
|
||||
private static OptionItem CooldownsResetEachOther;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
private byte QuarryId;
|
||||
public byte TargetId;
|
||||
private Stopwatch SeekTimer;
|
||||
private long LastUpdate;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(657800)
|
||||
.AutoSetupOption(ref TargetHasArrowToQuarry, true)
|
||||
.AutoSetupOption(ref CanVentDuringSeekTime, false)
|
||||
.AutoSetupOption(ref TargetDiesOnMeetingCall, false)
|
||||
.AutoSetupOption(ref SeekTime, 15, new IntegerValueRule(0, 120, 1), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref ShapeshiftCooldown, 15f, new FloatValueRule(0f, 180f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref KillCooldown, 22.5f, new FloatValueRule(0f, 180f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref CanVent, true)
|
||||
.AutoSetupOption(ref ImpostorVision, true)
|
||||
.AutoSetupOption(ref AbilityUseLimit, 2f, new FloatValueRule(0, 20, 0.05f), OptionFormat.Times)
|
||||
.AutoSetupOption(ref AbilityUseGainWithEachKill, 1f, new FloatValueRule(0f, 5f, 0.1f), OptionFormat.Times)
|
||||
.AutoSetupOption(ref CooldownsResetEachOther, true);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
Instances = [];
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
QuarryId = playerId;
|
||||
TargetId = byte.MaxValue;
|
||||
SeekTimer = new();
|
||||
LastUpdate = 0;
|
||||
Instances.Add(this);
|
||||
playerId.SetAbilityUseLimit(AbilityUseLimit.GetFloat());
|
||||
}
|
||||
|
||||
public override void Remove(byte playerId)
|
||||
{
|
||||
Instances.Remove(this);
|
||||
}
|
||||
|
||||
public override void SetKillCooldown(byte id)
|
||||
{
|
||||
Main.AllPlayerKillCooldown[id] = KillCooldown.GetFloat();
|
||||
}
|
||||
|
||||
public override void ApplyGameOptions(IGameOptions opt, byte id)
|
||||
{
|
||||
opt.SetVision(ImpostorVision.GetBool());
|
||||
AURoleOptions.ShapeshifterCooldown = ShapeshiftCooldown.GetFloat();
|
||||
AURoleOptions.ShapeshifterDuration = 1f;
|
||||
}
|
||||
|
||||
public override bool CanUseImpostorVentButton(PlayerControl pc)
|
||||
{
|
||||
return CanVent.GetBool() && (TargetId == byte.MaxValue || CanVentDuringSeekTime.GetBool());
|
||||
}
|
||||
|
||||
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
return target.PlayerId != TargetId;
|
||||
}
|
||||
|
||||
public override void OnMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (CooldownsResetEachOther.GetBool())
|
||||
killer.RpcResetAbilityCooldown();
|
||||
}
|
||||
|
||||
public override bool OnShapeshift(PlayerControl shapeshifter, PlayerControl target, bool shapeshifting)
|
||||
{
|
||||
if (!shapeshifting) return true;
|
||||
|
||||
if (shapeshifter.GetAbilityUseLimit() < 1) return false;
|
||||
shapeshifter.RpcRemoveAbilityUse();
|
||||
|
||||
Vector2 pos = shapeshifter.Pos();
|
||||
shapeshifter.TP(target);
|
||||
target.TP(pos);
|
||||
|
||||
TargetId = target.PlayerId;
|
||||
SeekTimer = Stopwatch.StartNew();
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, QuarryId, 1, TargetId);
|
||||
Main.Instance.StartCoroutine(ContinuouslyResetAbilityCooldown());
|
||||
if (TargetHasArrowToQuarry.GetBool()) TargetArrow.Add(target.PlayerId, shapeshifter.PlayerId);
|
||||
|
||||
var sender = CustomRpcSender.Create("QuarryTarget", SendOption.Reliable);
|
||||
var hasValue = false;
|
||||
hasValue |= sender.Notify(target, string.Format(Translator.GetString("Quarry.TargetSeekBeginNotify"), CustomRoles.Quarry.ToColoredString()));
|
||||
hasValue |= sender.RpcSetRole(target, RoleTypes.Impostor, target.OwnerId);
|
||||
hasValue |= sender.RpcSetRole(shapeshifter, RoleTypes.Crewmate, target.OwnerId);
|
||||
hasValue |= sender.NotifyRolesSpecific(target, shapeshifter, out sender, out bool cleared);
|
||||
if (cleared) hasValue = false;
|
||||
sender.SendMessage(dispose: !hasValue);
|
||||
|
||||
if (CooldownsResetEachOther.GetBool())
|
||||
shapeshifter.SetKillCooldown();
|
||||
|
||||
return false;
|
||||
|
||||
System.Collections.IEnumerator ContinuouslyResetAbilityCooldown()
|
||||
{
|
||||
while (TargetId != byte.MaxValue)
|
||||
{
|
||||
int interval = ShapeshiftCooldown.GetInt() - 2;
|
||||
Stopwatch stopwatch = Stopwatch.StartNew();
|
||||
while (stopwatch.Elapsed.Seconds < interval) yield return null;
|
||||
if (TargetId == byte.MaxValue) yield break;
|
||||
shapeshifter.RpcResetAbilityCooldown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnFixedUpdate(PlayerControl pc)
|
||||
{
|
||||
if (!Main.IntroDestroyed || !GameStates.IsInTask || ExileController.Instance || AntiBlackout.SkipTasks || !pc.IsAlive() || TargetId == byte.MaxValue) return;
|
||||
|
||||
long now = Utils.TimeStamp;
|
||||
if (now == LastUpdate) return;
|
||||
LastUpdate = now;
|
||||
|
||||
PlayerControl target = TargetId.GetPlayer();
|
||||
|
||||
if (target == null || !target.IsAlive())
|
||||
{
|
||||
TargetId = byte.MaxValue;
|
||||
SeekTimer.Reset();
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, QuarryId, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (SeekTimer.GetRemainingTime(SeekTime.GetInt()) <= 0)
|
||||
{
|
||||
target.Suicide();
|
||||
TargetId = byte.MaxValue;
|
||||
SeekTimer.Reset();
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, QuarryId, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
|
||||
Utils.NotifyRoles(SpecifySeer: target, SpecifyTarget: target);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool OnAnyoneCheckMurderStart(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
foreach (Quarry instance in Instances)
|
||||
{
|
||||
if (instance.TargetId == killer.PlayerId && instance.QuarryId == target.PlayerId)
|
||||
{
|
||||
killer.Kill(target);
|
||||
killer.RpcSetRoleDesync(killer.GetRoleTypes(), killer.OwnerId);
|
||||
instance.TargetId = byte.MaxValue;
|
||||
instance.SeekTimer.Reset();
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, instance.QuarryId, 2);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool OnAnyoneCheckReportDeadBody(PlayerControl reporter)
|
||||
{
|
||||
return !Instances.Exists(x => x.TargetId == reporter.PlayerId);
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
{
|
||||
return pc.PlayerId != TargetId;
|
||||
}
|
||||
|
||||
public override void OnReportDeadBody()
|
||||
{
|
||||
if (TargetId == byte.MaxValue) return;
|
||||
|
||||
TargetId = byte.MaxValue;
|
||||
SeekTimer.Reset();
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, QuarryId, 2);
|
||||
|
||||
PlayerControl target = TargetId.GetPlayer();
|
||||
if (target == null || !target.IsAlive()) return;
|
||||
|
||||
if (TargetDiesOnMeetingCall.GetBool())
|
||||
target.Suicide();
|
||||
else
|
||||
{
|
||||
PlayerControl pc = QuarryId.GetPlayer();
|
||||
if (pc == null || !pc.IsAlive()) return;
|
||||
|
||||
pc.RpcIncreaseAbilityUseLimitBy(1);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReceiveRPC(MessageReader reader)
|
||||
{
|
||||
switch (reader.ReadPackedInt32())
|
||||
{
|
||||
case 1:
|
||||
TargetId = reader.ReadByte();
|
||||
SeekTimer = Stopwatch.StartNew();
|
||||
break;
|
||||
case 2:
|
||||
TargetId = byte.MaxValue;
|
||||
SeekTimer.Reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
if ((seer.PlayerId != QuarryId && seer.PlayerId != TargetId) || seer.PlayerId != target.PlayerId || meeting || hud || TargetId == byte.MaxValue) return string.Empty;
|
||||
string timer = string.Format(Translator.GetString("Quarry.TimeLeftSuffix"), SeekTimer.GetRemainingTime(SeekTime.GetInt()) - 1, CustomRoles.Quarry.ToColoredString());
|
||||
return seer.PlayerId == TargetId ? $"{TargetArrow.GetAllArrows(TargetId)}\n{timer}" : timer;
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ public class Starspawn : RoleBase
|
||||
|
||||
public override bool CanUseKillButton(PlayerControl pc)
|
||||
{
|
||||
return pc.IsAlive();
|
||||
return pc.GetAbilityUseLimit() >= 1;
|
||||
}
|
||||
|
||||
public override void SetKillCooldown(byte id)
|
||||
@@ -68,7 +68,9 @@ public class Starspawn : RoleBase
|
||||
|
||||
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
IsolatedPlayers.Add(target.PlayerId);
|
||||
if (!IsolatedPlayers.Add(target.PlayerId)) return false;
|
||||
killer.RpcRemoveAbilityUse();
|
||||
killer.SetKillCooldown(AbilityCooldown.GetFloat());
|
||||
Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target);
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, killer.PlayerId, target.PlayerId);
|
||||
return false;
|
||||
@@ -92,7 +94,7 @@ public class Starspawn : RoleBase
|
||||
|
||||
if (AmongUsClient.Instance.AmHost)
|
||||
{
|
||||
var command = $"/daybreak";
|
||||
const string command = "/daybreak";
|
||||
ChatCommands.DayBreakCommand(PlayerControl.LocalPlayer, "Command.Daybreak", command, command.Split(' '));
|
||||
}
|
||||
else
|
||||
@@ -105,7 +107,7 @@ public class Starspawn : RoleBase
|
||||
LateTask.New(() => starspawnButton.SetActive(true), 1f, "StarspawnButton");
|
||||
}
|
||||
|
||||
public static void CreateStarspawnButton(MeetingHud __instance)
|
||||
private static void CreateStarspawnButton(MeetingHud __instance)
|
||||
{
|
||||
if (GameObject.Find("StarspawnButton") != null) Object.Destroy(GameObject.Find("StarspawnButton").gameObject);
|
||||
if (PlayerControl.LocalPlayer == null || !PlayerControl.LocalPlayer.IsAlive()) return;
|
||||
|
||||
Reference in new issue
Block a user