Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions NewMod/Buttons/Shade/DeployShadow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using MiraAPI.GameOptions;
using MiraAPI.Hud;
using MiraAPI.Keybinds;
using MiraAPI.Utilities.Assets;
using UnityEngine;
using NewMod.Components;
using SH = NewMod.Roles.NeutralRoles.Shade;
using NewMod.Options.Roles.ShadeOptions;

namespace NewMod.Buttons.Shade
{
/// <summary>
/// Custom action button for the Shade role.
/// Deploys a Shadow Zone where the Shade becomes invisible and gains kill power.
/// </summary>
public class ShadeButton : CustomActionButton
{
/// <summary>
/// Display name of the button.
/// </summary>
public override string Name => "Deploy Shadow";

/// <summary>
/// Cooldown duration between zone deployments.
/// </summary>
public override float Cooldown => OptionGroupSingleton<ShadeOptions>.Instance.Cooldown;

/// <summary>
/// Maximum number of uses.
/// </summary>
public override int MaxUses => (int)OptionGroupSingleton<ShadeOptions>.Instance.MaxUses;

/// <summary>
/// Button HUD placement.
/// </summary>
public override ButtonLocation Location => ButtonLocation.BottomLeft;

/// <summary>
/// Default keybind.
/// </summary>
public override MiraKeybind Keybind => MiraGlobalKeybinds.PrimaryAbility;

/// <summary>
/// Button icon.
/// </summary>
public override LoadableAsset<Sprite> Sprite => NewModAsset.DeployZone;

/// <summary>
/// Button enabled only for the Shade role.
/// </summary>
public override bool Enabled(RoleBehaviour role) => role is SH;

/// <summary>
/// Deploys a shadow zone at the Shade's position.
/// </summary>
protected override void OnClick()
{
var player = PlayerControl.LocalPlayer;

Vector2 pos = player.GetTruePosition();

float radius = OptionGroupSingleton<ShadeOptions>.Instance.Radius;
float dur = OptionGroupSingleton<ShadeOptions>.Instance.Duration;

ShadowZone.RpcDeployZone(player, pos, radius, dur);
}
}
}
42 changes: 42 additions & 0 deletions NewMod/Components/ScreenEffects/DistorationWaveEffect.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using Reactor.Utilities.Attributes;
using UnityEngine;

namespace NewMod.Components.ScreenEffects
{
[RegisterInIl2Cpp]
public class DistorationWaveEffect(IntPtr ptr) : MonoBehaviour(ptr)
{
public float amplitude = 0.05f;
public float frequency = 5f;
public float speed = 1f;
public float radius = 0.25f;
public float falloff = 1f;
public Vector2 center = new Vector2(0.5f, 0.5f);
public Color tint = Color.white;
private readonly Shader _shader = NewModAsset.DistorationWaveShader.LoadAsset();
public Material _mat;

public void OnEnable()
{
_mat = new Material(_shader) { hideFlags = HideFlags.DontSave };
}

public void OnDisable()
{
Destroy(_mat);
}

public void OnRenderImage(RenderTexture src, RenderTexture dst)
{
_mat.SetFloat("_Amplitude", amplitude);
_mat.SetFloat("_Frequency", frequency);
_mat.SetFloat("_Speed", speed);
_mat.SetFloat("_Radius", radius);
_mat.SetFloat("_Falloff", falloff);
_mat.SetVector("_Center", center);
_mat.SetColor("_Tint", tint);
Graphics.Blit(src, dst, _mat);
}
}
}
1 change: 1 addition & 0 deletions NewMod/Components/ScreenEffects/GlitchEffect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class GlitchEffect(IntPtr ptr) : MonoBehaviour(ptr)
public float speed = 4f;
private readonly Shader _shader = NewModAsset.GlitchShader.LoadAsset();
public Material _mat;

