Skip to content

Commit 36c9af8

Browse files
committed
Added a dungeon guardian easter egg.
1 parent abb9895 commit 36c9af8

3 files changed

Lines changed: 124 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
- Added an easter egg.
10+
911
## [2.0.9] - 2023-12-02
1012

1113
- Changed stinky easter to include chat messages (and other minor changes).

Code/Effects/WorldEffects/SpawnGuardian.cs

Lines changed: 115 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Microsoft.Xna.Framework;
77
using Microsoft.Xna.Framework.Graphics;
88
using Terraria;
9+
using Terraria.Audio;
910
using Terraria.GameContent;
1011
using Terraria.ID;
1112
using Terraria.Localization;
@@ -52,15 +53,26 @@ public SpawnGuardian(bool isFake) : base(isFake ? EffectID.SpawnFakeGuardian : E
5253

5354
protected override CrowdControlResponseStatus OnStart()
5455
{
56+
var mode = CrowdControlGuardian.GuardianMode.None;
57+
if (SteamUtils.IsTeebu)
58+
{
59+
mode |= CrowdControlGuardian.GuardianMode.Fishron;
60+
}
61+
62+
if (SteamUtils.IsKulprid && !_isFake)
63+
{
64+
mode |= CrowdControlGuardian.GuardianMode.Persistent;
65+
}
66+
5567
if (NetUtils.IsSinglePlayer)
5668
{
5769
// In single-player, simply spawn the custom dungeon guardian
58-
Spawn(GetLocalPlayer(), SteamUtils.IsTeebu);
70+
Spawn(GetLocalPlayer(), mode);
5971
}
6072
else
6173
{
6274
// If on server, spawn on server (no need to pass arguments)
63-
SendPacket(PacketID.HandleEffect, SteamUtils.IsTeebu);
75+
SendPacket(PacketID.HandleEffect, (int)mode);
6476
}
6577

6678
return CrowdControlResponseStatus.Success;
@@ -87,10 +99,10 @@ protected override void SendStartMessage(string viewerString, string playerStrin
8799
protected override void OnReceivePacket(CrowdControlPlayer player, BinaryReader reader)
88100
{
89101
// Spawn the dungeon guardian on the server
90-
Spawn(player, reader.ReadBoolean());
102+
Spawn(player, (CrowdControlGuardian.GuardianMode)reader.ReadInt32());
91103
}
92104

93-
private void Spawn(CrowdControlPlayer player, bool isTeebu)
105+
private void Spawn(CrowdControlPlayer player, CrowdControlGuardian.GuardianMode mode)
94106
{
95107
// Determine spawn position
96108
var circleEdge = Main.rand.NextVector2CircularEdge(HalfRangeWidth, HalfRangeHeight);
@@ -107,7 +119,9 @@ private void Spawn(CrowdControlPlayer player, bool isTeebu)
107119
// Set whether it is fake or not
108120
var guardian = (CrowdControlGuardian)npc.ModNPC;
109121
guardian.IsFake = _isFake;
110-
guardian.IsTeebu = isTeebu;
122+
guardian.Mode = mode;
123+
var isTeebu = guardian.IsFishron;
124+
var isKulprid = guardian.IsPersistent;
111125

112126
if (isTeebu)
113127
{
@@ -135,14 +149,14 @@ private void Spawn(CrowdControlPlayer player, bool isTeebu)
135149
NetMessage.SendData(MessageID.SyncNPC, -1, -1, null, index);
136150
}
137151

138-
if (!isTeebu)
152+
if (!isTeebu && !isKulprid)
139153
{
140154
return;
141155
}
142156

143157
// Special life for teebu
144-
npc.lifeMax = 69420;
145-
npc.life = 69420;
158+
npc.lifeMax = isKulprid ? Main.hardMode ? 100 : 30 : 69420;
159+
npc.life = npc.lifeMax;
146160

147161
if (NetUtils.IsServer)
148162
{
@@ -157,6 +171,18 @@ private void Spawn(CrowdControlPlayer player, bool isTeebu)
157171
// ReSharper disable once ClassNeverInstantiated.Local
158172
private sealed class CrowdControlGuardian : ModNPC
159173
{
174+
#region Enums
175+
176+
[Flags]
177+
public enum GuardianMode
178+
{
179+
None = 0,
180+
Fishron = 1,
181+
Persistent = 2
182+
}
183+
184+
#endregion
185+
160186
#region Fields
161187

162188
private int _timeLeft;
@@ -175,14 +201,24 @@ public bool IsFake
175201
}
176202

177203
/// <summary>
178-
/// Whether the guardian is an easter egg for Teebu.
204+
/// Guardian mode.
179205
/// </summary>
180-
public bool IsTeebu
206+
public GuardianMode Mode
181207
{
182-
get => NPC.ai[NPC.maxAI - 1] > 0f;
183-
set => NPC.ai[NPC.maxAI - 1] = value ? 1f : 0f;
208+
get => (GuardianMode)(int)NPC.ai[NPC.maxAI - 1];
209+
set => NPC.ai[NPC.maxAI - 1] = (int)value;
184210
}
185211

212+
/// <summary>
213+
/// Uses a fishron overlay (Teebu easter egg).
214+
/// </summary>
215+
public bool IsFishron => Mode.HasFlag(GuardianMode.Fishron);
216+
217+
/// <summary>
218+
/// Doesn't despawn but has reduced life (Kulprid easter egg).
219+
/// </summary>
220+
public bool IsPersistent => Mode.HasFlag(GuardianMode.Persistent);
221+
186222
public override string Texture => $"Terraria/Images/NPC_{NPCID.DungeonGuardian}";
187223

188224
public override LocalizedText DisplayName => Lang.GetNPCName(NPCID.DungeonGuardian);
@@ -199,7 +235,7 @@ public bool IsTeebu
199235

200236
public override void ModifyTypeName(ref string typeName)
201237
{
202-
if (IsTeebu)
238+
if (IsFishron)
203239
{
204240
typeName = "Teebu's Favourite Boss";
205241
}
@@ -216,8 +252,49 @@ public override void SetDefaults()
216252
public override bool PreAI()
217253
{
218254
NPC.type = NPCID.DungeonGuardian;
219-
NPC.ShowNameOnHover = NetUtils.IsSinglePlayer || !IsTeebu;
255+
NPC.ShowNameOnHover = NetUtils.IsSinglePlayer || !IsFishron;
220256

257+
if (IsPersistent)
258+
{
259+
// Never run out of time if persistent
260+
_timeLeft = 60 * SurvivalDuration;
261+
NPC.timeLeft = 60 * SurvivalDuration + 1;
262+
263+
if (!NetUtils.IsServer && NPC.localAI[0] == 0f)
264+
{
265+
NPC.localAI[0] = 1f;
266+
Roar();
267+
}
268+
269+
// Ensure vanilla AI doesn't despawn the guardian if all players are dead
270+
if (NPC.target < 0 || NPC.target >= Main.maxPlayers || !Main.player[NPC.target].active || Main.player[NPC.target].dead)
271+
{
272+
NPC.target = -1;
273+
NPC.TargetClosest(false);
274+
if (NPC.target < 0 || NPC.target >= Main.maxPlayers || !Main.player[NPC.target].active || Main.player[NPC.target].dead)
275+
{
276+
NPC.target = -1;
277+
NPC.rotation %= MathF.PI * 2;
278+
NPC.rotation *= 0.95f;
279+
NPC.velocity *= 0.98f;
280+
return false;
281+
}
282+
283+
// Reacquired a target, so roar!
284+
if (!NetUtils.IsServer)
285+
{
286+
Roar();
287+
}
288+
}
289+
290+
// Move towards player (emulating vanilla behaviour)
291+
var speedMult = NPC.DistanceSQ(Main.player[NPC.target].position) > 16 * 16 * 180 * 180 ? 5f : 1f;
292+
NPC.velocity = NPC.DirectionTo(Main.player[NPC.target].position) * 8.75f * speedMult;
293+
NPC.rotation += MathHelper.ToRadians(11);
294+
295+
return false;
296+
}
297+
221298
// Reduce the time left timer
222299
_timeLeft--;
223300
if (_timeLeft != 0)
@@ -235,6 +312,22 @@ public override bool PreAI()
235312
return base.PreAI();
236313
}
237314

315+
public override bool CheckActive()
316+
{
317+
// Don't despawn if persistent
318+
if (IsPersistent && _timeLeft > 0)
319+
{
320+
return false;
321+
}
322+
323+
return base.CheckActive();
324+
}
325+
326+
public override void OnKill()
327+
{
328+
Item.NewItem(null, NPC.position, NPC.width, NPC.height, ItemID.GoldCoin, 2);
329+
}
330+
238331
public override bool CanHitPlayer(Player target, ref int cooldownSlot)
239332
{
240333
// Ignore player if fake
@@ -249,15 +342,15 @@ public override bool CanHitNPC(NPC target)
249342

250343
public override void BossHeadSlot(ref int index)
251344
{
252-
if (IsTeebu)
345+
if (IsFishron)
253346
{
254347
index = NPCID.Sets.BossHeadTextures[NPCID.DukeFishron];
255348
}
256349
}
257350

258351
public override bool PreDraw(SpriteBatch spriteBatch, Vector2 screenPos, Color drawColor)
259352
{
260-
if (!IsTeebu)
353+
if (!IsFishron)
261354
{
262355
return base.PreDraw(spriteBatch, screenPos, drawColor);
263356
}
@@ -289,6 +382,12 @@ public override bool PreDraw(SpriteBatch spriteBatch, Vector2 screenPos, Color d
289382
return false;
290383
}
291384

385+
private void Roar()
386+
{
387+
var pos = Main.LocalPlayer.position + Main.LocalPlayer.DirectionTo(NPC.position) * 16f * 20f;
388+
SoundEngine.PlaySound(SoundID.Roar with {SoundLimitBehavior = SoundLimitBehavior.ReplaceOldest}, pos);
389+
}
390+
292391
#endregion
293392
}
294393

Code/Utilities/SteamUtils.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public static class SteamUtils
2424
private static bool? _isMoonlightFaye;
2525
private static bool? _isMakenbacon;
2626
private static bool? _isPixyWixy;
27+
private static bool? _isKulprid;
2728

2829
#endregion
2930

@@ -117,6 +118,11 @@ public static ulong SteamId
117118
/// https://www.twitch.tv/pixywixy
118119
/// </summary>
119120
public static bool IsPixyWixy => CrowdControlConfig.GetInstance().ForceEasterEggs || (_isPixyWixy ?? (_isPixyWixy = SteamId == 76561198109975921UL).Value);
120-
121+
122+
/// <summary>
123+
/// https://www.twitch.tv/kulprid
124+
/// </summary>
125+
public static bool IsKulprid => CrowdControlConfig.GetInstance().ForceEasterEggs || (_isKulprid ?? (_isKulprid = SteamId == 76561198049559387UL).Value);
126+
121127
#endregion
122128
}

0 commit comments

Comments
 (0)