mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-20 01:46:25 +00:00
rework time master + report fail notifies
This commit is contained in:
1 parent
d1f635a012
commit
27a336fe2b
13 files changed
+189
-115
No files matched your search
@@ -75,6 +75,15 @@ internal static class CustomTeamManager
|
||||
|
||||
foreach ((CustomTeam team, HashSet<byte> players) in CustomTeamPlayerIds)
|
||||
{
|
||||
if (!IsSettingEnabledForTeam(team, CTAOption.WinWithOriginalTeam))
|
||||
{
|
||||
foreach (byte id in players)
|
||||
{
|
||||
if (Main.PlayerStates.TryGetValue(id, out var ps))
|
||||
ps.countTypes = CountTypes.CustomTeam;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsSettingEnabledForTeam(team, CTAOption.Arrows)) continue;
|
||||
|
||||
foreach (byte player in players)
|
||||
|
||||
@@ -1097,7 +1097,7 @@ internal static class ExtendedPlayerControl
|
||||
}, 0.4f + delay, "Fix Desync Reactor");
|
||||
}
|
||||
|
||||
public static void ReactorFlash(this PlayerControl pc, float delay = 0f)
|
||||
public static void ReactorFlash(this PlayerControl pc, float delay = 0f, float flashDuration = float.NaN)
|
||||
{
|
||||
if (pc == null) return;
|
||||
|
||||
@@ -1110,8 +1110,6 @@ internal static class ExtendedPlayerControl
|
||||
_ => SystemTypes.Reactor
|
||||
};
|
||||
|
||||
float FlashDuration = Options.KillFlashDuration.GetFloat();
|
||||
|
||||
pc.RpcDesyncRepairSystem(systemtypes, 128);
|
||||
|
||||
LateTask.New(() =>
|
||||
@@ -1120,7 +1118,7 @@ internal static class ExtendedPlayerControl
|
||||
|
||||
if (Main.NormalOptions.MapId == 4) // on Airship
|
||||
pc.RpcDesyncRepairSystem(systemtypes, 17);
|
||||
}, FlashDuration + delay, "Fix Desync Reactor");
|
||||
}, (float.IsNaN(flashDuration) ? Options.KillFlashDuration.GetFloat() : flashDuration) + delay, "Fix Desync Reactor");
|
||||
}
|
||||
|
||||
public static string GetRealName(this PlayerControl player, bool isMeeting = false)
|
||||
@@ -1211,7 +1209,7 @@ internal static class ExtendedPlayerControl
|
||||
|
||||
public static bool CanUseKillButton(this PlayerControl pc)
|
||||
{
|
||||
if (AntiBlackout.SkipTasks || !pc.IsAlive()) return false;
|
||||
if (AntiBlackout.SkipTasks || TimeMaster.Rewinding || !pc.IsAlive()) return false;
|
||||
|
||||
switch (Options.CurrentGameMode)
|
||||
{
|
||||
|
||||
@@ -280,9 +280,6 @@ public static class Options
|
||||
public static OptionItem MarioVentCD;
|
||||
public static OptionItem VeteranSkillCooldown;
|
||||
public static OptionItem VeteranSkillDuration;
|
||||
public static OptionItem TimeMasterSkillCooldown;
|
||||
public static OptionItem TimeMasterSkillDuration;
|
||||
public static OptionItem TimeMasterMaxUses;
|
||||
public static OptionItem VeteranSkillMaxOfUseage;
|
||||
public static OptionItem BodyguardProtectRadius;
|
||||
public static OptionItem BodyguardKillsKiller;
|
||||
@@ -341,13 +338,6 @@ public static class Options
|
||||
public static OptionItem SecurityGuardSkillCooldown;
|
||||
public static OptionItem SecurityGuardSkillDuration;
|
||||
public static OptionItem SecurityGuardSkillMaxOfUseage;
|
||||
public static OptionItem EscapeeSSCD;
|
||||
public static OptionItem MinerSSCD;
|
||||
public static OptionItem RevolutionistDrawTime;
|
||||
public static OptionItem RevolutionistCooldown;
|
||||
public static OptionItem RevolutionistDrawCount;
|
||||
public static OptionItem RevolutionistKillProbability;
|
||||
public static OptionItem RevolutionistVentCountDown;
|
||||
public static OptionItem ShapeImperiusCurseShapeshiftDuration;
|
||||
public static OptionItem ImperiusCurseShapeshiftCooldown;
|
||||
public static OptionItem CrewpostorCanKillAllies;
|
||||
@@ -2730,7 +2720,6 @@ public static class Options
|
||||
// ReSharper disable NotAccessedField.Global
|
||||
|
||||
// Ability Use Gain With Each Task Completed
|
||||
public static OptionItem TimeMasterAbilityUseGainWithEachTaskCompleted;
|
||||
public static OptionItem VeteranAbilityUseGainWithEachTaskCompleted;
|
||||
public static OptionItem GrenadierAbilityUseGainWithEachTaskCompleted;
|
||||
public static OptionItem LighterAbilityUseGainWithEachTaskCompleted;
|
||||
@@ -2742,7 +2731,6 @@ public static class Options
|
||||
public static OptionItem LighterAbilityChargesWhenFinishedTasks;
|
||||
public static OptionItem SecurityGuardAbilityChargesWhenFinishedTasks;
|
||||
public static OptionItem DovesOfNeaceAbilityChargesWhenFinishedTasks;
|
||||
public static OptionItem TimeMasterAbilityChargesWhenFinishedTasks;
|
||||
public static OptionItem VeteranAbilityChargesWhenFinishedTasks;
|
||||
|
||||
// ReSharper restore NotAccessedField.Global
|
||||
|
||||
+4
-4
@@ -2669,7 +2669,7 @@ public static class Utils
|
||||
CustomRoles.Grenadier => Options.GrenadierSkillCooldown.GetInt() + (includeDuration ? Options.GrenadierSkillDuration.GetInt() : 0),
|
||||
CustomRoles.Lighter => Options.LighterSkillCooldown.GetInt() + (includeDuration ? Options.LighterSkillDuration.GetInt() : 0),
|
||||
CustomRoles.SecurityGuard => Options.SecurityGuardSkillCooldown.GetInt() + (includeDuration ? Options.SecurityGuardSkillDuration.GetInt() : 0),
|
||||
CustomRoles.TimeMaster => Options.TimeMasterSkillCooldown.GetInt() + (includeDuration ? Options.TimeMasterSkillDuration.GetInt() : 0),
|
||||
CustomRoles.TimeMaster => TimeMaster.TimeMasterSkillCooldown.GetInt() + (includeDuration ? TimeMaster.TimeMasterSkillDuration.GetInt() : 0),
|
||||
CustomRoles.Veteran => Options.VeteranSkillCooldown.GetInt() + (includeDuration ? Options.VeteranSkillDuration.GetInt() : 0),
|
||||
CustomRoles.Rhapsode => Rhapsode.AbilityCooldown.GetInt() + (includeDuration ? Rhapsode.AbilityDuration.GetInt() : 0),
|
||||
CustomRoles.Whisperer => Whisperer.Cooldown.GetInt() + (includeDuration ? Whisperer.Duration.GetInt() : 0),
|
||||
@@ -2691,8 +2691,8 @@ public static class Utils
|
||||
CustomRoles.Bomber => Bomber.BombCooldown.GetInt(),
|
||||
CustomRoles.Nuker => Bomber.NukeCooldown.GetInt(),
|
||||
CustomRoles.Sapper => Sapper.ShapeshiftCooldown.GetInt(),
|
||||
CustomRoles.Miner => Options.MinerSSCD.GetInt(),
|
||||
CustomRoles.Escapee => Options.EscapeeSSCD.GetInt(),
|
||||
CustomRoles.Miner => Miner.MinerSSCD.GetInt(),
|
||||
CustomRoles.Escapee => Escapee.EscapeeSSCD.GetInt(),
|
||||
CustomRoles.QuickShooter => QuickShooter.ShapeshiftCooldown.GetInt(),
|
||||
CustomRoles.Disperser => Disperser.DisperserShapeshiftCooldown.GetInt(),
|
||||
CustomRoles.Twister => Twister.ShapeshiftCooldown.GetInt(),
|
||||
@@ -3063,7 +3063,7 @@ public static class Utils
|
||||
public static (int Drawn, int All) GetDrawPlayerCount(byte playerId, out List<PlayerControl> winnerList)
|
||||
{
|
||||
var draw = 0;
|
||||
int all = Options.RevolutionistDrawCount.GetInt();
|
||||
int all = Revolutionist.RevolutionistDrawCount.GetInt();
|
||||
int max = Main.AllAlivePlayerControls.Length;
|
||||
if (!Main.PlayerStates[playerId].IsDead) max--;
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ public static class PhantomRolePatch
|
||||
PlayerControl phantom = __instance;
|
||||
Logger.Info($"Player: {phantom.GetNameWithRole()}", "CheckVanish");
|
||||
|
||||
if (!Rhapsode.CheckAbilityUse(phantom) || Stasis.IsTimeFrozen || !Main.PlayerStates[__instance.PlayerId].Role.OnVanish(__instance))
|
||||
if (!Rhapsode.CheckAbilityUse(phantom) || Stasis.IsTimeFrozen || TimeMaster.Rewinding || !Main.PlayerStates[__instance.PlayerId].Role.OnVanish(__instance))
|
||||
{
|
||||
if (phantom.AmOwner)
|
||||
{
|
||||
|
||||
@@ -786,9 +786,11 @@ internal static class ShapeshiftPatch
|
||||
|
||||
bool shapeshifting = shapeshifter.PlayerId != target.PlayerId;
|
||||
|
||||
if (AmongUsClient.Instance.AmHost && shapeshifting)
|
||||
CustomRoles role = shapeshifter.GetCustomRole();
|
||||
|
||||
if (AmongUsClient.Instance.AmHost && (shapeshifting || role.AlwaysUsesUnshift() || (role.SimpleAbilityTrigger() && Options.UseUnshiftTrigger.GetBool() && (!shapeshifter.IsNeutralKiller() || Options.UseUnshiftTriggerForNKs.GetBool()))))
|
||||
{
|
||||
if (!Rhapsode.CheckAbilityUse(shapeshifter) || Stasis.IsTimeFrozen) return false;
|
||||
if (!Rhapsode.CheckAbilityUse(shapeshifter) || Stasis.IsTimeFrozen || TimeMaster.Rewinding) return false;
|
||||
|
||||
if (shapeshifter.Is(CustomRoles.Trainee) && MeetingStates.FirstMeeting)
|
||||
{
|
||||
@@ -815,8 +817,6 @@ internal static class ShapeshiftPatch
|
||||
isSSneeded = false;
|
||||
}
|
||||
|
||||
CustomRoles role = shapeshifter.GetCustomRole();
|
||||
|
||||
bool forceCancel = role.ForceCancelShapeshift();
|
||||
bool unshiftTrigger = role.SimpleAbilityTrigger() && Options.UseUnshiftTrigger.GetBool() && (!role.IsNeutral() || Options.UseUnshiftTriggerForNKs.GetBool());
|
||||
|
||||
@@ -946,13 +946,27 @@ internal static class ReportDeadBodyPatch
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
if (__instance.Is(CustomRoles.Jester) && !Jester.JesterCanUseButton.GetBool()) return false;
|
||||
if (__instance.Is(CustomRoles.NiceSwapper) && !NiceSwapper.CanStartMeeting.GetBool()) return false;
|
||||
if (__instance.Is(CustomRoles.Adrenaline) && !Adrenaline.CanCallMeeting(__instance)) return false;
|
||||
if (__instance.Is(CustomRoles.Jester) && !Jester.JesterCanUseButton.GetBool())
|
||||
{
|
||||
Notify("JesterCannotCallEmergencyMeeting");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (__instance.Is(CustomRoles.NiceSwapper) && !NiceSwapper.CanStartMeeting.GetBool())
|
||||
{
|
||||
Notify("NiceSwapperCannotCallEmergencyMeeting");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (__instance.Is(CustomRoles.Adrenaline) && !Adrenaline.CanCallMeeting(__instance))
|
||||
{
|
||||
Notify("AdrenalineCannotCallEmergencyMeeting");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SoulHunter.IsSoulHunterTarget(__instance.PlayerId))
|
||||
{
|
||||
__instance.Notify(GetString("SoulHunterTargetNotifyNoMeeting"));
|
||||
Notify("SoulHunterTargetNotifyNoMeeting");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -960,39 +974,40 @@ internal static class ReportDeadBodyPatch
|
||||
if (target != null)
|
||||
{
|
||||
if (Bloodhound.UnreportablePlayers.Contains(target.PlayerId)
|
||||
|| Vulture.UnreportablePlayers.Contains(target.PlayerId))
|
||||
|| Vulture.UnreportablePlayers.Contains(target.PlayerId)
|
||||
|| (killer != null && killer.Is(CustomRoles.Goddess))
|
||||
|| Main.PlayerStates[target.PlayerId].deathReason == PlayerState.DeathReason.Gambled
|
||||
|| killerRole == CustomRoles.Scavenger
|
||||
|| Cleaner.CleanerBodies.Contains(target.PlayerId))
|
||||
{
|
||||
Notify("UnreportableBody");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Librarian.OnAnyoneReport(__instance)) return false;
|
||||
if (!Occultist.OnAnyoneReportDeadBody(target)) return false;
|
||||
if (!Altruist.OnAnyoneCheckReportDeadBody(__instance, target)) return false;
|
||||
if (!BoobyTrap.OnAnyoneCheckReportDeadBody(__instance, target)) return false;
|
||||
|
||||
if ((!Occultist.OnAnyoneReportDeadBody(target)) ||
|
||||
(!Altruist.OnAnyoneCheckReportDeadBody(__instance, target)))
|
||||
{
|
||||
Notify("PlayerWasRevived");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Librarian.OnAnyoneReport(__instance)) return false; // Player dies, no notify needed
|
||||
if (!BoobyTrap.OnAnyoneCheckReportDeadBody(__instance, target)) return false; // Player dies, no notify needed
|
||||
|
||||
if (!Hypnotist.OnAnyoneReport())
|
||||
{
|
||||
if (__instance.IsLocalPlayer())
|
||||
Achievements.Type.Hypnosis.CompleteAfterGameEnd();
|
||||
|
||||
Notify("HypnosisNoMeeting");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (killer != null && killer.Is(CustomRoles.Goddess)) return false;
|
||||
|
||||
if (!Main.PlayerStates[__instance.PlayerId].Role.CheckReportDeadBody(__instance, target, killer)) return false;
|
||||
|
||||
if (Main.PlayerStates[target.PlayerId].deathReason == PlayerState.DeathReason.Gambled
|
||||
|| killerRole == CustomRoles.Scavenger
|
||||
|| Cleaner.CleanerBodies.Contains(target.PlayerId))
|
||||
return false;
|
||||
|
||||
PlayerControl tpc = target.Object;
|
||||
|
||||
if (__instance.Is(CustomRoles.Oblivious))
|
||||
{
|
||||
if (!tpc.Is(CustomRoles.Bait) || Options.ObliviousBaitImmune.GetBool()) /* && (target?.Object != null)*/
|
||||
return false;
|
||||
}
|
||||
|
||||
if (__instance.Is(CustomRoles.Unlucky) && (tpc == null || !tpc.Is(CustomRoles.Bait)))
|
||||
{
|
||||
if (IRandom.Instance.Next(0, 100) < Options.UnluckyReportSuicideChance.GetInt())
|
||||
@@ -1002,7 +1017,11 @@ internal static class ReportDeadBodyPatch
|
||||
}
|
||||
}
|
||||
|
||||
if (tpc.Is(CustomRoles.Unreportable)) return false;
|
||||
if (tpc.Is(CustomRoles.Unreportable))
|
||||
{
|
||||
Notify("TargetDisregarded");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (Options.SyncButtonMode.GetBool() && target == null)
|
||||
@@ -1012,6 +1031,7 @@ internal static class ReportDeadBodyPatch
|
||||
if (Options.SyncedButtonCount.GetFloat() <= Options.UsedButtonCount)
|
||||
{
|
||||
Logger.Info("The ship has no more emergency meetings left", "ReportDeadBody");
|
||||
Notify("ShipHasNoMoreMeetingsLeft");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1025,6 +1045,8 @@ internal static class ReportDeadBodyPatch
|
||||
catch (Exception e) { ThrowException(e); }
|
||||
|
||||
return true;
|
||||
|
||||
void Notify(string str) => __instance.Notify(ColorString(Color.yellow, GetString("CheckReportFail") + GetString(str)), 15f);
|
||||
}
|
||||
|
||||
public static void AfterReportTasks(PlayerControl player, NetworkedPlayerInfo target, bool force = false)
|
||||
@@ -1996,7 +2018,7 @@ internal static class CoEnterVentPatch
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Rhapsode.CheckAbilityUse(__instance.myPlayer) || Stasis.IsTimeFrozen)
|
||||
if (!Rhapsode.CheckAbilityUse(__instance.myPlayer) || Stasis.IsTimeFrozen || TimeMaster.Rewinding)
|
||||
{
|
||||
LateTask.New(() => __instance.RpcBootFromVent(id), 0.5f, log: false);
|
||||
return true;
|
||||
|
||||
@@ -367,7 +367,7 @@ public static class SabotageSystemTypeRepairDamagePatch
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Rhapsode.CheckAbilityUse(player) || Stasis.IsTimeFrozen) return false;
|
||||
if (!Rhapsode.CheckAbilityUse(player) || Stasis.IsTimeFrozen || TimeMaster.Rewinding) return false;
|
||||
|
||||
if (player.Is(Team.Impostor) && !player.IsAlive() && Options.DeadImpCantSabotage.GetBool()) return false;
|
||||
if (!player.Is(Team.Impostor) && !player.IsAlive()) return false;
|
||||
|
||||
@@ -778,7 +778,7 @@
|
||||
"SpiritualistInfoLong": "(Crewmates):\\nAs the Spiritualist, you will get an arrow pointing to the ghost of the body that was reported in the last meeting.\\n\\nIf the player was good, it may lead you to an Impostor or neutral killer.\\nIf the player was bad, it may just lead you to a fellow crewmate.",
|
||||
"ChameleonInfoLong": "(Crewmates):\\nAs the Chameleon, you can vent to disguise (invisible). While disguising, nobody can interact with you. Venting while disguised will reveal yourself.",
|
||||
"ParityCopInfoLong": "(Crewmates):\\nTo check if two players are on the same team, use the command /cp [player id 1] [player id 2]. Neutral killers are counted as part of the same team. Non-Killing Neutrals are counted as separate teams. The Trickster is counted as crew, and Madmates are counted as Impostor. The Lawyer/Follower and its target count as the same team. You will receive an affirmation message if they are in the same team or a denial message if they are not.",
|
||||
"TimeMasterInfoLong": "(Crewmates):\\nAs the Time Master, use the vents/pet to mark everyone's position. When using the ability again, every alive player will be teleported back to the marked positions.\\n\\nDuring the ability duration, and the Time Master gains a time shield, which protects them from death.",
|
||||
"TimeMasterInfoLong": "(Crewmates):\\nAs the Time Master, use the vents/pet to rewind time to a few seconds before. Players will be teleported back to their positions from that time. Players who died within that time will be revived.",
|
||||
"CrusaderInfoLong": "(Crewmates):\\nAs the Crusader, use your kill button to crusade a player. If that player gets attacked, you'll kill the attacker.",
|
||||
"ArsonistInfoLong": "(Neutrals):\\nArsonist can douse by clicking the kill button on a player and following them for a few seconds. When dousing starts, successful dousing is indicated by a shield animation (only visible to themselves). When the Arsonist douses all surviving players, they can vent/pet their pet to start a fire and win alone. If player name shows 「△」, they're being doused; if it shows 「▲」, they're completely doused.",
|
||||
"JesterInfoLong": "(Neutrals):\\nJester wins alone if voted out but loses if still alive at the end. Jester, Executioner, and Innocent can win together.",
|
||||
@@ -1780,6 +1780,15 @@
|
||||
"AddictWarning": "<#ffff00>You must vent soon or you'll suicide!</color>",
|
||||
"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>",
|
||||
"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",
|
||||
"PlayerWasRevived": "The player whose body you tried to report was revived",
|
||||
"HypnosisNoMeeting": "Everyone is in hypnosis, no meeting calls are allowed currently",
|
||||
"TargetDisregarded": "The player whose body you tried to report is <#FF6347>Disregarded</color>",
|
||||
"ShipHasNoMoreMeetingsLeft": "The ship has no more emergency meetings left",
|
||||
"UnreportableBody": "This body is unreportable due to a role-specific action",
|
||||
"CheckMurderFail": "<#ff0000>The murder was canceled due to the following reason:\\n</color>",
|
||||
"SameCTATeam": "The player you attacked is in the same custom team as you",
|
||||
"AFKShielded": "According to the Host's settings, AFK players cannot be killed",
|
||||
@@ -5071,6 +5080,8 @@
|
||||
"TimeMasterSkillStop": "Time Shield has ended!",
|
||||
"TimeMasterVentButtonText": "Time Shield",
|
||||
"TimeMasterMaxUses": "(Initial) Max Amount of Ability Uses",
|
||||
"TimeMasterRewindStart": "The {0} is rewinding time!",
|
||||
"RevivedByTimeMaster": "<size=150%><#ffff00>You were revived during the rewind!</color></size>",
|
||||
"UnderdogMaximumPlayersNeededToKill": "Maximum players needed for low cooldown",
|
||||
"UnderdogKillCooldownWithMorePlayersAlive": "Kill Cooldown when more players are alive",
|
||||
"UnluckyTaskSuicideChance": "Chance to suicide from doing tasks",
|
||||
@@ -5509,7 +5520,7 @@
|
||||
"CovenLeaderInfoLong": "(Coven):\\nYou will always spawn if the Coven is enabled. You are the only Coven member who can directly kill. You gain the Necronomicon first. When your powers are enhanced by the Necronomicon, your kill cooldown is halved, and you gain a long kill distance.",
|
||||
"SpellCaster": "Spellcaster",
|
||||
"SpellCasterInfo": "Hex all players to win",
|
||||
"SpellCasterInfoLong": "(Coven):\\nConjure hexes on players; when all players are hexed, the Coven wins after the next meeting if any Spellcasters are still alive (other players will know about this). Upon gaining the Necronomicon, you will spell (like the Witch) all targets that are or become hexed.",
|
||||
"SpellCasterInfoLong": "(Coven):\\nConjure hexes on players; when all players are hexed, the Coven wins after the next meeting if any Spellcasters are still alive (other players will know about this; you appear as hexed if more than half of the alive players are hexed). Upon gaining the Necronomicon, you will spell (like the Witch) all targets that are or become hexed.",
|
||||
"SpellCaster.WinConditionMet": "The Coven will win after this meeting if the {0} is still alive.",
|
||||
"SpellCaster.WinConditionMetTitle": "<#ffff00>⚠</color> The {0} has hexed all non-Coven players! <#ffff00>⚠</color>",
|
||||
"PotionMaster": "Potion Master",
|
||||
|
||||
@@ -57,7 +57,8 @@ public class SpellCaster : Coven
|
||||
|
||||
protected override void OnReceiveNecronomicon()
|
||||
{
|
||||
VisibleHexes = HexedPlayers.Keys.Concat(PlayerIdList).ToHashSet();
|
||||
var appearsAsSpelled = Main.AllAlivePlayerControls.Length / 2 < HexedPlayers.Keys.Count ? PlayerIdList : [];
|
||||
VisibleHexes = HexedPlayers.Keys.Concat(appearsAsSpelled).ToHashSet();
|
||||
HexedPlayers.SetAllValues(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
using EHR.Modules;
|
||||
using UnityEngine;
|
||||
using static EHR.Options;
|
||||
|
||||
namespace EHR.Crewmate;
|
||||
@@ -8,13 +11,26 @@ namespace EHR.Crewmate;
|
||||
internal class TimeMaster : RoleBase
|
||||
{
|
||||
public static bool On;
|
||||
public static Dictionary<byte, Vector2> TimeMasterBackTrack = [];
|
||||
private static readonly Dictionary<byte, long> TimeMasterInProtect = [];
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public static OptionItem TimeMasterRewindTimeLength;
|
||||
public static OptionItem TimeMasterSkillCooldown;
|
||||
public static OptionItem TimeMasterSkillDuration;
|
||||
public static OptionItem TimeMasterMaxUses;
|
||||
public static OptionItem TimeMasterAbilityChargesWhenFinishedTasks;
|
||||
public static OptionItem TimeMasterAbilityUseGainWithEachTaskCompleted;
|
||||
|
||||
private static Dictionary<long, Dictionary<byte, Vector2>> BackTrack = [];
|
||||
public static bool Rewinding;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
SetupRoleOptions(8950, TabGroup.CrewmateRoles, CustomRoles.TimeMaster);
|
||||
|
||||
TimeMasterRewindTimeLength = new IntegerOptionItem(8959, "TimeMasterRewindTimeLength", new(0, 10, 1), 15, TabGroup.CrewmateRoles)
|
||||
.SetParent(CustomRoleSpawnChances[CustomRoles.TimeMaster])
|
||||
.SetValueFormat(OptionFormat.Seconds);
|
||||
|
||||
TimeMasterSkillCooldown = new FloatOptionItem(8960, "TimeMasterSkillCooldown", new(0f, 180f, 1f), 20f, TabGroup.CrewmateRoles)
|
||||
.SetParent(CustomRoleSpawnChances[CustomRoles.TimeMaster])
|
||||
@@ -56,16 +72,6 @@ internal class TimeMaster : RoleBase
|
||||
AURoleOptions.EngineerInVentMaxTime = 1f;
|
||||
}
|
||||
|
||||
public override string GetProgressText(byte playerId, bool comms)
|
||||
{
|
||||
var ProgressText = new StringBuilder();
|
||||
|
||||
ProgressText.Append(Utils.GetAbilityUseLimitDisplay(playerId, TimeMasterInProtect.ContainsKey(playerId)));
|
||||
ProgressText.Append(Utils.GetTaskCount(playerId, comms));
|
||||
|
||||
return ProgressText.ToString();
|
||||
}
|
||||
|
||||
public override void SetButtonTexts(HudManager hud, byte id)
|
||||
{
|
||||
if (UsePets.GetBool())
|
||||
@@ -76,70 +82,89 @@ internal class TimeMaster : RoleBase
|
||||
|
||||
public override void OnPet(PlayerControl pc)
|
||||
{
|
||||
Rewind(pc);
|
||||
if (pc.GetAbilityUseLimit() < 1) return;
|
||||
pc.RpcRemoveAbilityUse();
|
||||
|
||||
Main.Instance.StartCoroutine(Rewind());
|
||||
}
|
||||
|
||||
public override void OnEnterVent(PlayerControl pc, Vent vent)
|
||||
{
|
||||
pc.MyPhysics?.RpcBootFromVent(vent.Id);
|
||||
Rewind(pc);
|
||||
|
||||
if (pc.GetAbilityUseLimit() < 1) return;
|
||||
pc.RpcRemoveAbilityUse();
|
||||
|
||||
Main.Instance.StartCoroutine(Rewind());
|
||||
}
|
||||
|
||||
private static void Rewind(PlayerControl pc)
|
||||
private static System.Collections.IEnumerator Rewind()
|
||||
{
|
||||
if (TimeMasterInProtect.ContainsKey(pc.PlayerId)) return;
|
||||
|
||||
if (pc.GetAbilityUseLimit() >= 1)
|
||||
try
|
||||
{
|
||||
pc.RpcRemoveAbilityUse();
|
||||
TimeMasterInProtect.Remove(pc.PlayerId);
|
||||
TimeMasterInProtect.Add(pc.PlayerId, Utils.TimeStamp);
|
||||
pc.Notify(Translator.GetString("TimeMasterOnGuard"), TimeMasterSkillDuration.GetFloat());
|
||||
Rewinding = true;
|
||||
|
||||
const float delay = 0.3f;
|
||||
long now = Utils.TimeStamp;
|
||||
int length = TimeMasterRewindTimeLength.GetInt();
|
||||
|
||||
Main.AllPlayerSpeed.SetAllValues(Main.MinSpeed);
|
||||
ReportDeadBodyPatch.CanReport.SetAllValues(false);
|
||||
|
||||
string notify = Utils.ColorString(Color.yellow, string.Format(Translator.GetString("TimeMasterRewindStart"), CustomRoles.TimeMaster.ToColoredString()));
|
||||
|
||||
foreach (PlayerControl player in Main.AllPlayerControls)
|
||||
{
|
||||
if (TimeMasterBackTrack.TryGetValue(player.PlayerId, out Vector2 position))
|
||||
{
|
||||
if (!pc.inVent && !pc.inMovingPlat && pc.IsAlive() && !pc.onLadder && pc.MyPhysics != null && !pc.MyPhysics.Animations.IsPlayingAnyLadderAnimation() && !pc.MyPhysics.Animations.IsPlayingEnterVentAnimation() && player.PlayerId != pc.PlayerId)
|
||||
player.TP(position);
|
||||
else if (pc.PlayerId == player.PlayerId) player.MyPhysics?.RpcBootFromVent(Main.LastEnteredVent.TryGetValue(player.PlayerId, out Vent vent) ? vent.Id : player.PlayerId);
|
||||
|
||||
TimeMasterBackTrack.Remove(player.PlayerId);
|
||||
}
|
||||
else TimeMasterBackTrack.Add(player.PlayerId, player.Pos());
|
||||
player.ReactorFlash(flashDuration: length * delay);
|
||||
player.Notify(notify, length * delay);
|
||||
player.MarkDirtySettings();
|
||||
}
|
||||
|
||||
if (pc.IsLocalPlayer())
|
||||
Achievements.Type.APerfectTimeToRewindIt.Complete();
|
||||
}
|
||||
else pc.Notify(Translator.GetString("OutOfAbilityUsesDoMoreTasks"));
|
||||
}
|
||||
for (long i = now - 1; i >= now - length; i--)
|
||||
{
|
||||
if (!BackTrack.TryGetValue(i, out Dictionary<byte, Vector2> track)) continue;
|
||||
|
||||
foreach ((byte playerId, Vector2 pos) in track)
|
||||
{
|
||||
var player = playerId.GetPlayer();
|
||||
if (player == null || !player.IsAlive()) continue;
|
||||
|
||||
public override bool OnCheckMurderAsTarget(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (killer.PlayerId != target.PlayerId && TimeMasterInProtect.TryGetValue(target.PlayerId, out long ts) && ts + TimeMasterSkillDuration.GetInt() >= Utils.TimeStamp)
|
||||
{
|
||||
foreach (PlayerControl player in Main.AllPlayerControls)
|
||||
if (!killer.Is(CustomRoles.Pestilence) && TimeMasterBackTrack.TryGetValue(player.PlayerId, out Vector2 pos))
|
||||
player.TP(pos);
|
||||
}
|
||||
|
||||
yield return new WaitForSeconds(delay);
|
||||
}
|
||||
|
||||
killer.SetKillCooldown(target: target);
|
||||
return false;
|
||||
foreach (DeadBody deadBody in Object.FindObjectsOfType<DeadBody>())
|
||||
{
|
||||
if (!Main.PlayerStates.TryGetValue(deadBody.ParentId, out var ps)) continue;
|
||||
|
||||
if (ps.RealKiller.TimeStamp.AddSeconds(length) >= DateTime.Now)
|
||||
{
|
||||
ps.Player.RpcRevive();
|
||||
ps.Player.TP(deadBody.TruePosition);
|
||||
ps.Player.Notify(Translator.GetString("RevivedByTimeMaster"), 15f);
|
||||
}
|
||||
}
|
||||
|
||||
Main.AllPlayerSpeed.SetAllValues(Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod));
|
||||
ReportDeadBodyPatch.CanReport.SetAllValues(true);
|
||||
Utils.MarkEveryoneDirtySettings();
|
||||
}
|
||||
finally
|
||||
{
|
||||
Rewinding = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnFixedUpdate(PlayerControl player)
|
||||
{
|
||||
byte playerId = player.PlayerId;
|
||||
if (GameStates.IsMeeting || ExileController.Instance || !player.IsAlive()) return;
|
||||
|
||||
if (TimeMasterInProtect.TryGetValue(playerId, out long ttime) && ttime + TimeMasterSkillDuration.GetInt() < Utils.TimeStamp)
|
||||
{
|
||||
TimeMasterInProtect.Remove(playerId);
|
||||
player.RpcResetAbilityCooldown();
|
||||
player.Notify(Translator.GetString("TimeMasterSkillStop"), (int)player.GetAbilityUseLimit());
|
||||
}
|
||||
long now = Utils.TimeStamp;
|
||||
if (BackTrack.ContainsKey(now)) return;
|
||||
|
||||
BackTrack[now] = Main.AllAlivePlayerControls.ToDictionary(x => x.PlayerId, x => x.Pos());
|
||||
}
|
||||
|
||||
public override bool CanUseVent(PlayerControl pc, int ventId)
|
||||
|
||||
@@ -6,16 +6,23 @@ namespace EHR.Impostor;
|
||||
internal class Escapee : RoleBase
|
||||
{
|
||||
public static bool On;
|
||||
public Vector2? EscapeeLocation;
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public static OptionItem EscapeeSSCD;
|
||||
public static OptionItem CanVent;
|
||||
|
||||
public Vector2? EscapeeLocation;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
Options.SetupRoleOptions(3600, TabGroup.ImpostorRoles, CustomRoles.Escapee);
|
||||
|
||||
Options.EscapeeSSCD = new FloatOptionItem(3611, "ShapeshiftCooldown", new(1f, 180f, 1f), 5f, TabGroup.ImpostorRoles)
|
||||
EscapeeSSCD = new FloatOptionItem(3611, "ShapeshiftCooldown", new(1f, 180f, 1f), 5f, TabGroup.ImpostorRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Escapee])
|
||||
.SetValueFormat(OptionFormat.Seconds);
|
||||
|
||||
CanVent = new BooleanOptionItem(3612, "CanVent", false, TabGroup.ImpostorRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Escapee]);
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
@@ -30,6 +37,11 @@ internal class Escapee : RoleBase
|
||||
EscapeeLocation = null;
|
||||
}
|
||||
|
||||
public override bool CanUseImpostorVentButton(PlayerControl pc)
|
||||
{
|
||||
return CanVent.GetBool();
|
||||
}
|
||||
|
||||
public override void SetButtonTexts(HudManager hud, byte id)
|
||||
{
|
||||
if (Options.UsePets.GetBool())
|
||||
@@ -41,12 +53,12 @@ internal class Escapee : RoleBase
|
||||
public override void ApplyGameOptions(IGameOptions opt, byte id)
|
||||
{
|
||||
if (Options.UsePhantomBasis.GetBool())
|
||||
AURoleOptions.PhantomCooldown = Options.EscapeeSSCD.GetFloat();
|
||||
AURoleOptions.PhantomCooldown = EscapeeSSCD.GetFloat();
|
||||
else
|
||||
{
|
||||
if (Options.UsePets.GetBool()) return;
|
||||
|
||||
AURoleOptions.ShapeshifterCooldown = Options.EscapeeSSCD.GetFloat();
|
||||
AURoleOptions.ShapeshifterCooldown = EscapeeSSCD.GetFloat();
|
||||
AURoleOptions.ShapeshifterDuration = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,12 +6,14 @@ internal class Miner : RoleBase
|
||||
{
|
||||
public static bool On;
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public static OptionItem MinerSSCD;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
Options.SetupRoleOptions(3800, TabGroup.ImpostorRoles, CustomRoles.Miner);
|
||||
|
||||
Options.MinerSSCD = new FloatOptionItem(3811, "ShapeshiftCooldown", new(1f, 180f, 1f), 5f, TabGroup.ImpostorRoles)
|
||||
MinerSSCD = new FloatOptionItem(3811, "ShapeshiftCooldown", new(1f, 180f, 1f), 5f, TabGroup.ImpostorRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.Miner])
|
||||
.SetValueFormat(OptionFormat.Seconds);
|
||||
}
|
||||
@@ -29,12 +31,12 @@ internal class Miner : RoleBase
|
||||
public override void ApplyGameOptions(IGameOptions opt, byte playerId)
|
||||
{
|
||||
if (Options.UsePhantomBasis.GetBool())
|
||||
AURoleOptions.PhantomCooldown = Options.MinerSSCD.GetFloat();
|
||||
AURoleOptions.PhantomCooldown = MinerSSCD.GetFloat();
|
||||
else
|
||||
{
|
||||
if (Options.UsePets.GetBool()) return;
|
||||
|
||||
AURoleOptions.ShapeshifterCooldown = Options.MinerSSCD.GetFloat();
|
||||
AURoleOptions.ShapeshifterCooldown = MinerSSCD.GetFloat();
|
||||
AURoleOptions.ShapeshifterDuration = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,12 @@ internal class Revolutionist : RoleBase
|
||||
public static bool On;
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public static OptionItem RevolutionistDrawTime;
|
||||
public static OptionItem RevolutionistCooldown;
|
||||
public static OptionItem RevolutionistDrawCount;
|
||||
public static OptionItem RevolutionistKillProbability;
|
||||
public static OptionItem RevolutionistVentCountDown;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
SetupRoleOptions(18400, TabGroup.NeutralRoles, CustomRoles.Revolutionist);
|
||||
|
||||
Reference in new issue
Block a user