using System; using System.Collections; using System.Linq; using Assets.CoreScripts; using Hazel; using InnerNet; using UnityEngine; public class MeetingHud : InnerNetObject, IDisconnectHandler { private const float ResultsTime = 5f; private const float Depth = -100f; public static MeetingHud Instance; public Transform ButtonParent; public TextRenderer TitleText; public Vector3 VoteOrigin = new Vector3(-3.6f, 1.75f); public Vector3 VoteButtonOffsets = new Vector2(3.6f, -0.91f); private Vector3 CounterOrigin = new Vector2(0.5f, -0.13f); private Vector3 CounterOffsets = new Vector2(0.3f, 0f); public PlayerVoteArea SkipVoteButton; [HideInInspector] private PlayerVoteArea[] playerStates; public PlayerVoteArea PlayerButtonPrefab; public SpriteRenderer PlayerVotePrefab; public Sprite CrackedGlass; public SpriteRenderer Glass; public PassiveButton ProceedButton; public ExileController ExileCutscenePrefab; public AudioClip VoteSound; public AudioClip VoteLockinSound; public AudioClip VoteEndingSound; private MeetingHud.VoteStates state; public SpriteRenderer SkippedVoting; public SpriteRenderer HostIcon; public Sprite KillBackground; private GameData.PlayerInfo exiledPlayer; private bool wasTie; public TextRenderer TimerText; public float discussionTimer; private byte reporterId; private bool amDead; private float resultsStartedAt; private int lastSecond = 10; public enum VoteStates { Discussion, NotVoted, Voted, Results, Proceeding } private void Awake() { if (!MeetingHud.Instance) { MeetingHud.Instance = this; return; } if (MeetingHud.Instance != this) { UnityEngine.Object.Destroy(base.gameObject); } } private void Start() { DestroyableSingleton.Instance.Chat.gameObject.SetActive(true); DestroyableSingleton.Instance.Chat.SetPosition(this); DestroyableSingleton.Instance.StopOxyFlash(); DestroyableSingleton.Instance.StopReactorFlash(); this.SkipVoteButton.TargetPlayerId = -1; this.SkipVoteButton.Parent = this; Camera.main.GetComponent().Locked = true; if (PlayerControl.LocalPlayer.Data.IsDead) { this.SetForegroundForDead(); } AmongUsClient.Instance.DisconnectHandlers.AddUnique(this); } private void SetForegroundForDead() { this.amDead = true; this.SkipVoteButton.gameObject.SetActive(false); this.Glass.sprite = this.CrackedGlass; this.Glass.color = Color.white; } public void Update() { this.discussionTimer += Time.deltaTime; this.UpdateButtons(); switch (this.state) { case MeetingHud.VoteStates.Discussion: { if (this.discussionTimer < (float)PlayerControl.GameOptions.DiscussionTime) { float f = (float)PlayerControl.GameOptions.DiscussionTime - this.discussionTimer; this.TimerText.Text = string.Format("Voting Begins In: {0}s", Mathf.CeilToInt(f)); for (int i = 0; i < this.playerStates.Length; i++) { this.playerStates[i].SetDisabled(); } this.SkipVoteButton.SetDisabled(); return; } this.state = MeetingHud.VoteStates.NotVoted; bool active = PlayerControl.GameOptions.VotingTime > 0; this.TimerText.gameObject.SetActive(active); for (int j = 0; j < this.playerStates.Length; j++) { this.playerStates[j].SetEnabled(); } this.SkipVoteButton.SetEnabled(); return; } case MeetingHud.VoteStates.NotVoted: case MeetingHud.VoteStates.Voted: if (PlayerControl.GameOptions.VotingTime > 0) { float num = this.discussionTimer - (float)PlayerControl.GameOptions.DiscussionTime; float f2 = Mathf.Max(0f, (float)PlayerControl.GameOptions.VotingTime - num); this.TimerText.Text = string.Format("Voting Ends In: {0}s", Mathf.CeilToInt(f2)); if (this.state == MeetingHud.VoteStates.NotVoted && Mathf.CeilToInt(f2) <= this.lastSecond) { this.lastSecond--; base.StartCoroutine(Effects.PulseColor(this.TimerText, Color.red, Color.white, 0.25f)); SoundManager.Instance.PlaySound(this.VoteEndingSound, false, 1f).pitch = Mathf.Lerp(1.5f, 0.8f, (float)this.lastSecond / 10f); } if (AmongUsClient.Instance.AmHost && num >= (float)PlayerControl.GameOptions.VotingTime) { this.ForceSkipAll(); return; } } break; case MeetingHud.VoteStates.Results: if (AmongUsClient.Instance.GameMode == GameModes.OnlineGame) { float num2 = this.discussionTimer - this.resultsStartedAt; float num3 = Mathf.Max(0f, 5f - num2); this.TimerText.Text = string.Format("Proceeding In: {0}s", Mathf.CeilToInt(num3)); if (AmongUsClient.Instance.AmHost && num3 <= 0f) { this.HandleProceed(); } } break; default: return; } } public IEnumerator CoIntro(PlayerControl reporter, GameData.PlayerInfo targetPlayer) { if (DestroyableSingleton.InstanceExists) { DestroyableSingleton.Instance.Chat.ForceClosed(); base.transform.SetParent(DestroyableSingleton.Instance.transform); base.transform.localPosition = new Vector3(0f, -10f, -100f); DestroyableSingleton.Instance.SetHudActive(false); } OverlayKillAnimation killAnimPrefab = (targetPlayer == null) ? DestroyableSingleton.Instance.KillOverlay.EmergencyOverlay : DestroyableSingleton.Instance.KillOverlay.ReportOverlay; DestroyableSingleton.Instance.KillOverlay.ShowOne(killAnimPrefab, reporter, targetPlayer); yield return DestroyableSingleton.Instance.KillOverlay.WaitForFinish(); Vector3 temp = new Vector3(0f, 0f, -50f); for (float timer = 0f; timer < 0.25f; timer += Time.deltaTime) { float t = timer / 0.25f; temp.y = Mathf.SmoothStep(-10f, 0f, t); base.transform.localPosition = temp; yield return null; } temp.y = 0f; base.transform.localPosition = temp; this.TitleText.Text = "Who Is The Impostor?"; if (!PlayerControl.LocalPlayer.Data.IsDead) { yield return DestroyableSingleton.Instance.ShowEmblem(false); } for (int i = 0; i < this.playerStates.Length; i++) { base.StartCoroutine(this.playerStates[i].CoAnimateOverlay()); } yield break; } private IEnumerator CoStartCutscene() { yield return DestroyableSingleton.Instance.CoFadeFullScreen(Color.clear, Color.black, 1f); ExileController exileController = UnityEngine.Object.Instantiate(this.ExileCutscenePrefab); exileController.transform.SetParent(DestroyableSingleton.Instance.transform, false); exileController.transform.localPosition = new Vector3(0f, 0f, -60f); exileController.Begin(this.exiledPlayer, this.wasTie); this.DespawnOnDestroy = false; UnityEngine.Object.Destroy(base.gameObject); yield break; } public void ServerStart(byte reporter) { this.reporterId = reporter; this.PopulateButtons(reporter); } public void Close() { GameData.PlayerInfo data = PlayerControl.LocalPlayer.Data; DestroyableSingleton.Instance.Chat.SetPosition(null); DestroyableSingleton.Instance.Chat.SetVisible(data.IsDead); base.StartCoroutine(this.CoStartCutscene()); } private void VotingComplete(byte[] states, GameData.PlayerInfo exiled, bool tie) { if (this.state == MeetingHud.VoteStates.Results) { return; } this.state = MeetingHud.VoteStates.Results; this.resultsStartedAt = this.discussionTimer; this.exiledPlayer = exiled; this.wasTie = tie; this.SkipVoteButton.gameObject.SetActive(false); this.SkippedVoting.gameObject.SetActive(true); AmongUsClient.Instance.DisconnectHandlers.Remove(this); this.PopulateResults(states); this.SetupProceedButton(); } public bool Select(int suspectStateIdx) { if (this.discussionTimer < (float)PlayerControl.GameOptions.DiscussionTime) { return false; } if (PlayerControl.LocalPlayer.Data.IsDead) { return false; } SoundManager.Instance.PlaySound(this.VoteSound, false, 1f).volume = 0.8f; for (int i = 0; i < this.playerStates.Length; i++) { PlayerVoteArea playerVoteArea = this.playerStates[i]; if (suspectStateIdx != (int)playerVoteArea.TargetPlayerId) { playerVoteArea.ClearButtons(); } } if (suspectStateIdx != -1) { this.SkipVoteButton.ClearButtons(); } return true; } public void Confirm(sbyte suspectStateIdx) { if (PlayerControl.LocalPlayer.Data.IsDead) { return; } for (int i = 0; i < this.playerStates.Length; i++) { PlayerVoteArea playerVoteArea = this.playerStates[i]; playerVoteArea.ClearButtons(); playerVoteArea.voteComplete = true; } this.SkipVoteButton.ClearButtons(); this.SkipVoteButton.voteComplete = true; this.SkipVoteButton.gameObject.SetActive(false); MeetingHud.VoteStates voteStates = this.state; if (voteStates != MeetingHud.VoteStates.NotVoted) { return; } this.state = MeetingHud.VoteStates.Voted; this.CmdCastVote(PlayerControl.LocalPlayer.PlayerId, suspectStateIdx); } public void HandleDisconnect(PlayerControl pc, DisconnectReasons reason) { if (!AmongUsClient.Instance.AmHost) { return; } int num = this.playerStates.IndexOf((PlayerVoteArea pv) => pv.TargetPlayerId == (sbyte)pc.PlayerId); PlayerVoteArea playerVoteArea = this.playerStates[num]; playerVoteArea.isDead = true; playerVoteArea.Overlay.gameObject.SetActive(true); for (int i = 0; i < this.playerStates.Length; i++) { PlayerVoteArea playerVoteArea2 = this.playerStates[i]; if (!playerVoteArea2.isDead && playerVoteArea2.didVote && playerVoteArea2.votedFor == (sbyte)pc.PlayerId) { playerVoteArea2.UnsetVote(); base.SetDirtyBit(1U << i); GameData.PlayerInfo playerById = GameData.Instance.GetPlayerById((byte)playerVoteArea2.TargetPlayerId); if (playerById != null) { int clientIdFromCharacter = AmongUsClient.Instance.GetClientIdFromCharacter(playerById.Object); if (clientIdFromCharacter != -1) { this.RpcClearVote(clientIdFromCharacter); } } } } base.SetDirtyBit(1U << num); this.CheckForEndVoting(); if (this.state == MeetingHud.VoteStates.Results) { this.SetupProceedButton(); } } public void HandleDisconnect() { } private void ForceSkipAll() { for (int i = 0; i < this.playerStates.Length; i++) { PlayerVoteArea playerVoteArea = this.playerStates[i]; if (!playerVoteArea.didVote) { playerVoteArea.didVote = true; playerVoteArea.votedFor = -2; base.SetDirtyBit(1U << i); } } this.CheckForEndVoting(); } public void CastVote(byte srcPlayerId, sbyte suspectPlayerId) { int num = this.playerStates.IndexOf((PlayerVoteArea pv) => pv.TargetPlayerId == (sbyte)srcPlayerId); PlayerVoteArea playerVoteArea = this.playerStates[num]; if (!playerVoteArea.isDead && !playerVoteArea.didVote) { if (PlayerControl.LocalPlayer.PlayerId == srcPlayerId || AmongUsClient.Instance.GameMode != GameModes.LocalGame) { SoundManager.Instance.PlaySound(this.VoteLockinSound, false, 1f); } playerVoteArea.SetVote(suspectPlayerId); base.SetDirtyBit(1U << num); this.CheckForEndVoting(); PlayerControl.LocalPlayer.RpcSendChatNote(srcPlayerId, ChatNoteTypes.DidVote); } } public void ClearVote() { for (int i = 0; i < this.playerStates.Length; i++) { this.playerStates[i].voteComplete = false; } this.SkipVoteButton.voteComplete = false; this.SkipVoteButton.gameObject.SetActive(true); this.state = MeetingHud.VoteStates.NotVoted; } private void CheckForEndVoting() { if (this.playerStates.All((PlayerVoteArea ps) => ps.isDead || ps.didVote)) { byte[] self = this.CalculateVotes(); bool tie; int maxIdx = self.IndexOfMax((byte p) => (int)p, out tie) - 1; GameData.PlayerInfo exiled = GameData.Instance.AllPlayers.FirstOrDefault((GameData.PlayerInfo v) => (int)v.PlayerId == maxIdx); byte[] states = (from ps in this.playerStates select ps.GetState()).ToArray(); this.RpcVotingComplete(states, exiled, tie); } } private byte[] CalculateVotes() { byte[] array = new byte[11]; for (int i = 0; i < this.playerStates.Length; i++) { PlayerVoteArea playerVoteArea = this.playerStates[i]; if (playerVoteArea.didVote) { int num = (int)(playerVoteArea.votedFor + 1); if (num >= 0 && num < array.Length) { byte[] array2 = array; int num2 = num; array2[num2] += 1; } } } return array; } public override bool Serialize(MessageWriter writer, bool initialState) { if (this.playerStates == null) { return false; } if (initialState) { for (int i = 0; i < this.playerStates.Length; i++) { this.playerStates[i].Serialize(writer); } } else { writer.WritePacked(this.DirtyBits); for (int j = 0; j < this.playerStates.Length; j++) { if ((this.DirtyBits & 1U << j) != 0U) { this.playerStates[j].Serialize(writer); } } } this.DirtyBits = 0U; return true; } public override void Deserialize(MessageReader reader, bool initialState) { if (initialState) { MeetingHud.Instance = this; this.PopulateButtons(0); for (int i = 0; i < this.playerStates.Length; i++) { PlayerVoteArea playerVoteArea = this.playerStates[i]; playerVoteArea.Deserialize(reader); if (playerVoteArea.didReport) { this.reporterId = (byte)playerVoteArea.TargetPlayerId; } } return; } uint num = reader.ReadPackedUInt32(); for (int j = 0; j < this.playerStates.Length; j++) { if ((num & 1U << j) != 0U) { this.playerStates[j].Deserialize(reader); } } } public void HandleProceed() { if (!AmongUsClient.Instance.AmHost) { base.StartCoroutine(Effects.Shake(this.HostIcon.transform, 0.75f, 0.25f)); return; } if (this.state != MeetingHud.VoteStates.Results) { return; } this.state = MeetingHud.VoteStates.Proceeding; this.RpcClose(); } private void SetupProceedButton() { if (AmongUsClient.Instance.GameMode != GameModes.OnlineGame) { this.TimerText.gameObject.SetActive(false); this.ProceedButton.gameObject.SetActive(true); this.HostIcon.gameObject.SetActive(true); GameData.PlayerInfo host = GameData.Instance.GetHost(); if (host != null) { PlayerControl.SetPlayerMaterialColors((int)host.ColorId, this.HostIcon); return; } this.HostIcon.enabled = false; } } private void PopulateResults(byte[] states) { DestroyableSingleton.Instance.WriteMeetingEnded(states, this.discussionTimer); this.TitleText.Text = "Voting Results"; int num = 0; for (int i = 0; i < this.playerStates.Length; i++) { PlayerVoteArea playerVoteArea = this.playerStates[i]; playerVoteArea.ClearForResults(); int num2 = 0; for (int j = 0; j < this.playerStates.Length; j++) { if (!states[j].HasAnyBit(128)) { GameData.PlayerInfo playerById = GameData.Instance.GetPlayerById((byte)this.playerStates[j].TargetPlayerId); int num3 = (int)((states[j] & 15) - 1); if (num3 == (int)playerVoteArea.TargetPlayerId) { SpriteRenderer spriteRenderer = UnityEngine.Object.Instantiate(this.PlayerVotePrefab); PlayerControl.SetPlayerMaterialColors((int)playerById.ColorId, spriteRenderer); spriteRenderer.transform.SetParent(playerVoteArea.transform); spriteRenderer.transform.localPosition = this.CounterOrigin + new Vector3(this.CounterOffsets.x * (float)num2, 0f, 0f); spriteRenderer.transform.localScale = Vector3.zero; base.StartCoroutine(Effects.Bloop((float)num2 * 0.5f, spriteRenderer.transform, 0.5f)); num2++; } else if (i == 0 && num3 == -1) { SpriteRenderer spriteRenderer2 = UnityEngine.Object.Instantiate(this.PlayerVotePrefab); PlayerControl.SetPlayerMaterialColors((int)playerById.ColorId, spriteRenderer2); spriteRenderer2.transform.SetParent(this.SkippedVoting.transform); spriteRenderer2.transform.localPosition = this.CounterOrigin + new Vector3(this.CounterOffsets.x * (float)num, 0f, 0f); spriteRenderer2.transform.localScale = Vector3.zero; base.StartCoroutine(Effects.Bloop((float)num * 0.5f, spriteRenderer2.transform, 0.5f)); num++; } } } } } private void UpdateButtons() { if (PlayerControl.LocalPlayer.Data.IsDead && !this.amDead) { this.SetForegroundForDead(); } if (AmongUsClient.Instance.AmHost) { for (int i = 0; i < this.playerStates.Length; i++) { PlayerVoteArea playerVoteArea = this.playerStates[i]; GameData.PlayerInfo playerById = GameData.Instance.GetPlayerById((byte)playerVoteArea.TargetPlayerId); if (playerById == null) { playerVoteArea.SetDisabled(); } else { bool flag = playerById.Disconnected || playerById.IsDead; if (flag != playerVoteArea.isDead) { playerVoteArea.SetDead(playerById.PlayerId == PlayerControl.LocalPlayer.PlayerId, this.reporterId == playerById.PlayerId, flag); base.SetDirtyBit(1U << i); } } } } } private void PopulateButtons(byte reporter) { this.playerStates = new PlayerVoteArea[GameData.Instance.PlayerCount]; for (int i = 0; i < this.playerStates.Length; i++) { GameData.PlayerInfo playerInfo = GameData.Instance.AllPlayers[i]; PlayerVoteArea playerVoteArea = this.playerStates[i] = this.CreateButton(playerInfo); playerVoteArea.Parent = this; playerVoteArea.TargetPlayerId = (sbyte)playerInfo.PlayerId; playerVoteArea.SetDead(playerInfo.PlayerId == PlayerControl.LocalPlayer.PlayerId, reporter == playerInfo.PlayerId, playerInfo.Disconnected || playerInfo.IsDead); } this.SortButtons(); } private void SortButtons() { PlayerVoteArea[] array = this.playerStates.OrderBy(delegate(PlayerVoteArea p) { if (!p.isDead) { return 0; } return 50; }).ThenBy((PlayerVoteArea p) => p.TargetPlayerId).ToArray(); for (int i = 0; i < array.Length; i++) { int num = i % 2; int num2 = i / 2; array[i].transform.localPosition = this.VoteOrigin + new Vector3(this.VoteButtonOffsets.x * (float)num, this.VoteButtonOffsets.y * (float)num2, -1f); } } private PlayerVoteArea CreateButton(GameData.PlayerInfo playerInfo) { PlayerVoteArea playerVoteArea = UnityEngine.Object.Instantiate(this.PlayerButtonPrefab, this.ButtonParent.transform); PlayerControl.SetPlayerMaterialColors((int)playerInfo.ColorId, playerVoteArea.PlayerIcon); playerVoteArea.NameText.Text = playerInfo.PlayerName; bool flag = PlayerControl.LocalPlayer.Data.IsImpostor && playerInfo.IsImpostor; playerVoteArea.NameText.Color = (flag ? Palette.ImpostorRed : Color.white); playerVoteArea.transform.localScale = Vector3.one; return playerVoteArea; } public bool DidVote(byte playerId) { return this.playerStates.First((PlayerVoteArea p) => p.TargetPlayerId == (sbyte)playerId).didVote; } public int GetVotesRemaining() { int result; try { result = this.playerStates.Count((PlayerVoteArea ps) => !ps.isDead && !ps.didVote); } catch { result = 0; } return result; } private enum RpcCalls { Close, VotingComplete, CastVote, ClearVote } public void RpcClose() { if (AmongUsClient.Instance.AmClient) { this.Close(); } AmongUsClient.Instance.SendRpc(this.NetId, 0, SendOption.Reliable); } public void CmdCastVote(byte playerId, sbyte suspectIdx) { if (AmongUsClient.Instance.AmHost) { this.CastVote(playerId, suspectIdx); return; } MessageWriter messageWriter = AmongUsClient.Instance.StartRpcImmediately(this.NetId, 2, SendOption.Reliable, AmongUsClient.Instance.HostId); messageWriter.Write(playerId); messageWriter.Write(suspectIdx); AmongUsClient.Instance.FinishRpcImmediately(messageWriter); } private void RpcVotingComplete(byte[] states, GameData.PlayerInfo exiled, bool tie) { if (AmongUsClient.Instance.AmClient) { this.VotingComplete(states, exiled, tie); } MessageWriter messageWriter = AmongUsClient.Instance.StartRpc(this.NetId, 1, SendOption.Reliable); messageWriter.WriteBytesAndSize(states); messageWriter.Write((exiled != null) ? exiled.PlayerId : byte.MaxValue); messageWriter.Write(tie); messageWriter.EndMessage(); } private void RpcClearVote(int clientId) { if (AmongUsClient.Instance.ClientId == clientId) { this.ClearVote(); return; } MessageWriter msg = AmongUsClient.Instance.StartRpcImmediately(this.NetId, 3, SendOption.Reliable, clientId); AmongUsClient.Instance.FinishRpcImmediately(msg); } public override void HandleRpc(byte callId, MessageReader reader) { switch (callId) { case 0: this.Close(); return; case 1: { byte[] states = reader.ReadBytesAndSize(); GameData.PlayerInfo playerById = GameData.Instance.GetPlayerById(reader.ReadByte()); bool tie = reader.ReadBoolean(); this.VotingComplete(states, playerById, tie); return; } case 2: { byte srcPlayerId = reader.ReadByte(); sbyte suspectPlayerId = reader.ReadSByte(); this.CastVote(srcPlayerId, suspectPlayerId); return; } case 3: this.ClearVote(); return; default: return; } } }