diff --git a/ClassicalSharp/ClassicalSharp.csproj b/ClassicalSharp/ClassicalSharp.csproj index ea19983c9..b841d29ae 100644 --- a/ClassicalSharp/ClassicalSharp.csproj +++ b/ClassicalSharp/ClassicalSharp.csproj @@ -150,13 +150,14 @@ - - - + + + + - + @@ -167,6 +168,7 @@ + @@ -341,8 +343,9 @@ - + + diff --git a/ClassicalSharp/Entities/Components/LocalInterpComponent.cs b/ClassicalSharp/Entities/Components/LocalInterpComponent.cs new file mode 100644 index 000000000..e3e3563ee --- /dev/null +++ b/ClassicalSharp/Entities/Components/LocalInterpComponent.cs @@ -0,0 +1,71 @@ +// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 +using System; +using ClassicalSharp.Renderers; +using OpenTK; + +namespace ClassicalSharp.Entities { + + /// Entity component that performs interpolation of position and model head yaw over time. + public sealed class LocalInterpComponent { + + Entity entity; + public LocalInterpComponent(Game game, Entity entity) { + this.entity = entity; + } + + internal Vector3 lastPos, nextPos; + internal float lastHeadYaw, nextHeadYaw, lastPitch, nextPitch, lastYaw, nextYaw; + int yawStateCount; + float[] yawStates = new float[15]; + + public void SetLocation(LocationUpdate update, bool interpolate) { + if (update.IncludesPosition) { + nextPos = update.RelativePosition ? nextPos + update.Pos : update.Pos; + double blockOffset = nextPos.Y - Math.Floor(nextPos.Y); + if (blockOffset < Entity.Adjustment) + nextPos.Y += Entity.Adjustment; + if (!interpolate) { + lastPos = entity.Position = nextPos; + } + } + + if (update.IncludesOrientation) { + nextHeadYaw = update.Yaw; + nextPitch = update.Pitch; + if (!interpolate) { + lastHeadYaw = entity.YawDegrees = nextHeadYaw; + lastPitch = entity.PitchDegrees = nextPitch; + entity.HeadYawDegrees = entity.YawDegrees; + yawStateCount = 0; + } else { + for (int i = 0; i < 3; i++) + AddYaw(Utils.LerpAngle(lastHeadYaw, nextHeadYaw, (i + 1) / 3f)); + } + } + } + + public void AdvanceState() { + lastPos = entity.Position = nextPos; + lastHeadYaw = nextHeadYaw; + lastYaw = nextYaw; + lastPitch = nextPitch; + + if (yawStateCount > 0) { + nextYaw = yawStates[0]; + RemoveOldest(yawStates, ref yawStateCount); + } + } + + void AddYaw(float state) { + if (yawStateCount == yawStates.Length) + RemoveOldest(yawStates, ref yawStateCount); + yawStates[yawStateCount++] = state; + } + + void RemoveOldest(T[] array, ref int count) { + for (int i = 0; i < array.Length - 1; i++) + array[i] = array[i + 1]; + count--; + } + } +} \ No newline at end of file diff --git a/ClassicalSharp/Entities/Components/InterpolatedComponent.cs b/ClassicalSharp/Entities/Components/NetInterpComponent.cs similarity index 96% rename from ClassicalSharp/Entities/Components/InterpolatedComponent.cs rename to ClassicalSharp/Entities/Components/NetInterpComponent.cs index a1c04da47..b618b4008 100644 --- a/ClassicalSharp/Entities/Components/InterpolatedComponent.cs +++ b/ClassicalSharp/Entities/Components/NetInterpComponent.cs @@ -6,10 +6,10 @@ using OpenTK; namespace ClassicalSharp.Entities { /// Entity component that performs interpolation of position and model head yaw over time. - public sealed class InterpolatedComponent { + public sealed class NetInterpComponent { Entity entity; - public InterpolatedComponent(Game game, Entity entity) { + public NetInterpComponent(Game game, Entity entity) { this.entity = entity; } diff --git a/ClassicalSharp/Entities/Components/SoundComponent.cs b/ClassicalSharp/Entities/Components/SoundComponent.cs index 6e722b6ae..5df8d7c12 100644 --- a/ClassicalSharp/Entities/Components/SoundComponent.cs +++ b/ClassicalSharp/Entities/Components/SoundComponent.cs @@ -22,7 +22,7 @@ namespace ClassicalSharp.Entities { Vector3 lastSoundPos; public void Tick(bool wasOnGround) { - Vector3 soundPos = p.nextPos; + Vector3 soundPos = p.interp.nextPos; GetSound(); if (!anyNonAir) soundPos = Utils.MaxPos(); @@ -55,7 +55,7 @@ namespace ClassicalSharp.Entities { bool anyNonAir = false; SoundType sndType = SoundType.None; void GetSound() { - Vector3 pos = p.nextPos; + Vector3 pos = p.interp.nextPos; AABB bounds = p.Bounds; sndType = SoundType.None; anyNonAir = false; diff --git a/ClassicalSharp/Entities/LocalPlayer.cs b/ClassicalSharp/Entities/LocalPlayer.cs index ffc659a6f..29610691f 100644 --- a/ClassicalSharp/Entities/LocalPlayer.cs +++ b/ClassicalSharp/Entities/LocalPlayer.cs @@ -26,12 +26,13 @@ namespace ClassicalSharp.Entities { get { return (float)PhysicsComponent.GetMaxHeight(physics.jumpVel); } } - internal float curWalkTime, curSwing; + internal float curSwing; internal CollisionsComponent collisions; public HacksComponent Hacks; internal PhysicsComponent physics; internal InputComponent input; internal SoundComponent sound; + internal LocalInterpComponent interp; public LocalPlayer(Game game) : base(game) { DisplayName = game.Username; @@ -43,6 +44,7 @@ namespace ClassicalSharp.Entities { physics = new PhysicsComponent(game, this); input = new InputComponent(game, this); sound = new SoundComponent(game, this); + interp = new LocalInterpComponent(game, this); physics.hacks = Hacks; input.Hacks = Hacks; physics.collisions = collisions; @@ -55,9 +57,7 @@ namespace ClassicalSharp.Entities { && Hacks.CanSpeed ? 1 : 0.5f; OldVelocity = Velocity; float xMoving = 0, zMoving = 0; - lastPos = Position = nextPos; - lastYaw = nextYaw; - lastPitch = nextPitch; + interp.AdvanceState(); bool wasOnGround = onGround; HandleInput(ref xMoving, ref zMoving); @@ -65,20 +65,17 @@ namespace ClassicalSharp.Entities { physics.UpdateVelocityState(xMoving, zMoving); physics.PhysicsTick(xMoving, zMoving); - nextPos = Position; - Position = lastPos; - anim.UpdateAnimState(lastPos, nextPos, delta); + interp.nextPos = Position; Position = interp.lastPos; + anim.UpdateAnimState(interp.lastPos, interp.nextPos, delta); CheckSkin(); sound.Tick(wasOnGround); - UpdateCurrentBodyYaw(); } public override void RenderModel(double deltaTime, float t) { anim.GetCurrentAnimState(t); curSwing = Utils.Lerp(anim.swingO, anim.swingN, t); - curWalkTime = Utils.Lerp(anim.walkTimeO, anim.walkTimeN, t); - + if (!game.Camera.IsThirdPerson) return; Model.Render(this); } @@ -118,65 +115,18 @@ namespace ClassicalSharp.Entities { } } - internal Vector3 lastPos, nextPos; - internal float lastYaw, nextYaw, lastPitch, nextPitch; - float newYaw, oldYaw; - int yawStateCount; - float[] yawStates = new float[15]; - public override void SetLocation(LocationUpdate update, bool interpolate) { - if (update.IncludesPosition) { - nextPos = update.RelativePosition ? nextPos + update.Pos : update.Pos; - double blockOffset = nextPos.Y - Math.Floor(nextPos.Y); - if (blockOffset < Entity.Adjustment) - nextPos.Y += Entity.Adjustment; - if (!interpolate) { - lastPos = Position = nextPos; - } - } - if (update.IncludesOrientation) { - nextYaw = update.Yaw; - nextPitch = update.Pitch; - if (!interpolate) { - lastYaw = YawDegrees = nextYaw; - lastPitch = PitchDegrees = nextPitch; - HeadYawDegrees = YawDegrees; - yawStateCount = 0; - } else { - for (int i = 0; i < 3; i++) - AddYaw(Utils.LerpAngle(lastYaw, nextYaw, (i + 1) / 3f)); - } - } + interp.SetLocation(update, interpolate); } /// Linearly interpolates position and rotation between the previous and next state. public void SetInterpPosition(float t) { if (!Hacks.WOMStyleHacks || !Hacks.Noclip) - Position = Vector3.Lerp(lastPos, nextPos, t); + Position = Vector3.Lerp(interp.lastPos, interp.nextPos, t); - HeadYawDegrees = Utils.LerpAngle(lastYaw, nextYaw, t); - YawDegrees = Utils.LerpAngle(oldYaw, newYaw, t); - PitchDegrees = Utils.LerpAngle(lastPitch, nextPitch, t); - } - - void AddYaw(float state) { - if (yawStateCount == yawStates.Length) - RemoveOldest(yawStates, ref yawStateCount); - yawStates[yawStateCount++] = state; - } - - void UpdateCurrentBodyYaw() { - oldYaw = newYaw; - if (yawStateCount > 0) { - newYaw = yawStates[0]; - RemoveOldest(yawStates, ref yawStateCount); - } - } - - void RemoveOldest(T[] array, ref int count) { - for (int i = 0; i < array.Length - 1; i++) - array[i] = array[i + 1]; - count--; + HeadYawDegrees = Utils.LerpAngle(interp.lastHeadYaw, interp.nextHeadYaw, t); + YawDegrees = Utils.LerpAngle(interp.lastYaw, interp.nextYaw, t); + PitchDegrees = Utils.LerpAngle(interp.lastPitch, interp.nextPitch, t); } public void Init(Game game) { diff --git a/ClassicalSharp/Entities/AI/AI.cs b/ClassicalSharp/Entities/Mobs/AI/AI.cs similarity index 96% rename from ClassicalSharp/Entities/AI/AI.cs rename to ClassicalSharp/Entities/Mobs/AI/AI.cs index 99ffb8de6..7f063aaf6 100644 --- a/ClassicalSharp/Entities/AI/AI.cs +++ b/ClassicalSharp/Entities/Mobs/AI/AI.cs @@ -2,7 +2,7 @@ using System; using OpenTK; -namespace ClassicalSharp.Entities { +namespace ClassicalSharp.Entities.Mobs { public abstract class AI { diff --git a/ClassicalSharp/Entities/AI/FleeAI.cs b/ClassicalSharp/Entities/Mobs/AI/FleeAI.cs similarity index 91% rename from ClassicalSharp/Entities/AI/FleeAI.cs rename to ClassicalSharp/Entities/Mobs/AI/FleeAI.cs index a292f69f1..455bf2c7d 100644 --- a/ClassicalSharp/Entities/AI/FleeAI.cs +++ b/ClassicalSharp/Entities/Mobs/AI/FleeAI.cs @@ -2,7 +2,7 @@ using System; using OpenTK; -namespace ClassicalSharp.Entities { +namespace ClassicalSharp.Entities.Mobs { public sealed class FleeAI : AI { diff --git a/ClassicalSharp/Entities/AI/HostileAI.cs b/ClassicalSharp/Entities/Mobs/AI/HostileAI.cs similarity index 93% rename from ClassicalSharp/Entities/AI/HostileAI.cs rename to ClassicalSharp/Entities/Mobs/AI/HostileAI.cs index ba6cc2bed..fa38119f0 100644 --- a/ClassicalSharp/Entities/AI/HostileAI.cs +++ b/ClassicalSharp/Entities/Mobs/AI/HostileAI.cs @@ -2,7 +2,7 @@ using System; using OpenTK; -namespace ClassicalSharp.Entities { +namespace ClassicalSharp.Entities.Mobs { public sealed class HostileAI : AI { diff --git a/ClassicalSharp/Entities/Mobs/MobEntity.cs b/ClassicalSharp/Entities/Mobs/MobEntity.cs new file mode 100644 index 000000000..caed43251 --- /dev/null +++ b/ClassicalSharp/Entities/Mobs/MobEntity.cs @@ -0,0 +1,42 @@ +// Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 +using System; +using OpenTK; + +namespace ClassicalSharp.Entities.Mobs { + + public class MobEntity : Entity { + + LocalInterpComponent interp; + public MobEntity(Game game, string model) : base(game) { + StepSize = 0.5f; + SetModel(model); + interp = new LocalInterpComponent(game, this); + } + + public override void Despawn() { } + public override void RenderName() { } + + // TODO: this is just so the entities do something, remove later + static Random rand = new Random(); + public override void Tick(double delta) { + interp.AdvanceState(); + float inc = rand.Next(0, 10); + LocationUpdate update = LocationUpdate.MakeOri(interp.nextHeadYaw + inc, 0); + SetLocation(update, true); + } + + public override void SetLocation(LocationUpdate update, bool interpolate) { + interp.SetLocation(update, interpolate); + } + + public override void RenderModel(double deltaTime, float t) { + Position = Vector3.Lerp(interp.lastPos, interp.nextPos, t); + HeadYawDegrees = Utils.LerpAngle(interp.lastHeadYaw, interp.nextHeadYaw, t); + YawDegrees = Utils.LerpAngle(interp.lastYaw, interp.nextYaw, t); + PitchDegrees = Utils.LerpAngle(interp.lastPitch, interp.nextPitch, t); + + anim.GetCurrentAnimState(t); + Model.Render(this); + } + } +} diff --git a/ClassicalSharp/Entities/NetPlayer.cs b/ClassicalSharp/Entities/NetPlayer.cs index 581c286e0..e7e4f7aff 100644 --- a/ClassicalSharp/Entities/NetPlayer.cs +++ b/ClassicalSharp/Entities/NetPlayer.cs @@ -6,12 +6,12 @@ namespace ClassicalSharp.Entities { public sealed class NetPlayer : Player { - InterpolatedComponent interp; + NetInterpComponent interp; public NetPlayer(string displayName, string skinName, Game game, byte id) : base(game) { DisplayName = displayName; SkinName = skinName; SkinIdentifier = "skin_" + id; - interp = new InterpolatedComponent(game, this); + interp = new NetInterpComponent(game, this); } public override void SetLocation(LocationUpdate update, bool interpolate) { diff --git a/ClassicalSharp/Game/PickingHandler.cs b/ClassicalSharp/Game/PickingHandler.cs index 2545643b2..296c0fccf 100644 --- a/ClassicalSharp/Game/PickingHandler.cs +++ b/ClassicalSharp/Game/PickingHandler.cs @@ -84,7 +84,7 @@ namespace ClassicalSharp { // NOTE: We need to also test against nextPos here, because otherwise // we can fall through the block as collision is performed against nextPos AABB localBB = AABB.Make(p.Position, p.Size); - localBB.Min.Y = Math.Min(p.nextPos.Y, localBB.Min.Y); + localBB.Min.Y = Math.Min(p.interp.nextPos.Y, localBB.Min.Y); if (p.Hacks.Noclip || !localBB.Intersects(blockBB)) return true; if (p.Hacks.CanPushbackBlocks && p.Hacks.PushbackPlacing && p.Hacks.Enabled) @@ -94,7 +94,7 @@ namespace ClassicalSharp { if (localBB.Intersects(blockBB)) return false; // Push player up if they are jumping and trying to place a block underneath them. - Vector3 next = game.LocalPlayer.nextPos; + Vector3 next = game.LocalPlayer.interp.nextPos; next.Y = pos.Y + game.BlockInfo.MaxBB[block].Y + Entity.Adjustment; LocationUpdate update = LocationUpdate.MakePos(next, false); game.LocalPlayer.SetLocation(update, false); diff --git a/ClassicalSharp/Mode/Survival.cs b/ClassicalSharp/Mode/Survival.cs index 74922841c..530662ae0 100644 --- a/ClassicalSharp/Mode/Survival.cs +++ b/ClassicalSharp/Mode/Survival.cs @@ -1,5 +1,8 @@ // Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 using System; +using ClassicalSharp.Entities; +using ClassicalSharp.Entities.Mobs; +using OpenTK; using OpenTK.Input; namespace ClassicalSharp.Mode { @@ -40,7 +43,8 @@ namespace ClassicalSharp.Mode { } public bool PickEntity(byte id) { - return false; + game.Chat.Add("PICKED ON: " + id + "," + game.Entities[id].ModelName); + return true; } @@ -92,6 +96,17 @@ namespace ClassicalSharp.Mode { public void OnNewMapLoaded(Game game) { game.Chat.Add("&fScore: &e" + score, MessageType.Status1); + + string[] models = { "sheep", "pig", "skeleton", "zombie", "creeper" }; + for (int i = 0; i < 254; i++) { + MobEntity fail = new MobEntity(game, models[rnd.Next(models.Length)]); + float x = rnd.Next(0, game.World.Width) + 0.5f; + float z = rnd.Next(0, game.World.Length) + 0.5f; + + Vector3 pos = Respawn.FindSpawnPosition(game, x, z, fail.Size); + fail.SetLocation(LocationUpdate.MakePos(pos, false), false); + game.Entities[i] = fail; + } } public void Init(Game game) { diff --git a/ClassicalSharp/Singleplayer/Server.cs b/ClassicalSharp/Singleplayer/Server.cs index 0eda0e645..ba5908931 100644 --- a/ClassicalSharp/Singleplayer/Server.cs +++ b/ClassicalSharp/Singleplayer/Server.cs @@ -110,24 +110,12 @@ namespace ClassicalSharp.Singleplayer { this.generator = generator; game.Gui.SetNewScreen(new LoadingMapScreen(game, "Generating level", "Generating..")); generator.GenerateAsync(game, width, height, length, seed); - } - + } void ResetPlayerPosition() { - Vector3 spawn = default(Vector3); - spawn.X = (game.World.Width / 2) + 0.5f; - spawn.Y = game.World.Height + Entity.Adjustment; - spawn.Z = (game.World.Length / 2) + 0.5f; - - AABB bb = AABB.Make(spawn, game.LocalPlayer.Size); - spawn.Y = 0; - for (int y = game.World.Height; y >= 0; y--) { - float highestY = Respawn.HighestFreeY(game, ref bb); - if (highestY != float.NegativeInfinity) { - spawn.Y = highestY; break; - } - bb.Min.Y -= 1; bb.Max.Y -= 1; - } + float x = (game.World.Width / 2) + 0.5f; + float z = (game.World.Length / 2) + 0.5f; + Vector3 spawn = Respawn.FindSpawnPosition(game, x, z, game.LocalPlayer.Size); LocationUpdate update = LocationUpdate.MakePosAndOri(spawn, 0, 0, false); game.LocalPlayer.SetLocation(update, false); diff --git a/ClassicalSharp/Utils/Camera.cs b/ClassicalSharp/Utils/Camera.cs index 875207dec..2f52088af 100644 --- a/ClassicalSharp/Utils/Camera.cs +++ b/ClassicalSharp/Utils/Camera.cs @@ -94,14 +94,14 @@ namespace ClassicalSharp { static readonly float sensiFactor = 0.0002f / 3 * Utils.Rad2Deg; private void UpdateMouseRotation() { float sensitivity = sensiFactor * game.MouseSensitivity; - float yaw = player.nextYaw + delta.X * sensitivity; + float yaw = player.interp.nextHeadYaw + delta.X * sensitivity; float yAdj = game.InvertMouse ? -delta.Y * sensitivity : delta.Y * sensitivity; - float pitch = player.nextPitch + yAdj; + float pitch = player.interp.nextPitch + yAdj; LocationUpdate update = LocationUpdate.MakeOri(yaw, pitch); // Need to make sure we don't cross the vertical axes, because that gets weird. if (update.Pitch >= 90 && update.Pitch <= 270) - update.Pitch = player.nextPitch < 180 ? 89.9f : 270.1f; + update.Pitch = player.interp.nextPitch < 180 ? 89.9f : 270.1f; game.LocalPlayer.SetLocation(update, true); } diff --git a/ClassicalSharp/Utils/Respawn.cs b/ClassicalSharp/Utils/Respawn.cs index 1c3c1a362..4c2cd718d 100644 --- a/ClassicalSharp/Utils/Respawn.cs +++ b/ClassicalSharp/Utils/Respawn.cs @@ -1,5 +1,6 @@ // Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 using System; +using ClassicalSharp.Entities; using ClassicalSharp.Physics; using OpenTK; @@ -31,5 +32,21 @@ namespace ClassicalSharp { } return spawnY; } + + public static Vector3 FindSpawnPosition(Game game, float x, float z, Vector3 modelSize) { + Vector3 spawn = new Vector3(x, 0, z); + spawn.Y = game.World.Height + Entity.Adjustment; + AABB bb = AABB.Make(spawn, modelSize); + spawn.Y = 0; + + for (int y = game.World.Height; y >= 0; y--) { + float highestY = HighestFreeY(game, ref bb); + if (highestY != float.NegativeInfinity) { + spawn.Y = highestY; break; + } + bb.Min.Y -= 1; bb.Max.Y -= 1; + } + return spawn; + } } } \ No newline at end of file