Fix CastVote event issue

This commit is contained in:
ang-xd committed 2025-04-06 14:50:29 -04:00
1 parent 0d1d1891a8
commit e5b6400eb8
4 files changed
+92 -84

No files matched your search

@@ -24,6 +24,11 @@ public class HandleVoteEvent : MiraCancelableEvent
/// </summary>
public byte TargetId { get; }
/// <summary>
/// Gets a value indicating whether to prevent the vote from commencing.
/// </summary>
public bool PreventVote { get; }
/// <summary>
/// Gets the player info of the target.
/// </summary>
@@ -39,6 +44,7 @@ public class HandleVoteEvent : MiraCancelableEvent
VoteData = playerVoteData;
Player = playerVoteData.Owner;
TargetId = targetId;
PreventVote = false;
TargetPlayerInfo = GameData.Instance.GetPlayerById(targetId);
}
}
+5
View File
@@ -44,4 +44,9 @@ public enum MiraRpc : uint
/// Custom RPC to undo a player's vote.
/// </summary>
RemoveVote,
/// <summary>
/// Custom RPC to replace Among Us CastVote
/// </summary>
CastVote,
}
+10 -79
View File
@@ -8,6 +8,7 @@ using MiraAPI.Events.Vanilla.Meeting.Voting;
using MiraAPI.Modifiers;
using MiraAPI.Utilities;
using MiraAPI.Voting;
using Reactor.Utilities;
namespace MiraAPI.Patches.Voting;
@@ -140,84 +141,6 @@ internal static class MeetingHudPatches
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(MeetingHud.CastVote))]
public static bool CastVotePatch(MeetingHud __instance, byte srcPlayerId, byte suspectPlayerId)
{
var plr = GameData.Instance.GetPlayerById(srcPlayerId);
if (plr == null)
{
return false;
}
var voteData = plr.Object.GetVoteData();
if (voteData == null || voteData.VotesRemaining == 0 ||
voteData.VotedFor(suspectPlayerId))
{
return false;
}
VotingUtils.HandleVote(voteData, suspectPlayerId);
__instance.SetDirtyBit(1U);
__instance.CheckForEndVoting();
if (voteData.VotesRemaining != 0)
{
return false;
}
__instance.playerStates.First(x => x.TargetPlayerId == srcPlayerId).SetVote(suspectPlayerId);
PlayerControl.LocalPlayer.RpcSendChatNote(srcPlayerId, ChatNoteTypes.DidVote);
return false;
}
[HarmonyPostfix]
[HarmonyPatch(nameof(MeetingHud.CmdCastVote))]
public static void CmdCastVotePatch(MeetingHud __instance, byte playerId, byte suspectIdx)
{
var plr = GameData.Instance.GetPlayerById(playerId);
var voteData = plr.Object.GetVoteData();
if (!AmongUsClient.Instance.AmHost)
{
if (voteData == null || voteData.VotesRemaining == 0 ||
voteData.VotedFor(suspectIdx))
{
return;
}
VotingUtils.HandleVote(voteData, suspectIdx);
if (voteData.VotesRemaining == 0)
{
__instance.playerStates.First(x => x.TargetPlayerId == playerId).SetVote(suspectIdx);
}
}
if (PlayerControl.LocalPlayer.PlayerId != playerId)
{
return;
}
SoundManager.Instance.PlaySound(__instance.VoteLockinSound, false);
foreach (var playerVoteArea in __instance.playerStates)
{
playerVoteArea.ClearButtons();
}
__instance.SkipVoteButton.ClearButtons();
var localVoteData = PlayerControl.LocalPlayer.GetVoteData();
if (localVoteData == null || localVoteData.VotesRemaining != 0) return;
__instance.SkipVoteButton.voteComplete = true;
__instance.SkipVoteButton.gameObject.SetActive(false);
}
[HarmonyPrefix]
[HarmonyPatch(nameof(MeetingHud.CheckForEndVoting))]
public static bool EndCheck(MeetingHud __instance)
@@ -280,12 +203,20 @@ internal static class MeetingHudPatches
return false;
}
// TODO: figure out a way to do host-authorization since right now any player can send RpcCastVote
[HarmonyPrefix]
[HarmonyPatch(nameof(MeetingHud.CmdCastVote))]
public static bool CmdCastVoteOverridePatch(MeetingHud __instance, byte playerId, byte suspectIdx)
{
VotingUtils.RpcCastVote(PlayerControl.LocalPlayer, playerId, suspectIdx);
return false;
}
[HarmonyPrefix]
[HarmonyPatch(nameof(MeetingHud.Confirm))]
public static bool ConfirmPatch(MeetingHud __instance, [HarmonyArgument(0)] byte suspect)
{
__instance.CmdCastVote(PlayerControl.LocalPlayer.PlayerId, suspect);
return false;
}
}
+71 -5
View File
@@ -41,16 +41,26 @@ public static class VotingUtils
/// </summary>
/// <param name="voteData">The player's vote data.</param>
/// <param name="suspectIdx">Who the player voted for.</param>
public static void HandleVote(PlayerVoteData voteData, byte suspectIdx)
/// <param name="cancelVote">Whether you want the vote to commence or not.</param>
private static void HandleVote(PlayerVoteData voteData, byte suspectIdx, out bool cancelVote)
{
var @event = new HandleVoteEvent(voteData, suspectIdx);
MiraEventManager.InvokeEvent(@event);
cancelVote = @event.PreventVote;
if (@event.IsCancelled)
{
return;
}
if (voteData.VotesRemaining == 0 ||
voteData.VotedFor(suspectIdx))
{
cancelVote = true;
return;
}
voteData.DecreaseRemainingVotes(1);
voteData.VoteForPlayer(suspectIdx);
}
@@ -64,10 +74,7 @@ public static class VotingUtils
[MethodRpc((uint)MiraRpc.RemoveVote)]
public static void RpcRemoveVote(PlayerControl source, byte voterId, byte votedFor)
{
if (source.OwnerId != AmongUsClient.Instance.HostId)
{
return;
}
if (!source.IsHost()) return;
MeetingHud.Instance.playerStates.First(state => state.TargetPlayerId == voterId).UnsetVote();
@@ -94,6 +101,65 @@ public static class VotingUtils
MeetingHud.Instance.SkipVoteButton.gameObject.SetActive(true);
}
/// <summary>
/// Networks the casting of a vote. We replace the vanilla solution with a custom version that works for the use case.
/// </summary>
/// <param name="source">The player who sent this RPC.</param>
/// <param name="srcPlayerId">The id of the player who casted the vote.</param>
/// <param name="suspectPlayerId">The voted player's id.</param>
[MethodRpc((uint)MiraRpc.CastVote)]
public static void RpcCastVote(PlayerControl source, byte srcPlayerId, byte suspectPlayerId)
{
CustomCastVote(srcPlayerId, suspectPlayerId);
}
private static void CustomCastVote(byte srcPlayerId, byte suspectPlayerId)
{
var plr = GameData.Instance.GetPlayerById(srcPlayerId);
if (!plr) return;
var voteData = plr.Object.GetVoteData();
if (!voteData) return;
HandleVote(voteData, suspectPlayerId, out var cancelVote);
// Handle local behaviour for the voter (for some reason checking AmOwner does not work for host)
if (PlayerControl.LocalPlayer.PlayerId == srcPlayerId)
{
if (!cancelVote) SoundManager.Instance.PlaySound(MeetingHud.Instance.VoteLockinSound, false);
foreach (var playerVoteArea in MeetingHud.Instance.playerStates)
{
playerVoteArea.ClearButtons();
}
MeetingHud.Instance.SkipVoteButton.ClearButtons();
var localVoteData = PlayerControl.LocalPlayer.GetVoteData();
if (!localVoteData || localVoteData.VotesRemaining != 0) return;
MeetingHud.Instance.SkipVoteButton.voteComplete = true;
MeetingHud.Instance.SkipVoteButton.gameObject.SetActive(false);
}
if (cancelVote) return;
// If player has no more votes, then make it show that the player has used all their votes.
if (voteData.VotesRemaining == 0)
{
MeetingHud.Instance.playerStates.First(x => x.TargetPlayerId == srcPlayerId).SetVote(suspectPlayerId);
}
// If host, then check end voting, and set votes/send chat if applicable
if (!PlayerControl.LocalPlayer.IsHost()) return;
MeetingHud.Instance.SetDirtyBit(1U);
MeetingHud.Instance.CheckForEndVoting();
if (voteData.VotesRemaining != 0) return;
PlayerControl.LocalPlayer.RpcSendChatNote(srcPlayerId, ChatNoteTypes.DidVote);
}
/// <summary>
/// Calculates the total number of votes.
/// </summary>