better mob AI

This commit is contained in:
UnknownShadow200 2017-02-09 23:28:36 +11:00
parent 6ce84bc623
commit f7acc5150a
9 changed files with 128 additions and 115 deletions

View File

@ -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" />

View File

@ -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) &&

View File

@ -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);

View 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) {
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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) {
}
}
}

View File

@ -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);
}

View File

@ -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) {