mirror of
https://github.com/Gurge44/EndlessHostRoles.git
synced 2026-10-19 15:51:58 +00:00
Add Meeting Angel
This commit is contained in:
1 parent
e52944c4f4
commit
a2f32b7045
8 files changed
+141
-31
No files matched your search
@@ -579,6 +579,7 @@ public enum CustomRoles
|
||||
Madmate,
|
||||
Magnet,
|
||||
Mare,
|
||||
MeetingAngel, // Ghost role
|
||||
Messenger,
|
||||
Mimic,
|
||||
Minion, // Ghost role
|
||||
|
||||
@@ -668,6 +668,7 @@ public class Main : BasePlugin
|
||||
{ CustomRoles.GA, "#8cffff" },
|
||||
{ CustomRoles.Facilitator, CovenColor },
|
||||
{ CustomRoles.Shade, "#060270" },
|
||||
{ CustomRoles.MeetingAngel, "#a7e8b5" },
|
||||
// GM
|
||||
{ CustomRoles.GM, "#ff5b70" },
|
||||
// Add-ons
|
||||
|
||||
@@ -179,7 +179,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 (ps.VotedFor.GetPlayer() && CheckRole(ps.VotedFor, CustomRoles.Zombie)) canVote = false;
|
||||
if (Poache.PoachedPlayers.Contains(ps.TargetPlayerId)) canVote = false;
|
||||
if (Silencer.ForSilencer.Contains(ps.TargetPlayerId) && Main.AllAlivePlayerControls.Count > Silencer.MaxPlayersAliveForSilencedToVote.GetInt()) canVote = false;
|
||||
|
||||
@@ -243,6 +243,7 @@ internal static class CheckForEndVotingPatch
|
||||
|
||||
Blackmailer.ManipulateVotingResult(votingData, states);
|
||||
Swapper.ManipulateVotingResult(votingData, states);
|
||||
MeetingAngel.NegateVotes(votingData, states);
|
||||
Assumer.OnVotingEnd(votingData);
|
||||
|
||||
var exileId = byte.MaxValue;
|
||||
|
||||
@@ -4014,6 +4014,18 @@
|
||||
"HaunterRevealedYou": "You have been revealed by the Haunter!",
|
||||
"HaunterStopped": "<#00ff00>The Haunter was successfully stopped!</color>",
|
||||
"HaunterStoppedSelf": "<#ff1313>An impostor came too close to you, your role was removed.</color>",
|
||||
"MeetingAngel": "MeetingAngel",
|
||||
"MeetingAngelInfo": "Remove Votes",
|
||||
"MeetingAngelInfoLong": "(Crewmate Ghost):\\nAs the Meeting Angel, you can use your protect button on someone every X meetings (specified by settings) to remove a certain number of votes targeting them.",
|
||||
"MeetingAngel.NumVotesNegated": "Number of Votes Removed",
|
||||
"MeetingAngel.NumMeetingsCooldown": "Per how many meetings can someone be protected",
|
||||
"MeetingAngel.CanProtectOptions.Crewmate": "Can Protect <#8cffff>Crewmates</color>",
|
||||
"MeetingAngel.CanProtectOptions.Impostor": "Can Protect <#ff1919>Impostor</color>",
|
||||
"MeetingAngel.CanProtectOptions.NeutralKilling": "Can Protect <#ffab1b>Neutral</color> <#ff1919>Killers</color>",
|
||||
"MeetingAngel.CanProtectOptions.NeutralEvil": "Can Protect <#ffab1b>Neutral</color> <#ff5555>Evils</color>",
|
||||
"MeetingAngel.CanProtectOptions.NeutralPariah": "Can Protect <#ffab1b>Neutral</color> <#ffff44>Pariahs</color>",
|
||||
"MeetingAngel.CanProtectOptions.NeutralBenign": "Can Protect <#ffab1b>Neutral</color> <#00ffa5>Benigns</color>",
|
||||
"MeetingAngel.CanProtectOptions.Coven": "Can Protect <#7b3fbb>Covens</color>",
|
||||
"Minion": "Minion",
|
||||
"MinionInfo": "Help the Impostors",
|
||||
"MinionInfoLong": "(Impostor/Neutral Ghost):\\nAs the Minion, you can protect someone (optimally a crewmate) to blind them for a few seconds.",
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using AmongUs.GameOptions;
|
||||
using EHR.Modules;
|
||||
|
||||
namespace EHR.Roles;
|
||||
|
||||
public class MeetingAngel : IGhostRole
|
||||
{
|
||||
private static OptionItem NumVotesNegated;
|
||||
private static OptionItem NumMeetingsCooldown;
|
||||
private static readonly Dictionary<TeamOptions, OptionItem> CanProtectOptions = [];
|
||||
|
||||
public Team Team => Team.Crewmate;
|
||||
public RoleTypes RoleTypes => RoleTypes.GuardianAngel;
|
||||
public int Cooldown => 5;
|
||||
|
||||
private int LastUsedAtMeetingNum = int.MinValue;
|
||||
public byte TargetId = 252;
|
||||
|
||||
public void OnProtect(PlayerControl pc, PlayerControl target)
|
||||
{
|
||||
if (LastUsedAtMeetingNum + NumMeetingsCooldown.GetInt() > MeetingStates.MeetingNum) return;
|
||||
|
||||
TeamOptions targetTeam = target.GetTeam() switch
|
||||
{
|
||||
Team.Crewmate => TeamOptions.Crewmate,
|
||||
Team.Impostor => TeamOptions.Impostor,
|
||||
Team.Coven => TeamOptions.Coven,
|
||||
Team.Neutral => target.GetCustomRole().GetNeutralRoleCategory() switch
|
||||
{
|
||||
RoleOptionType.Neutral_Killing => TeamOptions.NeutralKilling,
|
||||
RoleOptionType.Neutral_Evil => TeamOptions.NeutralEvil,
|
||||
RoleOptionType.Neutral_Pariah => TeamOptions.NeutralPariah,
|
||||
RoleOptionType.Neutral_Benign => TeamOptions.NeutralBenign,
|
||||
_ => default(TeamOptions)
|
||||
},
|
||||
_ => default(TeamOptions)
|
||||
};
|
||||
if (CanProtectOptions.TryGetValue(targetTeam, out OptionItem optionItem) && !optionItem.GetBool()) return;
|
||||
|
||||
LastUsedAtMeetingNum = MeetingStates.MeetingNum;
|
||||
TargetId = target.PlayerId;
|
||||
}
|
||||
|
||||
public void OnAssign(PlayerControl pc)
|
||||
{
|
||||
LastUsedAtMeetingNum = int.MinValue;
|
||||
TargetId = 252;
|
||||
}
|
||||
|
||||
public void SetupCustomOption()
|
||||
{
|
||||
Options.SetupRoleOptions(658600, TabGroup.OtherRoles, CustomRoles.MeetingAngel);
|
||||
|
||||
NumVotesNegated = new IntegerOptionItem(658602, "MeetingAngel.NumVotesNegated", new(1, 30, 1), 2, TabGroup.OtherRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.MeetingAngel])
|
||||
.SetValueFormat(OptionFormat.Votes);
|
||||
|
||||
NumMeetingsCooldown = new IntegerOptionItem(658603, "MeetingAngel.NumMeetingsCooldown", new(1, 30, 1), 2, TabGroup.OtherRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.MeetingAngel]);
|
||||
|
||||
Enum.GetValues<TeamOptions>()[1..].Do(x => CanProtectOptions[x] = new BooleanOptionItem(658603 + (int)x, $"MeetingAngel.CanProtectOptions.{x}", x == TeamOptions.NeutralBenign, TabGroup.OtherRoles)
|
||||
.SetParent(Options.CustomRoleSpawnChances[CustomRoles.MeetingAngel]));
|
||||
}
|
||||
|
||||
public static void NegateVotes(Dictionary<byte, int> votingData, MeetingHud.VoterState[] states)
|
||||
{
|
||||
foreach ((CustomRoles _, IGhostRole instance) in GhostRolesManager.AssignedGhostRoles.Values)
|
||||
{
|
||||
if (instance is not MeetingAngel { TargetId: < 252 } meetingAngel) continue;
|
||||
|
||||
int negateNum = NumVotesNegated.GetInt();
|
||||
votingData[meetingAngel.TargetId] -= negateNum;
|
||||
|
||||
int negated = 0;
|
||||
|
||||
for (var index = 0; index < states.Length && negated < negateNum; index++)
|
||||
{
|
||||
ref MeetingHud.VoterState state = ref states[index];
|
||||
|
||||
if (state.VotedForId == meetingAngel.TargetId)
|
||||
{
|
||||
state.VotedForId = 254;
|
||||
negated++;
|
||||
}
|
||||
}
|
||||
|
||||
Utils.SendMessage(string.Format(Translator.GetString("MeetingAngel.NegatedMsg"), negated, meetingAngel.TargetId.ColoredPlayerName()), title: CustomRoles.MeetingAngel.ToColoredString());
|
||||
meetingAngel.TargetId = 252;
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
enum TeamOptions
|
||||
{
|
||||
Crewmate,
|
||||
Impostor,
|
||||
NeutralKilling,
|
||||
NeutralEvil,
|
||||
NeutralPariah,
|
||||
NeutralBenign,
|
||||
Coven
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ internal class Blackmailer : RoleBase
|
||||
|
||||
if (vs.SkippedVote)
|
||||
vote = 253;
|
||||
else if (vs.AmDead || vs.VotedForId.GetPlayer() == null)
|
||||
else if (vs.AmDead || !vs.VotedForId.GetPlayer())
|
||||
vote = 254;
|
||||
else
|
||||
vote = vs.VotedForId;
|
||||
|
||||
@@ -3,11 +3,10 @@ using System.Linq;
|
||||
using AmongUs.GameOptions;
|
||||
using EHR.Modules;
|
||||
using Hazel;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EHR.Roles;
|
||||
|
||||
using static EHR.Translator;
|
||||
using static Translator;
|
||||
|
||||
public class Chainbinder : RoleBase
|
||||
{
|
||||
@@ -105,7 +104,7 @@ public class Chainbinder : RoleBase
|
||||
|
||||
public override void OnFixedUpdate(PlayerControl pc)
|
||||
{
|
||||
if (pc == null || pc.PlayerId != ChainbinderId || !HasLink) return;
|
||||
if (!HasLink) return;
|
||||
|
||||
if (!pc.IsAlive() || !GameStates.IsInTask)
|
||||
{
|
||||
@@ -161,9 +160,7 @@ public class Chainbinder : RoleBase
|
||||
|
||||
public override string GetSuffix(PlayerControl seer, PlayerControl target, bool hud = false, bool meeting = false)
|
||||
{
|
||||
if (seer.PlayerId != target.PlayerId || seer.PlayerId != ChainbinderId || (seer.IsModdedClient() && !hud) || meeting) return string.Empty;
|
||||
if (!HasLink) return string.Empty;
|
||||
|
||||
if (seer.PlayerId != target.PlayerId || seer.PlayerId != ChainbinderId || (seer.IsModdedClient() && !hud) || meeting || !HasLink) return string.Empty;
|
||||
return string.Format(GetString("Chainbinder.PairSuffix"), FirstTarget.ColoredPlayerName(), SecondTarget.ColoredPlayerName());
|
||||
}
|
||||
|
||||
@@ -177,7 +174,7 @@ public class Chainbinder : RoleBase
|
||||
|
||||
private void TryChainPlayers(PlayerControl binder, PlayerControl requestedTarget = null)
|
||||
{
|
||||
if (binder == null || binder.PlayerId != ChainbinderId || !binder.IsAlive()) return;
|
||||
if (!binder || binder.PlayerId != ChainbinderId || !binder.IsAlive()) return;
|
||||
|
||||
if (!TryGetTargets(binder, requestedTarget, out PlayerControl first, out PlayerControl second))
|
||||
{
|
||||
@@ -204,11 +201,11 @@ public class Chainbinder : RoleBase
|
||||
first = null;
|
||||
second = null;
|
||||
|
||||
List<PlayerControl> validTargets = Main.AllAlivePlayerControls
|
||||
List<PlayerControl> validTargets = Main.EnumerateAlivePlayerControls()
|
||||
.Where(x => IsValidTarget(binder, x))
|
||||
.ToList();
|
||||
|
||||
if (requestedTarget != null)
|
||||
if (requestedTarget)
|
||||
{
|
||||
if (!IsValidTarget(binder, requestedTarget)) return false;
|
||||
|
||||
@@ -220,7 +217,7 @@ public class Chainbinder : RoleBase
|
||||
.OrderBy(x => Vector2.Distance(x.Pos(), firstPosition))
|
||||
.FirstOrDefault();
|
||||
|
||||
return second != null;
|
||||
return second;
|
||||
}
|
||||
|
||||
PlayerControl[] closestTargets = validTargets
|
||||
@@ -237,7 +234,7 @@ public class Chainbinder : RoleBase
|
||||
|
||||
private bool IsValidTarget(PlayerControl binder, PlayerControl target)
|
||||
{
|
||||
if (binder == null || target == null || target.PlayerId == binder.PlayerId || !target.IsAlive()) return false;
|
||||
if (!binder || !target || target.PlayerId == binder.PlayerId || !target.IsAlive()) return false;
|
||||
if (Pelican.IsEaten(target.PlayerId) || target.onLadder || target.inMovingPlat || target.inVent) return false;
|
||||
if (!FastVector2.DistanceWithinRange(binder.Pos(), target.Pos(), BindRange.GetFloat())) return false;
|
||||
if (!CanBindImpostors.GetBool() && target.GetCustomRole().IsImpostor()) return false;
|
||||
@@ -248,7 +245,7 @@ public class Chainbinder : RoleBase
|
||||
{
|
||||
foreach (byte playerId in PlayerIdList)
|
||||
{
|
||||
if (playerId == ChainbinderId || !Main.PlayerStates.TryGetValue(playerId, out PlayerState state) || state.Role is not Chainbinder cb || !cb.HasLink) continue;
|
||||
if (playerId == ChainbinderId || !Main.PlayerStates.TryGetValue(playerId, out PlayerState state) || state.Role is not Chainbinder { HasLink: true } cb) continue;
|
||||
if (cb.FirstTarget == targetId || cb.SecondTarget == targetId) return true;
|
||||
}
|
||||
|
||||
@@ -257,7 +254,7 @@ public class Chainbinder : RoleBase
|
||||
|
||||
private static bool AreLinkedPlayersValid(PlayerControl first, PlayerControl second)
|
||||
{
|
||||
return first != null && second != null && first.IsAlive() && second.IsAlive() && !Pelican.IsEaten(first.PlayerId) && !Pelican.IsEaten(second.PlayerId);
|
||||
return first && second && first.IsAlive() && second.IsAlive() && !Pelican.IsEaten(first.PlayerId) && !Pelican.IsEaten(second.PlayerId);
|
||||
}
|
||||
|
||||
private void ClearLink(bool notifyBinder = false, bool sync = true, bool refresh = true)
|
||||
@@ -269,10 +266,10 @@ public class Chainbinder : RoleBase
|
||||
if (!hadLink) return;
|
||||
|
||||
PlayerControl binder = Utils.GetPlayerById(ChainbinderId);
|
||||
if (notifyBinder && binder != null && binder.IsAlive())
|
||||
if (notifyBinder && binder && binder.IsAlive())
|
||||
binder.Notify(GetString("Chainbinder.PairEnded"));
|
||||
|
||||
if (refresh && binder != null)
|
||||
if (refresh && binder)
|
||||
Utils.NotifyRoles(SpecifySeer: binder, SpecifyTarget: binder);
|
||||
}
|
||||
|
||||
@@ -286,7 +283,6 @@ public class Chainbinder : RoleBase
|
||||
|
||||
private void SyncState()
|
||||
{
|
||||
if (!Utils.DoRPC) return;
|
||||
Utils.SendRPC(CustomRPC.SyncRoleData, ChainbinderId, FirstTarget, SecondTarget);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
using AmongUs.GameOptions;
|
||||
using EHR.Modules;
|
||||
using UnityEngine;
|
||||
|
||||
namespace EHR.Roles;
|
||||
|
||||
using static EHR.Translator;
|
||||
using static Translator;
|
||||
|
||||
public class Jackpot : RoleBase
|
||||
{
|
||||
@@ -135,8 +134,6 @@ public class Jackpot : RoleBase
|
||||
|
||||
public override void OnMurder(PlayerControl killer, PlayerControl target)
|
||||
{
|
||||
if (killer == null || killer.PlayerId != JackpotId || !target) return;
|
||||
|
||||
Money += MoneyPerKill.GetInt();
|
||||
killer.Notify(string.Format(GetString("Jackpot.KillReward"), MoneyPerKill.GetInt(), Money));
|
||||
}
|
||||
@@ -148,8 +145,6 @@ public class Jackpot : RoleBase
|
||||
|
||||
public override void OnFixedUpdate(PlayerControl pc)
|
||||
{
|
||||
if (pc == null || pc.PlayerId != JackpotId) return;
|
||||
|
||||
if (!GameStates.IsInTask || !pc.IsAlive())
|
||||
{
|
||||
if (DomainActive || IsNearMissActive || IsJackpotActive)
|
||||
@@ -198,8 +193,6 @@ public class Jackpot : RoleBase
|
||||
|
||||
public override string GetProgressText(byte playerId, bool comms)
|
||||
{
|
||||
if (playerId != JackpotId) return string.Empty;
|
||||
|
||||
string text = IsJackpotActive
|
||||
? string.Format(GetString("Jackpot.Progress.Jackpot"), Money, Mathf.CeilToInt(JackpotTimer))
|
||||
: DomainActive
|
||||
@@ -211,7 +204,7 @@ public class Jackpot : RoleBase
|
||||
|
||||
private void StartDomain(PlayerControl player)
|
||||
{
|
||||
if (player == null || player.PlayerId != JackpotId || !player.IsAlive()) return;
|
||||
if (!player || player.PlayerId != JackpotId || !player.IsAlive()) return;
|
||||
|
||||
if (DomainActive || IsJackpotActive)
|
||||
{
|
||||
@@ -222,14 +215,14 @@ public class Jackpot : RoleBase
|
||||
|
||||
DomainActive = true;
|
||||
SpinTimer = SpinInterval.GetFloat();
|
||||
player.Notify($"{GetString("Jackpot.GameRulesTransmitted")}\n<size=80%>{string.Format(GetString("Jackpot.DomainStarted"), SpinInterval.GetFloat())}</size>", 6f);
|
||||
player.Notify($"{GetString("Jackpot.GameRulesTransmitted")}\n<size=80%>{string.Format(GetString("Jackpot.DomainStarted"), SpinInterval.GetFloat())}</size>");
|
||||
RefreshTemporaryBuffs(player, refreshCooldown: false);
|
||||
}
|
||||
|
||||
// Domain phase: rolls keep happening until the player jackpots, dies, or a meeting interrupts the sequence.
|
||||
private void ResolveSpin(PlayerControl player)
|
||||
{
|
||||
if (player == null || !player.IsAlive()) return;
|
||||
if (!player || !player.IsAlive()) return;
|
||||
|
||||
int roll = IRandom.Instance.Next(100);
|
||||
|
||||
@@ -287,7 +280,7 @@ public class Jackpot : RoleBase
|
||||
|
||||
Main.AllPlayerSpeed[JackpotId] = speed;
|
||||
|
||||
if (player == null || !player.IsAlive()) return;
|
||||
if (!player || !player.IsAlive()) return;
|
||||
|
||||
if (refreshCooldown) player.ResetKillCooldown();
|
||||
else player.MarkDirtySettings();
|
||||
@@ -308,7 +301,7 @@ public class Jackpot : RoleBase
|
||||
|
||||
RefreshTemporaryBuffs(player, refreshCooldown);
|
||||
|
||||
if (notifyEnd && wasJackpotActive && player != null && player.IsAlive())
|
||||
if (notifyEnd && wasJackpotActive && player && player.IsAlive())
|
||||
player.Notify(GetString("Jackpot.JackpotEnded"));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user