diff --git a/NPCs/Corrupt/Pinwheel.cs b/NPCs/Corrupt/Pinwheel.cs index cabe6996a..d3fa917a8 100644 --- a/NPCs/Corrupt/Pinwheel.cs +++ b/NPCs/Corrupt/Pinwheel.cs @@ -16,6 +16,7 @@ namespace Origins.NPCs.Corrupt { public class Pinwheel : ModNPC, IWikiNPC { + public float KnockbackMult => DigState == state_air ? 1 : 0.5f; public Rectangle DrawRect => new(0, 0, 30, 30); public int AnimationFrames => 6; public int FrameDuration => 3; @@ -27,8 +28,6 @@ public override void SetStaticDefaults() { AssimilationLoader.AddNPCAssimilation(Type, 0.03f); } public override void SetDefaults() { - NPC.aiStyle = NPCAIStyleID.Fighter; - AIType = NPCID.GoblinScout; NPC.lifeMax = 85; NPC.defense = 6; NPC.damage = 16; @@ -37,7 +36,7 @@ public override void SetDefaults() { NPC.friendly = false; NPC.HitSound = SoundID.NPCHit1; NPC.DeathSound = SoundID.NPCDeath1; - NPC.knockBackResist = 0; + NPC.knockBackResist = KnockbackMult; NPC.value = 61; NPC.behindTiles = true; } @@ -53,27 +52,97 @@ public override void SetBestiary(BestiaryDatabase database, BestiaryEntry bestia } public float DigTime = 60 * 0.5f; public override void OnSpawn(IEntitySource source) { - NPC.aiStyle = NPCAIStyleID.Fighter; // don't remove me, for some reason aiStyle is "-1" without me - NPC.localAI[0] = DigTime; + NPC.ai[0] = DigTime; } public override void ModifyNPCLoot(NPCLoot npcLoot) { npcLoot.Add(ItemDropRule.Common(ItemID.RottenChunk, 3)); npcLoot.Add(ItemDropRule.Common(ItemID.DemoniteOre, 25)); } - public bool wasCollideY = false; - public override bool PreAI() { - float bladeOffsetMax = NPC.frame.Size().Y / 16; // how much of the blade goes into the ground - float bladeAllowedOut = bladeOffsetMax * 0.3f; // how deep the blade doesn't need to be in the ground before requiring to stop the burrow - float acc = 1.2f; + Vector2 lastAIVelocity = Vector2.Zero; + public ref float DigState => ref NPC.ai[1]; + public ref float Spin => ref NPC.ai[2]; + public const float state_air = 0; + public const float state_normal = 1; + public const float state_front_wall = 2; + public const float state_back_wall = 3; + public const float state_ceiling = 4; + public override void AI() { + NPC.TargetClosest(); + + float acceleration = 0.2f; + float air_deceleration_flat = 0.02f; + float air_deceleration_mult = 0.99f; + float max_speed = 6f; #region Movement - if (NPC.collideY) { - if (!wasCollideY || NPC.localAI[1] < bladeOffsetMax) { + bool justKnocked = NPC.velocity != lastAIVelocity; + if (justKnocked) { + Spin = NPC.velocity.X; + NPC.collideY = NPC.collideX = false; + } + if (DigState == state_ceiling || NPC.collideY || NPC.collideX) { + Spin += acceleration * NPC.direction; + if (Spin * NPC.direction > max_speed) Spin = max_speed * NPC.direction; + if (DigState == state_ceiling || (NPC.collideY && NPC.collideX && NPC.oldVelocity.Y < 0)) { + if (NPC.collideY) { + DigState = state_ceiling; + NPC.velocity.X = -Spin; + Vector2 climbUp = Vector2.UnitY * NPC.direction * -Spin; + NPC.velocity.Y = Collision.TileCollision(NPC.position, climbUp, NPC.width, NPC.height, true, true).Y; + Min(ref NPC.velocity.Y, -2); + NPC.GravityMultiplier *= -1; + } else { + DigState = state_air; + Vector2 climbUp = Vector2.UnitX * NPC.direction * Spin; + if (climbUp.TrySet(Collision.TileCollision(NPC.position, climbUp, NPC.width, NPC.height, true, true))) { + if (DigState.TrySet(state_normal)) NPC.position += climbUp; + NPC.velocity.X = Spin; + NPC.velocity.Y = -1; + } + } + } else if (NPC.collideX) { + NPC.velocity.Y = Spin * -NPC.direction; + if (Math.Abs(NPC.velocity.X) < 1) NPC.velocity.X = NPC.direction; + DigState = state_normal; + } else { + NPC.velocity.X = Spin; + DigState = state_normal; + } + } else { + if (justKnocked) DigState = state_air; + bool hasTarget = NPC.target >= 0; + bool fall = hasTarget && NPC.targetRect.Bottom().Y - NPC.velocity.Y > NPC.Bottom.Y; + Vector2 climbDown = Vector2.UnitX * NPC.direction * -Spin; + if (DigState == state_air) { + Spin *= air_deceleration_mult; + MathUtils.LinearSmoothing(ref Spin, 0, air_deceleration_flat); + DigState = state_air; + } else if (NPC.velocity.Y < 0 && Math.Abs(NPC.velocity.X) == 1) { + NPC.velocity.X = Spin; + NPC.velocity.Y = 0; + DigState = state_front_wall; + } else if (climbDown.TrySet(Collision.TileCollision(NPC.position, climbDown, NPC.width, NPC.height, fall, fall))) { + if (NPC.targetRect.Y > NPC.Bottom.Y) { + if (DigState.TrySet(state_back_wall)) NPC.position += climbDown; + NPC.velocity.X = 0; + NPC.velocity.Y = Spin; + } else { + NPC.velocity.X = Spin; + NPC.velocity.Y = -Spin; + DigState = state_air; + } + } else { + DigState = state_air; + } + } + NPC.knockBackResist = KnockbackMult; + /*if (NPC.collideY) { + if (!wasCollideY || DigState < bladeOffsetMax) { NPC.netUpdate = true; - if (MathUtils.LinearSmoothing(ref NPC.localAI[1], bladeOffsetMax, acc / 4)) { // the value after "acc / " is the speed multiplier for burrowing the blade + if (MathUtils.LinearSmoothing(ref DigState, bladeOffsetMax, acceleration / 4)) { // the value after "acc / " is the speed multiplier for burrowing the blade wasCollideY = true; - } - if (NPC.localAI[1] <= -bladeAllowedOut) { - NPC.rotation += ((acc * 3) / NPC.width) * NPC.direction; + } + if (DigState <= -bladeAllowedOut) { + NPC.rotation += ((acceleration * 3) / NPC.width) * NPC.direction; if (!NPC.collideX) NPC.velocity.X *= 0.1f * NPC.direction; int width = 60 - 40; for (int i = -width; i <= width; i += width / 16) { @@ -83,20 +152,21 @@ public override bool PreAI() { Tile tile = Framing.GetTileSafely(pos.ToTileCoordinates()); if (tile.HasSolidTile() && Main.rand.NextBool(30)) Collision.HitTiles(tile.GetTilePosition().ToWorldCoordinates(), new(0, -2.5f), 0, 0); } - return false; } } - if (!NPC.collideX) NPC.velocity.X += acc * NPC.direction; + if (!NPC.collideX) { + NPC.velocity.X += acceleration * NPC.direction; + if (NPC.velocity.X * NPC.direction > max_speed) NPC.velocity.X = max_speed * NPC.direction; + } + NPC.knockBackResist = 0.2f; } else { - if (NPC.localAI[1] > -bladeOffsetMax) MathUtils.LinearSmoothing(ref NPC.localAI[1], -bladeOffsetMax, acc / 6); // the value after "acc / " is the speed multiplier for unburrowing the blade - wasCollideY = NPC.localAI[1] > -bladeAllowedOut; + if (DigState > -bladeOffsetMax) MathUtils.LinearSmoothing(ref DigState, -bladeOffsetMax, acceleration / 6); // the value after "acc / " is the speed multiplier for unburrowing the blade + wasCollideY = DigState > -bladeAllowedOut; NPC.netUpdate = true; - } - NPC.rotation += (1f / NPC.width) * NPC.velocity.X; // I love radians + }*/ + NPC.rotation += Spin / NPC.width; // I love radians #endregion Movement - return true; - } - public override void AI() { + #region Dig up Dust if (Math.Abs(NPC.velocity.X) > 0.07f && NPC.collideY) { Vector2 pos = NPC.velocity.X < 0 ? NPC.BottomRight : NPC.BottomLeft; @@ -106,7 +176,7 @@ public override void AI() { NPC.netUpdate = true; int extra = 0; Vector2 vel = new(0.6f * -NPC.velocity.X, -2.5f); - if (NPC.localAI[0]-- <= 0) { + if (NPC.ai[0]-- <= 0) { extra = 2; int type = Lingering_Shadowflame.ShadeIDs[Main.rand.Next(Lingering_Shadowflame.ShadeIDs.Count)]; int dmg = 10; @@ -114,8 +184,8 @@ public override void AI() { type = Lingering_Shadowflame.CursedIDs[Main.rand.Next(Lingering_Shadowflame.CursedIDs.Count)]; dmg = 20; } - Projectile.NewProjectile(NPC.GetSource_FromAI(), pos, vel, type, dmg, 0); - NPC.localAI[0] = DigTime; + NPC.SpawnProjectile(NPC.GetSource_FromAI(), pos, vel, type, dmg, 0); + NPC.ai[0] = DigTime; } if (Main.rand.NextBool(10) || extra > 0) { vel.Y *= 5; @@ -129,31 +199,20 @@ public override void AI() { } #endregion Dig up Dust } - public override void SendExtraAI(BinaryWriter writer) { - for (int i = 0; i < NPC.localAI.Length; i++) { - writer.Write(NPC.localAI[i]); - } - writer.Write(wasCollideY); - } - public override void ReceiveExtraAI(BinaryReader reader) { - for (int i = 0; i < NPC.localAI.Length; i++) { - NPC.localAI[i] = reader.ReadSingle(); - } - wasCollideY = reader.ReadBoolean(); - } public override void FindFrame(int frameHeight) { + lastAIVelocity = NPC.velocity; // oddly, this is run on the server too, so it can safely be used to store the velocity after gravity & collisions + NPC.collideX = NPC.velocity.X != NPC.oldVelocity.X; + NPC.collideY = NPC.velocity.Y != NPC.oldVelocity.Y; NPC.DoFrames(Main.rand.Next(3, 6)); // meant to simulate random blinking times } public override bool PreDraw(SpriteBatch spriteBatch, Vector2 screenPos, Color drawColor) { Texture2D texture = TextureAssets.Npc[Type].Value; - Vector2 offset = new(0, NPC.localAI[1]); if (NPC.IsABestiaryIconDummy) { - offset.Y = NPC.frame.Size().Y / 16; drawColor = Color.White; } Main.EntitySpriteDraw( texture, - NPC.Center + offset - screenPos, + NPC.Center - screenPos, NPC.frame, drawColor, NPC.rotation, @@ -165,7 +224,7 @@ public override bool PreDraw(SpriteBatch spriteBatch, Vector2 screenPos, Color d if (NPC.IsABestiaryIconDummy) { debugText = $"Color: {drawColor}\n Offset: {offset}\n Rot: {NPC.rotation}"; } else { - debugText = $"AI: {NPC.aiStyle}, {AIType}, S/Dir: {NPC.spriteDirection}, {NPC.direction}\nOffset: {offset}\nVel: {NPC.velocity}, {Math.Abs(NPC.velocity.X)}, {Math.Abs(NPC.velocity.Y)}\nWasCollideY: {wasCollideY}\nTarget: {NPC.HasValidTarget}, {NPC.GetTargetData().Invalid}, {NPC.GetTargetData().Type}\nAI: {NPC.ai[0]}, {NPC.ai[1]}, {NPC.ai[2]}, {NPC.ai[3]}\nLAI: {NPC.localAI[0]}, {NPC.localAI[1]}, {NPC.localAI[2]}, {NPC.localAI[3]}"; + debugText = $"AI: {NPC.aiStyle}, {AIType}, S/Dir: {NPC.spriteDirection}, {NPC.direction}\nOffset: {offset}\nVel: {NPC.velocity}, {Math.Abs(NPC.velocity.X)}, {Math.Abs(NPC.velocity.Y)}\nWasCollideY: {wasCollideY}\nTarget: {NPC.HasValidTarget}, {NPC.GetTargetData().Invalid}, {NPC.GetTargetData().Type}\nAI: {NPC.ai[0]}, {DigState}, {Spin}, {NPC.ai[3]}\nLAI: {NPC.ai[0]}, {DigState}, {Spin}, {NPC.ai[3]}"; } OriginExtensions.DrawDebugTextAbove(spriteBatch, debugText, NPC.Top + offset - screenPos);*/ //NPC.Hitbox.DrawDebugOutlineSprite(Color.White, -screenPos, false); diff --git a/completionList.txt b/completionList.txt index 23a04bc13..94add8b78 100644 --- a/completionList.txt +++ b/completionList.txt @@ -237,7 +237,7 @@ ENEMY NPCS: (X) Limestone Creeper (^) Optiphage (X) Packhunter !!! -(#3, ^2) Pinwheel (randomly gets stuck on walls, idk how to fix) +(☻) Pinwheel (^) Power Suit Zombie (^) Pustule Jellyfish (X) QM-76 'Quakemaker' !!!