Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion Maple2.File.Ingest/Mapper/MapDataMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
using Maple2.Model.Common;
using Maple2.Model.Game.Field;
using Maple2.Model.Metadata;
using Maple2.Model.Metadata.FieldEntity;
using Maple2.PacketLib.Tools;
using Maple2.Tools.Extensions;
using Maple2.Tools.VectorMath;
using System.IO.Compression;
using System.Numerics;
using Maple2.Model.Metadata.FieldEntity;

namespace Maple2.File.Ingest.Mapper;

Expand Down Expand Up @@ -145,6 +145,21 @@ private FieldAccelerationStructure ParseMapEntities(IEnumerable<IMapEntity> enti
break;
}

if (entity is IMS2CubeProp cube && cube.CubeSalableGroup != 0) {
entityBounds.Min = -new Vector3(HALF_BLOCK, HALF_BLOCK, 0);
entityBounds.Max = new Vector3(HALF_BLOCK, HALF_BLOCK, BLOCK_SIZE);
fieldEntity = new FieldSellableTile(
Id: entityId,
Position: placeable.Position,
Rotation: placeable.Rotation,
Scale: placeable.Scale,
Bounds: entityBounds,
SellableGroup: cube.CubeSalableGroup
);

break;
}

bool isFluid = false;

if (entity is IMS2MapProperties meshMapProperties) {
Expand Down
43 changes: 33 additions & 10 deletions Maple2.Model/Game/Field/FieldAccelerationStructure.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using Maple2.Model.Common;
using Maple2.Model.Metadata;
using Maple2.Model.Metadata.FieldEntity;
using Maple2.PacketLib.Tools;
using Maple2.Tools;
using Maple2.Tools.Extensions;
using Maple2.Tools.VectorMath;
using Microsoft.VisualBasic;
using System.Numerics;
using System.Runtime.InteropServices;
using Maple2.Model.Metadata;
using Maple2.Model.Metadata.FieldEntity;

namespace Maple2.Model.Game.Field;

Expand All @@ -19,21 +18,21 @@ internal enum FieldEntityMembers : byte {
Rotation = 0x4,
Scale = 0x8,
Bounds = 0x10,
Llid = 0x20
Llid = 0x20,
}

/*
* FieldAccelerationStructure is a helper class that specializes in various spatial queries for objects.
* It's designed to maximize performance for finding objects in a 3D space with desired constraints.
*
*
* Internally it uses specialized storage techniques with fast look up times, but knowledge on these
* techniques isn't necessary for use. The Query functions allow you to find the objects you need without
* knowing any of the implementation details.
*
*
* You can do general queries or queries for specific entity types for all types of queries available.
* Every entity matching the query constraints will be reported back through a callback, or in a list with
* Query___List() methods.
*
*
* Available query types include:
* - Cell: Captures all entities in a specific grid cell, or range of cells. Doesn't capture freely floating entities.
* - Point: Captures all entities intersecting with a specific point.
Expand All @@ -54,7 +53,7 @@ internal enum FieldEntityMembers : byte {
* - Frustum: Captures all entities overlapping with a frustum. Useful for map rendering.
* - CellsInFrustum: Captures all entities overlapping with a frustum. Useful for map rendering. Doesn't capture freely floating entities.
* - TreeInFrustum: Captures all entities overlapping with a frustum. Useful for map rendering. Doesn't capture cells.
*
*
* Special purpose queries:
* - Spawns: Captures all mob spawn candidates in a sphere.
* - Fluids: Captures all fluids within a bounding box.
Expand Down Expand Up @@ -178,12 +177,24 @@ public List<FieldFluidEntity> QueryFluidsList(BoundingBox3 box) {

public void QueryFluids(Vector3 min, Vector3 max, Action<FieldFluidEntity> callback) {
QueryCells(min, max, (entity) => {
if (entity is FieldFluidEntity fluid && fluid.IsSurface && !fluid.IsShallow) {
if (entity is FieldFluidEntity { IsSurface: true, IsShallow: false } fluid) {
callback(fluid);
}
});
}

public void QuerySellableTiles(Vector3 min, Vector3 max, Action<FieldSellableTile> callback) {
QueryCells(min, max, (entity) => {
if (entity is FieldSellableTile tile) {
callback(tile);
}
});
}

public void QuerySellableTilesCenter(Vector3 center, Vector3 size, Action<FieldSellableTile> callback) {
QuerySellableTiles(center - 0.5f * size, center + 0.5f * size, callback);
}

public List<FieldFluidEntity> QueryFluidsList(Vector3 min, Vector3 max) {
List<FieldFluidEntity> fluids = new();

Expand Down Expand Up @@ -681,7 +692,8 @@ public void WriteTo(FieldEntity entity, IByteWriter writer) {
FieldFluidEntity => FieldEntityType.Fluid,
FieldMeshColliderEntity => FieldEntityType.MeshCollider,
FieldCellEntities => FieldEntityType.Cell,
_ => FieldEntityType.Unknown
FieldSellableTile => FieldEntityType.SellableTile,
_ => FieldEntityType.Unknown,
};

FieldEntityMembers memberFlags = FieldEntityMembers.None;
Expand Down Expand Up @@ -753,6 +765,9 @@ public void WriteTo(FieldEntity entity, IByteWriter writer) {
WriteTo(childEntity, writer);
}
break;
case FieldSellableTile sellableTile:
writer.Write(sellableTile.SellableGroup);
break;
default:
throw new InvalidDataException($"Writing unhandled field entity type: {entity.GetType().FullName}");
}
Expand Down Expand Up @@ -928,6 +943,14 @@ public FieldEntity ReadEntity(IByteReader reader) {
Scale: scale,
Bounds: bounds,
Entities: children);
case FieldEntityType.SellableTile:
return new FieldSellableTile(
Id: id,
Position: position,
Rotation: rotation,
Scale: scale,
Bounds: bounds,
SellableGroup: reader.Read<int>());
default:
throw new InvalidDataException($"Reading unhandled field entity type: {type}");
}
Expand Down
16 changes: 13 additions & 3 deletions Maple2.Model/Metadata/FieldEntity/FieldEntity.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using Maple2.Tools.VectorMath;
using System.Numerics;
using System.Numerics;
using Maple2.Tools.VectorMath;

namespace Maple2.Model.Metadata.FieldEntity;

Expand All @@ -10,7 +10,8 @@ public enum FieldEntityType : byte {
BoxCollider, // for cube tiles (IsWhiteBox = false) & arbitrarily sized white boxes
MeshCollider,
Fluid,
Cell // for cells culled from the grid & demoted to AABB tree
SellableTile,
Cell, // for cells culled from the grid & demoted to AABB tree
}

public record FieldEntityId(
Expand Down Expand Up @@ -83,3 +84,12 @@ public record FieldCellEntities(
float Scale,
BoundingBox3 Bounds,
List<FieldEntity> Entities) : FieldEntity(Id, Position, Rotation, Scale, Bounds);

public record FieldSellableTile(
FieldEntityId Id,
Vector3 Position,
Vector3 Rotation,
float Scale,
BoundingBox3 Bounds,
int SellableGroup
) : FieldEntity(Id, Position, Rotation, Scale, Bounds);
44 changes: 44 additions & 0 deletions Maple2.Server.Game/Commands/DebugCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ public DebugQueryCommand(GameSession session, MapDataStorage mapDataStorage) : b
AddCommand(new DebugQuerySpawnCommand(session, mapDataStorage));
AddCommand(new DebugQueryFluidCommand(session, mapDataStorage));
AddCommand(new DebugQueryVibrateCommand(session, mapDataStorage));
AddCommand(new DebugQuerySellableTileCommand(session, mapDataStorage));
}

private class DebugQuerySpawnCommand : Command {
Expand Down Expand Up @@ -274,5 +275,48 @@ private void Handle(InvocationContext ctx, float x, float y, float z) {
});
}
}

private class DebugQuerySellableTileCommand : Command {
private readonly GameSession session;
private readonly MapDataStorage mapDataStorage;

public DebugQuerySellableTileCommand(GameSession session, MapDataStorage mapDataStorage) : base("sellable", "Searches for nearby sellable tiles.") {
this.session = session;
this.mapDataStorage = mapDataStorage;

var x = new Argument<float>("x", () => 300, "How far along the x axis to search from the player.");
var y = new Argument<float>("y", () => 300, "How far along the y axis to search from the player.");
var z = new Argument<float>("z", () => 300, "How far along the z axis to search from the player.");

AddArgument(x);
AddArgument(y);
AddArgument(z);

this.SetHandler<InvocationContext, float, float, float>(Handle, x, y, z);
}

private void Handle(InvocationContext ctx, float x, float y, float z) {
Comment thread
AngeloTadeucci marked this conversation as resolved.
if (!session.Field.MapMetadata.TryGet(session.Field.MapId, out MapMetadata? map)) {
return;
}

if (!mapDataStorage.TryGet(map.XBlock, out FieldAccelerationStructure? mapData)) {
return;
}
Comment thread
AngeloTadeucci marked this conversation as resolved.

Vector3 center = session.Player.Position;
Vector3S cell = FieldAccelerationStructure.PointToCell(center);

ctx.Console.Out.WriteLine($"Player at {center.X} {center.Y} {center.Z} in cell {cell.X} {cell.Y} {cell.Z}");

mapData.QuerySellableTilesCenter(session.Player.Position, 2 * new Vector3(x, y, z), (sellableTile) => {
Vector3 position = sellableTile.Position;
Vector3S toCell = FieldAccelerationStructure.PointToCell(position);


ctx.Console.Out.WriteLine($"SellableGroupId {sellableTile.SellableGroup} found at {position.X} {position.Y} {position.Z} in cell {toCell.X} {toCell.Y} {toCell.Z}");
});
}
}
}
}