mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-20 01:46:25 +00:00
Reduce allocations, CNO pooling, code improvements
This commit is contained in:
1 parent
c0ce71417a
commit
f2ac622318
47 files changed
+539
-440
No files matched your search
@@ -234,7 +234,6 @@ public enum CustomRoles
|
||||
Medic,
|
||||
Medium,
|
||||
Merchant,
|
||||
Telecommunication,
|
||||
Mole,
|
||||
Monarch,
|
||||
Mortician,
|
||||
@@ -277,6 +276,7 @@ public enum CustomRoles
|
||||
Swapper,
|
||||
Tar,
|
||||
TaskManager,
|
||||
Telecommunication,
|
||||
Telekinetic,
|
||||
Tether,
|
||||
TimeManager,
|
||||
|
||||
+11
-10
@@ -552,23 +552,24 @@ internal static class CustomHnS
|
||||
|
||||
try
|
||||
{
|
||||
var validIds = new HashSet<byte>();
|
||||
|
||||
foreach (var pc in Main.EnumeratePlayerControls())
|
||||
validIds.Add(pc.PlayerId);
|
||||
|
||||
var toRemove = new List<byte>();
|
||||
List<byte> toRemove = null;
|
||||
|
||||
foreach (var id in PlayerRoles.Keys)
|
||||
{
|
||||
if (!validIds.Contains(id))
|
||||
if (!id.GetPlayer())
|
||||
{
|
||||
toRemove ??= [];
|
||||
toRemove.Add(id);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var id in toRemove)
|
||||
if (toRemove != null)
|
||||
{
|
||||
PlayerRoles.Remove(id);
|
||||
Utils.SendRPC(CustomRPC.HNSSync, 2, id);
|
||||
foreach (var id in toRemove)
|
||||
{
|
||||
PlayerRoles.Remove(id);
|
||||
Utils.SendRPC(CustomRPC.HNSSync, 2, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
@@ -109,8 +109,7 @@ internal static class HotPotato
|
||||
switch (reader.ReadPackedInt32())
|
||||
{
|
||||
case 1:
|
||||
int timeLeft = reader.ReadPackedInt32();
|
||||
HotPotatoState.TimeLeft = timeLeft;
|
||||
HotPotatoState.TimeLeft = reader.ReadPackedInt32();
|
||||
break;
|
||||
case 2:
|
||||
HotPotatoState.HolderID = reader.ReadByte();
|
||||
@@ -119,6 +118,11 @@ internal static class HotPotato
|
||||
}
|
||||
}
|
||||
|
||||
public static void RecordDeath(byte id, int time)
|
||||
{
|
||||
SurvivalTimes[id] = time;
|
||||
}
|
||||
|
||||
public static int GetKillInterval()
|
||||
{
|
||||
return Time.GetInt() + Main.CurrentMap switch
|
||||
@@ -159,7 +163,7 @@ internal static class HotPotato
|
||||
if (HotPotatoState.TimeLeft <= 0)
|
||||
{
|
||||
holder.Suicide();
|
||||
SurvivalTimes[HotPotatoState.HolderID] = (HotPotatoState.RoundNum - 1) * GetKillInterval();
|
||||
RecordDeath(HotPotatoState.HolderID, (HotPotatoState.RoundNum - 1) * GetKillInterval());
|
||||
PassHotPotato();
|
||||
|
||||
if (holder.AmOwner)
|
||||
|
||||
+67
-35
@@ -747,51 +747,81 @@ public static class KingOfTheZones
|
||||
}
|
||||
else
|
||||
{
|
||||
List<SystemTypes> added = [];
|
||||
List<SystemTypes> removed = [];
|
||||
|
||||
foreach ((SystemTypes zone, long moveTS) in ZoneMoveSchedules)
|
||||
if (atOnce)
|
||||
{
|
||||
if (moveTS > now) continue;
|
||||
|
||||
try
|
||||
if (Zones.Count > 0 && ZoneMoveSchedules.TryGetValue(Zones[0], out long moveTS) && moveTS <= now)
|
||||
{
|
||||
SystemTypes newZone = AllRooms.Except(Zones).RandomElement();
|
||||
IEnumerable<SystemTypes> newZones = AllRooms.Except(Zones).TakeRandom(Zones.Count);
|
||||
|
||||
int index = Zones.IndexOf(zone);
|
||||
Zones.RemoveAt(index);
|
||||
Zones.Insert(index, newZone);
|
||||
Zones.Clear();
|
||||
ZoneDomination.Clear();
|
||||
|
||||
added.Add(newZone);
|
||||
|
||||
removed.Add(zone);
|
||||
ZoneDomination.Remove(zone);
|
||||
|
||||
Logger.Info($"Zone moved: {zone} => {newZone}", "KOTZ");
|
||||
foreach (SystemTypes newZone in newZones)
|
||||
{
|
||||
Zones.Add(newZone);
|
||||
ZoneDomination[newZone] = KOTZTeam.None;
|
||||
ZoneMoveSchedules[newZone] = now + moveTime + downtime;
|
||||
if (downtime > 0) ZoneDowntimeExpire[newZone] = now + downtime;
|
||||
}
|
||||
}
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
}
|
||||
|
||||
removed.ForEach(x => ZoneMoveSchedules.Remove(x));
|
||||
|
||||
added.ForEach(x =>
|
||||
else
|
||||
{
|
||||
ZoneMoveSchedules[x] = now + moveTime + downtime;
|
||||
if (downtime > 0) ZoneDowntimeExpire[x] = now + downtime;
|
||||
});
|
||||
SystemTypes? added = null;
|
||||
SystemTypes? removed = null;
|
||||
|
||||
foreach ((SystemTypes zone, long moveTS) in ZoneMoveSchedules)
|
||||
{
|
||||
if (moveTS > now) continue;
|
||||
|
||||
try
|
||||
{
|
||||
SystemTypes newZone = AllRooms.Except(Zones).RandomElement();
|
||||
|
||||
int index = Zones.IndexOf(zone);
|
||||
Zones.RemoveAt(index);
|
||||
Zones.Insert(index, newZone);
|
||||
|
||||
added = newZone;
|
||||
removed = zone;
|
||||
ZoneDomination.Remove(zone);
|
||||
|
||||
Logger.Info($"Zone moved: {zone} => {newZone}", "KOTZ");
|
||||
break;
|
||||
}
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
}
|
||||
|
||||
if (removed.HasValue) ZoneMoveSchedules.Remove(removed.Value);
|
||||
|
||||
if (added.HasValue)
|
||||
{
|
||||
ZoneDomination[added.Value] = KOTZTeam.None;
|
||||
ZoneMoveSchedules[added.Value] = now + moveTime + downtime;
|
||||
if (downtime > 0) ZoneDowntimeExpire[added.Value] = now + downtime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (downtime > 0 && ZoneDowntimeExpire.Count > 0)
|
||||
{
|
||||
List<SystemTypes> toRemove = [];
|
||||
|
||||
foreach ((SystemTypes zone, long downtimeExpire) in ZoneDowntimeExpire)
|
||||
if (atOnce)
|
||||
{
|
||||
if (downtimeExpire > now) continue;
|
||||
toRemove.Add(zone);
|
||||
if (Zones.Count > 0 && ZoneDowntimeExpire.TryGetValue(Zones[0], out long expireTS) && expireTS <= now)
|
||||
ZoneDowntimeExpire.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
SystemTypes? toRemove = null;
|
||||
|
||||
toRemove.ForEach(x => ZoneDowntimeExpire.Remove(x));
|
||||
foreach ((SystemTypes zone, long downtimeExpire) in ZoneDowntimeExpire)
|
||||
{
|
||||
if (downtimeExpire > now) continue;
|
||||
toRemove = zone;
|
||||
}
|
||||
|
||||
if (toRemove.HasValue) ZoneDowntimeExpire.Remove(toRemove.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -803,15 +833,16 @@ public static class KingOfTheZones
|
||||
{
|
||||
if (SpawnProtectionTimes.Count > 0)
|
||||
{
|
||||
List<byte> toRemove = [];
|
||||
List<byte> toRemove = null;
|
||||
|
||||
foreach ((byte id, long protectionTS) in SpawnProtectionTimes)
|
||||
{
|
||||
if (protectionTS > now) continue;
|
||||
toRemove ??= [];
|
||||
toRemove.Add(id);
|
||||
}
|
||||
|
||||
toRemove.ForEach(x => SpawnProtectionTimes.Remove(x));
|
||||
toRemove?.ForEach(x => SpawnProtectionTimes.Remove(x));
|
||||
}
|
||||
}
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
@@ -822,7 +853,7 @@ public static class KingOfTheZones
|
||||
{
|
||||
if (RespawnTimes.Count > 0)
|
||||
{
|
||||
List<byte> toRemove = [];
|
||||
List<byte> toRemove = null;
|
||||
|
||||
foreach ((byte id, long respawnTS) in RespawnTimes)
|
||||
{
|
||||
@@ -846,12 +877,13 @@ public static class KingOfTheZones
|
||||
}
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
|
||||
toRemove ??= [];
|
||||
toRemove.Add(id);
|
||||
|
||||
Logger.Info($"{Main.AllPlayerNames[id]} respawned", "KOTZ");
|
||||
}
|
||||
|
||||
toRemove.ForEach(x => RespawnTimes.Remove(x));
|
||||
toRemove?.ForEach(x => RespawnTimes.Remove(x));
|
||||
}
|
||||
}
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
|
||||
+103
-112
@@ -19,7 +19,7 @@ namespace EHR.Gamemodes;
|
||||
public static class NaturalDisasters
|
||||
{
|
||||
private const float Range = 1.5f;
|
||||
private static List<Type> AllDisasters = [];
|
||||
private static Dictionary<string, Type> AllDisasters = [];
|
||||
private static readonly List<Disaster> ActiveDisasters = [];
|
||||
private static readonly List<NaturalDisaster> PreparingDisasters = [];
|
||||
public static readonly Dictionary<byte, int> SurvivalTimes = [];
|
||||
@@ -38,7 +38,6 @@ public static class NaturalDisasters
|
||||
|
||||
private static readonly string[] LimitReachedOptions =
|
||||
[
|
||||
"ND_LimitReachedOptions.OnlySpawnInstantDisasters",
|
||||
"ND_LimitReachedOptions.RemoveRandom",
|
||||
"ND_LimitReachedOptions.RemoveOldest"
|
||||
];
|
||||
@@ -55,9 +54,9 @@ public static class NaturalDisasters
|
||||
return ActiveDisasters;
|
||||
}
|
||||
|
||||
public static List<Type> GetAllDisasters()
|
||||
public static IEnumerable<Type> GetAllDisasters()
|
||||
{
|
||||
return AllDisasters;
|
||||
return AllDisasters.Values;
|
||||
}
|
||||
|
||||
public static int FrequencyOfDisasters => DisasterFrequency.GetInt();
|
||||
@@ -90,7 +89,7 @@ public static class NaturalDisasters
|
||||
.SetParent(LimitMaximumDisastersAtOnce)
|
||||
.SetColor(color);
|
||||
|
||||
WhenLimitIsReached = new StringOptionItem(id++, "ND_WhenLimitIsReached", LimitReachedOptions, 2, TabGroup.GameSettings)
|
||||
WhenLimitIsReached = new StringOptionItem(id++, "ND_WhenLimitIsReached", LimitReachedOptions, 1, TabGroup.GameSettings)
|
||||
.SetGameMode(gameMode)
|
||||
.SetParent(LimitMaximumDisastersAtOnce)
|
||||
.SetColor(color);
|
||||
@@ -110,20 +109,24 @@ public static class NaturalDisasters
|
||||
|
||||
LoadAllDisasters();
|
||||
|
||||
AllDisasters.ConvertAll(x => x.Name).ForEach(x => DisasterSpawnChances[x] = new IntegerOptionItem(id++, "ND_Disaster.SpawnChance", new(0, 100, 5), 50, TabGroup.GameSettings)
|
||||
.SetGameMode(gameMode)
|
||||
.SetColor(color)
|
||||
.SetValueFormat(OptionFormat.Percent)
|
||||
.AddReplacement(("{disaster}", Translator.GetString($"ND_{x}"))));
|
||||
foreach ((string name, Type type) in AllDisasters)
|
||||
{
|
||||
DisasterSpawnChances[name] = new IntegerOptionItem(id++, "ND_Disaster.SpawnChance", new(0, 100, 5), 50, TabGroup.GameSettings)
|
||||
.SetGameMode(gameMode)
|
||||
.SetColor(color)
|
||||
.SetHeader(true)
|
||||
.SetValueFormat(OptionFormat.Percent)
|
||||
.AddReplacement(("{disaster}", Translator.GetString($"ND_{name}")));
|
||||
|
||||
AllDisasters.ForEach(x => x.GetMethod("SetupOwnCustomOption")?.Invoke(null, null));
|
||||
type.GetMethod("SetupOwnCustomOption")?.Invoke(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static void LoadAllDisasters()
|
||||
{
|
||||
AllDisasters = Main.AllTypes
|
||||
.Where(x => x.IsClass && !x.IsAbstract && x.IsSubclassOf(typeof(Disaster)))
|
||||
.ToList();
|
||||
.ToDictionary(x => x.Name, x => x);
|
||||
}
|
||||
|
||||
public static void OnGameStart()
|
||||
@@ -164,26 +167,31 @@ public static class NaturalDisasters
|
||||
|
||||
public static void RebuildSuffixText()
|
||||
{
|
||||
var allRooms = ShipStatus.Instance.AllRooms;
|
||||
var collapsedRooms = BuildingCollapse.CollapsedRooms;
|
||||
string cb;
|
||||
|
||||
if (allRooms.Count / 2 <= collapsedRooms.Count)
|
||||
try
|
||||
{
|
||||
SystemTypes[] remainingRooms = allRooms.Select(x => x.RoomId).Where(x => x is not (SystemTypes.Hallway or SystemTypes.Outside) && !x.ToString().Contains("Decontamination")).Except(collapsedRooms.ConvertAll(x => x.RoomId)).ToArray();
|
||||
cb = string.Format(Translator.GetString("AvailableBuildings"), remainingRooms.Length > 0
|
||||
? remainingRooms.Select(x => Translator.GetString($"{x}")).Distinct().Join()
|
||||
: $"<#ff0000>{Translator.GetString("None")}</color>");
|
||||
}
|
||||
else
|
||||
{
|
||||
cb = string.Format(Translator.GetString("CollapsedBuildings"), collapsedRooms.Count > 0
|
||||
? collapsedRooms.Select(x => Translator.GetString($"{x.RoomId}")).Distinct().Join()
|
||||
: Translator.GetString("None"));
|
||||
}
|
||||
var allRooms = ShipStatus.Instance.AllRooms;
|
||||
var collapsedRooms = BuildingCollapse.CollapsedRooms;
|
||||
string cb;
|
||||
|
||||
string ts = ActiveDisasters.Exists(x => x is Thunderstorm) ? $"\n{Translator.GetString("OngoingThunderstorm")}" : string.Empty;
|
||||
SuffixText = $"<size=70%>{cb}{ts}</size>";
|
||||
if (allRooms.Count / 2 <= collapsedRooms.Count)
|
||||
{
|
||||
SystemTypes[] remainingRooms = allRooms.Select(x => x.RoomId).Where(x => x is not (SystemTypes.Hallway or SystemTypes.Outside or SystemTypes.Decontamination2 or SystemTypes.Decontamination3)).Except(collapsedRooms.ConvertAll(x => x.RoomId)).ToArray();
|
||||
cb = string.Format(Translator.GetString("AvailableBuildings"), remainingRooms.Length > 0
|
||||
? remainingRooms.Select(x => Translator.GetString($"{x}")).Distinct().Join()
|
||||
: $"<#ff0000>{Translator.GetString("None")}</color>");
|
||||
}
|
||||
else
|
||||
{
|
||||
cb = string.Format(Translator.GetString("CollapsedBuildings"), collapsedRooms.Count > 0
|
||||
? collapsedRooms.Select(x => Translator.GetString($"{x.RoomId}")).Distinct().Join()
|
||||
: Translator.GetString("None"));
|
||||
}
|
||||
|
||||
string ts = ActiveDisasters.Exists(x => x is Thunderstorm) ? $"\n{Translator.GetString("OngoingThunderstorm")}" : string.Empty;
|
||||
string rp = string.Format(Translator.GetString("ND_RemainingPlayers"), Utils.ColorString(Utils.GetRoleColor(CustomRoles.NDPlayer), Main.AllAlivePlayerControls.Count.ToString()));
|
||||
SuffixText = $"<size=70%>{cb}{ts}\n{rp}</size>";
|
||||
}
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
}
|
||||
|
||||
public static int SurvivalTime(byte id)
|
||||
@@ -191,9 +199,13 @@ public static class NaturalDisasters
|
||||
return SurvivalTimes.GetValueOrDefault(id, 0);
|
||||
}
|
||||
|
||||
private static readonly List<(byte id, PlayerState.DeathReason deathReason, long recordTimeStamp)> DeathMessageQueue = [];
|
||||
|
||||
public static void RecordDeath(PlayerControl pc, PlayerState.DeathReason deathReason)
|
||||
{
|
||||
SurvivalTimes[pc.PlayerId] = (int)(Utils.TimeStamp - IntroCutsceneDestroyPatch.IntroDestroyTS - FixedUpdatePatch.WaitTime);
|
||||
RebuildSuffixText();
|
||||
long now = Utils.TimeStamp;
|
||||
SurvivalTimes[pc.PlayerId] = (int)(now - IntroCutsceneDestroyPatch.IntroDestroyTS - FixedUpdatePatch.WaitTime);
|
||||
|
||||
string message = Translator.GetString($"ND_DRLaughMessage-{IRandom.Instance.Next(4)}.{deathReason}");
|
||||
Color color = DeathReasonColor(deathReason);
|
||||
@@ -201,10 +213,7 @@ public static class NaturalDisasters
|
||||
LateTask.New(() => pc.Notify(message, 20f), 1f, $"{pc.GetRealName()} died with the reason {deathReason}, survived for {SurvivalTime(pc.PlayerId)} seconds");
|
||||
Utils.SendRPC(CustomRPC.NaturalDisastersSync, pc.PlayerId, SurvivalTimes[pc.PlayerId]);
|
||||
|
||||
var aapc = Main.AllAlivePlayerControls;
|
||||
string remaining = string.Format(Translator.GetString("ND_RemainingPlayers"), Utils.ColorString(Utils.GetRoleColor(CustomRoles.NDPlayer), aapc.Count.ToString()));
|
||||
string msgOthers = string.Format(Translator.GetString($"ND_DRLaughMessageOthers-{IRandom.Instance.Next(4)}.{deathReason}"), pc.PlayerId.ColoredPlayerName());
|
||||
aapc.NotifyPlayers($"<#ff0000>[╳]</color> {Utils.ColorString(color, msgOthers)} {remaining}", 10f);
|
||||
DeathMessageQueue.Add((pc.PlayerId, deathReason, now));
|
||||
}
|
||||
|
||||
private static Color DeathReasonColor(PlayerState.DeathReason deathReason)
|
||||
@@ -240,7 +249,6 @@ public static class NaturalDisasters
|
||||
public static class FixedUpdatePatch
|
||||
{
|
||||
public static readonly Stopwatch LastDisasterTimer = new();
|
||||
private static readonly Stopwatch FailSafeTimer = new();
|
||||
private static long LastSync = Utils.TimeStamp;
|
||||
public static int WaitTime;
|
||||
|
||||
@@ -269,46 +277,40 @@ public static class NaturalDisasters
|
||||
|
||||
UpdatePreparingDisasters();
|
||||
|
||||
for (var index = 0; index < ActiveDisasters.Count; index++)
|
||||
{
|
||||
if (ActiveDisasters[index].Update())
|
||||
index--;
|
||||
}
|
||||
for (int index = ActiveDisasters.Count - 1; index >= 0; index--)
|
||||
ActiveDisasters[index].Update();
|
||||
|
||||
Sinkhole.OnFixedUpdate();
|
||||
BuildingCollapse.OnFixedUpdate();
|
||||
|
||||
if (LimitMaximumDisastersAtOnce.GetBool())
|
||||
{
|
||||
int numDisasters = ActiveDisasters.Count + PreparingDisasters.Count + Sinkhole.Sinkholes.Count;
|
||||
int numDisasters = ActiveDisasters.Count + Sinkhole.Sinkholes.Count;
|
||||
|
||||
if (numDisasters > MaximumDisastersAtOnce.GetInt())
|
||||
{
|
||||
switch (WhenLimitIsReached.GetValue())
|
||||
if (Sinkhole.Sinkholes.Count > 0)
|
||||
{
|
||||
case 0:
|
||||
Sinkhole.RemoveRandomSinkhole();
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (WhenLimitIsReached.GetValue())
|
||||
{
|
||||
AllDisasters.RemoveAll(x => x.Name is "Earthquake" or "VolcanoEruption" or "Tornado" or "Thunderstorm" or "SandStorm" or "Tsunami");
|
||||
break;
|
||||
}
|
||||
case 1 or 2 when Sinkhole.Sinkholes.Count > 0:
|
||||
{
|
||||
Sinkhole.RemoveRandomSinkhole();
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
Disaster remove = PreferRemovingThunderstorm.GetBool() && ActiveDisasters.FindFirst(x => x is Thunderstorm, out Disaster thunderstorm) ? thunderstorm : ActiveDisasters.RandomElement();
|
||||
remove?.Duration = 0;
|
||||
remove?.RemoveIfExpired();
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
Disaster oldest = ActiveDisasters.MinBy(x => x.StartTimeStamp);
|
||||
oldest.Duration = 0;
|
||||
oldest.RemoveIfExpired();
|
||||
break;
|
||||
case 0:
|
||||
{
|
||||
Disaster remove = PreferRemovingThunderstorm.GetBool() && ActiveDisasters.FindFirst(x => x is Thunderstorm, out Disaster thunderstorm) ? thunderstorm : ActiveDisasters.RandomElement();
|
||||
remove?.Duration = 0;
|
||||
remove?.RemoveIfExpired();
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
Disaster oldest = ActiveDisasters.MinBy(x => x.StartTimeStamp);
|
||||
oldest.Duration = 0;
|
||||
oldest.RemoveIfExpired();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -324,40 +326,36 @@ public static class NaturalDisasters
|
||||
if (LastDisasterTimer.Elapsed.TotalSeconds >= frequency)
|
||||
{
|
||||
LastDisasterTimer.Restart();
|
||||
List<Type> disasters = AllDisasters.SelectMany(x => Enumerable.Repeat(x, DisasterSpawnChances[x.Name].GetInt() / 5)).ToList();
|
||||
|
||||
Dictionary<string, Type> pool = AllDisasters.ToDictionary(x => x.Key, x => x.Value);
|
||||
|
||||
if (ActiveDisasters.Exists(x => x is Thunderstorm))
|
||||
disasters.RemoveAll(x => x.Name == "Thunderstorm");
|
||||
pool.Remove("Thunderstorm");
|
||||
|
||||
(SystemTypes CollapsingRoom, Vector2 WarningPosition) buildingCollapseInfo = (default(SystemTypes), default(Vector2));
|
||||
PlainShipRoom[] nonCollapsedRooms = ShipStatus.Instance.AllRooms.Where(x => x.RoomId is not (SystemTypes.Hallway or SystemTypes.Outside or SystemTypes.Decontamination2 or SystemTypes.Decontamination3)).Except(BuildingCollapse.CollapsedRooms).ToArray();
|
||||
|
||||
if ((Main.LIMap ? ShipStatus.Instance.AllRooms.Count : RandomSpawn.SpawnMap.GetSpawnMap().Positions.Count - (Main.CurrentMap == MapNames.Polus ? 3 : 1)) <= BuildingCollapse.CollapsedRooms.Count)
|
||||
disasters.RemoveAll(x => x.Name == "BuildingCollapse");
|
||||
if (nonCollapsedRooms.Length == 0)
|
||||
{
|
||||
pool.Remove("BuildingCollapse");
|
||||
}
|
||||
else
|
||||
{
|
||||
PlainShipRoom collapsingRoom = nonCollapsedRooms.RandomElement();
|
||||
buildingCollapseInfo = (collapsingRoom.RoomId, (Main.LIMap ? [] : RandomSpawn.SpawnMap.GetSpawnMap().Positions).TryGetValue(collapsingRoom.RoomId, out Vector2 spawnLocation) ? spawnLocation : collapsingRoom.transform.position);
|
||||
}
|
||||
|
||||
Type disaster = disasters.RandomElement();
|
||||
Type disaster = pool.SelectMany(x => Enumerable.Repeat(x.Value, DisasterSpawnChances[x.Key].GetInt() / 5)).RandomElement();
|
||||
|
||||
if (disaster.Name == "Thunderstorm")
|
||||
{
|
||||
_ = new Thunderstorm(Vector2.zero, null);
|
||||
return;
|
||||
}
|
||||
|
||||
KeyValuePair<SystemTypes, Vector2> roomKvp;
|
||||
|
||||
if (Main.LIMap)
|
||||
{
|
||||
var plainShipRoom = ShipStatus.Instance.AllRooms.Where(x => x.RoomId is not (SystemTypes.Hallway or SystemTypes.Outside) && !x.RoomId.ToString().Contains("Decontamination")).Except(BuildingCollapse.CollapsedRooms).RandomElement();
|
||||
roomKvp = new KeyValuePair<SystemTypes, Vector2>(plainShipRoom.RoomId, plainShipRoom.transform.position);
|
||||
}
|
||||
else
|
||||
{
|
||||
roomKvp = RandomSpawn.SpawnMap.GetSpawnMap().Positions
|
||||
.Where(x => x.Key is not (SystemTypes.Hallway or SystemTypes.Outside) && !x.Key.ToString().Contains("Decontamination"))
|
||||
.ExceptBy(BuildingCollapse.CollapsedRooms.ConvertAll(x => x.RoomId), x => x.Key)
|
||||
.RandomElement();
|
||||
}
|
||||
|
||||
var aapc = Main.AllAlivePlayerControls;
|
||||
bool bc = disaster.Name == "BuildingCollapse";
|
||||
bool spawnOnPlayer = aapc.Count > 0 && DisasterSpawnMode.GetValue() switch
|
||||
bool spawnOnPlayer = !bc && aapc.Count > 0 && DisasterSpawnMode.GetValue() switch
|
||||
{
|
||||
0 => true,
|
||||
1 => false,
|
||||
@@ -365,25 +363,13 @@ public static class NaturalDisasters
|
||||
};
|
||||
|
||||
Vector2 position = bc
|
||||
? roomKvp.Value
|
||||
? buildingCollapseInfo.WarningPosition
|
||||
: spawnOnPlayer
|
||||
? aapc.RandomElement().Pos()
|
||||
: RandomInRoomPosition();
|
||||
: new(Random.Range(MapBounds.X.Left, MapBounds.X.Right), Random.Range(MapBounds.Y.Bottom, MapBounds.Y.Top));
|
||||
|
||||
SystemTypes? room = bc ? roomKvp.Key : null;
|
||||
SystemTypes? room = bc ? buildingCollapseInfo.CollapsingRoom : null;
|
||||
AddPreparingDisaster(position, disaster.Name, room);
|
||||
|
||||
static Vector2 RandomInRoomPosition()
|
||||
{
|
||||
FailSafeTimer.Start();
|
||||
Vector2 vector2;
|
||||
|
||||
do vector2 = new(Random.Range(MapBounds.X.Left, MapBounds.X.Right), Random.Range(MapBounds.Y.Top, MapBounds.Y.Bottom));
|
||||
while (FailSafeTimer.ElapsedMilliseconds < 3 && !vector2.GetPlainShipRoom());
|
||||
|
||||
FailSafeTimer.Reset();
|
||||
return vector2;
|
||||
}
|
||||
}
|
||||
|
||||
if (now - LastSync >= 15)
|
||||
@@ -399,28 +385,36 @@ public static class NaturalDisasters
|
||||
LastSync = now;
|
||||
Utils.MarkEveryoneDirtySettings();
|
||||
}
|
||||
|
||||
if (DeathMessageQueue.Count > 0 && DeathMessageQueue.FindFirst(x => x.recordTimeStamp + 3 <= now, out var expired))
|
||||
{
|
||||
var names = string.Join(", ", DeathMessageQueue.Where(x => x.deathReason == expired.deathReason).Select(x => x.id.ColoredPlayerName()));
|
||||
string msgOthers = string.Format(Translator.GetString($"ND_DRLaughMessageOthers-{IRandom.Instance.Next(4)}.{expired.deathReason}"), names);
|
||||
Main.EnumerateAlivePlayerControls().NotifyPlayers($"<#ff0000>[╳]</color> {Utils.ColorString(DeathReasonColor(expired.deathReason), msgOthers)}");
|
||||
DeathMessageQueue.RemoveAll(x => x.deathReason == expired.deathReason);
|
||||
}
|
||||
}
|
||||
|
||||
public static void UpdatePreparingDisasters()
|
||||
{
|
||||
for (var index = 0; index < PreparingDisasters.Count; index++)
|
||||
for (var index = PreparingDisasters.Count - 1; index >= 0; index--)
|
||||
{
|
||||
NaturalDisaster naturalDisaster = PreparingDisasters[index];
|
||||
naturalDisaster.Update();
|
||||
|
||||
if (float.IsNaN(naturalDisaster.SpawnTimer))
|
||||
if (!naturalDisaster.SpawnTimer.IsRunning)
|
||||
{
|
||||
Type type = AllDisasters.Find(d => d.Name == naturalDisaster.DisasterName);
|
||||
Type type = AllDisasters[naturalDisaster.DisasterName];
|
||||
Activator.CreateInstance(type, naturalDisaster.Position, naturalDisaster);
|
||||
PreparingDisasters.RemoveAt(index);
|
||||
index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddPreparingDisaster(Vector2 position, string disasterName, SystemTypes? room)
|
||||
{
|
||||
PreparingDisasters.Add(new(position, DisasterWarningTime.GetFloat(), Sprite(disasterName), disasterName, room));
|
||||
try { PreparingDisasters.Add(new(position, DisasterWarningTime.GetInt(), Sprite(disasterName), disasterName, room)); }
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,6 +441,9 @@ public static class NaturalDisasters
|
||||
case CustomGameMode.Mingle:
|
||||
Mingle.HandleDisconnect();
|
||||
goto default;
|
||||
case CustomGameMode.HotPotato:
|
||||
HotPotato.RecordDeath(pc.PlayerId, (int)(Utils.TimeStamp - IntroCutsceneDestroyPatch.IntroDestroyTS - FixedUpdatePatch.WaitTime));
|
||||
goto default;
|
||||
default:
|
||||
pc.Suicide(deathReason);
|
||||
return;
|
||||
@@ -515,7 +512,6 @@ public static class NaturalDisasters
|
||||
Color color = Utils.GetRoleColor(CustomRoles.NDPlayer);
|
||||
|
||||
DurationOpt = new IntegerOptionItem(id, "ND_Earthquake.DurationOpt", new(1, 120, 1), 30, TabGroup.GameSettings)
|
||||
.SetHeader(true)
|
||||
.SetGameMode(CustomGameMode.NaturalDisasters)
|
||||
.SetColor(color)
|
||||
.SetValueFormat(OptionFormat.Seconds);
|
||||
@@ -612,7 +608,6 @@ public static class NaturalDisasters
|
||||
Color color = Utils.GetRoleColor(CustomRoles.NDPlayer);
|
||||
|
||||
FlowStepDelay = new FloatOptionItem(id, "ND_VolcanoEruption.FlowStepDelay", new(0.5f, 5f, 0.5f), 1f, TabGroup.GameSettings)
|
||||
.SetHeader(true)
|
||||
.SetGameMode(CustomGameMode.NaturalDisasters)
|
||||
.SetColor(color)
|
||||
.SetValueFormat(OptionFormat.Seconds);
|
||||
@@ -679,7 +674,6 @@ public static class NaturalDisasters
|
||||
Color color = Utils.GetRoleColor(CustomRoles.NDPlayer);
|
||||
|
||||
DurationOpt = new IntegerOptionItem(id, "ND_Tornado.DurationOpt", new(1, 120, 1), 20, TabGroup.GameSettings)
|
||||
.SetHeader(true)
|
||||
.SetGameMode(CustomGameMode.NaturalDisasters)
|
||||
.SetColor(color)
|
||||
.SetValueFormat(OptionFormat.Seconds);
|
||||
@@ -789,7 +783,6 @@ public static class NaturalDisasters
|
||||
Color color = Utils.GetRoleColor(CustomRoles.NDPlayer);
|
||||
|
||||
DurationOpt = new IntegerOptionItem(id, "ND_Thunderstorm.DurationOpt", new(1, 120, 1), 20, TabGroup.GameSettings)
|
||||
.SetHeader(true)
|
||||
.SetGameMode(CustomGameMode.NaturalDisasters)
|
||||
.SetColor(color)
|
||||
.SetValueFormat(OptionFormat.Seconds);
|
||||
@@ -864,7 +857,6 @@ public static class NaturalDisasters
|
||||
Color color = Utils.GetRoleColor(CustomRoles.NDPlayer);
|
||||
|
||||
DurationOpt = new IntegerOptionItem(id, "ND_SandStorm.DurationOpt", new(1, 120, 1), 30, TabGroup.GameSettings)
|
||||
.SetHeader(true)
|
||||
.SetGameMode(CustomGameMode.NaturalDisasters)
|
||||
.SetColor(color)
|
||||
.SetValueFormat(OptionFormat.Seconds);
|
||||
@@ -1021,7 +1013,6 @@ public static class NaturalDisasters
|
||||
Color color = Utils.GetRoleColor(CustomRoles.NDPlayer);
|
||||
|
||||
MovingSpeed = new FloatOptionItem(id, "ND_Tsunami.MovingSpeed", new(0.25f, 10f, 0.25f), 2f, TabGroup.GameSettings)
|
||||
.SetHeader(true)
|
||||
.SetGameMode(CustomGameMode.NaturalDisasters)
|
||||
.SetColor(color)
|
||||
.SetValueFormat(OptionFormat.Multiplier);
|
||||
|
||||
+1
-1
@@ -502,7 +502,7 @@ public static class Quiz
|
||||
var spectators = aapc.ExceptBy(dyingPlayers, x => x.PlayerId).ToArray();
|
||||
spectators.Do(x => x.ExileTemporarily());
|
||||
yield return new WaitForSecondsRealtime(0.2f);
|
||||
var stillLiving = dyingPlayers.ToValidPlayers().FindAll(x => x.IsAlive());
|
||||
var stillLiving = dyingPlayers.ToValidPlayers().Where(x => x.IsAlive()).ToList();
|
||||
stillLiving.ForEach(x => x.RpcChangeRoleBasis(CustomRoles.SerialKiller));
|
||||
Utils.SendRPC(CustomRPC.QuizSync, AllowKills);
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ public static class RoomRush
|
||||
private static HashSet<byte> DonePlayers = [];
|
||||
|
||||
private static bool GameGoing;
|
||||
private static DateTime GameStartDateTime;
|
||||
public static DateTime GameStartDateTime;
|
||||
|
||||
private static RandomSpawn.SpawnMap Map;
|
||||
|
||||
|
||||
@@ -613,7 +613,7 @@ public static class TheMindGame
|
||||
Round = 5;
|
||||
|
||||
HiddenPoints.Clear();
|
||||
EjectedPlayers.ToValidPlayers().FindAll(x => !x.IsAlive()).ForEach(x => x.RpcRevive());
|
||||
EjectedPlayers.ToValidPlayers().Where(x => !x.IsAlive()).Do(x => x.RpcRevive());
|
||||
|
||||
yield return NotifyEveryone("TMG.Notify.Round4End", 3);
|
||||
if (Stop) yield break;
|
||||
|
||||
+114
-64
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using AmongUs.InnerNet.GameDataMessages;
|
||||
using EHR.Gamemodes;
|
||||
@@ -28,13 +29,15 @@ namespace EHR
|
||||
private string Sprite;
|
||||
|
||||
private int SnapToSendFrameCount;
|
||||
|
||||
private bool IsPooled;
|
||||
|
||||
public void RpcChangeSprite(string sprite)
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost) return;
|
||||
if (this is not NaturalDisaster nd || nd.SpawnTimer <= 0f) Logger.Info($" Change Custom Net Object {GetType().Name} (ID {Id}) sprite", "CNO.RpcChangeSprite");
|
||||
if (this is not NaturalDisaster nd || !nd.SpawnTimer.IsRunning) Logger.Info($" Change Custom Net Object {GetType().Name} (ID {Id}) sprite", "CNO.RpcChangeSprite");
|
||||
|
||||
bool notImportant = this is BedWarsItemGenerator || (this is NaturalDisaster { SpawnTimer: > 1f } && Options.CurrentGameMode != CustomGameMode.NaturalDisasters && GameStates.CurrentServerType == GameStates.ServerType.Vanilla);
|
||||
bool notImportant = this is BedWarsItemGenerator || (this is NaturalDisaster n && n.SpawnTimer.Elapsed.TotalSeconds < n.TotalWarningTime - 1 && Options.CurrentGameMode != CustomGameMode.NaturalDisasters && GameStates.CurrentServerType == GameStates.ServerType.Vanilla);
|
||||
SendOption channel = notImportant ? SendOption.None : SendOption.Reliable;
|
||||
|
||||
DataFlagRateLimiter.Enqueue(() =>
|
||||
@@ -92,15 +95,55 @@ namespace EHR
|
||||
public void TP(Vector2 position)
|
||||
{
|
||||
Position = position;
|
||||
SnapToSendFrameCount = 30;
|
||||
}
|
||||
|
||||
private bool TryReusePooledObject(string sprite, Vector2 position)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this is not NaturalDisaster) return false;
|
||||
|
||||
CustomNetObject pooled = AllObjects.Find(x => x.IsPooled && x.playerControl);
|
||||
if (pooled == null) return false;
|
||||
|
||||
Logger.Info($" Reusing pooled Custom Net Object NaturalDisaster (ID {pooled.Id})", "CNO.CreateNetObject");
|
||||
|
||||
playerControl = pooled.playerControl;
|
||||
Id = pooled.Id;
|
||||
IsPooled = false;
|
||||
|
||||
AllObjects.Remove(pooled);
|
||||
AllObjects.Add(this);
|
||||
|
||||
TP(position);
|
||||
RpcChangeSprite(sprite);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.ThrowException(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Despawn()
|
||||
public void Despawn(bool canPool = true)
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost) return;
|
||||
Logger.Info($" Despawn Custom Net Object {GetType().Name} (ID {Id})", "CNO.Despawn");
|
||||
|
||||
try
|
||||
{
|
||||
if (canPool && this is NaturalDisaster && playerControl)
|
||||
{
|
||||
Logger.Info($" Pooled Custom Net Object {GetType().Name} (ID {Id})", "CNO.Despawn");
|
||||
IsPooled = true;
|
||||
TP(new Vector2(50f, 50f));
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.Info($" Despawn Custom Net Object {GetType().Name} (ID {Id})", "CNO.Despawn");
|
||||
|
||||
if (playerControl)
|
||||
{
|
||||
MessageWriter writer = MessageWriter.Get(SendOption.Reliable);
|
||||
@@ -168,7 +211,7 @@ namespace EHR
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this is not ShapeshiftMenuElement)
|
||||
if (this is not ShapeshiftMenuElement && player.IsNonHostModdedClient())
|
||||
{
|
||||
LateTask.New(() =>
|
||||
{
|
||||
@@ -220,7 +263,7 @@ namespace EHR
|
||||
|
||||
protected void CreateNetObject(string sprite, Vector2 position)
|
||||
{
|
||||
if (GameStates.IsEnded || !AmongUsClient.Instance.AmHost) return;
|
||||
if (GameStates.IsEnded || !AmongUsClient.Instance.AmHost || TryReusePooledObject(sprite, position)) return;
|
||||
|
||||
Logger.Info($" Create Custom Net Object {GetType().Name} (ID {MaxId + 1}) at {position} - Time since game start: {Utils.TimeStamp - IntroCutsceneDestroyPatch.IntroDestroyTS}s", "CNO.CreateNetObject");
|
||||
Main.Instance.StartCoroutine(CoRoutine());
|
||||
@@ -303,50 +346,53 @@ namespace EHR
|
||||
|
||||
qa = DataFlagRateLimiter.Enqueue(() =>
|
||||
{
|
||||
int messages = 0;
|
||||
int packingLimit = AmongUsClient.Instance.GetMaxMessagePackingLimit();
|
||||
MessageWriter stream = MessageWriter.Get(SendOption.Reliable);
|
||||
stream.StartMessage(26);
|
||||
stream.WritePacked(AmongUsClient.Instance.GameId);
|
||||
|
||||
foreach (PlayerControl pc in Main.EnumeratePlayerControls())
|
||||
if (PlayerControl.AllPlayerControls.Count > 1)
|
||||
{
|
||||
if (pc.AmOwner) continue;
|
||||
int messages = 0;
|
||||
int packingLimit = AmongUsClient.Instance.GetMaxMessagePackingLimit();
|
||||
MessageWriter stream = MessageWriter.Get(SendOption.Reliable);
|
||||
stream.StartMessage(26);
|
||||
stream.WritePacked(AmongUsClient.Instance.GameId);
|
||||
|
||||
if (stream.Length > 500 || messages + 3 > packingLimit)
|
||||
foreach (PlayerControl pc in Main.EnumeratePlayerControls())
|
||||
{
|
||||
if (pc.AmOwner) continue;
|
||||
|
||||
if (stream.Length > 500 || messages + 3 > packingLimit)
|
||||
{
|
||||
stream.EndMessage();
|
||||
AmongUsClient.Instance.SendOrDisconnect(stream);
|
||||
stream.Clear(SendOption.Reliable);
|
||||
stream.StartMessage(26);
|
||||
stream.WritePacked(AmongUsClient.Instance.GameId);
|
||||
}
|
||||
|
||||
stream.StartMessage(6);
|
||||
stream.Write(AmongUsClient.Instance.GameId);
|
||||
stream.WritePacked(pc.OwnerId);
|
||||
stream.StartMessage(1);
|
||||
stream.WritePacked(playerControl.NetId);
|
||||
stream.Write(pc.PlayerId);
|
||||
stream.EndMessage();
|
||||
AmongUsClient.Instance.SendOrDisconnect(stream);
|
||||
stream.Clear(SendOption.Reliable);
|
||||
stream.StartMessage(26);
|
||||
stream.WritePacked(AmongUsClient.Instance.GameId);
|
||||
stream.StartMessage(2);
|
||||
stream.WritePacked(playerControl.NetId);
|
||||
stream.Write((byte)RpcCalls.MurderPlayer);
|
||||
stream.WriteNetObject(playerControl);
|
||||
stream.Write((int)MurderResultFlags.FailedError);
|
||||
stream.EndMessage();
|
||||
stream.StartMessage(1);
|
||||
stream.WritePacked(playerControl.NetId);
|
||||
stream.Write((byte)254);
|
||||
stream.EndMessage();
|
||||
stream.EndMessage();
|
||||
|
||||
messages += 3;
|
||||
}
|
||||
|
||||
stream.StartMessage(6);
|
||||
stream.Write(AmongUsClient.Instance.GameId);
|
||||
stream.WritePacked(pc.OwnerId);
|
||||
stream.StartMessage(1);
|
||||
stream.WritePacked(playerControl.NetId);
|
||||
stream.Write(pc.PlayerId);
|
||||
stream.EndMessage();
|
||||
stream.StartMessage(2);
|
||||
stream.WritePacked(playerControl.NetId);
|
||||
stream.Write((byte)RpcCalls.MurderPlayer);
|
||||
stream.WriteNetObject(playerControl);
|
||||
stream.Write((int)MurderResultFlags.FailedError);
|
||||
stream.EndMessage();
|
||||
stream.StartMessage(1);
|
||||
stream.WritePacked(playerControl.NetId);
|
||||
stream.Write((byte)254);
|
||||
stream.EndMessage();
|
||||
stream.EndMessage();
|
||||
|
||||
messages += 3;
|
||||
AmongUsClient.Instance.SendOrDisconnect(stream);
|
||||
stream.Recycle();
|
||||
}
|
||||
|
||||
stream.EndMessage();
|
||||
AmongUsClient.Instance.SendOrDisconnect(stream);
|
||||
stream.Recycle();
|
||||
|
||||
playerControl.CachedPlayerData = PlayerControl.LocalPlayer.Data;
|
||||
}, calls: 2); // WAITING FOR THE SLOTHS TO VERIFY THIS
|
||||
@@ -441,20 +487,20 @@ namespace EHR
|
||||
|
||||
public static void FixedUpdate()
|
||||
{
|
||||
foreach (CustomNetObject cno in AllObjects.ToArray())
|
||||
cno?.OnFixedUpdate();
|
||||
for (int index = AllObjects.Count - 1; index >= 0; index--)
|
||||
AllObjects[index]?.OnFixedUpdate();
|
||||
}
|
||||
|
||||
public static CustomNetObject Get(int id)
|
||||
{
|
||||
return AllObjects.FirstOrDefault(x => x.Id == id);
|
||||
return AllObjects.Find(x => x.Id == id);
|
||||
}
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
try
|
||||
{
|
||||
AllObjects.ToArray().Do(x => x.Despawn());
|
||||
AllObjects.ToArray().Do(x => x.Despawn(canPool: false));
|
||||
AllObjects.Clear();
|
||||
}
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
@@ -607,51 +653,56 @@ namespace EHR
|
||||
|
||||
public sealed class NaturalDisaster : CustomNetObject
|
||||
{
|
||||
public NaturalDisaster(Vector2 position, float time, string sprite, string disasterName, SystemTypes? room)
|
||||
public NaturalDisaster(Vector2 position, int time, string sprite, string disasterName, SystemTypes? room)
|
||||
{
|
||||
string name = Translator.GetString($"ND_{disasterName}");
|
||||
string warning = $"<size=250%>{Math.Ceiling(time):N0}</size>\n{name}";
|
||||
string warning = $"<size=250%>{time}</size>\n{name}";
|
||||
|
||||
if (room.HasValue)
|
||||
{
|
||||
warning = $"<#ff4444>{warning}</color>";
|
||||
Main.EnumerateAlivePlayerControls().DoIf(x => x.IsInRoom(room.Value), x => x.ReactorFlash());
|
||||
|
||||
try { Main.EnumerateAlivePlayerControls().DoIf(x => x.IsInRoom(room.Value), x => x.ReactorFlash()); }
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
}
|
||||
|
||||
SpawnTimer = time;
|
||||
TotalWarningTime = time;
|
||||
DisasterSprite = sprite;
|
||||
DisasterName = disasterName;
|
||||
DisasterNameTranslated = name;
|
||||
Room = room;
|
||||
|
||||
SpawnTimer = Stopwatch.StartNew();
|
||||
|
||||
CreateNetObject(warning, position);
|
||||
}
|
||||
|
||||
public SystemTypes? Room { get; }
|
||||
public string DisasterName { get; }
|
||||
public float SpawnTimer { get; private set; }
|
||||
public Stopwatch SpawnTimer { get; }
|
||||
public int TotalWarningTime { get; }
|
||||
private string DisasterSprite { get; }
|
||||
|
||||
private int TimeInt => (int)Math.Ceiling(SpawnTimer);
|
||||
private int TimeInt => (int)(SpawnTimer.Elapsed.TotalSeconds);
|
||||
private int PreviousTimeInt { get; set; }
|
||||
private string DisasterNameTranslated { get; }
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (float.IsNaN(SpawnTimer)) return;
|
||||
if (!SpawnTimer.IsRunning) return;
|
||||
|
||||
int newTime = TimeInt;
|
||||
|
||||
int oldTime = TimeInt;
|
||||
SpawnTimer -= Time.fixedDeltaTime;
|
||||
|
||||
if (SpawnTimer <= 0f)
|
||||
if (newTime >= TotalWarningTime)
|
||||
{
|
||||
SpawnTimer.Stop();
|
||||
if (!Room.HasValue) RpcChangeSprite(DisasterSprite);
|
||||
SpawnTimer = float.NaN;
|
||||
}
|
||||
else
|
||||
{
|
||||
int newTime = TimeInt;
|
||||
if (oldTime == newTime) return;
|
||||
string warning = $"<size=250%>{newTime:N0}</size>\n{DisasterNameTranslated}";
|
||||
if (PreviousTimeInt == newTime) return;
|
||||
PreviousTimeInt = newTime;
|
||||
string warning = $"<size=250%>{TotalWarningTime - newTime}</size>\n{DisasterNameTranslated}";
|
||||
if (Room.HasValue) warning = $"<#ff4444>{warning}</color>";
|
||||
RpcChangeSprite(warning);
|
||||
}
|
||||
@@ -660,7 +711,7 @@ namespace EHR
|
||||
|
||||
internal sealed class Lightning : CustomNetObject
|
||||
{
|
||||
private float Timer = 5f;
|
||||
private readonly Stopwatch Timer = Stopwatch.StartNew();
|
||||
|
||||
public Lightning(Vector2 position)
|
||||
{
|
||||
@@ -670,8 +721,7 @@ namespace EHR
|
||||
protected override void OnFixedUpdate()
|
||||
{
|
||||
base.OnFixedUpdate();
|
||||
Timer -= Time.fixedDeltaTime;
|
||||
if (Timer <= 0) Despawn();
|
||||
if (Timer.Elapsed.TotalSeconds >= 3f) Despawn();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ internal static class CustomRoleSelector
|
||||
Dictionary<RoleAssignType, List<RoleAssignInfo>> roles = [];
|
||||
Enum.GetValues<RoleAssignType>().Do(x => roles[x] = []);
|
||||
|
||||
foreach (byte id in Main.SetRoles.Keys.Where(id => Utils.GetPlayerById(id) == null).ToArray()) Main.SetRoles.Remove(id);
|
||||
foreach (byte id in Main.SetRoles.Keys.Where(id => !Utils.GetPlayerById(id)).ToArray()) Main.SetRoles.Remove(id);
|
||||
|
||||
(bool Spawning, bool OneIsImp) loversData = (Lovers.LegacyLovers.GetBool(), rd.Next(100) < Lovers.LovingImpostorSpawnChance.GetInt());
|
||||
loversData.Spawning &= rd.Next(100) < Options.CustomAdtRoleSpawnRate[CustomRoles.Lovers].GetInt();
|
||||
@@ -559,7 +559,7 @@ internal static class CustomRoleSelector
|
||||
{
|
||||
if (Options.CurrentGameMode != CustomGameMode.Standard) return;
|
||||
|
||||
foreach (byte id in Main.SetAddOns.Keys.Where(id => Utils.GetPlayerById(id) == null).ToArray()) Main.SetAddOns.Remove(id);
|
||||
foreach (byte id in Main.SetAddOns.Keys.Where(id => !Utils.GetPlayerById(id)).ToArray()) Main.SetAddOns.Remove(id);
|
||||
|
||||
AddonRolesList = [];
|
||||
|
||||
|
||||
@@ -85,9 +85,9 @@ public abstract class CustomSabotage
|
||||
|
||||
public static void UpdateAll()
|
||||
{
|
||||
foreach (CustomSabotage sabotage in Instances.ToArray())
|
||||
for (int index = Instances.Count - 1; index >= 0; index--)
|
||||
{
|
||||
try { sabotage.Update(); }
|
||||
try { Instances[index].Update(); }
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ public class ErrorText : MonoBehaviour
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (AllErrors.Count == 0) return;
|
||||
|
||||
AllErrors.ForEach(err => err.IncreaseTimer());
|
||||
ErrorData[] ToRemove = AllErrors.Where(err => err.ErrorLevel <= 1 && 30f < err.Timer).ToArray();
|
||||
|
||||
|
||||
@@ -170,7 +170,8 @@ internal static class ExtendedPlayerControl
|
||||
public void FreezeForOthers()
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost) return;
|
||||
|
||||
|
||||
bool hasValue = false;
|
||||
CustomRpcSender sender = CustomRpcSender.Create($"SnapTo Freeze ({player.GetNameWithRole()})", SendOption.Reliable);
|
||||
sender.StartPackedMessage();
|
||||
|
||||
@@ -188,16 +189,17 @@ internal static class ExtendedPlayerControl
|
||||
.EndRpc();
|
||||
sender.EndMessage();
|
||||
NumSnapToCallsThisRound += 2;
|
||||
hasValue = true;
|
||||
}
|
||||
|
||||
sender.SendMessage();
|
||||
sender.SendMessage(dispose: !hasValue);
|
||||
|
||||
player.Visible = false;
|
||||
}
|
||||
|
||||
public void SetChatVisible(bool visible, MessageWriter packedWriter = null)
|
||||
public bool SetChatVisible(bool visible, MessageWriter packedWriter = null)
|
||||
{
|
||||
if (!AmongUsClient.Instance.AmHost) return;
|
||||
if (!AmongUsClient.Instance.AmHost) return false;
|
||||
|
||||
Logger.Info($"Setting the chat {(visible ? "visible" : "hidden")} for {player.GetNameWithRole()}", "SetChatVisible");
|
||||
|
||||
@@ -205,7 +207,7 @@ internal static class ExtendedPlayerControl
|
||||
{
|
||||
HudManager.Instance.Chat.SetVisible(visible);
|
||||
HudManager.Instance.Chat.HideBanButton();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player.IsModdedClient())
|
||||
@@ -213,12 +215,12 @@ internal static class ExtendedPlayerControl
|
||||
var msg = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SetChatVisible, SendOption.Reliable, player.OwnerId);
|
||||
msg.Write(visible);
|
||||
AmongUsClient.Instance.FinishRpcImmediately(msg);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (packedWriter == null) DataFlagRateLimiter.Enqueue(Action, calls: 3);
|
||||
else Action();
|
||||
return;
|
||||
return true;
|
||||
|
||||
void Action()
|
||||
{
|
||||
@@ -409,14 +411,11 @@ internal static class ExtendedPlayerControl
|
||||
|
||||
if (setRoleMap)
|
||||
{
|
||||
foreach ((byte seerID, byte targetID) in StartGameHostPatch.RpcSetRoleReplacer.RoleMap.Keys.ToArray())
|
||||
foreach ((byte seerID, byte targetID) in StartGameHostPatch.RpcSetRoleReplacer.RoleMap.Keys.Where(x => x.TargetID == player.PlayerId).ToArray())
|
||||
{
|
||||
if (targetID == player.PlayerId)
|
||||
{
|
||||
var value = StartGameHostPatch.RpcSetRoleReplacer.RoleMap[(seerID, targetID)];
|
||||
value.RoleType = roleTypes;
|
||||
StartGameHostPatch.RpcSetRoleReplacer.RoleMap[(seerID, targetID)] = value;
|
||||
}
|
||||
var value = StartGameHostPatch.RpcSetRoleReplacer.RoleMap[(seerID, targetID)];
|
||||
value.RoleType = roleTypes;
|
||||
StartGameHostPatch.RpcSetRoleReplacer.RoleMap[(seerID, targetID)] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1514,7 +1513,8 @@ internal static class ExtendedPlayerControl
|
||||
AmongUsClient.Instance.FinishRpcImmediately(writer);
|
||||
NotifyRoles(SpecifyTarget: player);
|
||||
}
|
||||
|
||||
|
||||
bool hasValue = false;
|
||||
var sender = CustomRpcSender.Create("RpcMakeInvisible", SendOption.Reliable);
|
||||
sender.StartPackedMessage();
|
||||
|
||||
@@ -1534,9 +1534,10 @@ internal static class ExtendedPlayerControl
|
||||
sender.EndMessage();
|
||||
|
||||
NumSnapToCallsThisRound += 2;
|
||||
hasValue = true;
|
||||
}
|
||||
|
||||
sender.SendMessage();
|
||||
sender.SendMessage(dispose: !hasValue);
|
||||
Logger.Info($"Made {player.GetNameWithRole()} invisible", "RpcMakeInvisible");
|
||||
}
|
||||
|
||||
@@ -1565,7 +1566,8 @@ internal static class ExtendedPlayerControl
|
||||
AmongUsClient.Instance.FinishRpcImmediately(writer);
|
||||
NotifyRoles(SpecifyTarget: player);
|
||||
}
|
||||
|
||||
|
||||
bool hasValue = false;
|
||||
var sender = CustomRpcSender.Create("RpcMakeVisible", SendOption.Reliable);
|
||||
sender.StartPackedMessage();
|
||||
|
||||
@@ -1589,9 +1591,10 @@ internal static class ExtendedPlayerControl
|
||||
sender.EndMessage();
|
||||
|
||||
NumSnapToCallsThisRound += 3;
|
||||
hasValue = true;
|
||||
}
|
||||
|
||||
sender.SendMessage();
|
||||
sender.SendMessage(dispose: !hasValue);
|
||||
Logger.Info($"Made {player.GetNameWithRole()} visible", "RpcMakeVisible");
|
||||
}
|
||||
|
||||
@@ -1600,7 +1603,8 @@ internal static class ExtendedPlayerControl
|
||||
if (!AmongUsClient.Instance.AmHost) return;
|
||||
if (!Main.Invisible.Contains(player.PlayerId)) return;
|
||||
if (phantom && Options.CurrentGameMode != CustomGameMode.Standard) return;
|
||||
|
||||
|
||||
bool hasValue = false;
|
||||
var sender = CustomRpcSender.Create("RpcResetInvisibility", SendOption.Reliable);
|
||||
sender.StartPackedMessage();
|
||||
|
||||
@@ -1636,9 +1640,10 @@ internal static class ExtendedPlayerControl
|
||||
sender.EndMessage();
|
||||
|
||||
NumSnapToCallsThisRound += 4;
|
||||
hasValue = true;
|
||||
}
|
||||
|
||||
sender.SendMessage();
|
||||
sender.SendMessage(dispose: !hasValue);
|
||||
Logger.Info($"Reset invisibility for {player.GetNameWithRole()}", "RpcResetInvisibility");
|
||||
}
|
||||
|
||||
|
||||
@@ -484,16 +484,6 @@ public static class CollectionExtensions
|
||||
{
|
||||
return playerIds.Select(Utils.GetPlayer).Where(x => x);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a list of player IDs to a list of <see cref="PlayerControl" /> instances
|
||||
/// </summary>
|
||||
/// <param name="playerIds"></param>
|
||||
/// <returns></returns>
|
||||
public static List<PlayerControl> ToValidPlayers(this List<byte> playerIds)
|
||||
{
|
||||
return playerIds.ConvertAll(Utils.GetPlayer).FindAll(x => x);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
+39
-27
@@ -1195,35 +1195,47 @@ public static class GuessManager
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
public static void Postfix()
|
||||
{
|
||||
if (TextTemplate) Object.Destroy(TextTemplate.gameObject);
|
||||
if (GuesserUI) Object.Destroy(GuesserUI);
|
||||
|
||||
foreach (List<Transform> roleButtonsValue in RoleButtons.Values)
|
||||
{
|
||||
foreach (Transform transform in roleButtonsValue)
|
||||
{
|
||||
if (transform?.gameObject)
|
||||
Object.Destroy(transform.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (SpriteRenderer spriteRenderer in RoleSelectButtons.Values)
|
||||
{
|
||||
if (spriteRenderer?.gameObject)
|
||||
Object.Destroy(spriteRenderer.gameObject);
|
||||
}
|
||||
|
||||
foreach (SpriteRenderer spriteRenderer in PageButtons)
|
||||
{
|
||||
if (spriteRenderer?.gameObject)
|
||||
Object.Destroy(spriteRenderer.gameObject);
|
||||
}
|
||||
|
||||
if (TextTemplate && TextTemplate.gameObject) Object.Destroy(TextTemplate.gameObject);
|
||||
TextTemplate = null;
|
||||
|
||||
if (GuesserUI) Object.Destroy(GuesserUI);
|
||||
GuesserUI = null;
|
||||
RoleButtons.Clear();
|
||||
RoleSelectButtons.Clear();
|
||||
PageButtons.Clear();
|
||||
|
||||
if (RoleButtons != null)
|
||||
{
|
||||
foreach (List<Transform> roleButtonsValue in RoleButtons.Values)
|
||||
{
|
||||
foreach (Transform transform in roleButtonsValue)
|
||||
{
|
||||
if (transform && transform.gameObject)
|
||||
Object.Destroy(transform.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
RoleButtons = null;
|
||||
}
|
||||
|
||||
if (RoleSelectButtons != null)
|
||||
{
|
||||
foreach (SpriteRenderer spriteRenderer in RoleSelectButtons.Values)
|
||||
{
|
||||
if (spriteRenderer && spriteRenderer.gameObject)
|
||||
Object.Destroy(spriteRenderer.gameObject);
|
||||
}
|
||||
|
||||
RoleSelectButtons = null;
|
||||
}
|
||||
|
||||
if (PageButtons != null)
|
||||
{
|
||||
foreach (SpriteRenderer spriteRenderer in PageButtons)
|
||||
{
|
||||
if (spriteRenderer && spriteRenderer.gameObject)
|
||||
Object.Destroy(spriteRenderer.gameObject);
|
||||
}
|
||||
|
||||
PageButtons = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ internal static class LocateArrow
|
||||
{
|
||||
ArrowInfo arrowInfo = new(seer, locate);
|
||||
List<ArrowInfo> removeList = new(LocateArrows.Keys.Where(k => k.Equals(arrowInfo)));
|
||||
if (removeList.Count == 0) return;
|
||||
removeList.ForEach(a => LocateArrows.Remove(a));
|
||||
|
||||
Utils.SendRPC(CustomRPC.Arrow, false, 2, seer, locate);
|
||||
@@ -85,8 +86,8 @@ internal static class LocateArrow
|
||||
public static void RemoveAllTarget(byte seer)
|
||||
{
|
||||
List<ArrowInfo> removeList = new(LocateArrows.Keys.Where(k => k.From == seer));
|
||||
if (removeList.Count == 0) return;
|
||||
removeList.ForEach(a => LocateArrows.Remove(a));
|
||||
|
||||
Utils.SendRPC(CustomRPC.Arrow, false, 3, seer);
|
||||
Logger.Info($"Removed all locate arrows for: {seer} ({seer.GetPlayer()?.GetRealName()})", "LocateArrow");
|
||||
}
|
||||
@@ -129,7 +130,7 @@ internal static class LocateArrow
|
||||
|
||||
var update = false;
|
||||
|
||||
foreach (ArrowInfo arrowInfo in arrowList.ToArray())
|
||||
foreach (ArrowInfo arrowInfo in arrowList)
|
||||
{
|
||||
Vector3 loc = arrowInfo.To;
|
||||
|
||||
|
||||
@@ -60,21 +60,29 @@ public static class NameNotifyManager
|
||||
if (now == LastUpdate) return;
|
||||
LastUpdate = now;
|
||||
|
||||
List<byte> toNotify = [];
|
||||
List<byte> toNotify = null;
|
||||
|
||||
foreach ((byte id, Dictionary<string, long> notifies) in Notifies)
|
||||
{
|
||||
List<string> toRemove = [];
|
||||
List<string> toRemove = null;
|
||||
|
||||
notifies.DoIf(x => x.Value <= now, x => toRemove.Add(x.Key));
|
||||
notifies.DoIf(x => x.Value <= now, x =>
|
||||
{
|
||||
toRemove ??= [];
|
||||
toRemove.Add(x.Key);
|
||||
});
|
||||
|
||||
toRemove.ForEach(x => notifies.Remove(x));
|
||||
if (toRemove.Count > 0) toNotify.Add(id);
|
||||
if (toRemove != null)
|
||||
{
|
||||
toRemove.ForEach(x => notifies.Remove(x));
|
||||
toNotify ??= [];
|
||||
toNotify.Add(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (toNotify.Count == 0 || !AmongUsClient.Instance.AmHost) return;
|
||||
if (!AmongUsClient.Instance.AmHost) return;
|
||||
|
||||
toNotify.ToValidPlayers().ForEach(x => Utils.NotifyRoles(SpecifySeer: x, SpecifyTarget: x));
|
||||
toNotify?.ToValidPlayers().Do(x => Utils.NotifyRoles(SpecifySeer: x, SpecifyTarget: x));
|
||||
}
|
||||
|
||||
public static bool GetNameNotify(PlayerControl player, out string name)
|
||||
|
||||
@@ -72,7 +72,8 @@ internal static class TargetArrow
|
||||
{
|
||||
ArrowInfo arrowInfo = new(seer, target);
|
||||
List<ArrowInfo> removeList = new(TargetArrows.Keys.Where(k => k.Equals(arrowInfo)));
|
||||
foreach (ArrowInfo a in removeList.ToArray()) TargetArrows.Remove(a);
|
||||
if (removeList.Count == 0) return;
|
||||
removeList.ForEach(a => TargetArrows.Remove(a));
|
||||
|
||||
Utils.SendRPC(CustomRPC.Arrow, true, 2, seer, target);
|
||||
Logger.Info($"Removed target arrow: {seer} ({seer.GetPlayer()?.GetRealName()}) => {target} ({target.GetPlayer()?.GetRealName()})", "TargetArrow");
|
||||
@@ -85,7 +86,8 @@ internal static class TargetArrow
|
||||
public static void RemoveAllTarget(byte seer)
|
||||
{
|
||||
List<ArrowInfo> removeList = new(TargetArrows.Keys.Where(k => k.From == seer));
|
||||
foreach (ArrowInfo arrowInfo in removeList.ToArray()) TargetArrows.Remove(arrowInfo);
|
||||
if (removeList.Count == 0) return;
|
||||
removeList.ForEach(a => TargetArrows.Remove(a));
|
||||
|
||||
Utils.SendRPC(CustomRPC.Arrow, true, 3, seer);
|
||||
Logger.Info($"Removed all target arrows for {seer} ({seer.GetPlayer()?.GetRealName()})", "TargetArrow");
|
||||
@@ -128,7 +130,7 @@ internal static class TargetArrow
|
||||
|
||||
var update = false;
|
||||
|
||||
foreach (ArrowInfo arrowInfo in arrowList.ToArray())
|
||||
foreach (ArrowInfo arrowInfo in arrowList)
|
||||
{
|
||||
byte targetId = arrowInfo.To;
|
||||
PlayerControl target = Utils.GetPlayerById(targetId);
|
||||
|
||||
+24
-13
@@ -2549,19 +2549,30 @@ public static class Utils
|
||||
|
||||
public static PlayerControl GetPlayerById(int playerId, bool fast = true)
|
||||
{
|
||||
if (playerId is > byte.MaxValue or < byte.MinValue) return null;
|
||||
|
||||
if (fast && GameStates.InGame && Main.PlayerStates.TryGetValue((byte)playerId, out PlayerState state) && state.Player) return state.Player;
|
||||
|
||||
if (playerId == PlayerControl.LocalPlayer.PlayerId) return PlayerControl.LocalPlayer;
|
||||
|
||||
foreach (var pc in PlayerControl.AllPlayerControls)
|
||||
try
|
||||
{
|
||||
if (pc.PlayerId == playerId)
|
||||
return pc;
|
||||
}
|
||||
if (playerId == PlayerControl.LocalPlayer.PlayerId)
|
||||
return PlayerControl.LocalPlayer;
|
||||
|
||||
return null;
|
||||
byte id = (byte)playerId;
|
||||
|
||||
if (fast && GameStates.InGame &&
|
||||
Main.PlayerStates.TryGetValue(id, out PlayerState state) &&
|
||||
state.Player) { return state.Player; }
|
||||
|
||||
foreach (var pc in PlayerControl.AllPlayerControls)
|
||||
{
|
||||
if (pc.PlayerId == id)
|
||||
return pc;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ThrowException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetupLongRoleDescriptions()
|
||||
@@ -4534,8 +4545,8 @@ public static class Utils
|
||||
packedWriter.WritePacked(AmongUsClient.Instance.GameId);
|
||||
}
|
||||
|
||||
pc.SetChatVisible(visible, packedWriter);
|
||||
messages++;
|
||||
if (pc.SetChatVisible(visible, packedWriter))
|
||||
messages++;
|
||||
}
|
||||
|
||||
packedWriter.EndMessage();
|
||||
|
||||
+1
-1
@@ -1170,7 +1170,7 @@ internal static class TaskPanelBehaviourPatch
|
||||
{
|
||||
Dictionary<byte, string> SummaryText3 = new Dictionary<byte, string>();
|
||||
|
||||
foreach (byte id in Main.PlayerStates.Keys.ToArray())
|
||||
foreach (byte id in Main.PlayerStates.Keys)
|
||||
{
|
||||
string name = Main.AllPlayerNames[id].RemoveHtmlTags().Replace("\r\n", string.Empty);
|
||||
var summary = $"{Utils.GetProgressText(id)} {Utils.ColorString(Main.PlayerColors[id], name)}";
|
||||
|
||||
@@ -340,7 +340,7 @@ internal static class SetUpRoleTextPatch
|
||||
{
|
||||
LateTask.New(() =>
|
||||
{
|
||||
if (AmongUsClient.Instance.IsGameOver || GameStates.IsLobby || lp == null) return;
|
||||
if (AmongUsClient.Instance.IsGameOver || GameStates.IsLobby || !lp) return;
|
||||
lp.SetName(Main.AllPlayerNames[lp.PlayerId]);
|
||||
}, 1f, "Reset Name For Modded Client");
|
||||
}
|
||||
@@ -1272,7 +1272,7 @@ internal static class IntroCutsceneDestroyPatch
|
||||
|
||||
try
|
||||
{
|
||||
System.Collections.Generic.List<PlayerControl> spectators = ChatCommands.Spectators.ToList().ToValidPlayers();
|
||||
System.Collections.Generic.List<PlayerControl> spectators = ChatCommands.Spectators.ToValidPlayers().ToList();
|
||||
if (Main.GM.Value) spectators.Add(PlayerControl.LocalPlayer);
|
||||
|
||||
spectators.ForEach(x =>
|
||||
@@ -1374,6 +1374,9 @@ internal static class IntroCutsceneDestroyPatch
|
||||
case CustomGameMode.StopAndGo:
|
||||
StopAndGo.RoundTimer = Stopwatch.StartNew();
|
||||
break;
|
||||
case CustomGameMode.RoomRush:
|
||||
RoomRush.GameStartDateTime = DateTime.Now;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -769,7 +769,9 @@ internal static class MeetingHudStartPatch
|
||||
|
||||
if (MeetingStates.FirstMeeting && CustomRoles.Workaholic.RoleExist() && Workaholic.WorkaholicGiveAdviceAlive.GetBool() && !Workaholic.WorkaholicCannotWinAtDeath.GetBool() /* && !Options.GhostIgnoreTasks.GetBool()*/)
|
||||
{
|
||||
foreach (PlayerControl pc in Main.EnumerateAlivePlayerControls().Where(x => x.Is(CustomRoles.Workaholic)).ToArray()) Workaholic.WorkaholicAlive.Add(pc.PlayerId);
|
||||
foreach (PlayerControl pc in Main.EnumerateAlivePlayerControls())
|
||||
if (pc.Is(CustomRoles.Workaholic))
|
||||
Workaholic.WorkaholicAlive.Add(pc.PlayerId);
|
||||
|
||||
List<string> workaholicAliveList = [];
|
||||
workaholicAliveList.AddRange(Workaholic.WorkaholicAlive.Select(whId => Main.AllPlayerNames[whId]));
|
||||
@@ -781,7 +783,9 @@ internal static class MeetingHudStartPatch
|
||||
// Bait Notify
|
||||
if (MeetingStates.FirstMeeting && CustomRoles.Bait.RoleExist() && Options.BaitNotification.GetBool())
|
||||
{
|
||||
foreach (PlayerControl pc in Main.EnumerateAlivePlayerControls().Where(x => x.Is(CustomRoles.Bait)).ToArray()) Main.BaitAlive.Add(pc.PlayerId);
|
||||
foreach (PlayerControl pc in Main.EnumerateAlivePlayerControls())
|
||||
if (pc.Is(CustomRoles.Bait))
|
||||
Main.BaitAlive.Add(pc.PlayerId);
|
||||
|
||||
List<string> baitAliveList = [];
|
||||
baitAliveList.AddRange(Main.BaitAlive.Select(whId => Main.AllPlayerNames[whId]));
|
||||
|
||||
@@ -380,9 +380,9 @@ public static class TextBoxPatch
|
||||
|
||||
public static void OnMeetingStart()
|
||||
{
|
||||
PlaceHolderText?.transform.SetAsLastSibling();
|
||||
CommandInfoText?.transform.SetAsLastSibling();
|
||||
AdditionalInfoText?.transform.SetAsLastSibling();
|
||||
if (PlaceHolderText) PlaceHolderText.transform.SetAsLastSibling();
|
||||
if (CommandInfoText) CommandInfoText.transform.SetAsLastSibling();
|
||||
if (AdditionalInfoText) AdditionalInfoText.transform.SetAsLastSibling();
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(TextBoxTMP), nameof(TextBoxTMP.Update))]
|
||||
|
||||
@@ -4700,7 +4700,6 @@
|
||||
"ND_LimitMaximumDisastersAtOnce": "Limit Maximum Active Disasters at Once",
|
||||
"ND_MaximumDisastersAtOnce": "Maximum Active Disasters at Once",
|
||||
"ND_WhenLimitIsReached": "When the Limit is Reached",
|
||||
"ND_LimitReachedOptions.OnlySpawnInstantDisasters": "Only Spawn Instant Disasters",
|
||||
"ND_LimitReachedOptions.RemoveOldest": "Remove Oldest Disaster",
|
||||
"ND_LimitReachedOptions.RemoveRandom": "Remove Random Disaster",
|
||||
"ND_DisasterSpawnMode": "Disaster Spawn Mode",
|
||||
|
||||
@@ -81,7 +81,7 @@ internal class Asthmatic : IAddon
|
||||
{
|
||||
if (Timers.Count == 0) return;
|
||||
|
||||
List<byte> toRemove = [];
|
||||
List<byte> toRemove = null;
|
||||
|
||||
foreach ((byte id, Counter counter) in Timers)
|
||||
{
|
||||
@@ -89,6 +89,7 @@ internal class Asthmatic : IAddon
|
||||
|
||||
if (state.IsDead || !state.SubRoles.Contains(CustomRoles.Asthmatic) || state.MainRole == CustomRoles.Pestilence)
|
||||
{
|
||||
toRemove ??= [];
|
||||
toRemove.Add(id);
|
||||
continue;
|
||||
}
|
||||
@@ -96,7 +97,7 @@ internal class Asthmatic : IAddon
|
||||
counter.Update();
|
||||
}
|
||||
|
||||
if (toRemove.Count == 0) return;
|
||||
if (toRemove == null) return;
|
||||
|
||||
foreach (byte id in toRemove)
|
||||
{
|
||||
|
||||
@@ -94,16 +94,6 @@ public class Damocles : IAddon
|
||||
MessageWriter writer = AmongUsClient.Instance.StartRpcImmediately(PlayerControl.LocalPlayer.NetId, (byte)CustomRPC.SyncDamoclesTimer, SendOption.Reliable);
|
||||
writer.Write(playerId);
|
||||
writer.Write(Timer[playerId]);
|
||||
writer.Write(LastUpdate[playerId].ToString());
|
||||
List<int> pev = PreviouslyEnteredVents.GetValueOrDefault(playerId, []);
|
||||
writer.Write(pev.Count);
|
||||
|
||||
if (pev.Count > 0)
|
||||
{
|
||||
foreach (int vent in pev.ToArray())
|
||||
writer.Write(vent);
|
||||
}
|
||||
|
||||
AmongUsClient.Instance.FinishRpcImmediately(writer);
|
||||
}
|
||||
|
||||
@@ -111,14 +101,6 @@ public class Damocles : IAddon
|
||||
{
|
||||
byte playerId = reader.ReadByte();
|
||||
Timer[playerId] = reader.ReadInt32();
|
||||
LastUpdate[playerId] = long.Parse(reader.ReadString());
|
||||
int elements = reader.ReadInt32();
|
||||
|
||||
if (elements > 0)
|
||||
{
|
||||
for (var i = 0; i < elements; i++)
|
||||
PreviouslyEnteredVents[playerId].Add(reader.ReadInt32());
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnMurder(byte id)
|
||||
|
||||
@@ -749,16 +749,11 @@ internal class Randomizer : RoleBase
|
||||
{
|
||||
long now = Utils.TimeStamp;
|
||||
|
||||
foreach (KeyValuePair<Effect, (long StartTimeStamp, int Duration)> item in effects.ToArray())
|
||||
foreach (KeyValuePair<Effect, (long StartTimeStamp, int Duration)> item in effects.Where(item => item.Value.StartTimeStamp + item.Value.Duration < now).ToArray())
|
||||
{
|
||||
if (item.Value.StartTimeStamp + item.Value.Duration < now)
|
||||
{
|
||||
if (item.Key.IsVisionChangingEffect()) RevertVisionChangesForPlayer(pc, true);
|
||||
|
||||
if (item.Key.IsSpeedChangingEffect()) RevertSpeedChangesForPlayer(pc, true);
|
||||
|
||||
effects.Remove(item.Key);
|
||||
}
|
||||
if (item.Key.IsVisionChangingEffect()) RevertVisionChangesForPlayer(pc, true);
|
||||
if (item.Key.IsSpeedChangingEffect()) RevertSpeedChangesForPlayer(pc, true);
|
||||
effects.Remove(item.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,9 @@ internal class Tornado : RoleBase
|
||||
float tornadoRange = TornadoRange.GetFloat();
|
||||
int tornadoDuration = TornadoDuration.GetInt();
|
||||
|
||||
foreach (KeyValuePair<(Vector2 Location, string RoomName), long> tornado in Tornados.ToArray())
|
||||
List<(Vector2 Location, string RoomName)> toRemove = null;
|
||||
|
||||
foreach (KeyValuePair<(Vector2 Location, string RoomName), long> tornado in Tornados)
|
||||
{
|
||||
if (FastVector2.DistanceWithinRange(tornado.Key.Location, pc.Pos(), tornadoRange))
|
||||
{
|
||||
@@ -148,11 +150,14 @@ internal class Tornado : RoleBase
|
||||
|
||||
if (tornado.Value + tornadoDuration < now)
|
||||
{
|
||||
Tornados.Remove(tornado.Key);
|
||||
toRemove ??= [];
|
||||
toRemove.Add(tornado.Key);
|
||||
SendRPCAddTornado(false, tornado.Key.Location, tornado.Key.RoomName);
|
||||
NotifyRoles(SpecifySeer: TornadoPC, SpecifyTarget: TornadoPC);
|
||||
}
|
||||
}
|
||||
|
||||
toRemove?.ForEach(x => Tornados.Remove(x));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -166,16 +166,21 @@ public class Druid : RoleBase
|
||||
{
|
||||
if (!GameStates.IsInTask || Triggers.Count <= 0 || PlayerIdList.Contains(pc.PlayerId)) return;
|
||||
|
||||
foreach (KeyValuePair<Vector2, string> trigger in Triggers.ToArray())
|
||||
List<Vector2> toRemove = null;
|
||||
|
||||
foreach (KeyValuePair<Vector2, string> trigger in Triggers)
|
||||
{
|
||||
if (FastVector2.DistanceWithinRange(trigger.Key, pc.Pos(), 1.5f))
|
||||
{
|
||||
Triggers.Remove(trigger.Key);
|
||||
toRemove ??= [];
|
||||
toRemove.Add(trigger.Key);
|
||||
DruidPC.Notify(string.Format(GetString("DruidTriggerTriggered"), GetFormattedRoomName(trigger.Value), GetFormattedVectorText(trigger.Key)));
|
||||
SendRPCAddTrigger(false, DruidPC.PlayerId, trigger.Key);
|
||||
CustomNetObject.Get(TriggerIds[trigger.Key])?.Despawn();
|
||||
}
|
||||
}
|
||||
|
||||
toRemove?.ForEach(x => Triggers.Remove(x));
|
||||
}
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
|
||||
@@ -98,7 +98,7 @@ public class Scout : RoleBase
|
||||
|
||||
public static string GetTargetMark(PlayerControl seer, PlayerControl target)
|
||||
{
|
||||
return !(seer == null || target == null) && TrackerTarget.ContainsKey(seer.PlayerId) && TrackerTarget[seer.PlayerId].Contains(target.PlayerId) ? Utils.ColorString(seer.GetRoleColor(), "◀") : string.Empty;
|
||||
return TrackerTarget.ContainsKey(seer.PlayerId) && TrackerTarget[seer.PlayerId].Contains(target.PlayerId) ? Utils.ColorString(seer.GetRoleColor(), "◀") : string.Empty;
|
||||
}
|
||||
|
||||
public override bool OnVote(PlayerControl player, PlayerControl target)
|
||||
@@ -124,7 +124,7 @@ public class Scout : RoleBase
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
if (seer == null || seer.PlayerId != TrackerId || target != null && seer.PlayerId != target.PlayerId || !TrackerTarget.ContainsKey(seer.PlayerId) || meeting || hud) return string.Empty;
|
||||
if (seer.PlayerId != TrackerId || seer.PlayerId != target.PlayerId || !TrackerTarget.ContainsKey(seer.PlayerId) || meeting || hud) return string.Empty;
|
||||
return TrackerTarget[seer.PlayerId].Aggregate(string.Empty, (current, trackTarget) => current + Utils.ColorString(CanGetColoredArrow.GetBool() ? Main.PlayerColors[trackTarget] : Color.white, TargetArrow.GetArrows(seer, trackTarget))) + LocateArrow.GetArrows(seer);
|
||||
}
|
||||
|
||||
@@ -137,15 +137,13 @@ public class Scout : RoleBase
|
||||
|
||||
public static string GetArrowAndLastRoom(PlayerControl seer, PlayerControl target)
|
||||
{
|
||||
if (seer == null || target == null) return string.Empty;
|
||||
|
||||
Color roleColor = Utils.GetRoleColor(CustomRoles.Scout);
|
||||
string text = Utils.ColorString(roleColor, TargetArrow.GetArrows(seer, target.PlayerId));
|
||||
text += Utils.ColorString(roleColor, LocateArrow.GetArrows(seer));
|
||||
|
||||
PlainShipRoom room = Main.PlayerStates[target.PlayerId].LastRoom;
|
||||
|
||||
if (room == null)
|
||||
if (!room)
|
||||
text += Utils.ColorString(Color.gray, "@" + GetString("FailToTrack"));
|
||||
else
|
||||
text += Utils.ColorString(roleColor, "@" + GetString(room.RoomId.ToString()));
|
||||
@@ -155,7 +153,7 @@ public class Scout : RoleBase
|
||||
|
||||
public static void OnPlayerDeath(PlayerControl player)
|
||||
{
|
||||
if (player == null) return;
|
||||
if (!player) return;
|
||||
|
||||
foreach (KeyValuePair<byte, List<byte>> kvp in TrackerTarget)
|
||||
{
|
||||
@@ -178,7 +176,7 @@ public class Scout : RoleBase
|
||||
{
|
||||
PlayerControl pc = Utils.GetPlayerById(id);
|
||||
|
||||
if (pc == null || !pc.IsAlive())
|
||||
if (!pc || !pc.IsAlive())
|
||||
{
|
||||
kvp.Value.Remove(id);
|
||||
TargetArrow.Remove(kvp.Key, id);
|
||||
|
||||
@@ -41,9 +41,9 @@ public class PortalMaker : RoleBase
|
||||
|
||||
public override void OnPet(PlayerControl pc)
|
||||
{
|
||||
if (Marks.Count == 2)
|
||||
if (Marks.Count >= 2)
|
||||
{
|
||||
if (!PetToRemovePortals.GetBool()) return;
|
||||
if (!PetToRemovePortals.GetBool() && Marks.Count == 2) return;
|
||||
Marks.Clear();
|
||||
CustomNetObject.AllObjects.OfType<Portal>().Do(x => x.Despawn());
|
||||
pc.Notify(Translator.GetString("MarksCleared"));
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static EHR.Translator;
|
||||
|
||||
namespace EHR.Roles;
|
||||
@@ -78,15 +77,15 @@ internal class Benefactor : RoleBase
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (byte benefactorId in TaskIndex.Keys.ToArray())
|
||||
foreach ((byte benefactorId, List<int> taskIndexes) in TaskIndex)
|
||||
{
|
||||
if (TaskIndex[benefactorId].Contains(task.Index))
|
||||
if (taskIndexes.Contains(task.Index))
|
||||
{
|
||||
PlayerControl benefactorPC = Utils.GetPlayerById(benefactorId);
|
||||
if (benefactorPC == null) continue;
|
||||
if (!benefactorPC) continue;
|
||||
|
||||
player.Notify(GetString("BenefactorTargetGotShieldNotify"));
|
||||
TaskIndex[benefactorId].Remove(task.Index);
|
||||
taskIndexes.Remove(task.Index);
|
||||
ShieldedPlayers.Add(playerId);
|
||||
LateTask.New(() => ShieldedPlayers.Remove(playerId), ShieldDuration.GetInt());
|
||||
Logger.Info($"{player.GetAllRoleName()} got a shield because the task was marked by {benefactorPC.GetNameWithRole()}", "Benefactor");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
|
||||
namespace EHR.Roles;
|
||||
@@ -92,7 +93,7 @@ public class Crusader : RoleBase
|
||||
return;
|
||||
}
|
||||
|
||||
keepGameGoing = ForCrusade.ToValidPlayers().Exists(x => x.IsAlive());
|
||||
keepGameGoing = ForCrusade.ToValidPlayers().Any(x => x.IsAlive());
|
||||
countsAs = 1;
|
||||
}
|
||||
}
|
||||
@@ -56,7 +56,7 @@ public class TimeManager : RoleBase
|
||||
{
|
||||
var sec = 0;
|
||||
|
||||
foreach (byte playerId in PlayerIdList.ToArray())
|
||||
foreach (byte playerId in PlayerIdList)
|
||||
{
|
||||
if (Utils.GetPlayerById(playerId).Is(CustomRoles.Madmate))
|
||||
sec -= AdditionalTime(playerId);
|
||||
|
||||
@@ -123,12 +123,12 @@ internal class Haunter : IGhostRole
|
||||
|
||||
AllHauntedPlayers.UnionWith(targets);
|
||||
|
||||
var targetPcs = targets.ToValidPlayers();
|
||||
var targetPcs = targets.ToValidPlayers().ToArray();
|
||||
|
||||
targetPcs.ForEach(x => x.Notify(Translator.GetString("HaunterRevealedYou"), 10f));
|
||||
WarnedImps.ToValidPlayers().ForEach(x => x.Notify(Translator.GetString("HaunterFinishedTasks"), 10f));
|
||||
targetPcs.Do(x => x.Notify(Translator.GetString("HaunterRevealedYou"), 10f));
|
||||
WarnedImps.ToValidPlayers().Do(x => x.Notify(Translator.GetString("HaunterFinishedTasks"), 10f));
|
||||
|
||||
targetPcs.ForEach(x => Utils.NotifyRoles(SpecifyTarget: x));
|
||||
targetPcs.Do(x => Utils.NotifyRoles(SpecifyTarget: x));
|
||||
}
|
||||
|
||||
public void Update(PlayerControl pc)
|
||||
|
||||
@@ -76,7 +76,7 @@ public class Anonymous : RoleBase
|
||||
|
||||
public static void AddDeadBody(PlayerControl target)
|
||||
{
|
||||
if (target != null && !DeadBodyList.Contains(target.PlayerId)) DeadBodyList.Add(target.PlayerId);
|
||||
if (target && !DeadBodyList.Contains(target.PlayerId)) DeadBodyList.Add(target.PlayerId);
|
||||
}
|
||||
|
||||
public override bool OnShapeshift(PlayerControl pc, PlayerControl ssTarget, bool shapeshifting)
|
||||
@@ -87,12 +87,14 @@ public class Anonymous : RoleBase
|
||||
|
||||
var targetId = byte.MaxValue;
|
||||
|
||||
foreach (byte db in DeadBodyList.ToArray())
|
||||
foreach (byte db in DeadBodyList)
|
||||
{
|
||||
PlayerControl dp = Utils.GetPlayerById(db);
|
||||
if (dp == null || dp.GetRealKiller() == null) continue;
|
||||
if (!dp) continue;
|
||||
PlayerControl realKiller = dp.GetRealKiller();
|
||||
if (!realKiller) continue;
|
||||
|
||||
if (dp.GetRealKiller().PlayerId == pc.PlayerId) targetId = db;
|
||||
if (realKiller.PlayerId == pc.PlayerId) targetId = db;
|
||||
}
|
||||
|
||||
if (targetId == byte.MaxValue && DeadBodyList.Count > 0) targetId = DeadBodyList.RandomElement();
|
||||
|
||||
@@ -107,7 +107,7 @@ public class Lightning : RoleBase
|
||||
|
||||
public static bool CheckLightningMurder(PlayerControl killer, PlayerControl target, bool force = false)
|
||||
{
|
||||
if (killer == null || target == null || (!killer.Is(CustomRoles.Lightning) && !force)) return false;
|
||||
if (!killer.Is(CustomRoles.Lightning) && !force) return false;
|
||||
|
||||
if (IsGhost(target)) return false;
|
||||
|
||||
@@ -155,11 +155,11 @@ public class Lightning : RoleBase
|
||||
|
||||
List<byte> deList = [];
|
||||
|
||||
foreach (byte ghost in GhostPlayer.ToArray())
|
||||
foreach (byte ghost in GhostPlayer)
|
||||
{
|
||||
PlayerControl gs = Utils.GetPlayerById(ghost);
|
||||
|
||||
if (gs == null || !gs.IsAlive() || gs.Data.Disconnected)
|
||||
if (!gs || !gs.IsAlive() || gs.Data.Disconnected)
|
||||
{
|
||||
//deList.Add(gs.PlayerId); // This will always result in a null reference exception
|
||||
continue;
|
||||
@@ -181,7 +181,7 @@ public class Lightning : RoleBase
|
||||
{
|
||||
GhostPlayer.RemoveAll(deList.Contains);
|
||||
|
||||
foreach (byte gs in deList.ToArray())
|
||||
foreach (byte gs in deList)
|
||||
{
|
||||
SendRPC(gs);
|
||||
Utils.NotifyRoles(SpecifySeer: Utils.GetPlayerById(gs));
|
||||
@@ -193,10 +193,10 @@ public class Lightning : RoleBase
|
||||
{
|
||||
if (!(IsEnable || CustomRoles.Lightning.IsEnable())) return;
|
||||
|
||||
foreach (byte ghost in GhostPlayer.ToArray())
|
||||
foreach (byte ghost in GhostPlayer)
|
||||
{
|
||||
PlayerControl gs = Utils.GetPlayerById(ghost);
|
||||
if (gs == null) continue;
|
||||
if (!gs) continue;
|
||||
|
||||
CheckForEndVotingPatch.TryAddAfterMeetingDeathPlayers(PlayerState.DeathReason.Quantization, gs.PlayerId);
|
||||
gs.SetRealKiller(RealKiller[gs.PlayerId]);
|
||||
|
||||
@@ -63,7 +63,7 @@ public sealed class Stealth : RoleBase
|
||||
private IEnumerable<PlayerControl> FindPlayersInSameRoom(PlayerControl killedPlayer)
|
||||
{
|
||||
PlainShipRoom room = killedPlayer.GetPlainShipRoom();
|
||||
if (room == null) return null;
|
||||
if (!room) return null;
|
||||
|
||||
Collider2D roomArea = room.roomArea;
|
||||
SystemTypes roomName = room.RoomId;
|
||||
|
||||
@@ -123,13 +123,6 @@ public class Mafioso : RoleBase
|
||||
writer.Write(MafiosoId);
|
||||
writer.Write(Tier);
|
||||
writer.Write(XP);
|
||||
writer.Write(PreviouslyUsedVents.Count);
|
||||
|
||||
if (PreviouslyUsedVents.Count > 0)
|
||||
{
|
||||
foreach (int vent in PreviouslyUsedVents.ToArray())
|
||||
writer.Write(vent);
|
||||
}
|
||||
|
||||
AmongUsClient.Instance.FinishRpcImmediately(writer);
|
||||
}
|
||||
@@ -138,14 +131,6 @@ public class Mafioso : RoleBase
|
||||
{
|
||||
Tier = reader.ReadInt32();
|
||||
XP = reader.ReadInt32();
|
||||
PreviouslyUsedVents.Clear();
|
||||
int elements = reader.ReadInt32();
|
||||
|
||||
if (elements > 0)
|
||||
{
|
||||
for (var i = 0; i < elements; i++)
|
||||
PreviouslyUsedVents.Add(reader.ReadInt32());
|
||||
}
|
||||
}
|
||||
|
||||
private void SendRPCSyncPistolCD()
|
||||
@@ -156,7 +141,6 @@ public class Mafioso : RoleBase
|
||||
writer.Write(MafiosoId);
|
||||
writer.Write(Pistol1CD);
|
||||
writer.Write(Pistol2CD);
|
||||
writer.Write(lastUpdate.ToString());
|
||||
AmongUsClient.Instance.FinishRpcImmediately(writer);
|
||||
}
|
||||
|
||||
@@ -164,7 +148,6 @@ public class Mafioso : RoleBase
|
||||
{
|
||||
Pistol1CD = reader.ReadInt32();
|
||||
Pistol2CD = reader.ReadInt32();
|
||||
lastUpdate = long.Parse(reader.ReadString());
|
||||
}
|
||||
|
||||
public override void OnFixedUpdate(PlayerControl pc)
|
||||
@@ -201,21 +184,21 @@ public class Mafioso : RoleBase
|
||||
|
||||
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (!GameStates.IsInTask || target == null || killer == null || !killer.Is(CustomRoles.Mafioso) || Tier < 3 || !IsEnable) return true;
|
||||
if (!GameStates.IsInTask || !killer.Is(CustomRoles.Mafioso) || Tier < 3 || !IsEnable) return true;
|
||||
|
||||
if (Pistol1CD > 0 && Pistol2CD > 0) return false;
|
||||
|
||||
int KCD = Tier >= 4 ? (int)Math.Round(AdjustedDefaultKillCooldown) : (int)Math.Round(AdjustedDefaultKillCooldown * 1.5);
|
||||
KCD++;
|
||||
int kcd = Tier >= 4 ? (int)Math.Round(AdjustedDefaultKillCooldown) : (int)Math.Round(AdjustedDefaultKillCooldown * 1.5);
|
||||
kcd++;
|
||||
|
||||
if (Pistol1CD <= 0)
|
||||
{
|
||||
Pistol1CD = KCD;
|
||||
Pistol1CD = kcd;
|
||||
NotifyRoles(SpecifySeer: killer, SpecifyTarget: killer);
|
||||
}
|
||||
else if (Pistol2CD <= 0)
|
||||
{
|
||||
Pistol2CD = KCD;
|
||||
Pistol2CD = kcd;
|
||||
NotifyRoles(SpecifySeer: killer, SpecifyTarget: killer);
|
||||
}
|
||||
|
||||
@@ -224,13 +207,13 @@ public class Mafioso : RoleBase
|
||||
LateTask.New(() =>
|
||||
{
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalse ---- Can be null since it's a task that completes later
|
||||
if (target != null && target.IsAlive() && GameStates.IsInTask) target.Suicide(PlayerState.DeathReason.Kill, killer);
|
||||
if (target && target.IsAlive() && GameStates.IsInTask) target.Suicide(PlayerState.DeathReason.Kill, killer);
|
||||
}, Delay.GetInt(), "Mafioso Tier 5 Kill Delay");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Pistol1CD > 1 && Pistol2CD > 1) LateTask.New(() => { killer.SetKillCooldown(Math.Min(Pistol1CD, Pistol2CD) - 1); }, 0.1f, "Mafioso SetKillCooldown");
|
||||
if (Pistol1CD > 1 && Pistol2CD > 1) LateTask.New(() => killer.SetKillCooldown(Math.Min(Pistol1CD, Pistol2CD) - 1), 0.1f, "Mafioso SetKillCooldown");
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -240,10 +223,10 @@ public class Mafioso : RoleBase
|
||||
if (!IsEnable) return;
|
||||
|
||||
PreviouslyUsedVents.Clear();
|
||||
int KCD = Tier >= 4 ? (int)Math.Round(AdjustedDefaultKillCooldown) : (int)Math.Round(AdjustedDefaultKillCooldown * 1.5);
|
||||
KCD++;
|
||||
Pistol1CD = KCD;
|
||||
Pistol2CD = KCD;
|
||||
int kcd = Tier >= 4 ? (int)Math.Round(AdjustedDefaultKillCooldown) : (int)Math.Round(AdjustedDefaultKillCooldown * 1.5);
|
||||
kcd++;
|
||||
Pistol1CD = kcd;
|
||||
Pistol2CD = kcd;
|
||||
lastUpdate = TimeStamp;
|
||||
SendRPC();
|
||||
SendRPCSyncPistolCD();
|
||||
|
||||
@@ -115,8 +115,8 @@ public class EvilTracker : RoleBase
|
||||
|
||||
public static void OnAnyoneMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (CanSeeKillFlash && killer != null && killer.Is(CustomRoleTypes.Impostor) && killer != target && !PlayerIdList.Contains(killer.PlayerId))
|
||||
PlayerIdList.ToValidPlayers().ForEach(x => x.KillFlash());
|
||||
if (CanSeeKillFlash && killer.Is(CustomRoleTypes.Impostor) && killer != target && !PlayerIdList.Contains(killer.PlayerId))
|
||||
PlayerIdList.ToValidPlayers().Do(x => x.KillFlash());
|
||||
}
|
||||
|
||||
public override bool OnShapeshift(PlayerControl shapeshifter, PlayerControl target, bool shapeshifting)
|
||||
@@ -227,7 +227,7 @@ public class EvilTracker : RoleBase
|
||||
string text = Utils.ColorString(Palette.ImpostorRed, TargetArrow.GetArrows(seer, target.PlayerId));
|
||||
PlainShipRoom room = Main.PlayerStates[target.PlayerId].LastRoom;
|
||||
|
||||
if (room == null)
|
||||
if (!room)
|
||||
text += Utils.ColorString(Color.gray, "@" + GetString("FailToTrack"));
|
||||
else
|
||||
text += Utils.ColorString(Palette.ImpostorRed, "@" + GetString(room.RoomId.ToString()));
|
||||
|
||||
@@ -107,18 +107,18 @@ public class Devourer : RoleBase
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void OnDevourerDied(byte Devourer)
|
||||
public static void OnDevourerDied(byte devourer)
|
||||
{
|
||||
if (Main.PlayerStates[Devourer].Role is not Devourer { IsEnable: true } dv) return;
|
||||
if (Main.PlayerStates[devourer].Role is not Devourer { IsEnable: true } dv) return;
|
||||
|
||||
foreach (byte player in dv.PlayerSkinsCosumed.ToArray())
|
||||
foreach (byte player in dv.PlayerSkinsCosumed)
|
||||
{
|
||||
Camouflage.PlayerSkins[player] = OriginalPlayerSkins[player];
|
||||
|
||||
if (!Camouflage.IsCamouflage)
|
||||
{
|
||||
PlayerControl pc = Main.EnumerateAlivePlayerControls().FirstOrDefault(a => a.PlayerId == player);
|
||||
if (pc == null) continue;
|
||||
if (!pc) continue;
|
||||
|
||||
SetSkin(pc, OriginalPlayerSkins[player]);
|
||||
}
|
||||
|
||||
@@ -76,7 +76,9 @@ internal class Workaholic : RoleBase
|
||||
{
|
||||
Logger.Info("Workaholic Tasks Finished", "Workaholic");
|
||||
RPC.PlaySoundRPC(player.PlayerId, Sounds.KillSound);
|
||||
foreach (PlayerControl pc in Main.EnumerateAlivePlayerControls().Where(pc => pc.PlayerId != player.PlayerId).ToArray()) pc.Suicide(pc.PlayerId == player.PlayerId ? PlayerState.DeathReason.Overtired : PlayerState.DeathReason.Ashamed, player);
|
||||
|
||||
foreach (PlayerControl pc in Main.EnumerateAlivePlayerControls())
|
||||
pc.Suicide(pc.PlayerId == player.PlayerId ? PlayerState.DeathReason.Overtired : PlayerState.DeathReason.Ashamed, player);
|
||||
|
||||
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Workaholic);
|
||||
CustomWinnerHolder.WinnerIds.Add(player.PlayerId);
|
||||
|
||||
@@ -81,7 +81,7 @@ public class HeadHunter : RoleBase
|
||||
MessageWriter writer = Utils.CreateRPC(CustomRPC.SyncHeadHunter);
|
||||
writer.Write(HeadHunterId);
|
||||
writer.Write(Targets.Count);
|
||||
foreach (byte target in Targets.ToArray()) writer.Write(target);
|
||||
foreach (byte target in Targets) writer.Write(target);
|
||||
|
||||
Utils.EndRPC(writer);
|
||||
}
|
||||
|
||||
@@ -349,26 +349,35 @@ public class Magician : RoleBase
|
||||
|
||||
if (Bombs.Count > 0)
|
||||
{
|
||||
foreach (KeyValuePair<Vector2, long> bomb in Bombs.Where(bomb => bomb.Value + BombDelay.GetInt() < now).ToArray())
|
||||
List<Vector2> toRemove = null;
|
||||
|
||||
foreach (KeyValuePair<Vector2, long> bomb in Bombs)
|
||||
{
|
||||
foreach (PlayerControl tg in FastVector2.GetPlayersInRange(bomb.Key, BombRadius.GetFloat()))
|
||||
if (bomb.Value + BombDelay.GetInt() < now)
|
||||
{
|
||||
if (tg.PlayerId == pc.PlayerId)
|
||||
foreach (PlayerControl tg in FastVector2.GetPlayersInRange(bomb.Key, BombRadius.GetFloat()))
|
||||
{
|
||||
LateTask.New(() =>
|
||||
if (tg.PlayerId == pc.PlayerId)
|
||||
{
|
||||
if (!GameStates.IsEnded) pc.Suicide(PlayerState.DeathReason.Bombed);
|
||||
}, 0.5f, "Magician Bomb Suicide");
|
||||
continue;
|
||||
LateTask.New(() =>
|
||||
{
|
||||
if (!GameStates.IsEnded)
|
||||
pc.Suicide(PlayerState.DeathReason.Bombed);
|
||||
}, 0.5f, "Magician Bomb Suicide");
|
||||
continue;
|
||||
}
|
||||
|
||||
tg.Suicide(PlayerState.DeathReason.Bombed, pc);
|
||||
}
|
||||
|
||||
tg.Suicide(PlayerState.DeathReason.Bombed, pc);
|
||||
toRemove ??= [];
|
||||
toRemove.Add(bomb.Key);
|
||||
pc.Notify(GetString("MagicianBombExploded"));
|
||||
}
|
||||
|
||||
Bombs.Remove(bomb.Key);
|
||||
pc.Notify(GetString("MagicianBombExploded"));
|
||||
}
|
||||
|
||||
toRemove?.ForEach(x => Bombs.Remove(x));
|
||||
|
||||
var sb = new StringBuilder();
|
||||
long[] list = [.. Bombs.Values];
|
||||
foreach (long x in list) sb.Append(string.Format(GetString("MagicianBombExlodesIn"), BombDelay.GetInt() - (now - x) + 1));
|
||||
|
||||
@@ -85,7 +85,7 @@ public class Pelican : RoleBase
|
||||
if (playerId != byte.MaxValue)
|
||||
{
|
||||
writer.Write(EatenList[playerId].Count);
|
||||
foreach (byte el in EatenList[playerId].ToArray()) writer.Write(el);
|
||||
foreach (byte el in EatenList[playerId]) writer.Write(el);
|
||||
}
|
||||
|
||||
AmongUsClient.Instance.FinishRpcImmediately(writer);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
using Hazel;
|
||||
|
||||
@@ -108,9 +109,9 @@ public class SoulCollector : RoleBase
|
||||
{
|
||||
var sender = CustomRpcSender.Create("SoulCollector Post Exile", SendOption.Reliable);
|
||||
sender.StartMessage();
|
||||
List<PlayerControl> toExile = ToExile.ToValidPlayers();
|
||||
PlayerControl[] toExile = ToExile.ToValidPlayers().ToArray();
|
||||
ToExile = [];
|
||||
toExile.ForEach(x =>
|
||||
toExile.Do(x =>
|
||||
{
|
||||
Main.AllPlayerSpeed[x.PlayerId] = Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod);
|
||||
sender.RpcExiled(x, autoStartRpc: false);
|
||||
|
||||
@@ -143,7 +143,7 @@ public class Spider : RoleBase
|
||||
public override void OnFixedUpdate(PlayerControl pc)
|
||||
{
|
||||
long now = Utils.TimeStamp;
|
||||
List<Vector2> toRemove = [];
|
||||
List<Vector2> toRemove = null;
|
||||
|
||||
foreach ((Vector2 pos, Dictionary<byte, long> trapped) in Webs)
|
||||
{
|
||||
@@ -152,6 +152,7 @@ public class Spider : RoleBase
|
||||
|
||||
if (trapped.Values.Min() <= now)
|
||||
{
|
||||
toRemove ??= [];
|
||||
toRemove.Add(pos);
|
||||
LateTask.New(() =>
|
||||
{
|
||||
@@ -165,7 +166,7 @@ public class Spider : RoleBase
|
||||
}
|
||||
}
|
||||
|
||||
toRemove.ForEach(x =>
|
||||
toRemove?.ForEach(x =>
|
||||
{
|
||||
Webs.Remove(x);
|
||||
LocateArrow.Remove(pc.PlayerId, x);
|
||||
|
||||
Reference in new issue
Block a user