Skip to content

Commit d07e4d3

Browse files
Add Bard and Illusionist roles with abilities
Introduces Bard and Illusionist roles, each with their own ability buttons and options. Refactors Guardian, Shadow, and Trickster roles and options to use a unified MaxUses enum. Adds new RPCs and cosmetic effects for Bard and Illusionist abilities. Cleans up legacy spawn count/chance logic from roles and options. Updates project version to 1.5.1.
1 parent d66375e commit d07e4d3

22 files changed

Lines changed: 341 additions & 410 deletions
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using MiraAPI.Hud;
2+
using MiraAPI.Utilities.Assets;
3+
using UnityEngine;
4+
using TrollMod17.Networking;
5+
using MiraAPI.GameOptions;
6+
using TrollMod17.Options;
7+
8+
namespace TrollMod17.Buttons;
9+
10+
public class BardTuneButton : CustomActionButton
11+
{
12+
public override string Name => "Tune";
13+
public override float Cooldown => 25f;
14+
public override float EffectDuration => 6f;
15+
public override LoadableAsset<Sprite> Sprite => MiraAssets.RoundedBox;
16+
public override int MaxUses
17+
{
18+
get
19+
{
20+
var inst = OptionGroupSingleton<BardOptions>.Instance;
21+
var value = inst != null ? inst.BardMaxUses : global::TrollMod17.Options.MaxUses.One;
22+
return (int)value;
23+
}
24+
}
25+
public override float InitialCooldown => 10f;
26+
public override ButtonLocation Location => ButtonLocation.BottomLeft;
27+
private const float Radius = 3.5f;
28+
29+
public override bool Enabled(RoleBehaviour? role)
30+
{
31+
return role is TrollMod17.Roles.BardRole;
32+
}
33+
34+
protected override void OnClick()
35+
{
36+
var center = PlayerControl.LocalPlayer.GetTruePosition();
37+
AbilityRpcs.BardStartTune(PlayerControl.LocalPlayer, center, Radius, EffectDuration);
38+
}
39+
40+
public override void OnEffectEnd()
41+
{
42+
AbilityRpcs.BardEndTune(PlayerControl.LocalPlayer);
43+
}
44+
}

TrollMod17/Buttons/GuardianShieldButton.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using TrollMod17.Networking;
55
using Il2CppSystem;
66
using TrollMod17.Settings;
7+
using MiraAPI.GameOptions;
8+
using TrollMod17.Options;
79

810
namespace TrollMod17.Buttons;
911

@@ -13,7 +15,15 @@ public class GuardianShieldButton : CustomActionButton<PlayerControl>
1315
public override float Cooldown => 25f;
1416
public override float EffectDuration => 6f;
1517
public override LoadableAsset<Sprite> Sprite => MiraAssets.RefreshIcon;
16-
public override int MaxUses => 1;
18+
public override int MaxUses
19+
{
20+
get
21+
{
22+
var inst = OptionGroupSingleton<GuardianOptions>.Instance;
23+
var value = inst != null ? inst.GuardianMaxUses : global::TrollMod17.Options.MaxUses.One;
24+
return (int)value; // Infinite maps to 0
25+
}
26+
}
1727

1828
public override float Distance => 2.5f;
1929
public override ButtonLocation Location => TMLocalSettings.ButtonsPosition == ButtonPos.Left ? ButtonLocation.BottomLeft : ButtonLocation.BottomRight;
@@ -58,14 +68,12 @@ public override void SetOutline(bool active)
5868
protected override void OnClick()
5969
{
6070
if (Target == null) return;
61-
if (!AmongUsClient.Instance.AmHost) return;
6271
_lastShieldedId = Target.PlayerId;
6372
AbilityRpcs.GuardianStartShield(PlayerControl.LocalPlayer, Target.PlayerId, EffectDuration);
6473
}
6574

