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..3ebaf06 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.Votes.Count > 0; + } }