mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-23 04:34:58 -04:00
better mob AI
This commit is contained in:
parent
6ce84bc623
commit
f7acc5150a
@ -151,9 +151,7 @@
|
||||
<Compile Include="Blocks\DefaultSet.cs" />
|
||||
<Compile Include="Commands\SinglePlayerCommands.cs" />
|
||||
<Compile Include="Entities\Components\IInterpComponent.cs" />
|
||||
<Compile Include="Entities\Mobs\AI\AI.cs" />
|
||||
<Compile Include="Entities\Mobs\AI\FleeAI.cs" />
|
||||
<Compile Include="Entities\Mobs\AI\HostileAI.cs" />
|
||||
<Compile Include="Entities\Mobs\AI.cs" />
|
||||
<Compile Include="Entities\Components\AnimatedComponent.cs" />
|
||||
<Compile Include="Entities\Components\HacksComponent.cs" />
|
||||
<Compile Include="Entities\Components\InputComponent.cs" />
|
||||
@ -343,7 +341,6 @@
|
||||
<Folder Include="2D\Widgets\Chat" />
|
||||
<Folder Include="2D\Widgets\PlayerList" />
|
||||
<Folder Include="Blocks" />
|
||||
<Folder Include="Entities\Mobs\AI" />
|
||||
<Folder Include="Entities\Components" />
|
||||
<Folder Include="Entities\Mobs" />
|
||||
<Folder Include="Events" />
|
||||
|
@ -25,9 +25,7 @@ namespace ClassicalSharp.Entities {
|
||||
info = game.BlockInfo;
|
||||
}
|
||||
|
||||
public void UpdateVelocityState(float xMoving, float zMoving) {
|
||||
if (!hacks.NoclipSlide && (hacks.Noclip && xMoving == 0 && zMoving == 0))
|
||||
entity.Velocity = Vector3.Zero;
|
||||
public void UpdateVelocityState() {
|
||||
if (hacks.Flying || hacks.Noclip) {
|
||||
entity.Velocity.Y = 0; // eliminate the effect of gravity
|
||||
int dir = (hacks.FlyingUp || jumping) ? 1 : (hacks.FlyingDown ? -1 : 0);
|
||||
@ -104,7 +102,7 @@ namespace ClassicalSharp.Entities {
|
||||
airDrag = new Vector3(0.6f, 1f, 0.6f);
|
||||
const float liquidGrav = 0.02f, ropeGrav = 0.034f, normalGrav = 0.08f;
|
||||
|
||||
public void PhysicsTick(float xMoving, float zMoving) {
|
||||
public void PhysicsTick(Vector3 vel) {
|
||||
if (hacks.Noclip) entity.onGround = false;
|
||||
float multiply = GetBaseMultiply(true);
|
||||
float yMultiply = GetBaseMultiply(hacks.CanSpeed);
|
||||
@ -118,18 +116,20 @@ namespace ClassicalSharp.Entities {
|
||||
}
|
||||
|
||||
if (entity.TouchesAnyWater() && !hacks.Flying && !hacks.Noclip) {
|
||||
MoveNormal(xMoving, zMoving, 0.02f * horMul, waterDrag, liquidGrav, yMul);
|
||||
MoveNormal(vel, 0.02f * horMul, waterDrag, liquidGrav, yMul);
|
||||
} else if (entity.TouchesAnyLava() && !hacks.Flying && !hacks.Noclip) {
|
||||
MoveNormal(xMoving, zMoving, 0.02f * horMul, lavaDrag, liquidGrav, yMul);
|
||||
MoveNormal(vel, 0.02f * horMul, lavaDrag, liquidGrav, yMul);
|
||||
} else if (entity.TouchesAnyRope() && !hacks.Flying && !hacks.Noclip) {
|
||||
MoveNormal(xMoving, zMoving, 0.02f * 1.7f, ropeDrag, ropeGrav, yMul);
|
||||
MoveNormal(vel, 0.02f * 1.7f, ropeDrag, ropeGrav, yMul);
|
||||
} else {
|
||||
float factor = !(hacks.Flying || hacks.Noclip) && entity.onGround ? 0.1f : 0.02f;
|
||||
float gravity = useLiquidGravity ? liquidGrav : normalGrav;
|
||||
if (hacks.Flying || hacks.Noclip)
|
||||
MoveFlying(xMoving, zMoving, factor * horMul, normalDrag, gravity, yMul);
|
||||
else
|
||||
MoveNormal(xMoving, zMoving, factor * horMul, normalDrag, gravity, yMul);
|
||||
|
||||
if (hacks.Flying || hacks.Noclip) {
|
||||
MoveFlying(vel, factor * horMul, normalDrag, gravity, yMul);
|
||||
} else {
|
||||
MoveNormal(vel, factor * horMul, normalDrag, gravity, yMul);
|
||||
}
|
||||
|
||||
if (entity.BlockUnderFeet == Block.Ice && !(hacks.Flying || hacks.Noclip)) {
|
||||
// limit components to +-0.25f by rescaling vector to [-0.25, 0.25]
|
||||
@ -147,20 +147,19 @@ namespace ClassicalSharp.Entities {
|
||||
if (entity.onGround) { firstJump = false; secondJump = false; }
|
||||
}
|
||||
|
||||
void AdjHeadingVelocity(float x, float z, float factor) {
|
||||
float dist = (float)Math.Sqrt(x * x + z * z);
|
||||
void MoveHor(Vector3 vel, float factor) {
|
||||
float dist = (float)Math.Sqrt(vel.X * vel.X + vel.Z * vel.Z);
|
||||
if (dist < 0.00001f) return;
|
||||
if (dist < 1) dist = 1;
|
||||
|
||||
float multiply = factor / dist;
|
||||
entity.Velocity += Utils.RotateY(x * multiply, 0, z * multiply, entity.HeadYRadians);
|
||||
|
||||
entity.Velocity += vel * (factor / dist);
|
||||
}
|
||||
|
||||
void MoveFlying(float xMoving, float zMoving, float factor, Vector3 drag, float gravity, float yMul) {
|
||||
AdjHeadingVelocity(zMoving, xMoving, factor);
|
||||
void MoveFlying(Vector3 vel, float factor, Vector3 drag, float gravity, float yMul) {
|
||||
MoveHor(vel, factor);
|
||||
float yVel = (float)Math.Sqrt(entity.Velocity.X * entity.Velocity.X + entity.Velocity.Z * entity.Velocity.Z);
|
||||
// make horizontal speed the same as vertical speed.
|
||||
if ((xMoving != 0 || zMoving != 0) && yVel > 0.001f) {
|
||||
if ((vel.X != 0 || vel.Z != 0) && yVel > 0.001f) {
|
||||
entity.Velocity.Y = 0;
|
||||
yMul = 1;
|
||||
if (hacks.FlyingUp || jumping) entity.Velocity.Y += yVel;
|
||||
@ -169,8 +168,8 @@ namespace ClassicalSharp.Entities {
|
||||
Move(drag, gravity, yMul);
|
||||
}
|
||||
|
||||
void MoveNormal(float xMoving, float zMoving, float factor, Vector3 drag, float gravity, float yMul) {
|
||||
AdjHeadingVelocity(zMoving, xMoving, factor);
|
||||
void MoveNormal(Vector3 vel, float factor, Vector3 drag, float gravity, float yMul) {
|
||||
MoveHor(vel, factor);
|
||||
Move(drag, gravity, yMul);
|
||||
}
|
||||
|
||||
@ -269,9 +268,9 @@ namespace ClassicalSharp.Entities {
|
||||
public void DoEntityPush() {
|
||||
if (!game.ClassicMode || hacks.Noclip) return;
|
||||
|
||||
for (int id = 0; id < EntityList.SelfID; id++) {
|
||||
for (int id = 0; id < EntityList.MaxCount; id++) {
|
||||
Entity other = game.Entities[id];
|
||||
if (other == null) continue;
|
||||
if (other == null || other == entity) continue;
|
||||
|
||||
bool yIntersects =
|
||||
entity.Position.Y <= (other.Position.Y + other.Size.Y) &&
|
||||
|
@ -60,8 +60,12 @@ namespace ClassicalSharp.Entities {
|
||||
|
||||
HandleInput(ref xMoving, ref zMoving);
|
||||
physics.DoEntityPush();
|
||||
physics.UpdateVelocityState(xMoving, zMoving);
|
||||
physics.PhysicsTick(xMoving, zMoving);
|
||||
|
||||
// Immediate stop in noclip mode
|
||||
if (!Hacks.NoclipSlide && (Hacks.Noclip && xMoving == 0 && zMoving == 0))
|
||||
Velocity = Vector3.Zero;
|
||||
physics.UpdateVelocityState();
|
||||
physics.PhysicsTick(GetHeadingVelocity(zMoving, xMoving));
|
||||
|
||||
interp.next.Pos = Position; Position = interp.prev.Pos;
|
||||
anim.UpdateAnimState(interp.prev.Pos, interp.next.Pos, delta);
|
||||
@ -69,6 +73,11 @@ namespace ClassicalSharp.Entities {
|
||||
CheckSkin();
|
||||
sound.Tick(wasOnGround);
|
||||
}
|
||||
|
||||
Vector3 GetHeadingVelocity(float xMoving, float zMoving) {
|
||||
return Utils.RotateY(xMoving, 0, zMoving, HeadYRadians);
|
||||
}
|
||||
|
||||
|
||||
public override void RenderModel(double deltaTime, float t) {
|
||||
anim.GetCurrentAnimState(t);
|
||||
|
82
ClassicalSharp/Entities/Mobs/AI.cs
Normal file
82
ClassicalSharp/Entities/Mobs/AI.cs
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
using System;
|
||||
using OpenTK;
|
||||
|
||||
namespace ClassicalSharp.Entities.Mobs {
|
||||
|
||||
public abstract class AI {
|
||||
|
||||
protected Game game;
|
||||
protected Entity entity;
|
||||
static Random random = new Random();
|
||||
|
||||
public Vector3 MoveVelocity;
|
||||
|
||||
public AI(Game game, Entity entity) {
|
||||
this.game = game;
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
public abstract void Tick(Entity target);
|
||||
|
||||
public abstract void AttackedBy(Entity source);
|
||||
|
||||
|
||||
int randomCount;
|
||||
Vector3 randomDir;
|
||||
protected void MoveRandomly(Entity source) {
|
||||
// Move in new direction
|
||||
if (randomCount == 0) {
|
||||
randomDir.X = (float)(random.NextDouble() - 0.5);
|
||||
randomDir.Z = (float)(random.NextDouble() - 0.5);
|
||||
}
|
||||
|
||||
randomCount = (randomCount + 1) & 0x1F;
|
||||
MoveInDirection(source, randomDir);
|
||||
}
|
||||
|
||||
protected void MoveInDirection(Entity source, Vector3 dir) {
|
||||
double rotYRadians, headXRadians;
|
||||
Utils.GetHeading(dir, out rotYRadians, out headXRadians);
|
||||
|
||||
float rotY = (float)(rotYRadians * Utils.Rad2Deg);
|
||||
float headX = (float)(headXRadians * Utils.Rad2Deg);
|
||||
LocationUpdate update = LocationUpdate.MakeOri(rotY, headX);
|
||||
source.SetLocation(update, false);
|
||||
MoveVelocity = dir * 0.9f;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class FleeAI : AI {
|
||||
|
||||
public FleeAI(Game game, Entity entity) : base(game, entity) { }
|
||||
|
||||
public override void Tick(Entity target) {
|
||||
MoveRandomly(entity);
|
||||
}
|
||||
|
||||
public override void AttackedBy(Entity source) {
|
||||
Vector3 fleeDir = -Vector3.Normalize(source.Position - entity.Position);
|
||||
MoveInDirection(source, fleeDir * 5);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class HostileAI : AI {
|
||||
|
||||
public HostileAI(Game game, Entity entity) : base(game, entity) { }
|
||||
|
||||
public override void Tick(Entity target) {
|
||||
float distSq = (target.Position - entity.Position).LengthSquared;
|
||||
if (distSq > 32 * 32) {
|
||||
MoveRandomly(entity);
|
||||
} else {
|
||||
Vector3 dir = Vector3.Normalize(target.Position - entity.Position);
|
||||
dir.Y = 0;
|
||||
MoveInDirection(entity, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override void AttackedBy(Entity source) {
|
||||
}
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
using System;
|
||||
using OpenTK;
|
||||
|
||||
namespace ClassicalSharp.Entities.Mobs {
|
||||
|
||||
public abstract class AI {
|
||||
|
||||
protected Game game;
|
||||
protected Entity entity;
|
||||
static Random random = new Random();
|
||||
|
||||
public AI(Game game, Entity entity) {
|
||||
this.game = game;
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
public abstract void Tick(Entity target);
|
||||
|
||||
public abstract void AttackedBy(Entity source);
|
||||
|
||||
protected static void MoveRandomly(Entity source) {
|
||||
double dirX = random.NextDouble() - 0.5, dirZ = random.NextDouble() - 0.5;
|
||||
MoveInDirection(source, new Vector3((float)dirX, 0, (float)dirZ));
|
||||
}
|
||||
|
||||
protected static void MoveInDirection(Entity source, Vector3 dir) {
|
||||
double rotYRadians, headXRadians;
|
||||
Utils.GetHeading(dir, out rotYRadians, out headXRadians);
|
||||
|
||||
float rotY = (float)(rotYRadians * Utils.Rad2Deg);
|
||||
float headX = (float)(headXRadians * Utils.Rad2Deg);
|
||||
LocationUpdate update = LocationUpdate.MakePosAndOri(dir, rotY, headX, true);
|
||||
source.SetLocation(update, false);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
using System;
|
||||
using OpenTK;
|
||||
|
||||
namespace ClassicalSharp.Entities.Mobs {
|
||||
|
||||
public sealed class FleeAI : AI {
|
||||
|
||||
public FleeAI(Game game, Entity entity) : base(game, entity) { }
|
||||
|
||||
public override void Tick(Entity target) {
|
||||
MoveRandomly(entity);
|
||||
}
|
||||
|
||||
public override void AttackedBy(Entity source) {
|
||||
Vector3 fleeDir = -Vector3.Normalize(source.Position - entity.Position);
|
||||
MoveInDirection(source, fleeDir * 5);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
|
||||
using System;
|
||||
using OpenTK;
|
||||
|
||||
namespace ClassicalSharp.Entities.Mobs {
|
||||
|
||||
public sealed class HostileAI : AI {
|
||||
|
||||
public HostileAI(Game game, Entity entity) : base(game, entity) { }
|
||||
|
||||
public override void Tick(Entity target) {
|
||||
float distSq = (target.Position - entity.Position).LengthSquared;
|
||||
if (distSq > 32 * 32) {
|
||||
MoveRandomly(entity);
|
||||
} else {
|
||||
Vector3 dir = Vector3.Normalize(target.Position - entity.Position);
|
||||
MoveInDirection(entity, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override void AttackedBy(Entity source) {
|
||||
}
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ namespace ClassicalSharp.Entities.Mobs {
|
||||
CollisionsComponent collisions;
|
||||
PhysicsComponent physics;
|
||||
static HacksComponent hacks = new HacksComponent(null, null);
|
||||
AI ai;
|
||||
|
||||
public MobEntity(Game game, string model) : base(game) {
|
||||
StepSize = 0.5f;
|
||||
@ -20,6 +21,12 @@ namespace ClassicalSharp.Entities.Mobs {
|
||||
physics = new PhysicsComponent(game, this);
|
||||
physics.hacks = hacks;
|
||||
physics.collisions = collisions;
|
||||
|
||||
if (Utils.CaselessEquals(model, "pig") || Utils.CaselessEquals(model, "sheep")) {
|
||||
ai = new FleeAI(game, this);
|
||||
} else {
|
||||
ai = new HostileAI(game, this);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Despawn() { }
|
||||
@ -29,11 +36,11 @@ namespace ClassicalSharp.Entities.Mobs {
|
||||
static Random rand = new Random();
|
||||
public override void Tick(double delta) {
|
||||
if (game.World.IsNotLoaded) return;
|
||||
float xMoving = 0.98f, zMoving = 0.98f;
|
||||
interp.AdvanceState();
|
||||
bool wasOnGround = onGround;
|
||||
physics.UpdateVelocityState(xMoving, zMoving);
|
||||
physics.PhysicsTick(xMoving, zMoving);
|
||||
physics.UpdateVelocityState();
|
||||
|
||||
ai.Tick(game.LocalPlayer);
|
||||
physics.PhysicsTick(ai.MoveVelocity);
|
||||
interp.next.Pos = Position; Position = interp.prev.Pos;
|
||||
anim.UpdateAnimState(interp.prev.Pos, interp.next.Pos, delta);
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ namespace ClassicalSharp {
|
||||
|
||||
public static void GetHeading(Vector3 dir, out double yaw, out double pitch) {
|
||||
pitch = Math.Asin(-dir.Y);
|
||||
yaw = Math.Atan2(dir.Z, dir.X);
|
||||
yaw = Math.Atan2(dir.X, -dir.Z);
|
||||
}
|
||||
|
||||
public static int Floor(float value) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user