diff --git a/TrueCraft.API/PathResult.cs b/TrueCraft.API/PathResult.cs index 23d7da8..ea15708 100644 --- a/TrueCraft.API/PathResult.cs +++ b/TrueCraft.API/PathResult.cs @@ -5,6 +5,12 @@ namespace TrueCraft.API { public class PathResult { + public PathResult() + { + Index = 0; + } + public IList Waypoints; + public int Index; } } \ No newline at end of file diff --git a/TrueCraft.Core/Entities/CowEntity.cs b/TrueCraft.Core/Entities/CowEntity.cs new file mode 100644 index 0000000..323b96d --- /dev/null +++ b/TrueCraft.Core/Entities/CowEntity.cs @@ -0,0 +1,32 @@ +using System; +using TrueCraft.API; + +namespace TrueCraft.Core.Entities +{ + public class CowEntity : MobEntity + { + public override Size Size + { + get + { + return new Size(0.9, 1.3, 0.9); + } + } + + public override short MaxHealth + { + get + { + return 10; + } + } + + public override sbyte MobType + { + get + { + return 92; + } + } + } +} \ No newline at end of file diff --git a/TrueCraft.Core/Entities/CreeperEntity.cs b/TrueCraft.Core/Entities/CreeperEntity.cs new file mode 100644 index 0000000..631fdd1 --- /dev/null +++ b/TrueCraft.Core/Entities/CreeperEntity.cs @@ -0,0 +1,32 @@ +using System; +using TrueCraft.API; + +namespace TrueCraft.Core.Entities +{ + public class CreeperEntity : MobEntity + { + public override Size Size + { + get + { + return new Size(0.6, 1.8, 0.6); + } + } + + public override short MaxHealth + { + get + { + return 20; + } + } + + public override sbyte MobType + { + get + { + return 50; + } + } + } +} \ No newline at end of file diff --git a/TrueCraft.Core/Entities/GhastEntity.cs b/TrueCraft.Core/Entities/GhastEntity.cs new file mode 100644 index 0000000..9ee1b45 --- /dev/null +++ b/TrueCraft.Core/Entities/GhastEntity.cs @@ -0,0 +1,39 @@ +using System; +using TrueCraft.API; + +namespace TrueCraft.Core.Entities +{ + public class GhastEntity : MobEntity + { + public override Size Size + { + get + { + return new Size(4.0); + } + } + + public override short MaxHealth + { + get + { + return 10; + } + } + + public override sbyte MobType + { + get + { + return 56; + } + } + + public override bool BeginUpdate() + { + // Ghasts can fly, no need to work out gravity + // TODO: Think about how to deal with walls and such + return false; + } + } +} \ No newline at end of file diff --git a/TrueCraft.Core/Entities/GiantZombieEntity.cs b/TrueCraft.Core/Entities/GiantZombieEntity.cs new file mode 100644 index 0000000..e899aae --- /dev/null +++ b/TrueCraft.Core/Entities/GiantZombieEntity.cs @@ -0,0 +1,33 @@ +using System; +using TrueCraft.API; + +namespace TrueCraft.Core.Entities +{ + public class GiantZombieEntity : ZombieEntity + { + public override Size Size + { + get + { + return new Size(0.6, 1.8, 0.6) * 6; + } + } + + public override short MaxHealth + { + get + { + return 100; + } + } + + public override sbyte MobType + { + get + { + return 53; + } + } + } +} + diff --git a/TrueCraft.Core/Entities/HenEntity.cs b/TrueCraft.Core/Entities/HenEntity.cs new file mode 100644 index 0000000..5b0a0cc --- /dev/null +++ b/TrueCraft.Core/Entities/HenEntity.cs @@ -0,0 +1,32 @@ +using System; +using TrueCraft.API; + +namespace TrueCraft.Core.Entities +{ + public class HenEntity : MobEntity + { + public override Size Size + { + get + { + return new Size(0.4, 0.3, 0.4); + } + } + + public override short MaxHealth + { + get + { + return 4; + } + } + + public override sbyte MobType + { + get + { + return 93; + } + } + } +} \ No newline at end of file diff --git a/TrueCraft.Core/Entities/MobEntity.cs b/TrueCraft.Core/Entities/MobEntity.cs new file mode 100644 index 0000000..620edb1 --- /dev/null +++ b/TrueCraft.Core/Entities/MobEntity.cs @@ -0,0 +1,138 @@ +using System; +using TrueCraft.API.Networking; +using TrueCraft.Core.Networking.Packets; +using TrueCraft.API.Entities; +using TrueCraft.API; +using TrueCraft.API.Server; +using System.Linq; + +namespace TrueCraft.Core.Entities +{ + public abstract class MobEntity : LivingEntity, IAABBEntity + { + public MobEntity() + { + Speed = 1; // TODO: WTF + } + + public override IPacket SpawnPacket + { + get + { + return new SpawnMobPacket(EntityID, MobType, + MathHelper.CreateAbsoluteInt(Position.X), + MathHelper.CreateAbsoluteInt(Position.Y), + MathHelper.CreateAbsoluteInt(Position.Z), + MathHelper.CreateRotationByte(Yaw), + MathHelper.CreateRotationByte(Pitch), + Metadata); + } + } + + public abstract sbyte MobType { get; } + + public virtual void TerrainCollision(Vector3 collisionPoint, Vector3 collisionDirection) + { + // This space intentionally left blank + } + + public BoundingBox BoundingBox + { + get + { + return new BoundingBox(Position, Position + Size); + } + } + + public virtual bool BeginUpdate() + { + EnablePropertyChange = false; + return true; + } + + public virtual void EndUpdate(Vector3 newPosition) + { + EnablePropertyChange = true; + Position = newPosition; + } + + public float AccelerationDueToGravity + { + get + { + return 0.08f; + } + } + + public float Drag + { + get + { + return 0.02f; + } + } + + public float TerminalVelocity + { + get + { + return 3.92f; + } + } + + public PathResult CurrentPath { get; set; } + + /// + /// Mob's current speed in m/s. + /// + public virtual double Speed { get; } + + protected DateTime CurrentNodeStart = DateTime.MinValue; + + public void Face(Vector3 target) + { + var diff = target - Position; + Yaw = (float)MathHelper.RadiansToDegrees(-(Math.Atan2(diff.X, diff.Z) - Math.PI) + Math.PI); // "Flip" over the 180 mark + } + + protected void AdvancePath(bool faceRoute = true) + { + if (CurrentPath != null) + { + if (CurrentNodeStart == DateTime.MinValue) + CurrentNodeStart = DateTime.UtcNow; + // Advance along path + var target = (Vector3)CurrentPath.Waypoints[CurrentPath.Index]; + var diff = target - Position; + var max = (DateTime.UtcNow - CurrentNodeStart).TotalSeconds * Speed; + if (faceRoute) + Face(target); + diff.Clamp(max); + Position += diff; + if (Position == target) + { + CurrentPath.Index++; + if (CurrentPath.Index >= CurrentPath.Waypoints.Count) + { + CurrentPath = null; + CurrentNodeStart = DateTime.MinValue; + // TODO: Raise path complete event or something? + } + } + } + } + + public override void Update(IEntityManager entityManager) + { + AdvancePath(); + var nearby = entityManager.EntitiesInRange(Position, 30); + var player = nearby.FirstOrDefault(e => e is PlayerEntity); + if (player != null) + { + Face(player.Position); + } + base.Update(entityManager); + } + } +} + diff --git a/TrueCraft.Core/Entities/PigEntity.cs b/TrueCraft.Core/Entities/PigEntity.cs new file mode 100644 index 0000000..289e922 --- /dev/null +++ b/TrueCraft.Core/Entities/PigEntity.cs @@ -0,0 +1,32 @@ +using System; +using TrueCraft.API; + +namespace TrueCraft.Core.Entities +{ + public class PigEntity : MobEntity + { + public override Size Size + { + get + { + return new Size(0.9); + } + } + + public override short MaxHealth + { + get + { + return 10; + } + } + + public override sbyte MobType + { + get + { + return 90; + } + } + } +} \ No newline at end of file diff --git a/TrueCraft.Core/Entities/SheepEntity.cs b/TrueCraft.Core/Entities/SheepEntity.cs new file mode 100644 index 0000000..1b0e332 --- /dev/null +++ b/TrueCraft.Core/Entities/SheepEntity.cs @@ -0,0 +1,32 @@ +using System; +using TrueCraft.API; + +namespace TrueCraft.Core.Entities +{ + public class SheepEntity : MobEntity + { + public override Size Size + { + get + { + return new Size(0.9, 1.3, 0.9); + } + } + + public override short MaxHealth + { + get + { + return 8; + } + } + + public override sbyte MobType + { + get + { + return 91; + } + } + } +} \ No newline at end of file diff --git a/TrueCraft.Core/Entities/SkeletonEntity.cs b/TrueCraft.Core/Entities/SkeletonEntity.cs new file mode 100644 index 0000000..d5a81fd --- /dev/null +++ b/TrueCraft.Core/Entities/SkeletonEntity.cs @@ -0,0 +1,32 @@ +using System; +using TrueCraft.API; + +namespace TrueCraft.Core.Entities +{ + public class SkeletonEntity : MobEntity + { + public override Size Size + { + get + { + return new Size(0.6, 1.8, 0.6); + } + } + + public override short MaxHealth + { + get + { + return 20; + } + } + + public override sbyte MobType + { + get + { + return 51; + } + } + } +} \ No newline at end of file diff --git a/TrueCraft.Core/Entities/SlimeEntity.cs b/TrueCraft.Core/Entities/SlimeEntity.cs new file mode 100644 index 0000000..413046f --- /dev/null +++ b/TrueCraft.Core/Entities/SlimeEntity.cs @@ -0,0 +1,54 @@ +using System; +using TrueCraft.API; + +namespace TrueCraft.Core.Entities +{ + public class SlimeEntity : MobEntity + { + public byte SlimeSize { get; set; } + + public SlimeEntity() : this(4) + { + } + + public SlimeEntity(byte size) + { + SlimeSize = size; + } + + public override MetadataDictionary Metadata + { + get + { + var meta = base.Metadata; + meta[16] = new MetadataByte(SlimeSize); + return meta; + } + } + + public override Size Size + { + get + { + return new Size(0.6 * SlimeSize); + } + } + + public override short MaxHealth + { + get + { + return (short)(Math.Pow(SlimeSize, 2)); + } + } + + public override sbyte MobType + { + get + { + return 55; + } + } + } +} + diff --git a/TrueCraft.Core/Entities/SpiderEntity.cs b/TrueCraft.Core/Entities/SpiderEntity.cs new file mode 100644 index 0000000..b9bb17d --- /dev/null +++ b/TrueCraft.Core/Entities/SpiderEntity.cs @@ -0,0 +1,35 @@ +using System; +using TrueCraft.API.Networking; +using TrueCraft.API; +using TrueCraft.Core.Networking.Packets; + +namespace TrueCraft.Core.Entities +{ + public class SpiderEntity : MobEntity + { + public override Size Size + { + get + { + return new Size(1.4, 0.9, 1.4); + } + } + + public override short MaxHealth + { + get + { + return 16; + } + } + + public override sbyte MobType + { + get + { + return 52; + } + } + } +} + diff --git a/TrueCraft.Core/Entities/SquidEntity.cs b/TrueCraft.Core/Entities/SquidEntity.cs new file mode 100644 index 0000000..c9e8fa3 --- /dev/null +++ b/TrueCraft.Core/Entities/SquidEntity.cs @@ -0,0 +1,32 @@ +using System; +using TrueCraft.API; + +namespace TrueCraft.Core.Entities +{ + public class SquidEntity : MobEntity + { + public override Size Size + { + get + { + return new Size(0.95); + } + } + + public override short MaxHealth + { + get + { + return 10; + } + } + + public override sbyte MobType + { + get + { + return 94; + } + } + } +} \ No newline at end of file diff --git a/TrueCraft.Core/Entities/ZombieEntity.cs b/TrueCraft.Core/Entities/ZombieEntity.cs new file mode 100644 index 0000000..5185b08 --- /dev/null +++ b/TrueCraft.Core/Entities/ZombieEntity.cs @@ -0,0 +1,32 @@ +using System; +using TrueCraft.API; + +namespace TrueCraft.Core.Entities +{ + public class ZombieEntity : MobEntity + { + public override Size Size + { + get + { + return new Size(0.6, 1.8, 0.6); + } + } + + public override short MaxHealth + { + get + { + return 20; + } + } + + public override sbyte MobType + { + get + { + return 54; + } + } + } +} \ No newline at end of file diff --git a/TrueCraft.Core/Entities/ZombiePigmanEntity.cs b/TrueCraft.Core/Entities/ZombiePigmanEntity.cs new file mode 100644 index 0000000..f1931a1 --- /dev/null +++ b/TrueCraft.Core/Entities/ZombiePigmanEntity.cs @@ -0,0 +1,32 @@ +using System; +using TrueCraft.API; + +namespace TrueCraft.Core.Entities +{ + public class ZombiePigmanEntity : MobEntity + { + public override Size Size + { + get + { + return new Size(0.6, 1.8, 0.6); + } + } + + public override short MaxHealth + { + get + { + return 20; + } + } + + public override sbyte MobType + { + get + { + return 57; + } + } + } +} \ No newline at end of file diff --git a/TrueCraft.Core/Networking/Packets/SpawnMobPacket.cs b/TrueCraft.Core/Networking/Packets/SpawnMobPacket.cs index 2047966..0cc9013 100644 --- a/TrueCraft.Core/Networking/Packets/SpawnMobPacket.cs +++ b/TrueCraft.Core/Networking/Packets/SpawnMobPacket.cs @@ -11,11 +11,23 @@ namespace TrueCraft.Core.Networking.Packets { public byte ID { get { return 0x18; } } + public SpawnMobPacket(int entityId, sbyte type, int x, int y, int z, sbyte yaw, sbyte pitch, MetadataDictionary metadata) + { + EntityID = entityId; + MobType = type; + X = x; + Y = y; + Z = z; + Yaw = yaw; + Pitch = pitch; + Metadata = metadata; + } + public int EntityID; public sbyte MobType; public int X, Y, Z; public sbyte Yaw, Pitch; - public MetadataDictionary Metadata; // TODO: Import metadata implementation from Craft.Net + public MetadataDictionary Metadata; public void ReadPacket(IMinecraftStream stream) { diff --git a/TrueCraft.Core/TrueCraft.Core.csproj b/TrueCraft.Core/TrueCraft.Core.csproj index 7494e45..9f8213d 100644 --- a/TrueCraft.Core/TrueCraft.Core.csproj +++ b/TrueCraft.Core/TrueCraft.Core.csproj @@ -330,6 +330,20 @@ + + + + + + + + + + + + + + diff --git a/TrueCraft/Commands/DebugCommands.cs b/TrueCraft/Commands/DebugCommands.cs index 7b063fa..97df4ad 100644 --- a/TrueCraft/Commands/DebugCommands.cs +++ b/TrueCraft/Commands/DebugCommands.cs @@ -1,6 +1,14 @@ using TrueCraft.API.Networking; using TrueCraft.Core.Networking.Packets; using TrueCraft.API; +using System; +using TrueCraft.API.Entities; +using TrueCraft.Core; +using System.Collections.Generic; +using System.Linq; +using TrueCraft.Core.AI; +using TrueCraft.Core.Entities; +using System.Threading.Tasks; namespace TrueCraft.Commands { @@ -37,6 +45,167 @@ namespace TrueCraft.Commands } } + public class SpawnCommand : Command + { + public override string Name + { + get { return "spawn"; } + } + + public override string Description + { + get { return "Spawns a mob."; } + } + + public override string[] Aliases + { + get { return new string[0]; } + } + + public override void Handle(IRemoteClient client, string alias, string[] arguments) + { + if (arguments.Length != 1) + { + Help(client, alias, arguments); + return; + } + var entityTypes = new List(); + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + foreach (var t in assembly.GetTypes()) + { + if (typeof(IEntity).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface) + entityTypes.Add(t); + } + } + + arguments[0] = arguments[0].ToUpper(); + var type = entityTypes.SingleOrDefault(t => t.Name.ToUpper() == arguments[0] + "ENTITY"); + if (type == null) + { + client.SendMessage(ChatColor.Red + "Unknown entity type."); + return; + } + var entity = (IEntity)Activator.CreateInstance(type); + var em = client.Server.GetEntityManagerForWorld(client.World); + entity.Position = client.Entity.Position + MathHelper.FowardVector(client.Entity.Yaw) * 3; + em.SpawnEntity(entity); + } + + public override void Help(IRemoteClient client, string alias, string[] arguments) + { + client.SendMessage("/spawn [type]: Spawns a mob of that type."); + } + } + + public class ToMeCommand : Command + { + public override string Name + { + get { return "tome"; } + } + + public override string Description + { + get { return "Moves a mob towards your position."; } + } + + public override string[] Aliases + { + get { return new string[0]; } + } + + public override void Handle(IRemoteClient client, string alias, string[] arguments) + { + if (arguments.Length != 1) + { + Help(client, alias, arguments); + return; + } + + int id; + if (!int.TryParse(arguments[0], out id)) + { + Help(client, alias, arguments); + return; + } + + var manager = client.Server.GetEntityManagerForWorld(client.World); + var entity = manager.GetEntityByID(id) as MobEntity; + if (entity == null) + { + client.SendMessage(ChatColor.Red + "An entity with that ID does not exist in this world."); + return; + } + + Task.Factory.StartNew(() => + { + var astar = new AStarPathFinder(); + var path = astar.FindPath(client.World, entity.BoundingBox, (Coordinates3D)entity.Position, (Coordinates3D)client.Entity.Position); + if (path == null) + { + client.SendMessage(ChatColor.Red + "It is impossible for this entity to reach you."); + return; + } + entity.CurrentPath = path; + }); + } + + public override void Help(IRemoteClient client, string alias, string[] arguments) + { + client.SendMessage("/tome [id]: Moves a mob to your position."); + } + } + + public class DestroyCommand : Command + { + public override string Name + { + get { return "destroy"; } + } + + public override string Description + { + get { return "Destroys a mob. Violently."; } + } + + public override string[] Aliases + { + get { return new string[0]; } + } + + public override void Handle(IRemoteClient client, string alias, string[] arguments) + { + if (arguments.Length != 1) + { + Help(client, alias, arguments); + return; + } + + int id; + if (!int.TryParse(arguments[0], out id)) + { + Help(client, alias, arguments); + return; + } + + var manager = client.Server.GetEntityManagerForWorld(client.World); + var entity = manager.GetEntityByID(id) as MobEntity; + if (entity == null) + { + client.SendMessage(ChatColor.Red + "An entity with that ID does not exist in this world."); + return; + } + + manager.DespawnEntity(entity); + } + + public override void Help(IRemoteClient client, string alias, string[] arguments) + { + client.SendMessage("/destroy [id]: " + Description); + } + } + public class TrashCommand : Command { public override string Name diff --git a/TrueCraft/RemoteClient.cs b/TrueCraft/RemoteClient.cs index 2f49579..0b62463 100644 --- a/TrueCraft/RemoteClient.cs +++ b/TrueCraft/RemoteClient.cs @@ -316,6 +316,9 @@ namespace TrueCraft catch (OperationCanceledException) { } + catch (NullReferenceException) + { + } var packets = PacketReader.ReadPackets(this, e.Buffer, e.Offset, e.BytesTransferred);