diff --git a/MiraAPI/Events/Vanilla/Meeting/Voting/DummyVoteEvent.cs b/MiraAPI/Events/Vanilla/Meeting/Voting/DummyVoteEvent.cs
new file mode 100644
index 0000000..4b31694
--- /dev/null
+++ b/MiraAPI/Events/Vanilla/Meeting/Voting/DummyVoteEvent.cs
@@ -0,0 +1,44 @@
+using System;
+using MiraAPI.Utilities;
+using MiraAPI.Voting;
+
+namespace MiraAPI.Events.Vanilla.Meeting.Voting;
+
+///
+/// Invoked before the dummy selects their vote. If canceled, the dummy won't vote.
+///
+public class DummyVoteEvent : MiraCancelableEvent
+{
+ ///
+ /// Gets the dummy that needs to vote
+ ///
+ public DummyBehaviour Dummy { get; }
+
+ ///
+ /// Gets the instance of the dummy's vote data.
+ ///
+ public PlayerVoteData VoteData { get; }
+
+ ///
+ /// Gets or sets a value indicating whether the dummy can vote to skip the meeting.
+ ///
+ public bool CanSkip { get; set; }
+
+ ///
+ /// Gets or sets a predicate for validating whether the dummy can vote on a player.
+ /// By default, excludes dead and disconnected players.
+ ///
+ public Predicate PlayerIsValid { get; set; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The voting dummy.
+ public DummyVoteEvent(DummyBehaviour dummy)
+ {
+ Dummy = dummy;
+ VoteData = dummy.myPlayer.GetVoteData();
+ CanSkip = true;
+ PlayerIsValid = x => !x.Data.IsDead && !x.Data.Disconnected;
+ }
+}
diff --git a/MiraAPI/Patches/Voting/DummyBehaviourPatch.cs b/MiraAPI/Patches/Voting/DummyBehaviourPatch.cs
index a276946..04f3461 100644
--- a/MiraAPI/Patches/Voting/DummyBehaviourPatch.cs
+++ b/MiraAPI/Patches/Voting/DummyBehaviourPatch.cs
@@ -1,16 +1,71 @@
-using HarmonyLib;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using HarmonyLib;
+using MiraAPI.Events;
+using MiraAPI.Events.Vanilla.Meeting.Voting;
using MiraAPI.Utilities;
+using MiraAPI.Voting;
+using Reactor.Utilities;
+using Reactor.Utilities.Extensions;
+using UnityEngine;
namespace MiraAPI.Patches.Voting;
-[HarmonyPatch(typeof(DummyBehaviour), nameof(DummyBehaviour.Update))]
-internal static class DummyBehaviourPatch
+[HarmonyPatch(typeof(DummyBehaviour))]
+internal static class DummyBehaviourPatches
{
- public static void Postfix(DummyBehaviour __instance)
+ [HarmonyPrefix]
+ [HarmonyPatch(nameof(DummyBehaviour.Update))]
+ public static bool DummyUpdatePatch(DummyBehaviour __instance)
{
- if (MeetingHud.Instance && __instance.myPlayer.GetVoteData())
+ NetworkedPlayerInfo data = __instance.myPlayer.Data;
+ if (data == null || data.IsDead) return false;
+
+ if (MeetingHud.Instance && !__instance.DidVote())
{
- __instance.voted = __instance.myPlayer.GetVoteData().VotesRemaining == 0;
+ Coroutines.Start(CoDoVote(__instance));
+ }
+
+ return false;
+ }
+
+ private static IEnumerator CoDoVote(DummyBehaviour dummy)
+ {
+ dummy.voted = true;
+ yield return new WaitForSeconds(dummy.voteTime.Next());
+ if (DidVote(dummy)) yield break;
+
+ var dummyVoteEvent = new DummyVoteEvent(dummy);
+ MiraEventManager.InvokeEvent(dummyVoteEvent);
+
+ if (dummyVoteEvent.IsCancelled)
+ {
+ dummy.voted = false;
+ yield break;
+ }
+
+ List potentialSuspects = new();
+ potentialSuspects.AddRange(PlayerControl.AllPlayerControls
+ .ToArray()
+ .Where(p => p != dummy.myPlayer)
+ .Where(p => dummyVoteEvent.PlayerIsValid(p))
+ .Select(p => p.PlayerId));
+
+ if (dummyVoteEvent.CanSkip)
+ {
+ potentialSuspects.Add(253);
+ }
+
+ if (dummy.myPlayer.GetVoteData().VotesRemaining > 0)
+ {
+ VotingUtils.RpcCastVote(PlayerControl.LocalPlayer, dummy.myPlayer.PlayerId, potentialSuspects.Random());
}
}
+
+ private static bool DidVote(this DummyBehaviour dummy)
+ {
+ var dummyVoteData = dummy.myPlayer.GetVoteData();
+ return dummyVoteData.VotesRemaining > 0;
+ }
}