mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-20 01:46:25 +00:00
Add 13 Coven Roles
This commit is contained in:
1 parent
46c86b5962
commit
ced2804d26
29 files changed
+1200
-79
No files matched your search
@@ -366,6 +366,7 @@ public enum CustomRoles
|
||||
PotionMaster,
|
||||
Poache,
|
||||
Reaper,
|
||||
Death, // Reaper changes to Death
|
||||
VoodooMaster,
|
||||
Goddess,
|
||||
Augur,
|
||||
@@ -462,6 +463,7 @@ public enum CustomRoles
|
||||
Guesser,
|
||||
Haste,
|
||||
Haunter, // Ghost role
|
||||
Insane,
|
||||
Introvert,
|
||||
Knighted,
|
||||
LastImpostor,
|
||||
|
||||
@@ -923,6 +923,8 @@ public enum CustomWinner
|
||||
Cherokious = CustomRoles.Cherokious,
|
||||
Specter = CustomRoles.Specter,
|
||||
|
||||
Coven = CustomRoles.CovenLeader,
|
||||
|
||||
Bloodlust = CustomRoles.Bloodlust
|
||||
}
|
||||
|
||||
|
||||
@@ -8,17 +8,16 @@ using EHR.Neutral;
|
||||
|
||||
namespace EHR.Modules;
|
||||
|
||||
// ReSharper disable AccessToModifiedClosure
|
||||
internal static class CustomRoleSelector
|
||||
{
|
||||
public static Dictionary<byte, CustomRoles> RoleResult;
|
||||
|
||||
public static int AddScientistNum;
|
||||
public static int AddEngineerNum;
|
||||
public static int AddShapeshifterNum;
|
||||
public static int AddNoisemakerNum;
|
||||
public static int AddTrackerNum;
|
||||
public static int AddPhantomNum;
|
||||
|
||||
public static List<CustomRoles> AddonRolesList = [];
|
||||
|
||||
public static void SelectCustomRoles()
|
||||
@@ -113,6 +112,7 @@ internal static class CustomRoleSelector
|
||||
case CustomRoles.Convict:
|
||||
case CustomRoles.Refugee:
|
||||
case CustomRoles.CovenLeader:
|
||||
case CustomRoles.Death:
|
||||
case CustomRoles.GM:
|
||||
case CustomRoles.NotAssigned:
|
||||
continue;
|
||||
@@ -194,10 +194,7 @@ internal static class CustomRoleSelector
|
||||
var impLimits = subCategoryLimits.Where(x => x.Key.GetTabFromOptionType() == TabGroup.ImpostorRoles).ToDictionary(x => x.Key, x => x.Value);
|
||||
|
||||
if (impLimits.Count > 0 && impLimits.Sum(x => x.Value) < optImpNum)
|
||||
{
|
||||
// ReSharper disable once AccessToModifiedClosure
|
||||
impLimits.Keys.Do(x => subCategoryLimits[x] = Options.RoleSubCategoryLimits[x][2].GetInt());
|
||||
}
|
||||
}
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
|
||||
@@ -895,7 +892,7 @@ internal static class CustomRoleSelector
|
||||
case CustomRoles.Autopsy when Options.EveryoneSeesDeathReasons.GetBool():
|
||||
case CustomRoles.Mare or CustomRoles.Glow or CustomRoles.Sleep when Main.CurrentMap == MapNames.Fungle:
|
||||
case CustomRoles.Madmate when Options.MadmateSpawnMode.GetInt() != 0:
|
||||
case CustomRoles.Lovers or CustomRoles.LastImpostor or CustomRoles.Workhorse or CustomRoles.Undead:
|
||||
case CustomRoles.Lovers or CustomRoles.LastImpostor or CustomRoles.Workhorse or CustomRoles.Undead or CustomRoles.Insane:
|
||||
case CustomRoles.Nimble or CustomRoles.Physicist or CustomRoles.Bloodlust or CustomRoles.Finder or CustomRoles.Noisy: // Assigned at a different function due to role base change
|
||||
continue;
|
||||
}
|
||||
@@ -917,13 +914,9 @@ internal static class CustomRoleSelector
|
||||
private class RoleAssignInfo(CustomRoles role, int spawnChance, int maxCount)
|
||||
{
|
||||
public CustomRoles Role => role;
|
||||
|
||||
public int SpawnChance => spawnChance;
|
||||
|
||||
public int MaxCount => maxCount;
|
||||
|
||||
public int AssignedCount { get; set; }
|
||||
|
||||
public RoleOptionType OptionType { get; } = role.GetRoleOptionType();
|
||||
}
|
||||
}
|
||||
@@ -327,6 +327,7 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Godfather => CustomRoles.Impostor,
|
||||
CustomRoles.Silencer => Silencer.SilenceMode.GetValue() == 1 ? CustomRoles.Shapeshifter : CustomRoles.Impostor,
|
||||
CustomRoles.NoteKiller => CustomRoles.Crewmate,
|
||||
CustomRoles.Augur => CustomRoles.Crewmate,
|
||||
|
||||
// Vanilla roles (just in case)
|
||||
CustomRoles.ImpostorEHR => CustomRoles.Impostor,
|
||||
@@ -502,6 +503,21 @@ internal static class CustomRolesHelper
|
||||
CustomRoles.Spiritcaller => RoleTypes.Impostor,
|
||||
CustomRoles.Doppelganger => RoleTypes.Impostor,
|
||||
CustomRoles.Wizard => RoleTypes.Shapeshifter,
|
||||
|
||||
CustomRoles.CovenLeader => RoleTypes.Impostor,
|
||||
CustomRoles.SpellCaster => RoleTypes.Impostor,
|
||||
CustomRoles.PotionMaster => RoleTypes.Impostor,
|
||||
CustomRoles.Poache => RoleTypes.Impostor,
|
||||
CustomRoles.Reaper => RoleTypes.Impostor,
|
||||
CustomRoles.Death => RoleTypes.Impostor,
|
||||
CustomRoles.VoodooMaster => RoleTypes.Impostor,
|
||||
CustomRoles.Goddess => RoleTypes.Phantom,
|
||||
CustomRoles.Dreamweaver => RoleTypes.Impostor,
|
||||
CustomRoles.Banshee => RoleTypes.Phantom,
|
||||
CustomRoles.Illusionist => RoleTypes.Shapeshifter,
|
||||
CustomRoles.Timelord => RoleTypes.Impostor,
|
||||
CustomRoles.Enchanter => RoleTypes.Impostor,
|
||||
|
||||
_ => RoleTypes.GuardianAngel
|
||||
};
|
||||
}
|
||||
@@ -588,7 +604,7 @@ internal static class CustomRolesHelper
|
||||
|
||||
public static bool IsSnitchTarget(this CustomRoles role)
|
||||
{
|
||||
return role.IsNK() || role.Is(Team.Impostor);
|
||||
return role.IsNK() || role.Is(Team.Impostor) || role.IsCoven();
|
||||
}
|
||||
|
||||
public static bool IsGhostRole(this CustomRoles role)
|
||||
@@ -896,6 +912,7 @@ internal static class CustomRolesHelper
|
||||
public static bool ForceCancelShapeshift(this CustomRoles role)
|
||||
{
|
||||
return role is
|
||||
CustomRoles.Illusionist or
|
||||
CustomRoles.Swapster or
|
||||
CustomRoles.Echo or
|
||||
CustomRoles.Hangman or
|
||||
@@ -1296,7 +1313,9 @@ internal static class CustomRolesHelper
|
||||
? type
|
||||
: role.Is(Team.Impostor) || role == CustomRoles.Trickster
|
||||
? CountTypes.Impostor
|
||||
: CountTypes.Crew
|
||||
: role.IsCoven()
|
||||
? CountTypes.Coven
|
||||
: CountTypes.Crew
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1781,5 +1800,7 @@ public enum CountTypes
|
||||
Glitch,
|
||||
Arsonist,
|
||||
Cherokious,
|
||||
Sheriff
|
||||
Sheriff,
|
||||
|
||||
Coven
|
||||
}
|
||||
+9
-21
@@ -493,7 +493,7 @@ internal static class EAC
|
||||
|
||||
internal static bool CheckInvalidSabotage(SystemTypes systemType, PlayerControl player, byte amount)
|
||||
{
|
||||
if (player.IsHost() || !AmongUsClient.Instance.AmHost) return false;
|
||||
if (player.IsModClient() || !AmongUsClient.Instance.AmHost) return false;
|
||||
|
||||
if ((GameStates.IsMeeting && MeetingHud.Instance.state != MeetingHud.VoteStates.Animating) || ExileController.Instance)
|
||||
{
|
||||
@@ -504,22 +504,19 @@ internal static class EAC
|
||||
return true;
|
||||
}
|
||||
|
||||
byte Mapid = Main.NormalOptions.MapId;
|
||||
byte mapid = Main.NormalOptions.MapId;
|
||||
|
||||
switch (systemType)
|
||||
{
|
||||
case SystemTypes.LifeSupp:
|
||||
if (Mapid != 0 && Mapid != 1 && Mapid != 3) goto Cheat;
|
||||
|
||||
if (mapid != 0 && mapid != 1 && mapid != 3) goto Cheat;
|
||||
if (amount != 64 && amount != 65) goto Cheat;
|
||||
|
||||
break;
|
||||
case SystemTypes.Comms:
|
||||
switch (amount)
|
||||
{
|
||||
case 0:
|
||||
if (Mapid is 1 or 5) goto Cheat;
|
||||
|
||||
if (mapid is 1 or 5) goto Cheat;
|
||||
break;
|
||||
case 64:
|
||||
case 65:
|
||||
@@ -527,8 +524,7 @@ internal static class EAC
|
||||
case 33:
|
||||
case 16:
|
||||
case 17:
|
||||
if (Mapid is not (1 or 5)) goto Cheat;
|
||||
|
||||
if (mapid is not (1 or 5)) goto Cheat;
|
||||
break;
|
||||
default:
|
||||
goto Cheat;
|
||||
@@ -536,28 +532,20 @@ internal static class EAC
|
||||
|
||||
break;
|
||||
case SystemTypes.Electrical:
|
||||
if (Mapid == 5) goto Cheat;
|
||||
|
||||
if (mapid == 5) goto Cheat;
|
||||
if (amount >= 5) goto Cheat;
|
||||
|
||||
break;
|
||||
case SystemTypes.Laboratory:
|
||||
if (Mapid != 2) goto Cheat;
|
||||
|
||||
if (mapid != 2) goto Cheat;
|
||||
if (amount is not (64 or 65 or 32 or 33)) goto Cheat;
|
||||
|
||||
break;
|
||||
case SystemTypes.Reactor:
|
||||
if (Mapid is 2 or 4) goto Cheat;
|
||||
|
||||
if (mapid is 2 or 4) goto Cheat;
|
||||
if (amount is not (64 or 65 or 32 or 33)) goto Cheat;
|
||||
|
||||
break;
|
||||
case SystemTypes.HeliSabotage:
|
||||
if (Mapid != 4) goto Cheat;
|
||||
|
||||
if (mapid != 4) goto Cheat;
|
||||
if (amount is not (64 or 65 or 16 or 17 or 32 or 33)) goto Cheat;
|
||||
|
||||
break;
|
||||
case SystemTypes.MushroomMixupSabotage:
|
||||
goto Cheat;
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Reflection;
|
||||
using AmongUs.GameOptions;
|
||||
using EHR.AddOns.Crewmate;
|
||||
using EHR.AddOns.Impostor;
|
||||
using EHR.Coven;
|
||||
using EHR.Crewmate;
|
||||
using EHR.Impostor;
|
||||
using EHR.Modules;
|
||||
@@ -1660,7 +1661,7 @@ internal static class ExtendedPlayerControl
|
||||
|
||||
public static bool IsSnitchTarget(this PlayerControl player)
|
||||
{
|
||||
return player.Is(CustomRoles.Bloodlust) || Framer.FramedPlayers.Contains(player.PlayerId) || player.GetCustomRole().IsSnitchTarget();
|
||||
return player.Is(CustomRoles.Bloodlust) || Framer.FramedPlayers.Contains(player.PlayerId) || Enchanter.EnchantedPlayers.Contains(player.PlayerId) || player.GetCustomRole().IsSnitchTarget();
|
||||
}
|
||||
|
||||
public static bool IsMadmate(this PlayerControl player)
|
||||
|
||||
@@ -5,6 +5,7 @@ using AmongUs.GameOptions;
|
||||
using EHR.AddOns.Common;
|
||||
using EHR.AddOns.Crewmate;
|
||||
using EHR.AddOns.GhostRoles;
|
||||
using EHR.Coven;
|
||||
using EHR.Crewmate;
|
||||
using EHR.Modules;
|
||||
using EHR.Neutral;
|
||||
@@ -331,6 +332,9 @@ public class PlayerState(byte playerId)
|
||||
|
||||
if (AmongUsClient.Instance.AmHost)
|
||||
{
|
||||
if (Enchanter.EnchantedPlayers.Contains(PlayerId))
|
||||
deathReason = Enum.GetValues<DeathReason>().RandomElement();
|
||||
|
||||
RPC.SendDeathReason(PlayerId, deathReason);
|
||||
Utils.CheckAndSpawnAdditionalRefugee(Utils.GetPlayerInfoById(PlayerId));
|
||||
|
||||
@@ -354,9 +358,9 @@ public class PlayerState(byte playerId)
|
||||
return IsDead && RealKiller.TimeStamp != DateTime.MinValue ? RealKiller.ID : byte.MaxValue;
|
||||
}
|
||||
|
||||
public int GetKillCount(bool ExcludeSelfKill = false)
|
||||
public int GetKillCount(bool excludeSelfKill = false)
|
||||
{
|
||||
return Main.PlayerStates.Values.Where(state => !(ExcludeSelfKill && state.PlayerId == PlayerId) && state.GetRealKiller() == PlayerId).ToArray().Length;
|
||||
return Main.PlayerStates.Values.Count(state => !(excludeSelfKill && state.PlayerId == PlayerId) && state.GetRealKiller() == PlayerId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,8 @@ public static class GuessManager
|
||||
(pc.IsNeutralKiller() && !Options.NeutralKillersCanGuess.GetBool() && !pc.Is(CustomRoles.Guesser)) ||
|
||||
(pc.GetCustomRole().IsNonNK() && !Options.PassiveNeutralsCanGuess.GetBool() && !pc.Is(CustomRoles.Guesser) && pc.GetCustomRole() is not CustomRoles.Doomsayer and not CustomRoles.NecroGuesser) ||
|
||||
(pc.Is(CustomRoles.Lyncher) && Lyncher.GuessMode.GetValue() == 0) ||
|
||||
(Options.GuesserNumRestrictions.GetBool() && !Guessers.Contains(pc.PlayerId)))
|
||||
(Options.GuesserNumRestrictions.GetBool() && !Guessers.Contains(pc.PlayerId)) ||
|
||||
(pc.Is(Team.Coven) && !pc.Is(CustomRoles.Augur)))
|
||||
{
|
||||
if ((pc.Is(CustomRoles.Madmate) || pc.IsConverted()) && Options.BetrayalAddonsCanGuess.GetBool()) goto SkipCheck;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using AmongUs.GameOptions;
|
||||
using EHR.Coven;
|
||||
using EHR.Crewmate;
|
||||
using EHR.Impostor;
|
||||
using HarmonyLib;
|
||||
@@ -57,6 +58,8 @@ public static class MeetingTimeManager
|
||||
BonusMeetingTime += TimeThief.TotalDecreasedMeetingTime();
|
||||
}
|
||||
|
||||
if (Timelord.On) { BonusMeetingTime += -Timelord.GetTotalStolenTime(); }
|
||||
|
||||
if (TimeManager.PlayerIdList.Count > 0)
|
||||
{
|
||||
MeetingTimeMinTimeManager = TimeManager.MadMinMeetingTimeLimit.GetInt();
|
||||
@@ -67,9 +70,7 @@ public static class MeetingTimeManager
|
||||
int TotalMeetingTime = DiscussionTime + VotingTime;
|
||||
|
||||
if (TimeManager.PlayerIdList.Count > 0) BonusMeetingTime = Math.Clamp(TotalMeetingTime + BonusMeetingTime, MeetingTimeMinTimeManager, MeetingTimeMax) - TotalMeetingTime;
|
||||
|
||||
if (TimeThief.PlayerIdList.Count > 0) BonusMeetingTime = Math.Clamp(TotalMeetingTime + BonusMeetingTime, MeetingTimeMinTimeThief, MeetingTimeMax) - TotalMeetingTime;
|
||||
|
||||
if (TimeManager.PlayerIdList.Count == 0 && TimeThief.PlayerIdList.Count == 0) BonusMeetingTime = Math.Clamp(TotalMeetingTime + BonusMeetingTime, MeetingTimeMinTimeThief, MeetingTimeMax) - TotalMeetingTime;
|
||||
|
||||
if (BonusMeetingTime >= 0)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Linq;
|
||||
using EHR.AddOns.Common;
|
||||
using EHR.AddOns.GhostRoles;
|
||||
using EHR.Coven;
|
||||
using EHR.Crewmate;
|
||||
using EHR.Impostor;
|
||||
using EHR.Modules;
|
||||
@@ -157,6 +158,11 @@ public static class NameColorManager
|
||||
CustomRoles.Wasp when seerRoleClass is Wasp wasp && (wasp.DelayedKills.ContainsKey(target.PlayerId) || wasp.MeetingKills.Contains(target.PlayerId)) => "000000",
|
||||
CustomRoles.God when God.KnowInfo.GetValue() == 1 => target.GetTeam().GetTextColor(),
|
||||
CustomRoles.Curser when ((Curser)seerRoleClass).KnownFactionPlayers.Contains(target.PlayerId) => target.GetTeam().GetTextColor(),
|
||||
CustomRoles.Poache when Poache.PoachedPlayers.Contains(target.PlayerId) => "000000",
|
||||
CustomRoles.Reaper when ((Reaper)seerRoleClass).CursedPlayers.Contains(target.PlayerId) => "000000",
|
||||
CustomRoles.Dreamweaver when ((Dreamweaver)seerRoleClass).InsanePlayers.Contains(target.PlayerId) || target.Is(CustomRoles.Insane) => "000000",
|
||||
CustomRoles.Banshee when ((Banshee)seerRoleClass).ScreechedPlayers.Contains(target.PlayerId) => "000000",
|
||||
CustomRoles.Illusionist when ((Illusionist)seerRoleClass).SampledPlayerId == target.PlayerId => "000000",
|
||||
_ => color
|
||||
};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ using EHR.AddOns.Common;
|
||||
using EHR.AddOns.Crewmate;
|
||||
using EHR.AddOns.GhostRoles;
|
||||
using EHR.AddOns.Impostor;
|
||||
using EHR.Coven;
|
||||
using EHR.Crewmate;
|
||||
using EHR.Impostor;
|
||||
using EHR.Modules;
|
||||
@@ -2116,6 +2117,7 @@ public static class Utils
|
||||
{
|
||||
SelfMark.Append(Witch.GetSpelledMark(seer.PlayerId, isForMeeting));
|
||||
if (isForMeeting) SelfMark.Append(Wasp.GetStungMark(seer.PlayerId));
|
||||
if (isForMeeting) SelfMark.Append(SpellCaster.IsSpelled(seer.PlayerId) ? ColorString(Team.Coven.GetColor(), "\u25c0") : string.Empty);
|
||||
}
|
||||
|
||||
GameMode:
|
||||
@@ -2276,6 +2278,7 @@ public static class Utils
|
||||
|
||||
TargetMark.Append(Witch.GetSpelledMark(target.PlayerId, isForMeeting));
|
||||
if (isForMeeting) TargetMark.Append(Wasp.GetStungMark(target.PlayerId));
|
||||
if (isForMeeting) TargetMark.Append(SpellCaster.IsSpelled(seer.PlayerId) ? ColorString(Team.Coven.GetColor(), "\u25c0") : string.Empty);
|
||||
|
||||
if (target.Is(CustomRoles.SuperStar) && Options.EveryOneKnowSuperStar.GetBool())
|
||||
TargetMark.Append(ColorString(GetRoleColor(CustomRoles.SuperStar), "★"));
|
||||
@@ -2895,6 +2898,10 @@ public static class Utils
|
||||
case CustomRoles.Medic:
|
||||
Medic.IsDead(target);
|
||||
break;
|
||||
case CustomRoles.Dreamweaver:
|
||||
((Dreamweaver)Main.PlayerStates[target.PlayerId].Role).InsanePlayers.Clear();
|
||||
Main.PlayerStates.Values.Do(x => x.RemoveSubRole(CustomRoles.Insane));
|
||||
break;
|
||||
}
|
||||
|
||||
if (target == null) return;
|
||||
@@ -2930,6 +2937,7 @@ public static class Utils
|
||||
|
||||
Adventurer.OnAnyoneDead(target);
|
||||
Whisperer.OnAnyoneDied(target);
|
||||
Reaper.OnAnyoneDead(target);
|
||||
|
||||
if (QuizMaster.On) QuizMaster.Data.NumPlayersDeadThisRound++;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AmongUs.Data;
|
||||
using EHR.Coven;
|
||||
using EHR.Impostor;
|
||||
using EHR.Patches;
|
||||
using HarmonyLib;
|
||||
@@ -203,6 +204,9 @@ public static class ChatManager
|
||||
|
||||
if (CustomGameMode.FFA.IsActiveOrIntegrated() && GameStates.InGame && !message.StartsWith('/'))
|
||||
FFAManager.UpdateLastChatMessage(player.GetRealName(), message);
|
||||
|
||||
if (CustomGameMode.Standard.IsActiveOrIntegrated() && GameStates.InGame && operate != 1 && Banshee.On)
|
||||
Banshee.OnReceiveChat();
|
||||
}
|
||||
|
||||
public static void SendPreviousMessagesToAll(bool clear = false)
|
||||
@@ -252,10 +256,10 @@ public static class ChatManager
|
||||
finally { ChatUpdatePatch.DoBlockChat = false; }
|
||||
}
|
||||
|
||||
private static void SendRPC(InnerNetObject senderPlayer, string senderMessage)
|
||||
private static void SendRPC(InnerNetObject senderPlayer, string senderMessage, int targetClientId = -1)
|
||||
{
|
||||
var writer = CustomRpcSender.Create("MessagesToSend");
|
||||
writer.StartMessage();
|
||||
writer.StartMessage(targetClientId);
|
||||
|
||||
writer.StartRpc(senderPlayer.NetId, (byte)RpcCalls.SendChat)
|
||||
.Write(senderMessage)
|
||||
@@ -264,4 +268,21 @@ public static class ChatManager
|
||||
writer.EndMessage();
|
||||
writer.SendMessage();
|
||||
}
|
||||
|
||||
public static void ClearChatForSpecificPlayer(PlayerControl target)
|
||||
{
|
||||
var sender = Main.AllAlivePlayerControls.MinBy(x => x.PlayerId) ?? Main.AllPlayerControls.MinBy(x => x.PlayerId) ?? PlayerControl.LocalPlayer;
|
||||
if (sender == null) return;
|
||||
|
||||
if (!target.IsLocalPlayer())
|
||||
{
|
||||
var clientId = target.GetClientId();
|
||||
if (clientId == -1) return;
|
||||
|
||||
Loop.Times(20, _ => SendRPC(sender, Utils.EmptyMessage, clientId));
|
||||
return;
|
||||
}
|
||||
|
||||
Loop.Times(20, _ => DestroyableSingleton<HudManager>.Instance.Chat.AddChat(sender, Utils.EmptyMessage));
|
||||
}
|
||||
}
|
||||
@@ -505,15 +505,16 @@ internal static class GameEndChecker
|
||||
|
||||
int Imp = AlivePlayersCount(CountTypes.Impostor);
|
||||
int Crew = AlivePlayersCount(CountTypes.Crew) + sheriffCount;
|
||||
int Coven = AlivePlayersCount(CountTypes.Coven);
|
||||
|
||||
Dictionary<(CustomRoles? ROLE, CustomWinner WINNER), int> roleCounts = [];
|
||||
Dictionary<(CustomRoles? Role, CustomWinner Winner), int> roleCounts = [];
|
||||
|
||||
foreach (CustomRoles role in Enum.GetValues<CustomRoles>())
|
||||
{
|
||||
if ((!role.IsNK() && role is not CustomRoles.Bloodlust and not CustomRoles.Gaslighter) || role.IsMadmate() || role is CustomRoles.Sidekick) continue;
|
||||
|
||||
CountTypes countTypes = role.GetCountTypes();
|
||||
if (countTypes is CountTypes.Crew or CountTypes.Impostor or CountTypes.None or CountTypes.OutOfGame or CountTypes.CustomTeam) continue;
|
||||
if (countTypes is CountTypes.Crew or CountTypes.Impostor or CountTypes.None or CountTypes.OutOfGame or CountTypes.CustomTeam or CountTypes.Coven) continue;
|
||||
|
||||
CustomRoles? keyRole = role.IsRecruitingRole() ? null : role;
|
||||
var keyWinner = (CustomWinner)role;
|
||||
@@ -528,18 +529,14 @@ internal static class GameEndChecker
|
||||
{
|
||||
if (!x.Is(CustomRoles.DualPersonality)) continue;
|
||||
|
||||
if (x.Is(Team.Impostor))
|
||||
Imp++;
|
||||
if (x.Is(Team.Impostor)) Imp++;
|
||||
else if (x.Is(Team.Crewmate)) Crew++;
|
||||
else if (x.Is(Team.Coven)) Coven++;
|
||||
|
||||
if (x.Is(CustomRoles.Charmed)) roleCounts[(null, CustomWinner.Succubus)]++;
|
||||
|
||||
if (x.Is(CustomRoles.Undead)) roleCounts[(null, CustomWinner.Necromancer)]++;
|
||||
|
||||
if (x.Is(CustomRoles.Sidekick)) roleCounts[(null, CustomWinner.Jackal)]++;
|
||||
|
||||
if (x.Is(CustomRoles.Recruit)) roleCounts[(null, CustomWinner.Jackal)]++;
|
||||
|
||||
if (x.Is(CustomRoles.Contagious)) roleCounts[(null, CustomWinner.Virus)]++;
|
||||
}
|
||||
}
|
||||
@@ -551,26 +548,39 @@ internal static class GameEndChecker
|
||||
|
||||
if (totalNKAlive == 0)
|
||||
{
|
||||
if (Crew == 0 && Imp == 0)
|
||||
if (Coven == 0)
|
||||
{
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
winner = CustomWinner.None;
|
||||
}
|
||||
else if (Crew <= Imp)
|
||||
{
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
winner = CustomWinner.Impostor;
|
||||
}
|
||||
else if (Imp == 0)
|
||||
{
|
||||
reason = GameOverReason.HumansByVote;
|
||||
winner = CustomWinner.Crewmate;
|
||||
if (Crew == 0 && Imp == 0)
|
||||
{
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
winner = CustomWinner.None;
|
||||
}
|
||||
else if (Crew <= Imp)
|
||||
{
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
winner = CustomWinner.Impostor;
|
||||
}
|
||||
else if (Imp == 0)
|
||||
{
|
||||
reason = GameOverReason.HumansByVote;
|
||||
winner = CustomWinner.Crewmate;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
Logger.Info($"Crew: {Crew}, Imp: {Imp}, Coven: {Coven}", "CheckGameEndPatch.CheckGameEndByLivingPlayers");
|
||||
ResetAndSetWinner((CustomWinner)winner);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
{
|
||||
if (Imp >= 1) return false;
|
||||
if (Crew > Coven) return false;
|
||||
|
||||
Logger.Info($"Crew: {Crew}, Imp: {Imp}, Coven: {Coven}", "CheckGameEndPatch.CheckGameEndByLivingPlayers");
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
ResetAndSetWinner(CustomWinner.Coven);
|
||||
}
|
||||
|
||||
Logger.Info($"Crew: {Crew}, Imp: {Imp}", "CheckGameEndPatch.CheckGameEndByLivingPlayers");
|
||||
ResetAndSetWinner((CustomWinner)winner);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -583,10 +593,9 @@ internal static class GameEndChecker
|
||||
|
||||
switch (aliveCounts.Count)
|
||||
{
|
||||
// There are multiple types of NKs alive, game must continue
|
||||
case > 1:
|
||||
return false;
|
||||
// There are multiple types of NKs alive, the game must continue
|
||||
// If the Sheriff keeps the game going, the game must continue
|
||||
case > 1:
|
||||
case 1 when Sheriff.KeepsGameGoing.GetBool() && sheriffCount > 0:
|
||||
return false;
|
||||
// There is only one type of NK alive, they've won
|
||||
@@ -594,11 +603,11 @@ internal static class GameEndChecker
|
||||
{
|
||||
if (aliveCounts[0] != roleCounts.Values.Max()) Logger.Warn("There is something wrong here.", "CheckGameEndPatch");
|
||||
|
||||
foreach (KeyValuePair<(CustomRoles? ROLE, CustomWinner WINNER), int> keyValuePair in roleCounts.Where(keyValuePair => keyValuePair.Value == aliveCounts[0]))
|
||||
foreach (KeyValuePair<(CustomRoles? Role, CustomWinner Winner), int> keyValuePair in roleCounts.Where(keyValuePair => keyValuePair.Value == aliveCounts[0]))
|
||||
{
|
||||
reason = GameOverReason.ImpostorByKill;
|
||||
winner = keyValuePair.Key.WINNER;
|
||||
rl = keyValuePair.Key.ROLE;
|
||||
winner = keyValuePair.Key.Winner;
|
||||
rl = keyValuePair.Key.Role;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
using EHR.AddOns.Common;
|
||||
using EHR.Coven;
|
||||
using EHR.Crewmate;
|
||||
using EHR.Impostor;
|
||||
using EHR.Modules;
|
||||
@@ -169,6 +170,7 @@ internal static class CheckForEndVotingPatch
|
||||
bool canVote = !(CheckRole(ps.TargetPlayerId, CustomRoles.Glitch) && !Glitch.CanVote.GetBool());
|
||||
if (CheckRole(ps.TargetPlayerId, CustomRoles.Shifter) && !Shifter.CanVote.GetBool()) canVote = false;
|
||||
if (ps.VotedFor.GetPlayer() != null && CheckRole(ps.VotedFor, CustomRoles.Zombie)) canVote = false;
|
||||
if (Poache.PoachedPlayers.Contains(ps.TargetPlayerId)) canVote = false;
|
||||
|
||||
switch (Main.PlayerStates[ps.TargetPlayerId].Role)
|
||||
{
|
||||
@@ -563,6 +565,7 @@ internal static class CheckForEndVotingPatch
|
||||
if (Virus.PlayerIdList.Count > 0) Virus.OnCheckForEndVoting(deathReason, playerIds);
|
||||
if (deathReason == PlayerState.DeathReason.Vote) Gaslighter.OnExile(playerIds);
|
||||
if (Wasp.On && deathReason == PlayerState.DeathReason.Vote) Wasp.OnExile(playerIds);
|
||||
if (CustomRoles.SpellCaster.RoleExist()) SpellCaster.OnExile(playerIds);
|
||||
|
||||
foreach (byte playerId in playerIds)
|
||||
{
|
||||
@@ -638,6 +641,8 @@ internal static class ExtendedMeetingHud
|
||||
Collector.CollectorVotes(target, ps);
|
||||
}
|
||||
|
||||
if (Poache.PoachedPlayers.Contains(ps.TargetPlayerId)) VoteNum = 0;
|
||||
|
||||
if (CheckForEndVotingPatch.CheckRole(ps.TargetPlayerId, CustomRoles.Mayor)) VoteNum += Mayor.MayorAdditionalVote.GetInt();
|
||||
if (CheckForEndVotingPatch.CheckRole(ps.TargetPlayerId, CustomRoles.Knighted)) VoteNum += 1;
|
||||
if (CheckForEndVotingPatch.CheckRole(ps.TargetPlayerId, CustomRoles.Glitch) && !Glitch.CanVote.GetBool()) VoteNum = 0;
|
||||
@@ -1024,6 +1029,7 @@ internal static class MeetingHudStartPatch
|
||||
|
||||
sb.Append(Witch.GetSpelledMark(target.PlayerId, true));
|
||||
sb.Append(Wasp.GetStungMark(target.PlayerId));
|
||||
sb.Append(SpellCaster.IsSpelled(seer.PlayerId) ? Utils.ColorString(Team.Coven.GetColor(), "\u25c0") : string.Empty);
|
||||
|
||||
if (target.Is(CustomRoles.SuperStar) && Options.EveryOneKnowSuperStar.GetBool()) sb.Append(Utils.ColorString(Utils.GetRoleColor(CustomRoles.SuperStar), "★"));
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using EHR.AddOns.Common;
|
||||
using EHR.AddOns.Crewmate;
|
||||
using EHR.AddOns.GhostRoles;
|
||||
using EHR.AddOns.Impostor;
|
||||
using EHR.Coven;
|
||||
using EHR.Crewmate;
|
||||
using EHR.GameMode.HideAndSeekRoles;
|
||||
using EHR.Impostor;
|
||||
@@ -396,6 +397,7 @@ internal static class CheckMurderPatch
|
||||
Randomizer.IsShielded(target) ||
|
||||
Aid.ShieldedPlayers.ContainsKey(target.PlayerId) ||
|
||||
Gaslighter.IsShielded(target) ||
|
||||
!PotionMaster.OnAnyoneCheckMurder(target) ||
|
||||
!Grappler.OnAnyoneCheckMurder(target) ||
|
||||
!Adventurer.OnAnyoneCheckMurder(target) ||
|
||||
!Sentinel.OnAnyoneCheckMurder(killer) ||
|
||||
@@ -968,6 +970,9 @@ internal static class ReportDeadBodyPatch
|
||||
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 (!Hypnotist.OnAnyoneReport())
|
||||
{
|
||||
@@ -977,11 +982,7 @@ internal static class ReportDeadBodyPatch
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Occultist.OnAnyoneReportDeadBody(target)) return false;
|
||||
|
||||
if (!Altruist.OnAnyoneCheckReportDeadBody(__instance, target)) return false;
|
||||
|
||||
if (!BoobyTrap.OnAnyoneCheckReportDeadBody(__instance, target)) return false;
|
||||
if (killer != null && killer.Is(CustomRoles.Goddess)) return false;
|
||||
|
||||
if (!Main.PlayerStates[__instance.PlayerId].Role.CheckReportDeadBody(__instance, target, killer)) return false;
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace EHR.Coven;
|
||||
|
||||
public class Augur : Coven
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
public override NecronomiconReceivePriorities NecronomiconReceivePriority => NecronomiconReceivePriorities.Never;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(650070);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
using EHR.Modules;
|
||||
using Hazel;
|
||||
|
||||
namespace EHR.Coven;
|
||||
|
||||
public class Banshee : Coven
|
||||
{
|
||||
public static bool On;
|
||||
private static List<Banshee> Instances = [];
|
||||
|
||||
private static OptionItem AbilityCooldown;
|
||||
private static OptionItem Radius;
|
||||
|
||||
private byte BansheeId;
|
||||
|
||||
public HashSet<byte> ScreechedPlayers = [];
|
||||
|
||||
public override NecronomiconReceivePriorities NecronomiconReceivePriority => NecronomiconReceivePriorities.Random;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(650090)
|
||||
.AutoSetupOption(ref AbilityCooldown, 30f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref Radius, 3f, new FloatValueRule(0.25f, 10f, 0.25f), OptionFormat.Multiplier);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
Instances = [];
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
ScreechedPlayers = [];
|
||||
BansheeId = playerId;
|
||||
Instances.Add(this);
|
||||
}
|
||||
|
||||
public override bool OnVanish(PlayerControl pc)
|
||||
{
|
||||
var radius = Radius.GetFloat();
|
||||
var pos = pc.Pos();
|
||||
IEnumerable<PlayerControl> nearbyPlayers = Utils.GetPlayersInRadius(radius, pos).Without(pc);
|
||||
|
||||
if (!HasNecronomicon) ScreechedPlayers = nearbyPlayers.Select(x => x.PlayerId).ToHashSet();
|
||||
else nearbyPlayers.Do(x => x.Suicide(realKiller: pc));
|
||||
|
||||
if (ScreechedPlayers.Count > 0)
|
||||
{
|
||||
var w = Utils.CreateRPC(CustomRPC.SyncRoleData);
|
||||
w.Write(BansheeId);
|
||||
w.WritePacked(1);
|
||||
w.WritePacked(ScreechedPlayers.Count);
|
||||
ScreechedPlayers.Do(x => w.Write(x));
|
||||
|
||||
Utils.NotifyRoles(SpecifySeer: pc);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void ApplyGameOptions(IGameOptions opt, byte playerId)
|
||||
{
|
||||
AURoleOptions.PhantomCooldown = AbilityCooldown.GetFloat();
|
||||
AURoleOptions.PhantomDuration = 1f;
|
||||
}
|
||||
|
||||
public override void AfterMeetingTasks()
|
||||
{
|
||||
ScreechedPlayers.Clear();
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, BansheeId, 2);
|
||||
}
|
||||
|
||||
public void ReceiveRPC(MessageReader reader)
|
||||
{
|
||||
switch (reader.ReadPackedInt32())
|
||||
{
|
||||
case 1:
|
||||
ScreechedPlayers.Clear();
|
||||
int count = reader.ReadPackedInt32();
|
||||
Loop.Times(count, _ => ScreechedPlayers.Add(reader.ReadByte()));
|
||||
break;
|
||||
case 2:
|
||||
ScreechedPlayers.Clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void OnReceiveChat()
|
||||
{
|
||||
var screechedPlayers = Instances.SelectMany(x => x.ScreechedPlayers).ToValidPlayers().ToHashSet();
|
||||
if (screechedPlayers.Count == 0) return;
|
||||
|
||||
screechedPlayers.Do(ChatManager.ClearChatForSpecificPlayer);
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,19 @@ public class CovenLeader : Coven
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
private static OptionItem KillCooldown;
|
||||
|
||||
private byte CovenLeaderId;
|
||||
|
||||
public override NecronomiconReceivePriorities NecronomiconReceivePriority => NecronomiconReceivePriorities.First;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public override void SetupCustomOption() { }
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(650000)
|
||||
.AutoSetupOption(ref KillCooldown, 30f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
@@ -20,6 +28,7 @@ public class CovenLeader : Coven
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
CovenLeaderId = playerId;
|
||||
}
|
||||
|
||||
public override bool CanUseKillButton(PlayerControl pc)
|
||||
@@ -36,4 +45,9 @@ public class CovenLeader : Coven
|
||||
{
|
||||
Main.AllPlayerKillCooldown[id] = HasNecronomicon ? Options.DefaultKillCooldown / 2f : Options.DefaultKillCooldown;
|
||||
}
|
||||
|
||||
public override void OnReceiveNecronomicon()
|
||||
{
|
||||
CovenLeaderId.GetPlayer().ResetKillCooldown();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using EHR.Modules;
|
||||
using Hazel;
|
||||
|
||||
namespace EHR.Coven;
|
||||
|
||||
public class Dreamweaver : Coven
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
private static OptionItem AbilityCooldown;
|
||||
|
||||
private byte DreamweaverId;
|
||||
|
||||
public HashSet<byte> InsanePlayers = [];
|
||||
|
||||
public override NecronomiconReceivePriorities NecronomiconReceivePriority => NecronomiconReceivePriorities.Random;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(650080)
|
||||
.AutoSetupOption(ref AbilityCooldown, 30f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
InsanePlayers = [];
|
||||
DreamweaverId = playerId;
|
||||
}
|
||||
|
||||
public override bool CanUseKillButton(PlayerControl pc)
|
||||
{
|
||||
return pc.IsAlive();
|
||||
}
|
||||
|
||||
public override void SetKillCooldown(byte id)
|
||||
{
|
||||
Main.AllPlayerKillCooldown[id] = AbilityCooldown.GetFloat();
|
||||
}
|
||||
|
||||
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
InsanePlayers.Add(target.PlayerId);
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, DreamweaverId, 1, target.PlayerId);
|
||||
Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target);
|
||||
killer.SetKillCooldown(AbilityCooldown.GetFloat());
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void AfterMeetingTasks()
|
||||
{
|
||||
InsanePlayers.ToValidPlayers().Do(x => x.RpcSetCustomRole(CustomRoles.Insane));
|
||||
InsanePlayers.Clear();
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, DreamweaverId, 2);
|
||||
}
|
||||
|
||||
public override void OnGlobalFixedUpdate(PlayerControl pc, bool lowLoad)
|
||||
{
|
||||
if (lowLoad || GameStates.IsMeeting || ExileController.Instance || !Main.IntroDestroyed || !pc.IsAlive() || !pc.Is(CustomRoles.Insane) || Main.KillTimers[pc.PlayerId] > 0f) return;
|
||||
|
||||
var pos = pc.Pos();
|
||||
var nearbyPlayers = Utils.GetPlayersInRadius(1.5f, pos).Without(pc).ToArray();
|
||||
if (nearbyPlayers.Length == 0) return;
|
||||
|
||||
var nearestPlayer = nearbyPlayers.MinBy(x => Vector2.Distance(x.Pos(), pos));
|
||||
|
||||
RoleBase roleBase = Main.PlayerStates[pc.PlayerId].Role;
|
||||
var type = roleBase.GetType();
|
||||
|
||||
if (type.GetMethod("OnCheckMurder")?.DeclaringType == type)
|
||||
{
|
||||
roleBase.OnCheckMurder(pc, nearestPlayer);
|
||||
pc.SetKillCooldown();
|
||||
}
|
||||
|
||||
if (HasNecronomicon) pc.Suicide(realKiller: DreamweaverId.GetPlayer());
|
||||
}
|
||||
|
||||
public void ReceiveRPC(MessageReader reader)
|
||||
{
|
||||
switch (reader.ReadPackedInt32())
|
||||
{
|
||||
case 1:
|
||||
InsanePlayers.Add(reader.ReadByte());
|
||||
break;
|
||||
case 2:
|
||||
InsanePlayers.Clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System.Collections.Generic;
|
||||
using EHR.Modules;
|
||||
|
||||
namespace EHR.Coven;
|
||||
|
||||
public class Enchanter : Coven
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
private static OptionItem AbilityCooldown;
|
||||
|
||||
public static HashSet<byte> EnchantedPlayers = [];
|
||||
|
||||
public override NecronomiconReceivePriorities NecronomiconReceivePriority => NecronomiconReceivePriorities.Random;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(650120)
|
||||
.AutoSetupOption(ref AbilityCooldown, 30f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
EnchantedPlayers = [];
|
||||
}
|
||||
|
||||
public override bool CanUseKillButton(PlayerControl pc)
|
||||
{
|
||||
return pc.IsAlive();
|
||||
}
|
||||
|
||||
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (EnchantedPlayers.Contains(target.PlayerId)) return HasNecronomicon;
|
||||
if (HasNecronomicon) return killer.CheckDoubleTrigger(target, EnchantTarget);
|
||||
EnchantTarget();
|
||||
return false;
|
||||
|
||||
void EnchantTarget()
|
||||
{
|
||||
EnchantedPlayers.Add(target.PlayerId);
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, killer.PlayerId, 1, target.PlayerId);
|
||||
Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target);
|
||||
killer.SetKillCooldown(AbilityCooldown.GetFloat());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
using AmongUs.GameOptions;
|
||||
using EHR.Modules;
|
||||
using Hazel;
|
||||
|
||||
namespace EHR.Coven;
|
||||
|
||||
public class Goddess : Coven
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
private static OptionItem AbilityCooldown;
|
||||
private static OptionItem AbilityDuration;
|
||||
|
||||
private long AbilityEndTS;
|
||||
|
||||
private byte GoddessId;
|
||||
private long LastNotifyTS;
|
||||
|
||||
public override NecronomiconReceivePriorities NecronomiconReceivePriority => NecronomiconReceivePriorities.Random;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(650060)
|
||||
.AutoSetupOption(ref AbilityCooldown, 30f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref AbilityDuration, 10, new IntegerValueRule(1, 60, 1), OptionFormat.Seconds);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
AbilityEndTS = 0;
|
||||
LastNotifyTS = 0;
|
||||
GoddessId = playerId;
|
||||
}
|
||||
|
||||
public override bool CanUseKillButton(PlayerControl pc)
|
||||
{
|
||||
return HasNecronomicon;
|
||||
}
|
||||
|
||||
public override bool OnVanish(PlayerControl pc)
|
||||
{
|
||||
AbilityEndTS = Utils.TimeStamp + AbilityDuration.GetInt();
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, GoddessId, AbilityEndTS);
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void ApplyGameOptions(IGameOptions opt, byte playerId)
|
||||
{
|
||||
AURoleOptions.PhantomCooldown = AbilityCooldown.GetFloat();
|
||||
AURoleOptions.PhantomDuration = 1f;
|
||||
}
|
||||
|
||||
public override void OnReportDeadBody()
|
||||
{
|
||||
AbilityEndTS = 0;
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, GoddessId, AbilityEndTS);
|
||||
}
|
||||
|
||||
public override void OnFixedUpdate(PlayerControl pc)
|
||||
{
|
||||
if (AbilityEndTS > 0)
|
||||
{
|
||||
long now = Utils.TimeStamp;
|
||||
bool notify = now != LastNotifyTS;
|
||||
|
||||
if (now >= AbilityEndTS)
|
||||
{
|
||||
AbilityEndTS = 0;
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, GoddessId, AbilityEndTS);
|
||||
pc.RpcResetAbilityCooldown();
|
||||
notify = true;
|
||||
}
|
||||
|
||||
if (notify)
|
||||
{
|
||||
LastNotifyTS = now;
|
||||
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: pc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool OnCheckMurderAsTarget(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (AbilityEndTS == 0 || killer.Is(CustomRoles.Pestilence)) return true;
|
||||
|
||||
killer.SetRealKiller(target);
|
||||
target.Kill(killer);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ReceiveRPC(MessageReader reader)
|
||||
{
|
||||
AbilityEndTS = long.Parse(reader.ReadString());
|
||||
}
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
if (seer.PlayerId != GoddessId || seer.PlayerId != target.PlayerId || (seer.IsModClient() && !hud) || meeting || AbilityEndTS == 0) return string.Empty;
|
||||
return string.Format(Translator.GetString("Goddess.Suffix"), AbilityEndTS - Utils.TimeStamp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using AmongUs.GameOptions;
|
||||
using EHR.Modules;
|
||||
using Hazel;
|
||||
|
||||
namespace EHR.Coven;
|
||||
|
||||
public class Illusionist : Coven
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
private static OptionItem AbilityCooldown;
|
||||
private static OptionItem ShapeshiftAnimation;
|
||||
|
||||
public byte SampledPlayerId;
|
||||
|
||||
public override NecronomiconReceivePriorities NecronomiconReceivePriority => NecronomiconReceivePriorities.Random;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(650100)
|
||||
.AutoSetupOption(ref AbilityCooldown, 30f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref ShapeshiftAnimation, false);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
SampledPlayerId = byte.MaxValue;
|
||||
}
|
||||
|
||||
public override bool CanUseKillButton(PlayerControl pc)
|
||||
{
|
||||
return pc.IsAlive();
|
||||
}
|
||||
|
||||
public override void SetKillCooldown(byte id)
|
||||
{
|
||||
Main.AllPlayerKillCooldown[id] = AbilityCooldown.GetFloat();
|
||||
}
|
||||
|
||||
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (HasNecronomicon && SampledPlayerId == target.PlayerId)
|
||||
return true;
|
||||
|
||||
SampledPlayerId = target.PlayerId;
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, killer.PlayerId, 1, target.PlayerId);
|
||||
Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target);
|
||||
if (!HasNecronomicon) killer.SetKillCooldown(AbilityCooldown.GetFloat());
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void ApplyGameOptions(IGameOptions opt, byte playerId)
|
||||
{
|
||||
AURoleOptions.ShapeshifterCooldown = 1f;
|
||||
AURoleOptions.ShapeshifterDuration = 1f;
|
||||
}
|
||||
|
||||
public override bool OnShapeshift(PlayerControl shapeshifter, PlayerControl target, bool shapeshifting)
|
||||
{
|
||||
var sampledPlayer = SampledPlayerId.GetPlayer();
|
||||
if (sampledPlayer == null) return false;
|
||||
|
||||
target.RpcShapeshift(sampledPlayer, ShapeshiftAnimation.GetBool());
|
||||
if (HasNecronomicon) shapeshifter.SetKillCooldown(AbilityCooldown.GetFloat());
|
||||
|
||||
SampledPlayerId = byte.MaxValue;
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, shapeshifter.PlayerId, 2);
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void AfterMeetingTasks()
|
||||
{
|
||||
SampledPlayerId = byte.MaxValue;
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, byte.MaxValue, 2);
|
||||
}
|
||||
|
||||
public void ReceiveRPC(MessageReader reader)
|
||||
{
|
||||
SampledPlayerId = reader.ReadPackedInt32() switch
|
||||
{
|
||||
1 => reader.ReadByte(),
|
||||
2 => byte.MaxValue,
|
||||
_ => SampledPlayerId
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using System.Collections.Generic;
|
||||
using EHR.Modules;
|
||||
using Hazel;
|
||||
|
||||
namespace EHR.Coven;
|
||||
|
||||
public class Poache : Coven
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
private static OptionItem AbilityCooldown;
|
||||
private static OptionItem KillDelay;
|
||||
|
||||
public static HashSet<byte> PoachedPlayers = [];
|
||||
private static List<(byte ID, long KillTimeStamp)> KillDelays = [];
|
||||
|
||||
private byte PoacheId;
|
||||
|
||||
public override NecronomiconReceivePriorities NecronomiconReceivePriority => NecronomiconReceivePriorities.Random;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(650030)
|
||||
.AutoSetupOption(ref AbilityCooldown, 30f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref KillDelay, 3, new IntegerValueRule(1, 30, 1), OptionFormat.Seconds);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
PoachedPlayers = [];
|
||||
KillDelays = [];
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
PoacheId = playerId;
|
||||
}
|
||||
|
||||
public override bool CanUseKillButton(PlayerControl pc)
|
||||
{
|
||||
return pc.IsAlive();
|
||||
}
|
||||
|
||||
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (!HasNecronomicon)
|
||||
{
|
||||
PoachedPlayers.Add(target.PlayerId);
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, PoacheId, 1, target.PlayerId);
|
||||
}
|
||||
else KillDelays.Add((killer.PlayerId, Utils.TimeStamp + KillDelay.GetInt()));
|
||||
|
||||
killer.SetKillCooldown(AbilityCooldown.GetFloat());
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnGlobalFixedUpdate(PlayerControl pc, bool lowLoad)
|
||||
{
|
||||
if (lowLoad || GameStates.IsMeeting || ExileController.Instance || !Main.IntroDestroyed || !pc.IsAlive() || !KillDelays.FindFirst(x => x.ID == pc.PlayerId, out var killData) || Utils.TimeStamp < killData.KillTimeStamp) return;
|
||||
|
||||
pc.Suicide(realKiller: PoacheId.GetPlayer());
|
||||
KillDelays.Remove(killData);
|
||||
}
|
||||
|
||||
public override void AfterMeetingTasks()
|
||||
{
|
||||
PoachedPlayers.Clear();
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, PoacheId, 2);
|
||||
}
|
||||
|
||||
public override void OnReportDeadBody()
|
||||
{
|
||||
KillDelays.ForEach(x => x.ID.GetPlayer()?.Suicide(realKiller: PoacheId.GetPlayer()));
|
||||
KillDelays.Clear();
|
||||
}
|
||||
|
||||
public void ReceiveRPC(MessageReader reader)
|
||||
{
|
||||
switch (reader.ReadPackedInt32())
|
||||
{
|
||||
case 1:
|
||||
PoachedPlayers.Add(reader.ReadByte());
|
||||
break;
|
||||
case 2:
|
||||
PoachedPlayers.Clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using EHR.Modules;
|
||||
using Hazel;
|
||||
|
||||
namespace EHR.Coven;
|
||||
|
||||
public class PotionMaster : Coven
|
||||
{
|
||||
public static bool On;
|
||||
private static List<PotionMaster> Instances = [];
|
||||
|
||||
private static OptionItem AbilityCooldown;
|
||||
private static OptionItem KillCooldown;
|
||||
private static OptionItem ShieldDuration;
|
||||
|
||||
private byte PotionMasterId;
|
||||
private HashSet<byte> RevealedPlayers = [];
|
||||
|
||||
private Dictionary<byte, long> ShieldedPlayers = [];
|
||||
|
||||
public override NecronomiconReceivePriorities NecronomiconReceivePriority => NecronomiconReceivePriorities.Random;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(650020)
|
||||
.AutoSetupOption(ref AbilityCooldown, 30f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref KillCooldown, 30f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref ShieldDuration, 20, new IntegerValueRule(0, 600, 1), OptionFormat.Seconds);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
Instances = [];
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
ShieldedPlayers = [];
|
||||
RevealedPlayers = [];
|
||||
PotionMasterId = playerId;
|
||||
Instances.Add(this);
|
||||
}
|
||||
|
||||
public override bool CanUseKillButton(PlayerControl pc)
|
||||
{
|
||||
return pc.IsAlive();
|
||||
}
|
||||
|
||||
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (target.Is(Team.Coven))
|
||||
{
|
||||
long shieldEndTS = Utils.TimeStamp + ShieldDuration.GetInt();
|
||||
ShieldedPlayers[target.PlayerId] = shieldEndTS;
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, PotionMasterId, 1, target.PlayerId, shieldEndTS);
|
||||
Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target);
|
||||
Utils.NotifyRoles(SpecifySeer: target, SpecifyTarget: target);
|
||||
killer.SetKillCooldown(AbilityCooldown.GetFloat());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HasNecronomicon) return true;
|
||||
|
||||
RevealedPlayers.Add(target.PlayerId);
|
||||
Main.AllAlivePlayerControls.DoIf(x => x.Is(Team.Coven), x => Utils.NotifyRoles(SpecifySeer: x, SpecifyTarget: target));
|
||||
killer.SetKillCooldown(AbilityCooldown.GetFloat());
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnReceiveNecronomicon()
|
||||
{
|
||||
PotionMasterId.GetPlayer().ResetKillCooldown();
|
||||
}
|
||||
|
||||
public override void SetKillCooldown(byte id)
|
||||
{
|
||||
Main.AllPlayerKillCooldown[id] = HasNecronomicon ? KillCooldown.GetFloat() : AbilityCooldown.GetFloat();
|
||||
}
|
||||
|
||||
public override void OnFixedUpdate(PlayerControl pc)
|
||||
{
|
||||
if (GameStates.IsMeeting || ExileController.Instance || !Main.IntroDestroyed || ShieldedPlayers.Count == 0) return;
|
||||
|
||||
long now = Utils.TimeStamp;
|
||||
List<byte> toRemove = ShieldedPlayers.Where(x => now >= x.Value).Select(x => x.Key).ToList();
|
||||
|
||||
toRemove.ForEach(x =>
|
||||
{
|
||||
ShieldedPlayers.Remove(x);
|
||||
var player = x.GetPlayer();
|
||||
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: player);
|
||||
Utils.NotifyRoles(SpecifySeer: player, SpecifyTarget: player);
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, PotionMasterId, 2, x);
|
||||
});
|
||||
|
||||
foreach (byte shieldedId in ShieldedPlayers.Keys)
|
||||
{
|
||||
var player = shieldedId.GetPlayer();
|
||||
Utils.NotifyRoles(SpecifySeer: pc, SpecifyTarget: player);
|
||||
Utils.NotifyRoles(SpecifySeer: player, SpecifyTarget: player);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool KnowRole(PlayerControl seer, PlayerControl target)
|
||||
{
|
||||
if (base.KnowRole(seer, target)) return true;
|
||||
return seer.Is(Team.Coven) && RevealedPlayers.Contains(target.PlayerId);
|
||||
}
|
||||
|
||||
public static bool OnAnyoneCheckMurder(PlayerControl target) => !Instances.Any(x => x.ShieldedPlayers.ContainsKey(target.PlayerId));
|
||||
|
||||
public void ReceiveRPC(MessageReader reader)
|
||||
{
|
||||
switch (reader.ReadPackedInt32())
|
||||
{
|
||||
case 1:
|
||||
ShieldedPlayers[reader.ReadByte()] = long.Parse(reader.ReadString());
|
||||
break;
|
||||
case 2:
|
||||
ShieldedPlayers.Remove(reader.ReadByte());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
if (seer.PlayerId == target.PlayerId && seer.IsModClient() && !hud) return string.Empty;
|
||||
if (seer.PlayerId != target.PlayerId && seer.PlayerId != PotionMasterId) return string.Empty;
|
||||
if (!ShieldedPlayers.TryGetValue(target.PlayerId, out var shieldExpireTS)) return string.Empty;
|
||||
|
||||
return string.Format(Translator.GetString("PotionMaster.Suffix"), shieldExpireTS - Utils.TimeStamp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
using System.Collections.Generic;
|
||||
using EHR.Modules;
|
||||
using Hazel;
|
||||
|
||||
namespace EHR.Coven;
|
||||
|
||||
public class Reaper : Coven
|
||||
{
|
||||
public static bool On;
|
||||
private static List<Reaper> Instances = [];
|
||||
|
||||
private static OptionItem AbilityCooldown;
|
||||
private static OptionItem SoulsRequired;
|
||||
public static OptionItem KillCooldown;
|
||||
|
||||
public HashSet<byte> CursedPlayers = [];
|
||||
|
||||
private byte ReaperId;
|
||||
private int Souls;
|
||||
|
||||
public override NecronomiconReceivePriorities NecronomiconReceivePriority => NecronomiconReceivePriorities.Never;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(650040)
|
||||
.AutoSetupOption(ref AbilityCooldown, 10f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref SoulsRequired, 3, new IntegerValueRule(1, 10, 1))
|
||||
.AutoSetupOption(ref KillCooldown, 15f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
Instances = [];
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
Instances.Add(this);
|
||||
ReaperId = playerId;
|
||||
CursedPlayers = [];
|
||||
Souls = 0;
|
||||
}
|
||||
|
||||
public override bool CanUseKillButton(PlayerControl pc)
|
||||
{
|
||||
return pc.IsAlive();
|
||||
}
|
||||
|
||||
public override void SetKillCooldown(byte id)
|
||||
{
|
||||
Main.AllPlayerKillCooldown[id] = AbilityCooldown.GetFloat();
|
||||
}
|
||||
|
||||
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
CursedPlayers.Add(target.PlayerId);
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, killer.PlayerId, target.PlayerId);
|
||||
Utils.NotifyRoles(SpecifySeer: killer, SpecifyTarget: target);
|
||||
killer.SetKillCooldown(AbilityCooldown.GetFloat());
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ReceiveRPC(MessageReader reader)
|
||||
{
|
||||
CursedPlayers.Add(reader.ReadByte());
|
||||
}
|
||||
|
||||
public static void OnAnyoneDead(PlayerControl target)
|
||||
{
|
||||
Instances.DoIf(x => x.CursedPlayers.Contains(target.PlayerId) && ++x.Souls >= SoulsRequired.GetInt(), x =>
|
||||
{
|
||||
PlayerControl reaperPc = x.ReaperId.GetPlayer();
|
||||
if (reaperPc == null) return;
|
||||
|
||||
reaperPc.RpcSetCustomRole(CustomRoles.Death);
|
||||
reaperPc.SetKillCooldown(KillCooldown.GetFloat());
|
||||
reaperPc.ResetKillCooldown();
|
||||
|
||||
Utils.NotifyRoles(SpecifySeer: reaperPc);
|
||||
Utils.NotifyRoles(SpecifyTarget: reaperPc);
|
||||
});
|
||||
|
||||
Instances.RemoveAll(x => x.Souls >= SoulsRequired.GetInt());
|
||||
}
|
||||
}
|
||||
|
||||
public class Death : Coven
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
public override NecronomiconReceivePriorities NecronomiconReceivePriority => NecronomiconReceivePriorities.Never;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public override void SetupCustomOption() { }
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
}
|
||||
|
||||
public override bool CanUseKillButton(PlayerControl pc)
|
||||
{
|
||||
return pc.IsAlive();
|
||||
}
|
||||
|
||||
public override void SetKillCooldown(byte id)
|
||||
{
|
||||
Main.AllPlayerKillCooldown[id] = Reaper.KillCooldown.GetFloat();
|
||||
}
|
||||
|
||||
public override bool OnCheckMurderAsTarget(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using EHR.Patches;
|
||||
|
||||
namespace EHR.Coven;
|
||||
|
||||
public class SpellCaster : Coven
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
private static OptionItem AbilityCooldown;
|
||||
private static Dictionary<byte, bool> HexedPlayers = [];
|
||||
private static HashSet<byte> PlayerIdList = [];
|
||||
|
||||
public override NecronomiconReceivePriorities NecronomiconReceivePriority => NecronomiconReceivePriorities.Random;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(650010)
|
||||
.AutoSetupOption(ref AbilityCooldown, 30f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
PlayerIdList = [];
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
PlayerIdList.Add(playerId);
|
||||
HexedPlayers = [];
|
||||
}
|
||||
|
||||
public override bool CanUseKillButton(PlayerControl pc)
|
||||
{
|
||||
return pc.IsAlive();
|
||||
}
|
||||
|
||||
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
HexedPlayers[target.PlayerId] = HasNecronomicon;
|
||||
killer.SetKillCooldown(AbilityCooldown.GetFloat());
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnReceiveNecronomicon()
|
||||
{
|
||||
HexedPlayers.SetAllValues(true);
|
||||
}
|
||||
|
||||
public override void SetKillCooldown(byte id)
|
||||
{
|
||||
Main.AllPlayerKillCooldown[id] = AbilityCooldown.GetFloat();
|
||||
}
|
||||
|
||||
public static void OnExile(byte[] exileIds)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (exileIds.Any(PlayerIdList.Contains))
|
||||
HexedPlayers.Clear();
|
||||
|
||||
List<byte> curseDeathList = [];
|
||||
PlayerControl spellCaster = Main.AllAlivePlayerControls.First(x => x.Is(CustomRoles.SpellCaster));
|
||||
|
||||
foreach (PlayerControl pc in Main.AllAlivePlayerControls)
|
||||
{
|
||||
if (Main.AfterMeetingDeathPlayers.ContainsKey(pc.PlayerId)) continue;
|
||||
|
||||
if (IsSpelled(pc.PlayerId))
|
||||
{
|
||||
pc.SetRealKiller(spellCaster);
|
||||
curseDeathList.Add(pc.PlayerId);
|
||||
}
|
||||
}
|
||||
|
||||
CheckForEndVotingPatch.TryAddAfterMeetingDeathPlayers(PlayerState.DeathReason.Spell, [.. curseDeathList]);
|
||||
}
|
||||
catch (Exception e) { Utils.ThrowException(e); }
|
||||
}
|
||||
|
||||
public static bool IsSpelled(byte playerId)
|
||||
{
|
||||
return HexedPlayers.TryGetValue(playerId, out bool spell) && spell;
|
||||
}
|
||||
|
||||
public override void OnFixedUpdate(PlayerControl pc)
|
||||
{
|
||||
if (pc.IsAlive() && Main.AllAlivePlayerControls.All(x => x.Is(Team.Coven) || HexedPlayers.ContainsKey(x.PlayerId)))
|
||||
{
|
||||
CustomWinnerHolder.ResetAndSetWinner(CustomWinner.Coven);
|
||||
CustomWinnerHolder.WinnerIds.UnionWith(Main.AllAlivePlayerControls.Where(x => x.Is(Team.Coven)).Select(x => x.PlayerId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace EHR.Coven;
|
||||
|
||||
public class Timelord : Coven
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
private static OptionItem KillCooldown;
|
||||
private static OptionItem TimeStolenWithEachKill;
|
||||
|
||||
public override NecronomiconReceivePriorities NecronomiconReceivePriority => NecronomiconReceivePriorities.Random;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(650110)
|
||||
.AutoSetupOption(ref KillCooldown, 30f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds)
|
||||
.AutoSetupOption(ref TimeStolenWithEachKill, 10, new IntegerValueRule(0, 60, 1), OptionFormat.Seconds);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
}
|
||||
|
||||
public override bool CanUseKillButton(PlayerControl pc)
|
||||
{
|
||||
return pc.IsAlive();
|
||||
}
|
||||
|
||||
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (target.Is(Team.Coven)) target.SetKillCooldown(0.01f);
|
||||
else target.SetKillCooldown();
|
||||
|
||||
return HasNecronomicon;
|
||||
}
|
||||
|
||||
public static int GetTotalStolenTime()
|
||||
{
|
||||
return Main.PlayerStates.Values.Where(x => x.MainRole == CustomRoles.Timelord).Sum(x => x.GetKillCount()) * TimeStolenWithEachKill.GetInt();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
namespace EHR.Coven;
|
||||
|
||||
public class VoodooMaster : Coven
|
||||
{
|
||||
public static bool On;
|
||||
|
||||
private static OptionItem AbilityCooldown;
|
||||
|
||||
public override NecronomiconReceivePriorities NecronomiconReceivePriority => NecronomiconReceivePriorities.Random;
|
||||
|
||||
public override bool IsEnable => On;
|
||||
|
||||
public override void SetupCustomOption()
|
||||
{
|
||||
StartSetup(650050)
|
||||
.AutoSetupOption(ref AbilityCooldown, 30f, new FloatValueRule(0f, 120f, 0.5f), OptionFormat.Seconds);
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
On = false;
|
||||
}
|
||||
|
||||
public override void Add(byte playerId)
|
||||
{
|
||||
On = true;
|
||||
}
|
||||
|
||||
public override bool CanUseKillButton(PlayerControl pc)
|
||||
{
|
||||
return pc.IsAlive();
|
||||
}
|
||||
|
||||
public override void SetKillCooldown(byte id)
|
||||
{
|
||||
Main.AllPlayerKillCooldown[id] = AbilityCooldown.GetFloat();
|
||||
}
|
||||
|
||||
public override bool OnCheckMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
RoleBase roleBase = Main.PlayerStates[target.PlayerId].Role;
|
||||
var type = roleBase.GetType();
|
||||
|
||||
if (type.GetMethod("OnCheckMurder")?.DeclaringType == type) roleBase.OnCheckMurder(target, target);
|
||||
else if (type.GetMethod("OnPet")?.DeclaringType == type) roleBase.OnPet(target);
|
||||
|
||||
if (!HasNecronomicon) killer.SetKillCooldown(AbilityCooldown.GetFloat());
|
||||
return HasNecronomicon;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using EHR.Coven;
|
||||
using EHR.Impostor;
|
||||
using EHR.Modules;
|
||||
using Hazel;
|
||||
@@ -107,7 +108,7 @@ public class Psychic : RoleBase
|
||||
if (!IsEnable || !AmongUsClient.Instance.AmHost) return;
|
||||
|
||||
List<PlayerControl> BadListPc = Main.AllAlivePlayerControls.Where(x =>
|
||||
(x.Is(CustomRoleTypes.Impostor) && !x.Is(CustomRoles.Trickster)) || x.Is(CustomRoles.Madmate) || x.Is(CustomRoles.Rascal) || Framer.FramedPlayers.Contains(x.PlayerId) || x.IsConverted() ||
|
||||
(x.Is(CustomRoleTypes.Impostor) && !x.Is(CustomRoles.Trickster)) || x.Is(CustomRoles.Madmate) || x.Is(CustomRoles.Rascal) || Framer.FramedPlayers.Contains(x.PlayerId) || Enchanter.EnchantedPlayers.Contains(x.PlayerId) || x.IsConverted() ||
|
||||
(x.GetCustomRole().GetCrewmateRoleCategory() == RoleOptionType.Crewmate_Killing && CkshowEvil.GetBool()) ||
|
||||
(x.GetCustomRole().GetNeutralRoleCategory() == RoleOptionType.Neutral_Evil && NEshowEvil.GetBool()) ||
|
||||
(x.GetCustomRole().GetNeutralRoleCategory() == RoleOptionType.Neutral_Benign && NBshowEvil.GetBool())
|
||||
|
||||
Reference in new issue
Block a user