public void OnEnable()
{
_mat = new Material(_shader) { hideFlags = HideFlags.DontSave };
Expand Down
42 changes: 42 additions & 0 deletions NewMod/Components/ScreenEffects/ShadowFluxEffect.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using Reactor.Utilities.Attributes;
using UnityEngine;

namespace NewMod.Components.ScreenEffects
{
[RegisterInIl2Cpp]
public class ShadowFluxEffect(IntPtr ptr) : MonoBehaviour(ptr)
{
public Texture2D texture = NewModAsset.NoiseTex.LoadAsset();
public float noiseScale = 2f;
public float speed = 0.3f;
public float edgeWidth = 0.25f;
public float threshold = 0.55f;
public float opacity = 0.75f;
public float darkness = 0.8f;
public Color tint = Color.white;
public static Shader _shader = NewModAsset.ShadowFluxShader.LoadAsset();
public Material _mat;

public void OnEnable()
{
_mat = new Material(_shader) { hideFlags = HideFlags.DontSave };
_mat.SetTexture("_NoiseTex", texture);
}
public void OnDisable()
{
Destroy(_mat);
}
public void OnRenderImage(RenderTexture src, RenderTexture dst)
{
_mat.SetFloat("_NoiseScale", noiseScale);
_mat.SetFloat("_Speed", speed);
_mat.SetFloat("_EdgeWidth", edgeWidth);
_mat.SetFloat("_Threshold", threshold);
_mat.SetFloat("_Opacity", opacity);
_mat.SetFloat("_Darkness", darkness);
_mat.SetColor("_Tint", tint);
Graphics.Blit(src, dst, _mat);
}
}
}
136 changes: 136 additions & 0 deletions NewMod/Components/ShadowZone.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MiraAPI.GameOptions;
using NewMod.Components.ScreenEffects;
using NewMod.Options.Roles.ShadeOptions;
using NewMod.Roles.NeutralRoles;
using NewMod.Utilities;
using Reactor.Networking.Attributes;
using Reactor.Utilities;
using Reactor.Utilities.Attributes;
using UnityEngine;

namespace NewMod.Components
{
[RegisterInIl2Cpp]
public class ShadowZone(IntPtr ptr) : MonoBehaviour(ptr)
{
public byte shadeId;
public float radius;
public float duration;
private float timer;
private bool active;
public static readonly List<ShadowZone> zones = new();

public void Awake()
{
if (!zones.Contains(this))
zones.Add(this);
}

public void OnDestroy()
{
zones.Remove(this);
}

private bool Contains(Vector2 pos)
{
return Vector2.Distance(pos, (Vector2)transform.position) <= radius;
}

public void Update()
{
timer += Time.deltaTime;
if (timer >= duration)
{
Coroutines.Start(CoroutinesHelper.RemoveCameraEffect(Camera.main, 0f));
active = false;
Destroy(gameObject);
return;
}

var lp = PlayerControl.LocalPlayer;
var hud = HudManager.Instance;
var killButton = hud.KillButton;

bool inside = Contains(lp.GetTruePosition());
var mode = OptionGroupSingleton<ShadeOptions>.Instance.Behavior;
var cam = Camera.main;

if (inside && !active)
{
cam.gameObject.AddComponent<ShadowFluxEffect>();
if (lp.PlayerId == shadeId && lp.Data.Role is Shade)
{
if (mode is ShadeOptions.ShadowMode.Invisible or ShadeOptions.ShadowMode.Both)
{
lp.cosmetics.SetPhantomRoleAlpha(0);
lp.cosmetics.nameText.gameObject.SetActive(false);
}

killButton.gameObject.SetActive(true);
killButton.currentTarget = null;
}
active = true;
}

if (inside && active && lp.PlayerId == shadeId && lp.Data.Role is Shade)
{
if (mode is ShadeOptions.ShadowMode.KillEnabled or ShadeOptions.ShadowMode.Both)
{
var list = new Il2CppSystem.Collections.Generic.List<PlayerControl>();
lp.Data.Role.GetPlayersInAbilityRangeSorted(list);
var players = list.ToArray().Where(p => p.PlayerId != lp.PlayerId && !p.Data.IsDead).ToList();
var closest = players.Count > 0 ? players[0] : null;

if (killButton.currentTarget && killButton.currentTarget != closest)
killButton.currentTarget.ToggleHighlight(false, RoleTeamTypes.Impostor);

killButton.currentTarget = closest;
if (closest != null)
closest.ToggleHighlight(true, RoleTeamTypes.Impostor);
}
}
else if (!inside && active)
{
Coroutines.Start(CoroutinesHelper.RemoveCameraEffect(cam, 0f));
if (lp.PlayerId == shadeId)
{
lp.cosmetics.SetPhantomRoleAlpha(1);
lp.cosmetics.nameText.gameObject.SetActive(true);

if (killButton.currentTarget)
{
killButton.currentTarget.ToggleHighlight(false, RoleTeamTypes.Impostor);
killButton.currentTarget = null;
}
killButton.gameObject.SetActive(false);
}
active = false;
}
}

public static ShadowZone Create(byte id, Vector2 pos, float r, float dur)
{
var go = new GameObject("ShadowZone");
var z = go.AddComponent<ShadowZone>();
z.shadeId = id;
z.radius = r;
z.duration = dur;
go.transform.position = pos;
return z;
}

[MethodRpc((uint)CustomRPC.DeployZone)]
public static void RpcDeployZone(PlayerControl source, Vector2 pos, float radius, float duration)
{
Create(source.PlayerId, pos, radius, duration);
}

public static bool IsInsideAny(Vector2 pos)
{
return zones.Any(z => z && z.Contains(pos));
}
}
}
4 changes: 3 additions & 1 deletion NewMod/CustomRPC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ public enum CustomRPC
SuppressionDome,
WitnessTrap,
NotifyChampion,
SummonNPC
SummonNPC,
BeaconPulse,
DeployZone
}
Loading