Skip to content
Open
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
147 changes: 103 additions & 44 deletions NPCs/Corrupt/Pinwheel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -27,8 +28,6 @@ public override void SetStaticDefaults() {
AssimilationLoader.AddNPCAssimilation<Corrupt_Assimilation>(Type, 0.03f);
}
public override void SetDefaults() {
NPC.aiStyle = NPCAIStyleID.Fighter;
AIType = NPCID.GoblinScout;
NPC.lifeMax = 85;
NPC.defense = 6;
NPC.damage = 16;
Expand All @@ -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;
}
Expand All @@ -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) {
Expand All @@ -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;
Expand All @@ -106,16 +176,16 @@ 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;
if (Main.hardMode && Main.expertMode) {// enemies only scale with progress in expert mode and higher
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;
Expand All @@ -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,
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion completionList.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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' !!!
Expand Down