6675
public override void OnEffectEnd()
6776
{
68-
if (!AmongUsClient.Instance.AmHost) { ResetTarget(); return; }
6977
if (_lastShieldedId != 0)
7078
{
7179
AbilityRpcs.GuardianEndShield(PlayerControl.LocalPlayer, _lastShieldedId);
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using MiraAPI.Hud;
2+
using MiraAPI.Utilities.Assets;
3+
using UnityEngine;
4+
using TrollMod17.Networking;
5+
using MiraAPI.GameOptions;
6+
using TrollMod17.Options;
7+
8+
namespace TrollMod17.Buttons;
9+
10+
public class IllusionistGlimmerButton : CustomActionButton
11+
{
12+
public override string Name => "Glimmer";
13+
public override float Cooldown => 25f;
14+
public override float EffectDuration => 6f;
15+
public override LoadableAsset<Sprite> Sprite => MiraAssets.RoundedBox;
16+
public override int MaxUses
17+
{
18+
get
19+
{
20+
var inst = OptionGroupSingleton<IllusionistOptions>.Instance;
21+
var value = inst != null ? inst.IllusionistMaxUses : global::TrollMod17.Options.MaxUses.One;
22+
return (int)value;
23+
}
24+
}
25+
public override float InitialCooldown => 10f;
26+
public override ButtonLocation Location => ButtonLocation.BottomRight;
27+
private const float Radius = 3.5f;
28+
29+
public override bool Enabled(RoleBehaviour? role)
30+
{
31+
return role is TrollMod17.Roles.IllusionistRole;
32+
}
33+
34+
protected override void OnClick()
35+
{
36+
var center = PlayerControl.LocalPlayer.GetTruePosition();
37+
AbilityRpcs.IllusionistStartGlimmer(PlayerControl.LocalPlayer, center, Radius, EffectDuration);
38+
}
39+
40+
public override void OnEffectEnd()
41+
{
42+
AbilityRpcs.IllusionistEndGlimmer(PlayerControl.LocalPlayer);
43+
}
44+
}

TrollMod17/Buttons/ShadowCloakButton.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using TrollMod17.Networking;
55
using Il2CppSystem;
66
using TrollMod17.Settings;
7+
using MiraAPI.GameOptions;
8+
using TrollMod17.Options;
79

810
namespace TrollMod17.Buttons;
911

@@ -13,7 +15,15 @@ public class ShadowCloakButton : CustomActionButton<PlayerControl>
1315
public override float Cooldown => 25f;
1416
public override float EffectDuration => 6f;
1517
public override LoadableAsset<Sprite> Sprite => MiraAssets.RefreshIcon;
16-
public override int MaxUses => 1;
18+
public override int MaxUses
19+
{
20+
get
21+
{
22+
var inst = OptionGroupSingleton<ShadowOptions>.Instance;
23+
var value = inst != null ? inst.ShadowMaxUses : global::TrollMod17.Options.MaxUses.One;
24+
return (int)value; // Infinite maps to 0
25+
}
26+
}
1727
public override float Distance => 2.6f;
1828
public override ButtonLocation Location => TMLocalSettings.ButtonsPosition == ButtonPos.Left ? ButtonLocation.BottomLeft : ButtonLocation.BottomRight;
1929

@@ -54,7 +64,6 @@ public override void SetOutline(bool active)
5464

5565
protected override void OnClick()
5666
{
57-
if (!AmongUsClient.Instance.AmHost) return;
5867
var me = PlayerControl.LocalPlayer;
5968
if (Target)
6069
{
@@ -65,7 +74,6 @@ protected override void OnClick()
6574

6675
public override void OnEffectEnd()
6776
{
68-
if (!AmongUsClient.Instance.AmHost) return;
6977
var me = PlayerControl.LocalPlayer;
7078
AbilityRpcs.ShadowEndCloak(me, me.PlayerId);
7179
}

TrollMod17/Buttons/TricksterSmokeButton.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
using UnityEngine;
44
using TrollMod17.Networking;
55
using TrollMod17.Settings;
6+
using MiraAPI.GameOptions;
7+
using TrollMod17.Options;
68

79
namespace TrollMod17.Buttons;
810

@@ -12,7 +14,15 @@ public class TricksterSmokeButton : CustomActionButton
1214
public override float Cooldown => 25f;
1315
public override float EffectDuration => 6f;
1416
public override LoadableAsset<Sprite> Sprite => MiraAssets.RefreshIcon;
15-
public override int MaxUses => 1;
17+
public override int MaxUses
18+
{
19+
get
20+
{
21+
var inst = OptionGroupSingleton<TricksterOptions>.Instance;
22+
var value = inst != null ? inst.TricksterMaxUses : global::TrollMod17.Options.MaxUses.One;
23+
return (int)value; // Infinite maps to 0
24+
}
25+
}
1626

1727
public override float InitialCooldown => 10f;
1828
public override ButtonLocation Location => TMLocalSettings.ButtonsPosition == ButtonPos.Left ? ButtonLocation.BottomLeft : ButtonLocation.BottomRight;
@@ -25,14 +35,12 @@ public override bool Enabled(RoleBehaviour? role)
2535

2636
protected override void OnClick()
2737
{
28-
if (!AmongUsClient.Instance.AmHost) return;
2938
var center = PlayerControl.LocalPlayer.GetTruePosition();
3039
AbilityRpcs.TricksterStartSmoke(PlayerControl.LocalPlayer, center, SmokeRadius, EffectDuration);
3140
}
3241

3342
public override void OnEffectEnd()
3443
{
35-
if (!AmongUsClient.Instance.AmHost) return;
3644
AbilityRpcs.TricksterEndSmoke(PlayerControl.LocalPlayer);
3745
}
3846
}

TrollMod17/Cosmetics/AbilityCosmetics.cs

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,23 @@ namespace TrollMod17.Cosmetics;
88

99
public static class AbilityCosmetics
1010
{
11-
// Among Us native cosmetics-backed state
1211
private static readonly Dictionary<PlayerControl, float> _savedPhantomAlpha = new();
1312
private static readonly HashSet<PlayerControl> _shielded = new();
1413
private static readonly HashSet<PlayerControl> _smoked = new();
14+
private static readonly Dictionary<PlayerControl, Color> _originalColors = new();
15+
private static readonly HashSet<PlayerControl> _tuned = new();
16+
private static readonly HashSet<PlayerControl> _glimmered = new();
17+
private static readonly Dictionary<PlayerControl, float> _glimmerAlpha = new();
1518

1619
public static void ApplyShadowCloak(PlayerControl p, float alphaFactor)
1720
{
1821
if (!p || p.cosmetics == null) return;
19-
// Save current phantom alpha, then set reduced alpha
2022
try
2123
{
2224
var current = p.cosmetics.GetPhantomRoleAlpha();
2325
_savedPhantomAlpha[p] = current;
2426
}
25-
catch { /* ignore */ }
27+
catch { }
2628
p.cosmetics.SetPhantomRoleAlpha(Mathf.Clamp01(alphaFactor));
2729
}
2830

@@ -36,7 +38,6 @@ public static void RemoveShadowCloak(PlayerControl p)
3638
}
3739
else
3840
{
39-
// fallback reset
4041
p.cosmetics.SetPhantomRoleAlpha(1f);
4142
}
4243
}
@@ -60,24 +61,43 @@ public static void RemoveShieldTint(PlayerControl p)
6061

6162
public static void ApplySmokeDarkenInRadius(Vector2 center, float radius)
6263
{
64+
ClearSmokeDarken();
65+
6366
foreach (var pc in PlayerControl.AllPlayerControls)
6467
{
6568
if (!pc || pc.cosmetics == null) continue;
6669
var d = Vector2.Distance(center, pc.GetTruePosition());
6770
if (d > radius) continue;
68-
pc.cosmetics.FadeBlackCosmetics(0.6f);
69-
_smoked.Add(pc);
71+
72+
if (pc.cosmetics.currentBodySprite != null && pc.cosmetics.currentBodySprite.BodySprite != null)
73+
{
74+
var sprite = pc.cosmetics.currentBodySprite.BodySprite;
75+
_originalColors[pc] = sprite.color;
76+
77+
var darkenedColor = new Color(0.1f, 0.1f, 0.1f, sprite.color.a);
78+
sprite.color = darkenedColor;
79+
80+
_smoked.Add(pc);
81+
}
7082
}
7183
}
7284

7385
public static void ClearSmokeDarken()
7486
{
75-
foreach (var p in _smoked)
87+
foreach (var pc in _smoked)
7688
{
77-
if (!p || p.cosmetics == null) continue;
78-
p.cosmetics.FadeBlackCosmetics(0f);
89+
if (!pc || pc.cosmetics == null) continue;
90+
91+
if (_originalColors.TryGetValue(pc, out var originalColor) &&
92+
pc.cosmetics.currentBodySprite != null &&
93+
pc.cosmetics.currentBodySprite.BodySprite != null)
94+
{
95+
pc.cosmetics.currentBodySprite.BodySprite.color = originalColor;
96+
}
7997
}
98+
8099
_smoked.Clear();
100+
_originalColors.Clear();
81101
}
82102

83103
public static void SpawnPulseRing(Vector2 center, Color color, float duration)
@@ -109,4 +129,62 @@ private static IEnumerator SpawnPulseRingCo(Vector2 center, Color color, float d
109129

110130
if (go) UnityEngine.Object.Destroy(go);
111131
}
132+
133+
public static void ApplyTuneGlow(Vector2 center, float radius)
134+
{
135+
foreach (var pc in PlayerControl.AllPlayerControls)
136+
{
137+
if (!pc || pc.cosmetics == null) continue;
138+
var d = Vector2.Distance(center, pc.GetTruePosition());
139+
if (d > radius) continue;
140+
pc.cosmetics.SetOutline(true, new Nullable<Color>(new Color(1f, 0.9f, 0.2f, 1f)));
141+
_tuned.Add(pc);
142+
}
143+
}
144+
145+
public static void ClearTuneGlow()
146+
{
147+
foreach (var pc in _tuned)
148+
{
149+
if (!pc || pc.cosmetics == null) continue;
150+
pc.cosmetics.SetOutline(false, new Nullable<Color>());
151+
}
152+
_tuned.Clear();
153+
}
154+
155+
public static void ApplyGlimmerInRadius(Vector2 center, float radius)
156+
{
157+
foreach (var pc in PlayerControl.AllPlayerControls)
158+
{
159+
if (!pc || pc.cosmetics == null) continue;
160+
var d = Vector2.Distance(center, pc.GetTruePosition());
161+
if (d > radius) continue;
162+
if (!_glimmerAlpha.ContainsKey(pc))
163+
{
164+
float a = 1f;
165+
try { a = pc.cosmetics.GetPhantomRoleAlpha(); } catch { }
166+
_glimmerAlpha[pc] = a;
167+
}
168+
pc.cosmetics.SetPhantomRoleAlpha(0.5f);
169+
_glimmered.Add(pc);
170+
}
171+
}
172+
173+
public static void ClearGlimmer()
174+
{
175+
foreach (var pc in _glimmered)
176+
{
177+
if (!pc || pc.cosmetics == null) continue;
178+
if (_glimmerAlpha.TryGetValue(pc, out var a))
179+
{
180+
pc.cosmetics.SetPhantomRoleAlpha(a);
181+
}
182+
else
183+
{
184+
pc.cosmetics.SetPhantomRoleAlpha(1f);
185+
}
186+
}
187+
_glimmered.Clear();
188+
_glimmerAlpha.Clear();
189+
}
112190
}

0 commit comments

Comments
 (0)