mirror of
https://github.com/All-Of-Us-Mods/MiraAPI.git
synced 2026-10-20 01:46:15 +00:00
Add (unsynced) in-game timebar + adjust ui pos
This commit is contained in:
1 parent
d1d11e2bec
commit
95819cf83a
6 files changed
+410
-4
No files matched your search
@@ -218,6 +218,20 @@ public abstract class AbstractGameMode : IOptionable
|
||||
/// <returns>True if the sabotage map should be shown.</returns>
|
||||
public virtual bool ShouldShowSabotageMap(MapBehaviour map) => true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="MapOptions"/> to display on the map.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="MapOptions"/> for the gamemode.</returns>
|
||||
public virtual MapOptions GetMapOptions()
|
||||
{
|
||||
return new MapOptions
|
||||
{
|
||||
Mode = PlayerControl.LocalPlayer.Data.Role.IsImpostor && !MeetingHud.Instance
|
||||
? MapOptions.Modes.Sabotage
|
||||
: MapOptions.Modes.Normal,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Can a player vent in this gamemode.
|
||||
/// </summary>
|
||||
@@ -226,6 +240,52 @@ public abstract class AbstractGameMode : IOptionable
|
||||
/// <returns>True if venting is enabled in this mode.</returns>
|
||||
public virtual bool CanVent(Vent vent, NetworkedPlayerInfo playerInfo) => true;
|
||||
|
||||
/// <summary>
|
||||
/// Does the task bar appear in the gamemode.
|
||||
/// </summary>
|
||||
/// <returns>True if the task bar is enabled in this mode.</returns>
|
||||
public virtual bool ShowTaskBar => true;
|
||||
|
||||
public virtual void UpdateTaskPanel(TaskPanelBehaviour instance)
|
||||
{
|
||||
instance.background.transform.localScale = (instance.taskText.textBounds.size.x > 0f)
|
||||
? new Vector3(instance.taskText.textBounds.size.x + 0.2f, instance.taskText.textBounds.size.y + 0.2f, 1f)
|
||||
: Vector3.zero;
|
||||
Vector3 vector = instance.background.sprite.bounds.extents;
|
||||
vector.y = -vector.y;
|
||||
vector = vector.Mul(instance.background.transform.localScale);
|
||||
instance.background.transform.localPosition = vector;
|
||||
Vector3 vector2 = instance.tab.sprite.bounds.extents;
|
||||
vector2 = vector2.Mul(instance.tab.transform.localScale);
|
||||
vector2.y = -vector2.y;
|
||||
vector2.x += vector.x * 2f;
|
||||
instance.tab.transform.localPosition = vector2;
|
||||
if (GameManager.Instance == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var yPos = 0.6f;
|
||||
var xPos = -instance.background.sprite.bounds.size.x * instance.background.transform.localScale.x;
|
||||
instance.closedPosition = new Vector3(xPos, yPos, instance.closedPosition.z);
|
||||
instance.openPosition = new Vector3(instance.openPosition.x, yPos, instance.openPosition.z);
|
||||
if (instance.open)
|
||||
{
|
||||
instance.timer = Mathf.Min(1f, instance.timer + Time.deltaTime / instance.animationTimeSeconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
instance.timer = Mathf.Max(0f, instance.timer - Time.deltaTime / instance.animationTimeSeconds);
|
||||
}
|
||||
|
||||
Vector3 relativePos = new(
|
||||
Mathf.SmoothStep(instance.closedPosition.x, instance.openPosition.x, instance.timer),
|
||||
yPos,
|
||||
instance.openPosition.z);
|
||||
instance.transform.localPosition =
|
||||
AspectPosition.ComputePosition(AspectPosition.EdgeAlignments.LeftTop, relativePos);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString() => Name;
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using AmongUs.GameOptions;
|
||||
using HarmonyLib;
|
||||
using InnerNet;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.HnsReimplemented;
|
||||
using MiraAPI.HnsReimplemented.Options;
|
||||
using MiraAPI.PluginLoading;
|
||||
using MiraAPI.Roles;
|
||||
@@ -39,6 +40,7 @@ public class HideAndSeekMode : AbstractGameMode
|
||||
public override bool ShowNormalRoleSettings => false;
|
||||
public override float DefaultImpostorKillCooldown => 1f;
|
||||
|
||||
public override bool ShowTaskBar => false;
|
||||
public static int ImpostorPlayerID()
|
||||
{
|
||||
return OptionGroupSingleton<HnsImpostorOptions>.Instance.SelectedSeeker.Value;
|
||||
@@ -73,7 +75,7 @@ public class HideAndSeekMode : AbstractGameMode
|
||||
}
|
||||
IGameOptions currentGameOptions = GameOptionsManager.Instance.CurrentGameOptions;
|
||||
int adjustedNumImpostors = GameOptionsManager.Instance.CurrentGameOptions.GetAdjustedNumImpostors(list2.Count);
|
||||
AssignRolesForTeam(list2, currentGameOptions, RoleTeamTypes.Impostor, adjustedNumImpostors, RoleTypes.Impostor);
|
||||
AssignRolesForTeam(list2, currentGameOptions, RoleTeamTypes.Impostor, Math.Max(adjustedNumImpostors, 1), RoleTypes.Impostor);
|
||||
AssignRolesForTeam(list2, currentGameOptions, RoleTeamTypes.Crewmate, int.MaxValue, RoleTypes.Engineer);
|
||||
}
|
||||
|
||||
@@ -84,7 +86,7 @@ public class HideAndSeekMode : AbstractGameMode
|
||||
int teamMax,
|
||||
RoleTypes defaultRole)
|
||||
{
|
||||
Error($"MiraAPI.Patches.Roles.LogicRoleSelectionHnsPatch - AssignRolesForTeam: Team: {team}, Max: {teamMax}, Players: {players.Count}, DefaultRole: {defaultRole}");
|
||||
Error($"Hide And Seek Mode - AssignRolesForTeam: Team: {team}, Max: {teamMax}, Players: {players.Count}, DefaultRole: {defaultRole}");
|
||||
int num = 0;
|
||||
IRoleOptionsCollection roleOptions = opts.RoleOptions;
|
||||
var source = RoleManager.Instance.AllRoles.ToArray()
|
||||
@@ -400,6 +402,25 @@ public class HideAndSeekMode : AbstractGameMode
|
||||
deadPlayerCount = 0;
|
||||
ShipStatus.Instance.BreakEmergencyButton();
|
||||
PlayerControl.LocalPlayer.SetKillTimer(0.01f);
|
||||
if (HudManager.InstanceExists)
|
||||
{
|
||||
HudManager.Instance.gameObject.AddComponent<HideAndSeekHudHelper>();
|
||||
}
|
||||
}
|
||||
public override MapOptions GetMapOptions()
|
||||
{
|
||||
MapOptions mapOptions = new MapOptions
|
||||
{
|
||||
Mode = MapOptions.Modes.Normal,
|
||||
};
|
||||
if (PlayerControl.LocalPlayer.Data.Role.IsImpostor && HideAndSeekHudHelper.Instance.SeekerAdminMapEnabled(PlayerControl.LocalPlayer))
|
||||
{
|
||||
mapOptions.Mode = MapOptions.Modes.CountOverlay;
|
||||
mapOptions.AllowMovementWhileMapOpen = true;
|
||||
mapOptions.IncludeDeadBodies = false;
|
||||
mapOptions.ShowLivePlayerPosition = false;
|
||||
}
|
||||
return mapOptions;
|
||||
}
|
||||
|
||||
public override void CheckGameEnd(out bool runOriginal, LogicGameFlowNormal instance)
|
||||
@@ -411,10 +432,10 @@ public class HideAndSeekMode : AbstractGameMode
|
||||
}
|
||||
if (Helpers.GetAlivePlayers().Count(x => !x.Data.Role.IsImpostor) != 0)
|
||||
{
|
||||
/*if (instance.AllTimersExpired())
|
||||
if (HideAndSeekHudHelper.Instance.AllTimersExpired())
|
||||
{
|
||||
instance.Manager.RpcEndGame(GameOverReason.HideAndSeek_CrewmatesByTimer, !DataManager.Player.Ads.HasPurchasedAdRemoval);
|
||||
}*/
|
||||
}
|
||||
return;
|
||||
}
|
||||
instance.Manager.RpcEndGame(GameOverReason.HideAndSeek_ImpostorsByKills, !DataManager.Player.Ads.HasPurchasedAdRemoval);
|
||||
@@ -459,6 +480,45 @@ public class HideAndSeekMode : AbstractGameMode
|
||||
return PlayerBodyTypes.Normal;
|
||||
}
|
||||
}
|
||||
public override void UpdateTaskPanel(TaskPanelBehaviour instance)
|
||||
{
|
||||
instance.background.transform.localScale = (instance.taskText.textBounds.size.x > 0f)
|
||||
? new Vector3(instance.taskText.textBounds.size.x + 0.2f, instance.taskText.textBounds.size.y + 0.2f, 1f)
|
||||
: Vector3.zero;
|
||||
Vector3 vector = instance.background.sprite.bounds.extents;
|
||||
vector.y = -vector.y;
|
||||
vector = vector.Mul(instance.background.transform.localScale);
|
||||
instance.background.transform.localPosition = vector;
|
||||
Vector3 vector2 = instance.tab.sprite.bounds.extents;
|
||||
vector2 = vector2.Mul(instance.tab.transform.localScale);
|
||||
vector2.y = -vector2.y;
|
||||
vector2.x += vector.x * 2f;
|
||||
instance.tab.transform.localPosition = vector2;
|
||||
if (GameManager.Instance == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var yPos = 1.6f;
|
||||
var xPos = -instance.background.sprite.bounds.size.x * instance.background.transform.localScale.x;
|
||||
instance.closedPosition = new Vector3(xPos, yPos, instance.closedPosition.z);
|
||||
instance.openPosition = new Vector3(instance.openPosition.x, yPos, instance.openPosition.z);
|
||||
if (instance.open)
|
||||
{
|
||||
instance.timer = Mathf.Min(1f, instance.timer + Time.deltaTime / instance.animationTimeSeconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
instance.timer = Mathf.Max(0f, instance.timer - Time.deltaTime / instance.animationTimeSeconds);
|
||||
}
|
||||
|
||||
Vector3 relativePos = new(
|
||||
Mathf.SmoothStep(instance.closedPosition.x, instance.openPosition.x, instance.timer),
|
||||
yPos,
|
||||
instance.openPosition.z);
|
||||
instance.transform.localPosition =
|
||||
AspectPosition.ComputePosition(AspectPosition.EdgeAlignments.LeftTop, relativePos);
|
||||
}
|
||||
private int deadPlayerCount;
|
||||
public override void OnPlayerDeath(PlayerControl player, bool assignGhostRole)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using BepInEx.Unity.IL2CPP.Utils.Collections;
|
||||
using MiraAPI.GameOptions;
|
||||
using MiraAPI.HnsReimplemented.Options;
|
||||
using MiraAPI.Utilities;
|
||||
using Reactor.Utilities.Attributes;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MiraAPI.HnsReimplemented;
|
||||
|
||||
[RegisterInIl2Cpp]
|
||||
public sealed class HideAndSeekHudHelper(nint cppPtr) : MonoBehaviour(cppPtr)
|
||||
{
|
||||
public static HideAndSeekHudHelper Instance { get; private set; }
|
||||
|
||||
public AudioClip FinalHideAlertSfx;
|
||||
|
||||
public AudioClip FinalHideCountdownSfx;
|
||||
|
||||
private AudioClip TaskFinishedSound;
|
||||
|
||||
private const int SECONDS_TO_BEEP = 10;
|
||||
|
||||
private const float SECONDS_TO_SET_DIRTY = 1f;
|
||||
|
||||
private HideAndSeekTimerBar timerBar;
|
||||
|
||||
private float totalHideTime = float.MaxValue;
|
||||
|
||||
private float currentHideTime = float.MaxValue;
|
||||
|
||||
private float totalFinalHideTime = float.MaxValue;
|
||||
|
||||
private float currentFinalHideTime = float.MaxValue;
|
||||
|
||||
private float secondsSinceLastSetDirty;
|
||||
private Coroutine beepCoroutine;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
public bool IsFinalCountdown
|
||||
{
|
||||
get { return currentHideTime <= 0f; }
|
||||
}
|
||||
|
||||
public bool SeekerAdminMapEnabled(PlayerControl player)
|
||||
{
|
||||
int item = Helpers.GetAlivePlayers().Count(x => !x.Data.Role.IsImpostor);
|
||||
return !player.inVent && player.Data != null && player.Data.Role != null &&
|
||||
((!player.inVent && player.Data.Role.IsImpostor && IsFinalCountdown &&
|
||||
OptionGroupSingleton<HnsFinalHideOptions>.Instance.FinalHideSeekMap.Value) ||
|
||||
(player.Data.Role.IsImpostor &&
|
||||
item <=
|
||||
(GameData.Instance.PlayerCount - 1) / 3));
|
||||
}
|
||||
|
||||
public void OnTaskComplete(float timeDeduction)
|
||||
{
|
||||
if (timerBar != null)
|
||||
{
|
||||
timerBar.TaskComplete();
|
||||
}
|
||||
|
||||
AdjustEscapeTimer(timeDeduction, true);
|
||||
}
|
||||
|
||||
public float GetTotalRoundTime()
|
||||
{
|
||||
float escapeTime = OptionGroupSingleton<HnsCrewmateOptions>.Instance.HidingTime.Value;
|
||||
float finalCountdownTime = OptionGroupSingleton<HnsFinalHideOptions>.Instance.FinalHideTime.Value;
|
||||
return escapeTime + finalCountdownTime;
|
||||
}
|
||||
|
||||
public float GetTotalTimeRemaining()
|
||||
{
|
||||
return currentHideTime + currentFinalHideTime;
|
||||
}
|
||||
|
||||
public float GetRoundTimeElapsed()
|
||||
{
|
||||
return GetTotalRoundTime() - GetTotalTimeRemaining();
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
totalHideTime = OptionGroupSingleton<HnsCrewmateOptions>.Instance.HidingTime.Value;
|
||||
currentHideTime = totalHideTime;
|
||||
totalFinalHideTime = OptionGroupSingleton<HnsFinalHideOptions>.Instance.FinalHideTime.Value;
|
||||
currentFinalHideTime = totalFinalHideTime;
|
||||
if (timerBar != null)
|
||||
{
|
||||
Object.Destroy(timerBar);
|
||||
}
|
||||
|
||||
FinalHideAlertSfx = GameManagerCreator.Instance.HideAndSeekManagerPrefab.FinalHideAlertSFX;
|
||||
FinalHideCountdownSfx = GameManagerCreator.Instance.HideAndSeekManagerPrefab.FinalHideCountdownSFX;
|
||||
TaskFinishedSound = GameManagerCreator.Instance.HideAndSeekManagerPrefab.TaskFinishedSound;
|
||||
timerBar = Object.Instantiate<HideAndSeekTimerBar>(
|
||||
GameManagerCreator.Instance.HideAndSeekManagerPrefab.TimerBarPrefab,
|
||||
HudManager.Instance.transform.parent);
|
||||
}
|
||||
|
||||
public void OnGameEnd()
|
||||
{
|
||||
if (timerBar != null)
|
||||
{
|
||||
Object.Destroy(timerBar.gameObject);
|
||||
}
|
||||
|
||||
if (beepCoroutine != null)
|
||||
{
|
||||
StopCoroutine(beepCoroutine);
|
||||
}
|
||||
|
||||
beepCoroutine = null!;
|
||||
}
|
||||
|
||||
public void FixedUpdate()
|
||||
{
|
||||
secondsSinceLastSetDirty += Time.fixedDeltaTime;
|
||||
if (IsFinalCountdown)
|
||||
{
|
||||
AdjustFinalEscapeTimer(Time.fixedDeltaTime);
|
||||
return;
|
||||
}
|
||||
|
||||
AdjustEscapeTimer(Time.fixedDeltaTime, false);
|
||||
}
|
||||
|
||||
public void OnDestroy()
|
||||
{
|
||||
if (timerBar != null)
|
||||
{
|
||||
Object.Destroy(timerBar);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFinalCountdownTriggered()
|
||||
{
|
||||
foreach (PlayerControl playerControl in PlayerControl.AllPlayerControls)
|
||||
{
|
||||
if (!playerControl.Data.Role.IsImpostor && !playerControl.Data.IsDead)
|
||||
{
|
||||
playerControl.ClearTasks();
|
||||
PlayerTask.GetOrCreateTask<ImportantTextTask>(playerControl, 0).Text =
|
||||
DestroyableSingleton<TranslationController>.Instance.GetString(StringNames.HideActionButton);
|
||||
}
|
||||
}
|
||||
|
||||
if (!PlayerControl.LocalPlayer.Data.IsDead && Minigame.Instance != null)
|
||||
{
|
||||
Minigame instance = Minigame.Instance;
|
||||
if (instance != null)
|
||||
{
|
||||
instance.ForceClose();
|
||||
}
|
||||
}
|
||||
|
||||
timerBar.StartFinalHide();
|
||||
SoundManager.Instance.PlaySound(FinalHideAlertSfx, false, 1f, null);
|
||||
DestroyableSingleton<HudManager>.Instance.SetAlertOverlay(true);
|
||||
}
|
||||
|
||||
private void AdjustEscapeTimer(float timeDeduction, bool forceDirty)
|
||||
{
|
||||
float num = currentHideTime;
|
||||
currentHideTime -= timeDeduction;
|
||||
currentHideTime = Mathf.Max(currentHideTime, 0f);
|
||||
if (currentHideTime <= 10f && beepCoroutine == null)
|
||||
{
|
||||
beepCoroutine = StartCoroutine(BeepAlmostEverySecond().WrapToIl2Cpp());
|
||||
}
|
||||
|
||||
if (num > 0f && currentHideTime <= 0f)
|
||||
{
|
||||
OnFinalCountdownTriggered();
|
||||
}
|
||||
|
||||
timerBar.UpdateTimer(currentHideTime, totalHideTime);
|
||||
if (forceDirty || secondsSinceLastSetDirty > 1f)
|
||||
{
|
||||
secondsSinceLastSetDirty = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator BeepAlmostEverySecond()
|
||||
{
|
||||
while (!IsFinalCountdown)
|
||||
{
|
||||
float num = currentHideTime / 10f;
|
||||
float pitch = 1.5f - num / 2f;
|
||||
SoundManager.Instance.PlaySoundImmediate(FinalHideCountdownSfx, false, 1f, pitch, null);
|
||||
yield return new WaitForSeconds(1f);
|
||||
}
|
||||
|
||||
yield return Effects.Wait(currentFinalHideTime - 10f);
|
||||
while (currentFinalHideTime > 0f)
|
||||
{
|
||||
float num2 = currentFinalHideTime / 10f;
|
||||
float pitch2 = 1.5f - num2 / 2f;
|
||||
SoundManager.Instance.PlaySoundImmediate(FinalHideCountdownSfx, false, 1f, pitch2, null);
|
||||
yield return new WaitForSeconds(1f);
|
||||
}
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
private void AdjustFinalEscapeTimer(float timeDeduction)
|
||||
{
|
||||
currentFinalHideTime -= timeDeduction;
|
||||
currentFinalHideTime = Mathf.Max(currentFinalHideTime, 0f);
|
||||
timerBar.UpdateTimer(currentFinalHideTime, totalFinalHideTime);
|
||||
if (secondsSinceLastSetDirty > 1f)
|
||||
{
|
||||
secondsSinceLastSetDirty = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public bool AllTimersExpired()
|
||||
{
|
||||
return currentHideTime <= 0f && currentFinalHideTime <= 0f;
|
||||
}
|
||||
}
|
||||
@@ -28,4 +28,29 @@ internal static class GameManagerPatches
|
||||
|
||||
return true;
|
||||
}
|
||||
[HarmonyPatch(typeof(NormalGameManager), nameof(NormalGameManager.GetMapOptions))]
|
||||
[HarmonyPrefix]
|
||||
public static bool GetMapOptions(ref MapOptions __result)
|
||||
{
|
||||
if (CustomGameModeManager.ActiveMode == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__result = CustomGameModeManager.ActiveMode.GetMapOptions();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch(typeof(GameStartManager))]
|
||||
internal static class GameStartManagerPatches
|
||||
{
|
||||
[HarmonyPrefix, HarmonyPatch(nameof(GameStartManager.Start))]
|
||||
public static void StartPatch(GameStartManager __instance)
|
||||
{
|
||||
if (MiraApiPlugin.IsDevBuild)
|
||||
{
|
||||
__instance.MinPlayers = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.GameModes;
|
||||
|
||||
namespace MiraAPI.Patches.GameModes;
|
||||
[HarmonyPatch(typeof(ProgressTracker), nameof(ProgressTracker.Start))]
|
||||
internal static class ProgressTrackerPatch
|
||||
{
|
||||
public static bool Prefix(ProgressTracker __instance)
|
||||
{
|
||||
if (CustomGameModeManager.ActiveMode != null && !CustomGameModeManager.ActiveMode.ShowTaskBar)
|
||||
{
|
||||
__instance.gameObject.SetActive(false);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using HarmonyLib;
|
||||
using MiraAPI.GameModes;
|
||||
|
||||
namespace MiraAPI.Patches.GameModes;
|
||||
[HarmonyPatch(typeof(TaskPanelBehaviour), nameof(TaskPanelBehaviour.Update))]
|
||||
internal static class TaskPanelPatch
|
||||
{
|
||||
public static bool Prefix(TaskPanelBehaviour __instance)
|
||||
{
|
||||
if (CustomGameModeManager.ActiveMode != null)
|
||||
{
|
||||
CustomGameModeManager.ActiveMode.UpdateTaskPanel(__instance);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user