|
| 1 | +using BlockGame.main; |
| 2 | +using BlockGame.util; |
| 3 | +using BlockGame.world.item; |
| 4 | +using Molten.DoublePrecision; |
| 5 | +namespace BlockGame.world.entity; |
| 6 | + |
| 7 | +public class BigEye : Hostile { |
| 8 | + protected override double detectRadius => 24.0; |
| 9 | + protected override float attackDamage => 4.0f; |
| 10 | + protected override int attackCooldown => 40; |
| 11 | + protected override bool usePathfinding => false; // handles own flight movement |
| 12 | + |
| 13 | + private const double FLIGHT_SPEED = 1; |
| 14 | + private const double HOVER_HEIGHT = 18.0; // preferred height above ground |
| 15 | + |
| 16 | + private Vector3D? flyTarget; |
| 17 | + private int retargetCooldown; |
| 18 | + |
| 19 | + // don't take fall damage |
| 20 | + protected override bool needsFallDamage => false; |
| 21 | + |
| 22 | + public override bool burnInSunlight => true; |
| 23 | + public override double eyeHeight => 0; // it's a fucking floating eye |
| 24 | + |
| 25 | + public BigEye(World world) : base(world, "BigEye") { |
| 26 | + tex = "textures/entity/bigeye.png"; |
| 27 | + flyMode = true; |
| 28 | + hp = 100; |
| 29 | + } |
| 30 | + |
| 31 | + public override AABB calcAABB(Vector3D pos) { |
| 32 | + return new AABB( |
| 33 | + pos.X - 0.25, pos.Y - 0.25, pos.Z - 0.25, |
| 34 | + pos.X + 0.25, pos.Y + 0.25, pos.Z + 0.25 |
| 35 | + ); |
| 36 | + } |
| 37 | + |
| 38 | + public override void AI(double dt) { |
| 39 | + spawnTicks++; |
| 40 | + updateSunlightBurn(); |
| 41 | + |
| 42 | + // call base for attack detection/management |
| 43 | + base.AI(dt); |
| 44 | + } |
| 45 | + |
| 46 | + protected override void onTargetDetected(double dt, Entity target, double distance) { |
| 47 | + flyTowardsTarget(dt); |
| 48 | + } |
| 49 | + |
| 50 | + protected override void onNoTargetDetected(double dt) { |
| 51 | + idleFlight(dt); |
| 52 | + } |
| 53 | + |
| 54 | + private void flyTowardsTarget(double dt) { |
| 55 | + if (target == null) return; |
| 56 | + |
| 57 | + var dir = target.position - position; |
| 58 | + dir.Y += 1.0; |
| 59 | + |
| 60 | + if (dir.Length() > 0.01) { |
| 61 | + dir = Vector3D.Normalize(dir); |
| 62 | + |
| 63 | + velocity.X += dir.X * FLIGHT_SPEED; |
| 64 | + velocity.Y += dir.Y * FLIGHT_SPEED; |
| 65 | + velocity.Z += dir.Z * FLIGHT_SPEED; |
| 66 | + |
| 67 | + // look at target |
| 68 | + var yaw = Meth.rad2deg((float)Math.Atan2(dir.X, dir.Z)); |
| 69 | + var pitch = Meth.rad2deg((float)Math.Atan2(-dir.Y, Math.Sqrt(dir.X * dir.X + dir.Z * dir.Z))); |
| 70 | + |
| 71 | + rotation.Y = yaw; |
| 72 | + rotation.X = pitch; |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + private void idleFlight(double dt) { |
| 77 | + retargetCooldown--; |
| 78 | + |
| 79 | + // pick new target |
| 80 | + if (flyTarget == null || retargetCooldown <= 0) { |
| 81 | + var angle = Game.random.NextSingle(0, MathF.PI * 2); |
| 82 | + var dist = Game.random.NextSingle(8f, 16f); |
| 83 | + var tx = position.X + MathF.Cos(angle) * dist; |
| 84 | + var tz = position.Z + MathF.Sin(angle) * dist; |
| 85 | + |
| 86 | + // try to maintain hover height above ground |
| 87 | + var groundY = findGroundBelow((int)tx, (int)tz); |
| 88 | + var ty = groundY + HOVER_HEIGHT + Game.random.NextSingle(-2f, 2f); |
| 89 | + |
| 90 | + flyTarget = new Vector3D(tx, ty, tz); |
| 91 | + retargetCooldown = 60 + Game.random.Next(120); |
| 92 | + } |
| 93 | + |
| 94 | + // fly towards target |
| 95 | + if (flyTarget != null) { |
| 96 | + var dir = flyTarget.Value - position; |
| 97 | + var dist = dir.Length(); |
| 98 | + |
| 99 | + // reached target? |
| 100 | + if (dist < 2.0) { |
| 101 | + flyTarget = null; |
| 102 | + retargetCooldown = 20; |
| 103 | + return; |
| 104 | + } |
| 105 | + |
| 106 | + dir = Vector3D.Normalize(dir); |
| 107 | + |
| 108 | + velocity.X += dir.X * FLIGHT_SPEED * 0.6; |
| 109 | + velocity.Y += dir.Y * FLIGHT_SPEED * 0.6; |
| 110 | + velocity.Z += dir.Z * FLIGHT_SPEED * 0.6; |
| 111 | + |
| 112 | + var yaw = Meth.rad2deg((float)Math.Atan2(dir.X, dir.Z)); |
| 113 | + rotation.Y = yaw; |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + private double findGroundBelow(int x, int z) { |
| 118 | + // scan downwards to find ground level |
| 119 | + for (int y = (int)position.Y; y > 0; y--) { |
| 120 | + if (world.getBlock(x, y, z) != 0) { |
| 121 | + return y + 1; |
| 122 | + } |
| 123 | + } |
| 124 | + |
| 125 | + return 64; |
| 126 | + } |
| 127 | + |
| 128 | + // disable gravity |
| 129 | + protected override void prePhysics(double dt) { |
| 130 | + AI(dt); |
| 131 | + |
| 132 | + // apply drag |
| 133 | + velocity *= 0.85; |
| 134 | + } |
| 135 | + |
| 136 | + public override void getDrop(List<ItemStack> drops) { |
| 137 | + //if (id == BigEye.id && Game.random.Next(20) == 0) { |
| 138 | + // drops.Add(new ItemStack(LW_BOOTS.item, 1, 0)); |
| 139 | + //} |
| 140 | + } |
| 141 | +} |
0 commit comments