mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-19 15:51:58 +00:00
some changes & additions
This commit is contained in:
1 parent
03ab4127b7
commit
a39aa85fb8
27 files changed
+283
-69
No files matched your search
+1
-1
@@ -198,7 +198,7 @@ public enum CustomRoles
|
||||
Lighter,
|
||||
Lookout,
|
||||
Luckey,
|
||||
Lyncher,
|
||||
Decryptor,
|
||||
Markseeker,
|
||||
Marshall,
|
||||
Mathematician,
|
||||
|
||||
@@ -263,7 +263,7 @@ public static class NaturalDisasters
|
||||
{
|
||||
"BuildingCollapse" => roomKvp.Value,
|
||||
"Thunderstorm" => Pelican.GetBlackRoomPS(),
|
||||
_ => IRandom.Instance.Next(2) == 0
|
||||
_ => IRandom.Instance.Next(2) == 0 && Options.CurrentGameMode == CustomGameMode.NaturalDisasters
|
||||
? Main.AllAlivePlayerControls.RandomElement().Pos()
|
||||
: new(Random.Range(MapBounds.X.Left, MapBounds.X.Right), Random.Range(MapBounds.Y.Top, MapBounds.Y.Bottom))
|
||||
};
|
||||
|
||||
@@ -223,7 +223,7 @@ public static class RoomRush
|
||||
|
||||
Map = RandomSpawn.SpawnMap.GetSpawnMap();
|
||||
|
||||
yield return new WaitForSeconds(Main.CurrentMap == MapNames.Airship ? 4f : 0.2f);
|
||||
yield return new WaitForSeconds(Main.CurrentMap == MapNames.Airship ? 8f : 4f);
|
||||
|
||||
PlayerControl[] aapc = Main.AllAlivePlayerControls;
|
||||
aapc.Do(x => x.RpcSetCustomRole(CustomRoles.RRPlayer));
|
||||
@@ -234,7 +234,7 @@ public static class RoomRush
|
||||
|
||||
if (showTutorial)
|
||||
{
|
||||
var readingTime = 2;
|
||||
var readingTime = 0;
|
||||
|
||||
StringBuilder sb = new(Translator.GetString("RR_Tutorial_Basics"));
|
||||
sb.AppendLine();
|
||||
@@ -464,6 +464,7 @@ public static class RoomRush
|
||||
switch (reader.ReadPackedInt32())
|
||||
{
|
||||
case 1:
|
||||
PointsToWinValue = PointsToWin.GetInt() * Main.AllAlivePlayerControls.Length;
|
||||
int ventLimit = VentTimes.GetInt();
|
||||
VentLimit = Main.AllPlayerControls.ToDictionary(x => x.PlayerId, _ => ventLimit);
|
||||
if (WinByPointsInsteadOfDeaths.GetBool()) Points = Main.AllPlayerControls.ToDictionary(x => x.PlayerId, _ => 0);
|
||||
@@ -603,7 +604,8 @@ public class RRPlayer : RoleBase
|
||||
public override void OnExitVent(PlayerControl pc, Vent vent)
|
||||
{
|
||||
RoomRush.VentLimit[pc.PlayerId]--;
|
||||
Utils.SendRPC(CustomRPC.RoomRushDataSync, 2, RoomRush.VentLimit[pc.PlayerId], pc.PlayerId);
|
||||
if (RoomRush.VentLimit[pc.PlayerId] <= 0) pc.RpcChangeRoleBasis(CustomRoles.RRPlayer);
|
||||
int newLimit = RoomRush.VentLimit[pc.PlayerId];
|
||||
Utils.SendRPC(CustomRPC.RoomRushDataSync, 2, newLimit, pc.PlayerId);
|
||||
if (newLimit <= 0) pc.RpcChangeRoleBasis(CustomRoles.RRPlayer);
|
||||
}
|
||||
}
|
||||
@@ -402,7 +402,7 @@ public class Main : BasePlugin
|
||||
{ CustomRoles.Oxyman, "#ffa58c" },
|
||||
{ CustomRoles.Rhapsode, "#f5ad42" },
|
||||
{ CustomRoles.Chef, "#d6d6ff" },
|
||||
{ CustomRoles.Lyncher, "#14ba7d" },
|
||||
{ CustomRoles.Decryptor, "#14ba7d" },
|
||||
{ CustomRoles.Socialite, "#32a8a8" },
|
||||
{ CustomRoles.Adrenaline, "#ffff00" },
|
||||
{ CustomRoles.Safeguard, "#4949e3" },
|
||||
|
||||
@@ -76,7 +76,7 @@ public static class Achievements
|
||||
Speedrun, // Have a game end in under 1 minute
|
||||
Carried, // Win as a madmate while the other impostors are dead
|
||||
SorryToBurstYourBubble, // Explode 5 people with 1 encased player as the Bubble
|
||||
GetLynched, // Successfully guess 3 roles as lyncher
|
||||
GetDecrypted, // Successfully guess 3 roles as Decryptor
|
||||
FlagMaster, // Carry the flag the longest in CTF
|
||||
Tag, // Tag the most players in CTF
|
||||
Vectory, // Vent 50 times in one game
|
||||
@@ -230,6 +230,7 @@ public static class Achievements
|
||||
IEnumerator FetchAchievementsFromApiAsync()
|
||||
{
|
||||
while (PlayerControl.LocalPlayer == null) yield return null;
|
||||
yield return new WaitForSeconds(3f);
|
||||
|
||||
string userId = PlayerControl.LocalPlayer.GetClient().GetHashedPuid();
|
||||
var url = $"{ApiLoadEndpoint}?userId={userId}";
|
||||
|
||||
@@ -237,7 +237,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.President => CustomRoles.Crewmate,
|
||||
CustomRoles.Oxyman => CustomRoles.Engineer,
|
||||
CustomRoles.Chef => CustomRoles.Crewmate,
|
||||
CustomRoles.Lyncher => CustomRoles.Crewmate,
|
||||
CustomRoles.Decryptor => CustomRoles.Crewmate,
|
||||
CustomRoles.Adrenaline => CustomRoles.Crewmate,
|
||||
CustomRoles.Safeguard => CustomRoles.Crewmate,
|
||||
CustomRoles.Clairvoyant => UsePets ? CustomRoles.Crewmate : CustomRoles.Engineer,
|
||||
@@ -907,7 +907,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Merchant or
|
||||
CustomRoles.Mayor or
|
||||
CustomRoles.Insight or
|
||||
CustomRoles.Lyncher or
|
||||
CustomRoles.Decryptor or
|
||||
CustomRoles.Transporter;
|
||||
}
|
||||
|
||||
@@ -1730,7 +1730,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Detour => RoleOptionType.Crewmate_Power,
|
||||
CustomRoles.Dictator => RoleOptionType.Crewmate_Power,
|
||||
CustomRoles.Guardian => RoleOptionType.Crewmate_Power,
|
||||
CustomRoles.Lyncher => RoleOptionType.Crewmate_Power,
|
||||
CustomRoles.Decryptor => RoleOptionType.Crewmate_Power,
|
||||
CustomRoles.Marshall => RoleOptionType.Crewmate_Power,
|
||||
CustomRoles.Mayor => RoleOptionType.Crewmate_Power,
|
||||
CustomRoles.NiceEraser => RoleOptionType.Crewmate_Power,
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EHR.Modules;
|
||||
|
||||
public abstract class CustomSabotage
|
||||
{
|
||||
public static readonly List<CustomSabotage> Instances = [];
|
||||
|
||||
public virtual void Deteriorate()
|
||||
{
|
||||
Instances.Add(this);
|
||||
}
|
||||
|
||||
protected virtual void Update() { }
|
||||
|
||||
protected virtual void Fix()
|
||||
{
|
||||
Instances.Remove(this);
|
||||
}
|
||||
|
||||
protected virtual string GetSuffix(PlayerControl seer, PlayerControl target, bool hud, bool meeting)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
|
||||
protected static int GetDefaultSabotageTimeLimit(MapNames map)
|
||||
{
|
||||
return map switch
|
||||
{
|
||||
MapNames.Skeld or MapNames.Dleks => 30,
|
||||
MapNames.MiraHQ => 45,
|
||||
MapNames.Polus => 60,
|
||||
MapNames.Airship => 90,
|
||||
MapNames.Fungle => 60,
|
||||
_ => 75
|
||||
};
|
||||
}
|
||||
|
||||
protected static void AdjustTimeLimitBasedOnPlayerSpeed(ref int timeLimit)
|
||||
{
|
||||
float speed = Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod);
|
||||
timeLimit = (int)Math.Ceiling(timeLimit / speed);
|
||||
}
|
||||
|
||||
|
||||
public static string GetAllSuffix(PlayerControl seer, PlayerControl target, bool hud, bool meeting)
|
||||
{
|
||||
StringBuilder suffix = new();
|
||||
|
||||
foreach (CustomSabotage sabotage in Instances)
|
||||
{
|
||||
string tempSuffix = sabotage.GetSuffix(seer, target, hud, meeting).Trim();
|
||||
|
||||
if (!string.IsNullOrEmpty(tempSuffix))
|
||||
suffix.Append($"{tempSuffix}\n");
|
||||
}
|
||||
|
||||
return suffix.ToString().Trim();
|
||||
}
|
||||
|
||||
public static void UpdateAll()
|
||||
{
|
||||
foreach (CustomSabotage sabotage in Instances.ToArray())
|
||||
{
|
||||
try { sabotage.Update(); }
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
}
|
||||
}
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
Instances.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public class GrabOxygenMaskSabotage : CustomSabotage
|
||||
{
|
||||
private long LastNotify;
|
||||
private long LastReactorFlash;
|
||||
private long DeteriorateTS;
|
||||
private HashSet<byte> HasMask;
|
||||
private SystemTypes TargetRoom;
|
||||
private int TimeLimit;
|
||||
private Vector2 RoomPosition;
|
||||
|
||||
public override void Deteriorate()
|
||||
{
|
||||
base.Deteriorate();
|
||||
|
||||
DeteriorateTS = Utils.TimeStamp;
|
||||
HasMask = [];
|
||||
|
||||
MapNames map = Main.CurrentMap;
|
||||
|
||||
TargetRoom = map switch
|
||||
{
|
||||
MapNames.Skeld or MapNames.Dleks => SystemTypes.MedBay,
|
||||
MapNames.MiraHQ => SystemTypes.Balcony,
|
||||
MapNames.Polus => SystemTypes.Specimens,
|
||||
MapNames.Airship => SystemTypes.Medical,
|
||||
MapNames.Fungle => SystemTypes.Kitchen,
|
||||
_ => SystemTypes.Outside
|
||||
};
|
||||
|
||||
TimeLimit = GetDefaultSabotageTimeLimit(map);
|
||||
AdjustTimeLimitBasedOnPlayerSpeed(ref TimeLimit);
|
||||
|
||||
RoomPosition = RandomSpawn.SpawnMap.GetSpawnMap().Positions.GetValueOrDefault(TargetRoom, TargetRoom.GetRoomClass().transform.position);
|
||||
Main.AllAlivePlayerControls.Do(x => LocateArrow.Add(x.PlayerId, RoomPosition));
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
PlayerControl[] aapc = Main.AllAlivePlayerControls;
|
||||
byte[] playersInRoom = aapc.Select(x => (id: x.PlayerId, room: x.GetPlainShipRoom())).Where(x => x.room != null && x.room.RoomId == TargetRoom).Select(x => x.id).ToArray();
|
||||
playersInRoom.Except(HasMask).ToValidPlayers().NotifyPlayers(Translator.GetString("CustomSabotage.GrabOxygenMask.Done"));
|
||||
playersInRoom.Except(HasMask).Do(x => LocateArrow.Remove(x, RoomPosition));
|
||||
HasMask.UnionWith(playersInRoom);
|
||||
|
||||
long now = Utils.TimeStamp;
|
||||
|
||||
if (DeteriorateTS + TimeLimit <= now)
|
||||
{
|
||||
Main.AllPlayerControls.Do(x => LocateArrow.Remove(x.PlayerId, RoomPosition));
|
||||
aapc.ExceptBy(HasMask, x => x.PlayerId).Do(x => x.Suicide(PlayerState.DeathReason.OutOfOxygen));
|
||||
Fix();
|
||||
return;
|
||||
}
|
||||
|
||||
if (LastReactorFlash + 2 <= now)
|
||||
{
|
||||
LastReactorFlash = now;
|
||||
aapc.ExceptBy(HasMask, x => x.PlayerId).Do(x => x.ReactorFlash(flashDuration: 0.1f));
|
||||
}
|
||||
|
||||
if (LastNotify != now)
|
||||
{
|
||||
LastNotify = now;
|
||||
Utils.NotifyRoles();
|
||||
}
|
||||
}
|
||||
|
||||
protected override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud, bool meeting)
|
||||
{
|
||||
if (seer.PlayerId != target.PlayerId || hud || meeting || HasMask.Contains(seer.PlayerId)) return string.Empty;
|
||||
long now = Utils.TimeStamp;
|
||||
return Utils.ColorString(now % 2 == 0 ? Color.yellow : Color.red, $"[{Translator.GetString(TargetRoom.ToString())}] {Translator.GetString("CustomSabotage.GrabOxygenMask")} {TimeLimit - (now - DeteriorateTS)} {LocateArrow.GetArrow(seer, RoomPosition)}");
|
||||
}
|
||||
}
|
||||
@@ -6,15 +6,12 @@ namespace EHR;
|
||||
|
||||
internal static class DoubleTrigger
|
||||
{
|
||||
public static List<byte> PlayerIdList = [];
|
||||
|
||||
public static Dictionary<byte, float> FirstTriggerTimer = [];
|
||||
public static Dictionary<byte, byte> FirstTriggerTarget = [];
|
||||
public static Dictionary<byte, Action> FirstTriggerAction = [];
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
PlayerIdList = [];
|
||||
FirstTriggerTimer = [];
|
||||
FirstTriggerAction = [];
|
||||
}
|
||||
|
||||
@@ -1081,6 +1081,20 @@ internal static class ExtendedPlayerControl
|
||||
_ => SystemTypes.Reactor
|
||||
};
|
||||
|
||||
if (IsActive(systemtypes))
|
||||
{
|
||||
Main.PlayerStates[pc.PlayerId].IsBlackOut = true;
|
||||
pc.MarkDirtySettings();
|
||||
|
||||
LateTask.New(() =>
|
||||
{
|
||||
Main.PlayerStates[pc.PlayerId].IsBlackOut = false;
|
||||
pc.MarkDirtySettings();
|
||||
}, (float.IsNaN(flashDuration) ? Options.KillFlashDuration.GetFloat() : flashDuration) + delay, "Fix BlackOut Reactor Flash");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
pc.RpcDesyncRepairSystem(systemtypes, 128);
|
||||
|
||||
LateTask.New(() =>
|
||||
|
||||
@@ -80,12 +80,14 @@ public static class FixedUpdateCaller
|
||||
{
|
||||
NonLowLoadPlayerIndex++;
|
||||
|
||||
if (NonLowLoadPlayerIndex >= PlayerControl.AllPlayerControls.Count)
|
||||
NonLowLoadPlayerIndex = Math.Min(0, -(30 - PlayerControl.AllPlayerControls.Count));
|
||||
int count = PlayerControl.AllPlayerControls.Count;
|
||||
|
||||
if (NonLowLoadPlayerIndex >= count)
|
||||
NonLowLoadPlayerIndex = Math.Min(0, -(30 - count));
|
||||
|
||||
CustomGameMode currentGameMode = Options.CurrentGameMode;
|
||||
|
||||
for (var index = 0; index < PlayerControl.AllPlayerControls.Count; index++)
|
||||
for (var index = 0; index < count; index++)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -170,7 +170,7 @@ public sealed class PlayerGameOptionsSender(PlayerControl player) : GameOptionsS
|
||||
SetMaxVision();
|
||||
NaturalDisasters.ApplyGameOptions(opt, player.PlayerId);
|
||||
break;
|
||||
case CustomGameMode.RoomRush when RoomRush.VentLimit[player.PlayerId] > 0:
|
||||
case CustomGameMode.RoomRush when RoomRush.VentLimit.TryGetValue(player.PlayerId, out int vl) && vl > 0:
|
||||
AURoleOptions.EngineerCooldown = 0.01f;
|
||||
AURoleOptions.EngineerInVentMaxTime = 0f;
|
||||
goto case CustomGameMode.RoomRush;
|
||||
|
||||
@@ -66,6 +66,7 @@ public class PlayerState(byte playerId)
|
||||
Dragged,
|
||||
Mauled,
|
||||
WipedOut,
|
||||
OutOfOxygen,
|
||||
|
||||
// Natural Disasters
|
||||
Meteor,
|
||||
@@ -100,7 +101,7 @@ public class PlayerState(byte playerId)
|
||||
public DeathReason deathReason { get; set; } = DeathReason.etc;
|
||||
public bool IsBlackOut { get; set; }
|
||||
|
||||
public bool IsSuicide => deathReason == DeathReason.Suicide;
|
||||
public bool IsSuicide => deathReason is DeathReason.Suicide or DeathReason.Fall;
|
||||
public TaskState TaskState { get; set; } = new();
|
||||
|
||||
public void SetMainRole(CustomRoles role, bool failsafe = false)
|
||||
@@ -166,7 +167,7 @@ public class PlayerState(byte playerId)
|
||||
RemoveDisableDevicesPatch.UpdateDisableDevices();
|
||||
}
|
||||
|
||||
if (Lyncher.On) Lyncher.Instances.ForEach(x => x.OnRoleChange(PlayerId));
|
||||
if (Decryptor.On) Decryptor.Instances.ForEach(x => x.OnRoleChange(PlayerId));
|
||||
|
||||
if (!role.Is(Team.Impostor) && !(role == CustomRoles.Traitor && Traitor.CanGetImpostorOnlyAddons.GetBool()))
|
||||
SubRoles.ToArray().DoIf(x => x.IsImpOnlyAddon(), RemoveSubRole);
|
||||
|
||||
@@ -93,14 +93,14 @@ public static class GuessManager
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pc.Is(CustomRoles.Lyncher) && Lyncher.GuessMode.GetValue() == 2) goto SkipCheck;
|
||||
if (pc.Is(CustomRoles.Decryptor) && Decryptor.GuessMode.GetValue() == 2) goto SkipCheck;
|
||||
|
||||
if ((pc.IsCrewmate() && !Options.CrewmatesCanGuess.GetBool()) ||
|
||||
(pc.IsImpostor() && !Options.ImpostorsCanGuess.GetBool()) ||
|
||||
(pc.Is(CustomRoleTypes.Coven) && !Options.CovenCanGuess.GetBool()) ||
|
||||
(pc.IsNeutralKiller() && !Options.NeutralKillersCanGuess.GetBool()) ||
|
||||
(pc.GetCustomRole().IsNonNK() && !Options.PassiveNeutralsCanGuess.GetBool()) ||
|
||||
(pc.Is(CustomRoles.Lyncher) && Lyncher.GuessMode.GetValue() == 0) ||
|
||||
(pc.Is(CustomRoles.Decryptor) && Decryptor.GuessMode.GetValue() == 0) ||
|
||||
(Options.GuesserNumRestrictions.GetBool() && !Guessers.Contains(pc.PlayerId)))
|
||||
{
|
||||
if (pc.Is(CustomRoles.Guesser)) goto SkipCheck;
|
||||
@@ -113,7 +113,7 @@ public static class GuessManager
|
||||
|
||||
SkipCheck:
|
||||
|
||||
if (pc.GetCustomRole() is CustomRoles.Lyncher or CustomRoles.NecroGuesser ||
|
||||
if (pc.GetCustomRole() is CustomRoles.Decryptor or CustomRoles.NecroGuesser ||
|
||||
(pc.Is(CustomRoles.NiceGuesser) && Options.GGTryHideMsg.GetBool()) ||
|
||||
(pc.Is(CustomRoles.EvilGuesser) && Options.EGTryHideMsg.GetBool()) ||
|
||||
(pc.Is(CustomRoles.Doomsayer) && Doomsayer.DoomsayerTryHideMsg.GetBool()) ||
|
||||
@@ -1082,7 +1082,7 @@ public static class GuessManager
|
||||
CustomRoles.NecroGuesser => true,
|
||||
CustomRoles.Augur => true,
|
||||
CustomRoles.Doomsayer when !Doomsayer.CantGuess => true,
|
||||
CustomRoles.Lyncher when Lyncher.GuessMode.GetValue() == 2 => true,
|
||||
CustomRoles.Decryptor when Decryptor.GuessMode.GetValue() == 2 => true,
|
||||
_ when Options.GuesserMode.GetBool() => lp.GetTeam() switch
|
||||
{
|
||||
Team.Impostor => Options.ImpostorsCanGuess.GetBool(),
|
||||
|
||||
@@ -101,6 +101,18 @@ internal static class LocateArrow
|
||||
return LocateArrows.Keys.Where(ai => ai.From == seer.PlayerId).Aggregate(string.Empty, (current, arrowInfo) => current + LocateArrows[arrowInfo]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a specific visible target arrow
|
||||
/// </summary>
|
||||
/// <param name="seer"></param>
|
||||
/// <param name="position"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetArrow(PlayerControl seer, Vector3 position)
|
||||
{
|
||||
ArrowInfo arrowInfo = new(seer.PlayerId, position);
|
||||
return LocateArrows.FirstOrDefault(a => a.Key.Equals(arrowInfo)).Value ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check target arrow every FixedUpdate
|
||||
/// Issue NotifyRoles when there are updates
|
||||
|
||||
@@ -243,8 +243,8 @@ public static class Statistics
|
||||
if (exiled && exiledPlayer.PlayerId == lp.PlayerId && Main.PlayerStates.Values.Any(x => x.SubRoles.Contains(CustomRoles.Bait) && x.GetRealKiller() == lp.PlayerId))
|
||||
Achievements.Type.Gotcha.Complete();
|
||||
|
||||
if (lp.Is(CustomRoles.Lyncher) && Main.PlayerStates.Values.Count(x => x.deathReason == PlayerState.DeathReason.Gambled && x.GetRealKiller() == lp.PlayerId) >= 3)
|
||||
Achievements.Type.GetLynched.Complete();
|
||||
if (lp.Is(CustomRoles.Decryptor) && Main.PlayerStates.Values.Count(x => x.deathReason == PlayerState.DeathReason.Gambled && x.GetRealKiller() == lp.PlayerId) >= 3)
|
||||
Achievements.Type.GetDecrypted.Complete();
|
||||
|
||||
LateTask.New(() =>
|
||||
{
|
||||
|
||||
+5
-3
@@ -2268,17 +2268,19 @@ public static class Utils
|
||||
|
||||
public static string BuildSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
string suffix = string.Empty;
|
||||
StringBuilder suffix = new();
|
||||
|
||||
foreach (PlayerState state in Main.PlayerStates.Values)
|
||||
{
|
||||
string tempSuffix = state.Role.GetSuffix(seer, target, hud, meeting).Trim();
|
||||
|
||||
if (!string.IsNullOrEmpty(tempSuffix))
|
||||
suffix += $"{tempSuffix}\n";
|
||||
suffix.Append($"{tempSuffix}\n");
|
||||
}
|
||||
|
||||
return suffix.Trim();
|
||||
suffix.Append(CustomSabotage.GetAllSuffix(seer, target, hud, meeting));
|
||||
|
||||
return suffix.ToString().Trim();
|
||||
}
|
||||
|
||||
public static IEnumerator NotifyEveryoneAsync(int speed = 2, bool noCache = true)
|
||||
|
||||
@@ -76,6 +76,8 @@ internal static class GameEndChecker
|
||||
NameNotifyManager.Reset();
|
||||
NotifyRoles(ForceLoop: true);
|
||||
|
||||
CustomSabotage.Reset();
|
||||
|
||||
int saboWinner = Options.WhoWinsBySabotageIfNoImpAlive.GetValue();
|
||||
|
||||
if (reason == GameOverReason.ImpostorsBySabotage && saboWinner != 0 && !Main.AllAlivePlayerControls.Any(x => x.Is(Team.Impostor)))
|
||||
|
||||
@@ -680,7 +680,7 @@ public static class StringOptionPatch
|
||||
|
||||
if (role.IsExperimental()) name += $"<size=2>{Translator.GetString("ExperimentalRoleIndicator")}</size>";
|
||||
if (role.IsGhostRole()) name += GetGhostRoleTeam(role);
|
||||
if (role.IsDevFavoriteRole()) name += "<size=2><#00ffff>★</color></size>";
|
||||
if (role.IsDevFavoriteRole()) name += " <size=2><#00ffff>★</color></size>";
|
||||
|
||||
__instance.TitleText.fontWeight = FontWeight.Black;
|
||||
__instance.TitleText.outlineColor = new(255, 255, 255, 255);
|
||||
@@ -822,7 +822,7 @@ public static class StringOptionPatch
|
||||
|
||||
if (role.IsExperimental()) name += $"<size=2>{Translator.GetString("ExperimentalRoleIndicator")}</size>";
|
||||
if (role.IsGhostRole()) name += GetGhostRoleTeam(role);
|
||||
if (role.IsDevFavoriteRole()) name += "<size=2><#00ffff>★</color></size>";
|
||||
if (role.IsDevFavoriteRole()) name += " <size=2><#00ffff>★</color></size>";
|
||||
|
||||
__instance.TitleText.fontWeight = FontWeight.Black;
|
||||
__instance.TitleText.outlineColor = new(255, 255, 255, 255);
|
||||
|
||||
+10
-6
@@ -1050,9 +1050,7 @@ internal static class IntroCutsceneDestroyPatch
|
||||
|
||||
if (Options.UsePets.GetBool() && Options.CurrentGameMode is CustomGameMode.Standard or CustomGameMode.HideAndSeek or CustomGameMode.CaptureTheFlag)
|
||||
{
|
||||
Main.ProcessShapeshifts = false;
|
||||
|
||||
LateTask.New(() =>
|
||||
void GrantPetForEveryone()
|
||||
{
|
||||
foreach (PlayerControl pc in aapc)
|
||||
{
|
||||
@@ -1063,7 +1061,13 @@ internal static class IntroCutsceneDestroyPatch
|
||||
PetsHelper.SetPet(pc, petId);
|
||||
Logger.Info($"{pc.GetNameWithRole()} => {GetString(petId)} Pet", "PetAssign");
|
||||
}
|
||||
}, 0.3f, "Grant Pet For Everyone");
|
||||
}
|
||||
|
||||
Main.ProcessShapeshifts = false;
|
||||
|
||||
LateTask.New(GrantPetForEveryone, 0.3f, "Grant Pet For Everyone");
|
||||
LateTask.New(GrantPetForEveryone, 0.7f, "Grant Pet For Everyone");
|
||||
LateTask.New(GrantPetForEveryone, 1.1f, "Grant Pet For Everyone");
|
||||
|
||||
LateTask.New(() =>
|
||||
{
|
||||
@@ -1091,9 +1095,9 @@ internal static class IntroCutsceneDestroyPatch
|
||||
}
|
||||
catch (Exception ex) { Logger.Fatal(ex.ToString(), "IntroPatch.RpcShapeshift"); }
|
||||
}
|
||||
}, 0.4f, "Show Pet For Everyone");
|
||||
}, 1.4f, "Show Pet For Everyone");
|
||||
|
||||
LateTask.New(() => Main.ProcessShapeshifts = true, 1f, "Enable SS Processing");
|
||||
LateTask.New(() => Main.ProcessShapeshifts = true, 2f, "Enable SS Processing");
|
||||
}
|
||||
|
||||
try
|
||||
|
||||
@@ -360,6 +360,7 @@ internal static class ChangeRoleSettings
|
||||
}
|
||||
|
||||
FallFromLadder.Reset();
|
||||
CustomSabotage.Reset();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -920,18 +921,21 @@ internal static class StartGameHostPatch
|
||||
{
|
||||
case CustomGameMode.HideAndSeek:
|
||||
CustomHnS.StartSeekerBlindTime();
|
||||
break;
|
||||
goto default;
|
||||
case CustomGameMode.CaptureTheFlag:
|
||||
CaptureTheFlag.Init();
|
||||
break;
|
||||
goto default;
|
||||
case CustomGameMode.NaturalDisasters:
|
||||
NaturalDisasters.OnGameStart();
|
||||
break;
|
||||
case CustomGameMode.KingOfTheZones:
|
||||
KingOfTheZones.Init();
|
||||
break;
|
||||
goto default;
|
||||
case CustomGameMode.Quiz:
|
||||
Quiz.Init();
|
||||
goto default;
|
||||
default:
|
||||
if (Options.IntegrateNaturalDisasters.GetBool()) goto case CustomGameMode.NaturalDisasters;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -957,6 +957,12 @@ internal static class ReportDeadBodyPatch
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
if (CustomSabotage.Instances.Count > 0)
|
||||
{
|
||||
Notify("CannotCallEmergencyMeetingWhileSabotage");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (__instance.Is(CustomRoles.Jester) && !Jester.JesterCanUseButton.GetBool())
|
||||
{
|
||||
Notify("JesterCannotCallEmergencyMeeting");
|
||||
@@ -1213,6 +1219,8 @@ internal static class ReportDeadBodyPatch
|
||||
PhantomRolePatch.OnReportDeadBody(pc);
|
||||
}
|
||||
|
||||
CustomSabotage.Reset();
|
||||
|
||||
MeetingTimeManager.OnReportDeadBody();
|
||||
|
||||
NameNotifyManager.Reset();
|
||||
@@ -1344,6 +1352,7 @@ internal static class FixedUpdatePatch
|
||||
|
||||
if (localPlayer)
|
||||
{
|
||||
CustomSabotage.UpdateAll();
|
||||
Zoom.OnFixedUpdate();
|
||||
TextBoxPatch.CheckChatOpen();
|
||||
|
||||
|
||||
@@ -316,7 +316,7 @@ public static class TextBoxPatch
|
||||
public static bool IsCharAllowedPatch(TextBoxTMP __instance, [HarmonyArgument(0)] char i, ref bool __result)
|
||||
{
|
||||
if (!__instance.gameObject.HasParentInHierarchy("ChatScreenRoot/ChatScreenContainer")) return true;
|
||||
__result = i != '\b';
|
||||
__result = i is not '\b' and not '\n' and not '\r';
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,6 +98,10 @@ afootauto#1216,026ad9f54,你妈阴道有蛆
|
||||
rubykop#0410,a1de8a400,习主席近平
|
||||
netgaff#6739,01b5992f0,李克强
|
||||
fuckdaters#7129
|
||||
pucecoast#8105,d1134e5e5
|
||||
shoesilky#8411,c85846f8a
|
||||
apemv#5959,77c89daa8
|
||||
beadyloaf#3586,9037e9c8f
|
||||
|
||||
## 冒充开发者
|
||||
polkaoval#0453,自作Modな橋本環奈
|
||||
|
||||
@@ -1813,6 +1813,7 @@
|
||||
"DoppelgangerWarning": "<#ffff00>The Doppelganger swapped skins with you!</color>",
|
||||
"TriedToKillLastGameFirstKill": "Your target was the first to die in the last game.\\nLet them play!",
|
||||
"CheckReportFail": "<#ff0000>This meeting call was canceled due to the following reason:\\n</color>",
|
||||
"CannotCallEmergencyMeetingWhileSabotage": "Emergency meetings cannot be called during crisis",
|
||||
"JesterCannotCallEmergencyMeeting": "As the <#ec62a5>Jester</color>, according to the host's settings, you cannot call emergency meetings",
|
||||
"NiceSwapperCannotCallEmergencyMeeting": "As the <#922348>Nice Swapper</color>, according to the host's settings, you cannot call emergency meetings",
|
||||
"AdrenalineCannotCallEmergencyMeeting": "As the <#ffff00>Adrenaline</color>, you cannot call emergency meetings currently",
|
||||
@@ -2074,6 +2075,9 @@
|
||||
"StressedExtraTimeAfterFixingSabotage": "Time Gained For Fixing a Sabotage",
|
||||
"StressedExtraTimeAfterReporting": "Time Gained For Reporting a Dead Body",
|
||||
"StressedTimePenaltyAfterCrewmateEjected": "Time Penalty When a Crewmate Gets Ejected",
|
||||
"CustomSabotageActivated": "<#ffff00>Custom Sabotage Activated!</color>",
|
||||
"CustomSabotage.GrabOxygenMask": "Grab Oxygen Mask",
|
||||
"CustomSabotage.GrabOxygenMask.Done": "<#00ff00>Oxygen Mask Grabbed! You are safe.</color>",
|
||||
"SabotageCooldownControl": "Modify Sabotage Cooldown",
|
||||
"SabotageCooldown": "Sabotage Cooldown",
|
||||
"SabotageTimeControl": "Modify Sabotage Time Limit",
|
||||
@@ -2813,7 +2817,7 @@
|
||||
"Achievement.Speedrun.Description": "Have a game end in under 1 minute",
|
||||
"Achievement.Carried.Description": "Win as a Madmate while all other Impostors are dead",
|
||||
"Achievement.SorryToBurstYourBubble.Description": "Explode 5 people with 1 encased player as the Bubble",
|
||||
"Achievement.GetLynched.Description": "Successfully guess 3 roles as Lyncher",
|
||||
"Achievement.GetDecrypted.Description": "Successfully guess 3 roles as Decryptor",
|
||||
"Achievement.FlagMaster.Description": "Carry the flag the longest in Capture The Flag",
|
||||
"Achievement.Tag.Description": "Tag the most players in Capture The Flag",
|
||||
"Achievement.Vectory.Description": "Vent 50 times in one game",
|
||||
@@ -8959,12 +8963,12 @@
|
||||
"SleepInfo": "You Sleep During Lights Out",
|
||||
"SleepInfoLong": "(Add-ons):\\nYou sleep during lights out. You can't see anything, and you cannot move. You can only wake up when the lights are back on. If someone with the Glow add-on comes near you, you fully wake up and lose this add-on.",
|
||||
"Prefix.Sleep": "Sleeping",
|
||||
"Lyncher": "Lyncher",
|
||||
"LyncherInfo": "Get Letters From Everyone's Roles",
|
||||
"LyncherInfoLong": "(Crewmates):\\nAs the Lyncher, each X tasks you complete (X is set by the host), you get to know one more letter from everyone's roles. You can see these characters below players' names. The letters are not in order and each letter only appears once for every occurance in the role name. Settings determine whether you can guess.",
|
||||
"Lyncher.TaskNum": "Number of Tasks to Complete for Each Letter",
|
||||
"Lyncher.GuessMode": "Can Guess",
|
||||
"Lyncher.Suffix": "<size=70%><#00ffa5>Complete</color> <b>{0}</b> <#00ffa5>tasks for the next letters</color></size>",
|
||||
"Decryptor": "Decryptor",
|
||||
"DecryptorInfo": "Get Letters From Everyone's Roles",
|
||||
"DecryptorInfoLong": "(Crewmates):\\nAs the Decryptor, each X tasks you complete (X is set by the host), you get to know one more letter from everyone's roles. You can see these characters below players' names. The letters are not in order and each letter only appears once for every occurance in the role name. Settings determine whether you can guess.",
|
||||
"Decryptor.TaskNum": "Number of Tasks to Complete for Each Letter",
|
||||
"Decryptor.GuessMode": "Can Guess",
|
||||
"Decryptor.Suffix": "<size=70%><#00ffa5>Complete</color> <b>{0}</b> <#00ffa5>tasks for the next letters</color></size>",
|
||||
"ParityCopTryHideMsg": "Hide <color=#0D57AF>Inspector</color>'s commands",
|
||||
"MaxParityCheckLimit": "Max inspections per game",
|
||||
"ParityCheckLimitPerMeeting": "Max inspections per meeting",
|
||||
|
||||
+18
-18
@@ -6,10 +6,10 @@ using Hazel;
|
||||
|
||||
namespace EHR.Crewmate;
|
||||
|
||||
public class Lyncher : RoleBase
|
||||
public class Decryptor : RoleBase
|
||||
{
|
||||
public static bool On;
|
||||
public static List<Lyncher> Instances = [];
|
||||
public static List<Decryptor> Instances = [];
|
||||
|
||||
private static OptionItem TaskNum;
|
||||
public static OptionItem GuessMode;
|
||||
@@ -25,7 +25,7 @@ public class Lyncher : RoleBase
|
||||
private static Dictionary<byte, List<char>> AllRoleNames = [];
|
||||
|
||||
private Dictionary<byte, List<char>> KnownCharacters = [];
|
||||
private byte LyncherId;
|
||||
private byte DecryptorId;
|
||||
private int TasksCompleted;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
@@ -33,19 +33,19 @@ public class Lyncher : RoleBase
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
var id = 648550;
|
||||
Options.SetupRoleOptions(id++, TabGroup.CrewmateRoles, CustomRoles.Lyncher);
|
||||
Options.SetupRoleOptions(id++, TabGroup.CrewmateRoles, CustomRoles.Decryptor);
|
||||
|
||||
TaskNum = new IntegerOptionItem(++id, "Lyncher.TaskNum", new(1, 10, 1), 3, TabGroup.CrewmateRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Lyncher]);
|
||||
TaskNum = new IntegerOptionItem(++id, "Decryptor.TaskNum", new(1, 10, 1), 3, TabGroup.CrewmateRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Decryptor]);
|
||||
|
||||
GuessMode = new StringOptionItem(++id, "Lyncher.GuessMode", GuessModes, 1, TabGroup.CrewmateRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Lyncher]);
|
||||
GuessMode = new StringOptionItem(++id, "Decryptor.GuessMode", GuessModes, 1, TabGroup.CrewmateRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Decryptor]);
|
||||
|
||||
Vision = new FloatOptionItem(++id, "Vision", new(0f, 1.5f, 0.05f), 0.5f, TabGroup.CrewmateRoles)
|
||||
.SetValueFormat(OptionFormat.Multiplier)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Lyncher]);
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Decryptor]);
|
||||
|
||||
Options.OverrideTasksData.Create(++id, TabGroup.CrewmateRoles, CustomRoles.Lyncher);
|
||||
Options.OverrideTasksData.Create(++id, TabGroup.CrewmateRoles, CustomRoles.Decryptor);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
@@ -61,9 +61,9 @@ public class Lyncher : RoleBase
|
||||
{
|
||||
On = true;
|
||||
Instances.Add(this);
|
||||
LyncherId = playerId;
|
||||
DecryptorId = playerId;
|
||||
TasksCompleted = 0;
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, LyncherId, 1, TasksCompleted);
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, DecryptorId, 1, TasksCompleted);
|
||||
|
||||
if (Main.HasJustStarted)
|
||||
LateTask.New(Action, 12f, log: false);
|
||||
@@ -102,7 +102,7 @@ public class Lyncher : RoleBase
|
||||
else
|
||||
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
|
||||
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, LyncherId, 1, TasksCompleted);
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, DecryptorId, 1, TasksCompleted);
|
||||
}
|
||||
|
||||
private void RevealLetter()
|
||||
@@ -112,18 +112,18 @@ public class Lyncher : RoleBase
|
||||
KnownCharacters.Do(x =>
|
||||
{
|
||||
x.Value.Add(nextLetters[x.Key]);
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, LyncherId, 2, x.Key, nextLetters[x.Key]);
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, DecryptorId, 2, x.Key, nextLetters[x.Key]);
|
||||
});
|
||||
}
|
||||
|
||||
public void OnRoleChange(byte id)
|
||||
{
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, LyncherId, 3, id);
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, DecryptorId, 3, id);
|
||||
CustomRoles newRole = Main.PlayerStates[id].MainRole;
|
||||
AllRoleNames[id] = Translator.GetString($"{newRole}").ToUpper().Where(c => c is not '-' and not ' ').Shuffle();
|
||||
int count = KnownCharacters[id].Count;
|
||||
KnownCharacters[id] = AllRoleNames[id].Take(count).ToList();
|
||||
KnownCharacters[id].ForEach(x => Utils.SendRPC(CustomRPC.SyncRoleData, LyncherId, 2, id, x));
|
||||
KnownCharacters[id].ForEach(x => Utils.SendRPC(CustomRPC.SyncRoleData, DecryptorId, 2, id, x));
|
||||
}
|
||||
|
||||
public void ReceiveRPC(MessageReader reader)
|
||||
@@ -144,12 +144,12 @@ public class Lyncher : RoleBase
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
if (seer.PlayerId != LyncherId) return string.Empty;
|
||||
if (seer.PlayerId != DecryptorId) return string.Empty;
|
||||
|
||||
return (seer.PlayerId == target.PlayerId) switch
|
||||
{
|
||||
false when KnownCharacters.TryGetValue(target.PlayerId, out List<char> chars) && chars.Count > 0 => string.Join(' ', chars),
|
||||
true when !seer.IsModdedClient() || hud => string.Format(Translator.GetString("Lyncher.Suffix"), TaskNum.GetInt() - TasksCompleted),
|
||||
true when !seer.IsModdedClient() || hud => string.Format(Translator.GetString("Decryptor.Suffix"), TaskNum.GetInt() - TasksCompleted),
|
||||
_ => string.Empty
|
||||
};
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ public class Monarch : RoleBase
|
||||
|
||||
sender.SendMessage(!hasValue);
|
||||
|
||||
Logger.Info("SetRole:" + target.Data?.PlayerName + " = " + target.GetCustomRole() + " + " + CustomRoles.Knighted, "Assign " + CustomRoles.Knighted);
|
||||
Logger.Info($"Set Role: {target.Data?.PlayerName} = {target.GetCustomRole()} + {CustomRoles.Knighted}", $"Assign {CustomRoles.Knighted}");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ public class Gaslighter : RoleBase
|
||||
string role = IRandom.Instance.Next(2) == 0 ? CustomRoles.HexMaster.ToColoredString() : CustomRoles.Witch.ToColoredString();
|
||||
string text = string.Format(Translator.GetString("WitchCursedPlayersMessage"), cursed, role);
|
||||
Utils.SendMessage(text, title: Translator.GetString("MessageTitle.Attention"));
|
||||
}, 10f, "Witch Cursed Players Notify");
|
||||
}, 10f, "Gaslighter Cursed Players Notify");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,8 +187,10 @@ public class Gaslighter : RoleBase
|
||||
return true;
|
||||
case Round.Knight when killer.GetAbilityUseLimit() > 0 && !target.Is(CustomRoles.Knighted):
|
||||
target.RpcSetCustomRole(CustomRoles.Knighted);
|
||||
target.RpcGuardAndKill(killer);
|
||||
target.RpcGuardAndKill(target);
|
||||
target.Notify(Utils.ColorString(Utils.GetRoleColor(CustomRoles.Monarch), Translator.GetString("KnightedByMonarch")));
|
||||
Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target);
|
||||
Utils.NotifyRoles(SpecifySeer: target, SpecifyTarget: target);
|
||||
killer.RpcRemoveAbilityUse();
|
||||
killer.SetKillCooldown();
|
||||
break;
|
||||
|
||||
Reference in new issue
Block a user