More joka changes and map change and engineState is now a pointer.

This commit is contained in:
Kapendev 2025-03-10 12:13:30 +02:00
parent cf8f71cfaa
commit 14ce68833c
4 changed files with 154 additions and 154 deletions

View file

@ -10,18 +10,16 @@
module parin.engine; module parin.engine;
import rl = parin.rl; import rl = parin.rl;
import stdc = joka.stdc;
import joka.ascii; import joka.ascii;
import joka.io; import joka.io;
import joka.unions;
public import joka.containers; public import joka.containers;
public import joka.faults;
public import joka.math; public import joka.math;
public import joka.types; public import joka.types;
@safe @nogc nothrow: @safe @nogc nothrow:
EngineState engineState; EngineState* engineState;
enum defaultEngineFontsCapacity = 64; enum defaultEngineFontsCapacity = 64;
enum defaultEngineResourcesCapacity = 256; enum defaultEngineResourcesCapacity = 256;
@ -1272,6 +1270,7 @@ void openUrl(IStr url = "https://github.com/Kapendev/parin") {
@trusted @trusted
void openWindow(int width, int height, const(IStr)[] args, IStr title = "Parin") { void openWindow(int width, int height, const(IStr)[] args, IStr title = "Parin") {
if (rl.IsWindowReady) return; if (rl.IsWindowReady) return;
engineState = cast(EngineState*) stdc.malloc(EngineState.sizeof);
engineState.envArgsBuffer.clear(); engineState.envArgsBuffer.clear();
foreach (arg; args) engineState.envArgsBuffer.append(arg); foreach (arg; args) engineState.envArgsBuffer.append(arg);
// Set raylib stuff. // Set raylib stuff.
@ -1429,6 +1428,8 @@ void closeWindow() {
engineState.loadTextBuffer.free(); engineState.loadTextBuffer.free();
engineState.saveTextBuffer.free(); engineState.saveTextBuffer.free();
engineState.assetsPath.free(); engineState.assetsPath.free();
stdc.free(engineState);
engineState = null;
} }
// This is outside because who knows, maybe raylib needs that. // This is outside because who knows, maybe raylib needs that.
rl.CloseAudioDevice(); rl.CloseAudioDevice();

View file

@ -59,27 +59,26 @@ struct Tile {
} }
} }
// TODO: Look at it again now with the Grid changes. It works, I know that :)
// NOTE: Things like changing the grid row count might be interesting.
struct TileMap { struct TileMap {
Grid!short data; Grid!short data;
Sz softMaxRowCount; Sz softRowCount;
Sz softMaxColCount; Sz softColCount;
int tileWidth = 16; int tileWidth = 16;
int tileHeight = 16; int tileHeight = 16;
Vec2 position; Vec2 position;
enum maxRowCount = data.maxRowCount;
enum maxColCount = data.maxColCount;
enum maxCapacity = data.maxCapacity;
@safe @nogc nothrow: @safe @nogc nothrow:
this(int tileWidth, int tileHeight, Vec2 position = Vec2()) { this(Sz rowCount, Sz colCount, int tileWidth, int tileHeight) {
this.tileWidth = tileWidth; this.tileWidth = tileWidth;
this.tileHeight = tileHeight; this.tileHeight = tileHeight;
this.softMaxRowCount = maxRowCount; resizeHard(rowCount, colCount);
this.softMaxColCount = maxColCount; }
this.position = position;
this.data.fill(-1); this(int tileWidth, int tileHeight) {
this(defaultGridRowCount, defaultGridColCount, tileWidth, tileHeight);
} }
ref short opIndex(Sz row, Sz col) { ref short opIndex(Sz row, Sz col) {
@ -116,21 +115,7 @@ struct TileMap {
} }
Sz opDollar(Sz dim)() { Sz opDollar(Sz dim)() {
static if (dim == 0) { return data.opDollar!dim();
return rowCount;
} else static if (dim == 1) {
return colCount;
} else {
assert(0, "WTF!");
}
}
Sz rowCount() {
return data.length ? softMaxRowCount : 0;
}
Sz colCount() {
return data.length ? softMaxColCount : 0;
} }
bool isEmpty() { bool isEmpty() {
@ -138,29 +123,50 @@ struct TileMap {
} }
bool has(Sz row, Sz col) { bool has(Sz row, Sz col) {
return row < rowCount && col < colCount; return row < softRowCount && col < softColCount;
} }
bool has(IVec2 position) { bool has(IVec2 position) {
return has(position.y, position.x); return has(position.y, position.x);
} }
@trusted Sz hardRowCount() {
return data.rowCount;
}
Sz hardColCount() {
return data.colCount;
}
void resizeHard(Sz newHardRowCount, Sz newHardColCount) {
data.resizeBlank(newHardRowCount, newHardColCount);
data.fill(-1);
softRowCount = newHardRowCount;
softColCount = newHardColCount;
}
void resizeSoft(Sz newSoftRowCount, Sz newSoftColCount) {
if (newSoftRowCount > hardRowCount || newSoftColCount > hardColCount) {
assert(0, "Soft count must be smaller than hard count.");
}
softRowCount = newSoftRowCount;
softColCount = newSoftColCount;
}
void fill(short value) { void fill(short value) {
data.fill(value); data.fill(value);
} }
@trusted
void free() { void free() {
data.free(); data.free();
} }
int width() { int width() {
return cast(int) (colCount * tileWidth); return cast(int) (softColCount * tileWidth);
} }
int height() { int height() {
return cast(int) (rowCount * tileHeight); return cast(int) (softRowCount * tileHeight);
} }
/// Returns the size of the tile map. /// Returns the size of the tile map.
@ -173,25 +179,27 @@ struct TileMap {
return Vec2(tileWidth, tileHeight); return Vec2(tileWidth, tileHeight);
} }
Fault parse(IStr csv, int tileWidth, int tileHeight) { Fault parse(IStr csv, int newTileWidth, int newTileHeight) {
if (csv.length == 0) return Fault.invalid; if (csv.length == 0) return Fault.invalid;
this.tileWidth = tileWidth; if (data.isEmpty) {
this.tileHeight = tileHeight; data.resizeBlank(defaultGridRowCount, defaultGridColCount);
this.softMaxRowCount = 0; }
this.softMaxColCount = 0; tileWidth = newTileWidth;
this.data.fill(-1); tileHeight = newTileHeight;
softRowCount = 0;
softColCount = 0;
auto view = csv; auto view = csv;
while (view.length != 0) { while (view.length != 0) {
softMaxRowCount += 1; softRowCount += 1;
softMaxColCount = 0; softColCount = 0;
if (softMaxRowCount > maxRowCount) return Fault.invalid; if (softRowCount > data.rowCount) return Fault.invalid;
auto line = view.skipLine(); auto line = view.skipLine();
while (line.length != 0) { while (line.length != 0) {
softMaxColCount += 1; softColCount += 1;
if (softMaxColCount > maxColCount) return Fault.invalid; if (softColCount > data.colCount) return Fault.invalid;
auto tile = line.skipValue(',').toSigned(); auto tile = line.skipValue(',').toSigned();
if (tile.isNone) return Fault.invalid; if (tile.isNone) return Fault.invalid;
data[softMaxRowCount - 1, softMaxColCount - 1] = cast(short) tile.get(); data[softRowCount - 1, softColCount - 1] = cast(short) tile.get();
} }
} }
return Fault.none; return Fault.none;
@ -226,23 +234,23 @@ struct TileMap {
} }
IVec2 firstGridPosition(Vec2 topLeftWorldPosition, DrawOptions options = DrawOptions()) { IVec2 firstGridPosition(Vec2 topLeftWorldPosition, DrawOptions options = DrawOptions()) {
if (rowCount == 0 || colCount == 0) return IVec2(); if (softRowCount == 0 || softColCount == 0) return IVec2();
auto result = IVec2(); auto result = IVec2();
auto targetTileWidth = cast(int) (tileWidth * options.scale.x); auto targetTileWidth = cast(int) (tileWidth * options.scale.x);
auto targetTileHeight = cast(int) (tileHeight * options.scale.y); auto targetTileHeight = cast(int) (tileHeight * options.scale.y);
result.y = cast(int) floor(clamp((topLeftWorldPosition.y - position.y) / targetTileHeight, 0, rowCount - 1)); result.y = cast(int) floor(clamp((topLeftWorldPosition.y - position.y) / targetTileHeight, 0, softRowCount - 1));
result.x = cast(int) floor(clamp((topLeftWorldPosition.x - position.x) / targetTileWidth, 0, colCount - 1)); result.x = cast(int) floor(clamp((topLeftWorldPosition.x - position.x) / targetTileWidth, 0, softColCount - 1));
return result; return result;
} }
IVec2 lastGridPosition(Vec2 bottomRightWorldPosition, DrawOptions options = DrawOptions()) { IVec2 lastGridPosition(Vec2 bottomRightWorldPosition, DrawOptions options = DrawOptions()) {
if (rowCount == 0 || colCount == 0) return IVec2(); if (softRowCount == 0 || softColCount == 0) return IVec2();
auto result = IVec2(); auto result = IVec2();
auto targetTileWidth = cast(int) (tileWidth * options.scale.x); auto targetTileWidth = cast(int) (tileWidth * options.scale.x);
auto targetTileHeight = cast(int) (tileHeight * options.scale.y); auto targetTileHeight = cast(int) (tileHeight * options.scale.y);
auto extraTileCount = options.hook == Hook.topLeft ? 1 : 2; auto extraTileCount = options.hook == Hook.topLeft ? 1 : 2;
result.y = cast(int) floor(clamp((bottomRightWorldPosition.y - position.y) / targetTileHeight + extraTileCount, 0, rowCount - 1)); result.y = cast(int) floor(clamp((bottomRightWorldPosition.y - position.y) / targetTileHeight + extraTileCount, 0, softRowCount - 1));
result.x = cast(int) floor(clamp((bottomRightWorldPosition.x - position.x) / targetTileWidth + extraTileCount, 0, colCount - 1)); result.x = cast(int) floor(clamp((bottomRightWorldPosition.x - position.x) / targetTileWidth + extraTileCount, 0, softColCount - 1));
return result; return result;
} }
@ -271,7 +279,7 @@ struct TileMap {
} }
auto result = Range( auto result = Range(
colCount, softColCount,
firstGridPosition(topLeftWorldPosition, options), firstGridPosition(topLeftWorldPosition, options),
lastGridPosition(bottomRightWorldPosition, options), lastGridPosition(bottomRightWorldPosition, options),
); );
@ -286,10 +294,10 @@ struct TileMap {
Fault saveTileMap(IStr path, TileMap map) { Fault saveTileMap(IStr path, TileMap map) {
auto csv = prepareTempText(); auto csv = prepareTempText();
foreach (row; 0 .. map.rowCount) { foreach (row; 0 .. map.softRowCount) {
foreach (col; 0 .. map.colCount) { foreach (col; 0 .. map.softColCount) {
csv.append(map[row, col].toStr()); csv.append(map[row, col].toStr());
if (col != map.colCount - 1) csv.append(','); if (col != map.softColCount - 1) csv.append(',');
} }
csv.append('\n'); csv.append('\n');
} }

View file

@ -7,9 +7,9 @@
// --- // ---
// TODO: Update all the doc comments here. // TODO: Update all the doc comments here.
// TODO: Add spatial partitioning after testing this in a game.
// TODO: Add one-way collision support for moving walls. // TODO: Add one-way collision support for moving walls.
// NOTE: Maybe a world pixel size value could be useful. // TODO: Add spatial partitioning.
// NOTE: Was working on spatial partitioning. The grid is done, just need to add values in it.
/// The `platformer` module provides a pixel-perfect physics engine. /// The `platformer` module provides a pixel-perfect physics engine.
module parin.platformer; module parin.platformer;
@ -22,10 +22,17 @@ import joka.types;
@safe @nogc nothrow: @safe @nogc nothrow:
alias BaseBoxId = int; alias BaseBoxId = int;
alias BaseBoxIdGroup = FixedList!(BaseBoxId, 510);
alias ActorBoxId = BaseBoxId; alias ActorBoxId = BaseBoxId;
alias ActorBoxFlags = ubyte;
alias WallBoxId = BaseBoxId; alias WallBoxId = BaseBoxId;
alias WallBoxFlags = ubyte;
alias OneWaySide = RideSide; alias OneWaySide = RideSide;
enum boxPassableFlag = 0x1;
enum boxRidingFlag = 0x2;
enum RideSide : ubyte { enum RideSide : ubyte {
none, none,
top, top,
@ -111,86 +118,59 @@ struct BoxMover {
} }
} }
struct Box {
IVec2 position;
IVec2 size;
@safe @nogc nothrow:
pragma(inline, true)
this(IVec2 position, IVec2 size) {
this.position = position;
this.size = size;
}
pragma(inline, true)
this(int x, int y, int w, int h) {
this(IVec2(x, y), IVec2(w, h));
}
pragma(inline, true)
this(IVec2 position, int w, int h) {
this(position, IVec2(w, h));
}
pragma(inline, true)
this(int x, int y, IVec2 size) {
this(IVec2(x, y), size);
}
pragma(inline, true)
Rect toRect() {
return Rect(position.toVec(), size.toVec());
}
bool hasPoint(IVec2 point) {
return (
point.x > position.x &&
point.x < position.x + size.x &&
point.y > position.y &&
point.y < position.y + size.y
);
}
bool hasIntersection(Box area) {
return (
position.x + size.x > area.position.x &&
position.x < area.position.x + area.size.x &&
position.y + size.y > area.position.y &&
position.y < area.position.y + area.size.y
);
}
/// Returns a string representation with a limited lifetime.
IStr toStr() {
return "({}, {}, {}, {})".format(position.x, position.y, size.x, size.y);
}
}
struct WallBoxProperties { struct WallBoxProperties {
Vec2 remainder; Vec2 remainder;
OneWaySide oneWaySide; OneWaySide oneWaySide;
bool isPassable; WallBoxFlags flags;
byte gridX;
byte gridY;
} }
struct ActorBoxProperties { struct ActorBoxProperties {
Vec2 remainder; Vec2 remainder;
RideSide rideSide; RideSide rideSide;
bool isRiding; ActorBoxFlags flags;
bool isPassable; byte gridX;
byte gridY;
} }
struct BoxWorld { struct BoxWorld {
List!Box walls; List!IRect walls;
List!Box actors; List!IRect actors;
List!WallBoxProperties wallsProperties; List!WallBoxProperties wallsProperties;
List!ActorBoxProperties actorsProperties; List!ActorBoxProperties actorsProperties;
List!ActorBoxId squishedIdsBuffer; List!ActorBoxId squishedIdsBuffer;
List!BaseBoxId collisionIdsBuffer; List!BaseBoxId collisionIdsBuffer;
Grid!BaseBoxIdGroup grid;
int gridTileWidth;
int gridTileHeight;
@safe @nogc nothrow: @safe @nogc nothrow:
ref Box getWall(WallBoxId id) { void enableSpatialGrid(Sz rowCount, Sz colCount, int tileWidth, int tileHeight) {
gridTileWidth = tileWidth;
gridTileHeight = tileHeight;
foreach (i, ref properties; wallsProperties) {
properties.gridX = walls[i].position.x / gridTileWidth - (walls[i].position.x < 0);
properties.gridY = walls[i].position.y / gridTileHeight - (walls[i].position.y < 0);
}
foreach (i, ref properties; actorsProperties) {
properties.gridX = actors[i].position.x / gridTileWidth - (actors[i].position.x < 0);
properties.gridY = actors[i].position.y / gridTileHeight - (actors[i].position.y < 0);
}
grid.resizeBlank(rowCount, colCount);
foreach (ref group; grid) {
group.length = 0;
}
}
void disableSpatialGrid() {
gridTileWidth = 0;
gridTileHeight = 0;
grid.clear();
}
ref IRect getWall(WallBoxId id) {
if (id <= 0) { if (id <= 0) {
assert(0, "ID `0` is always invalid and represents a box that was never created."); assert(0, "ID `0` is always invalid and represents a box that was never created.");
} else if (id > walls.length) { } else if (id > walls.length) {
@ -199,7 +179,7 @@ struct BoxWorld {
return walls[id - 1]; return walls[id - 1];
} }
ref Box getActor(ActorBoxId id) { ref IRect getActor(ActorBoxId id) {
if (id <= 0) { if (id <= 0) {
assert(0, "ID `0` is always invalid and represents a box that was never created."); assert(0, "ID `0` is always invalid and represents a box that was never created.");
} else if (id > actors.length) { } else if (id > actors.length) {
@ -226,46 +206,54 @@ struct BoxWorld {
return actorsProperties[id - 1]; return actorsProperties[id - 1];
} }
WallBoxId appendWall(Box box, OneWaySide oneWaySide = OneWaySide.none) { WallBoxId appendWall(IRect box, OneWaySide oneWaySide = OneWaySide.none) {
walls.append(box); walls.append(box);
wallsProperties.append(WallBoxProperties()); wallsProperties.append(WallBoxProperties());
wallsProperties[$ - 1].oneWaySide = oneWaySide; wallsProperties[$ - 1].oneWaySide = oneWaySide;
if (gridTileWidth != 0 || gridTileHeight != 0) {
wallsProperties[$ - 1].gridX = box.position.x / gridTileWidth - (box.position.x < 0);
wallsProperties[$ - 1].gridY = box.position.y / gridTileHeight - (box.position.y < 0);
}
return cast(BaseBoxId) walls.length; return cast(BaseBoxId) walls.length;
} }
ActorBoxId appendActor(Box box, RideSide rideSide = RideSide.none) { ActorBoxId appendActor(IRect box, RideSide rideSide = RideSide.none) {
actors.append(box); actors.append(box);
actorsProperties.append(ActorBoxProperties()); actorsProperties.append(ActorBoxProperties());
actorsProperties[$ - 1].rideSide = rideSide; actorsProperties[$ - 1].rideSide = rideSide;
if (gridTileWidth != 0 || gridTileHeight != 0) {
actorsProperties[$ - 1].gridX = box.position.x / gridTileWidth - (box.position.x < 0);
actorsProperties[$ - 1].gridY = box.position.y / gridTileHeight - (box.position.y < 0);
}
return cast(BaseBoxId) actors.length; return cast(BaseBoxId) actors.length;
} }
WallBoxId hasWallCollision(Box box) { WallBoxId hasWallCollision(IRect box) {
foreach (i, wall; walls) { foreach (i, wall; walls) {
if (wall.hasIntersection(box) && !wallsProperties[i].isPassable) return cast(BaseBoxId) (i + 1); if (wall.hasIntersection(box) && ~wallsProperties[i].flags & boxPassableFlag) return cast(BaseBoxId) (i + 1);
} }
return 0; return 0;
} }
ActorBoxId hasActorCollision(Box box) { ActorBoxId hasActorCollision(IRect box) {
foreach (i, actor; actors) { foreach (i, actor; actors) {
if (actor.hasIntersection(box) && !actorsProperties[i].isPassable) return cast(BaseBoxId) (i + 1); if (actor.hasIntersection(box) && ~actorsProperties[i].flags & boxPassableFlag) return cast(BaseBoxId) (i + 1);
} }
return 0; return 0;
} }
WallBoxId[] getWallCollisions(Box box) { WallBoxId[] getWallCollisions(IRect box) {
collisionIdsBuffer.clear(); collisionIdsBuffer.clear();
foreach (i, wall; walls) { foreach (i, wall; walls) {
if (wall.hasIntersection(box) && !wallsProperties[i].isPassable) collisionIdsBuffer.append(cast(BaseBoxId) (i + 1)); if (wall.hasIntersection(box) && ~wallsProperties[i].flags & boxPassableFlag) collisionIdsBuffer.append(cast(BaseBoxId) (i + 1));
} }
return collisionIdsBuffer[]; return collisionIdsBuffer[];
} }
ActorBoxId[] getActorCollisions(Box box) { ActorBoxId[] getActorCollisions(IRect box) {
collisionIdsBuffer.clear(); collisionIdsBuffer.clear();
foreach (i, actor; actors) { foreach (i, actor; actors) {
if (actor.hasIntersection(box) && !actorsProperties[i].isPassable) collisionIdsBuffer.append(cast(BaseBoxId) (i + 1)); if (actor.hasIntersection(box) && ~actorsProperties[i].flags & boxPassableFlag) collisionIdsBuffer.append(cast(BaseBoxId) (i + 1));
} }
return collisionIdsBuffer[]; return collisionIdsBuffer[];
} }
@ -281,7 +269,7 @@ struct BoxWorld {
int moveSign = move.sign(); int moveSign = move.sign();
properties.remainder.x -= move; properties.remainder.x -= move;
while (move != 0) { while (move != 0) {
auto tempBox = Box(actor.position + IVec2(moveSign, 0), actor.size); auto tempBox = IRect(actor.position + IVec2(moveSign, 0), actor.size);
auto wallId = hasWallCollision(tempBox); auto wallId = hasWallCollision(tempBox);
if (wallId) { if (wallId) {
// One way stuff. // One way stuff.
@ -302,11 +290,12 @@ struct BoxWorld {
break; break;
} }
} }
if (!properties.isPassable && wallId) { if (~properties.flags & boxPassableFlag && wallId) {
return wallId; return wallId;
} else { } else {
actor.position.x += moveSign; actor.position.x += moveSign;
move -= moveSign; move -= moveSign;
properties.gridX = actor.position.x / gridTileWidth - (actor.position.x < 0);
} }
} }
return 0; return 0;
@ -335,7 +324,7 @@ struct BoxWorld {
int moveSign = move.sign(); int moveSign = move.sign();
properties.remainder.y -= move; properties.remainder.y -= move;
while (move != 0) { while (move != 0) {
auto tempBox = Box(actor.position + IVec2(0, moveSign), actor.size); auto tempBox = IRect(actor.position + IVec2(0, moveSign), actor.size);
auto wallId = hasWallCollision(tempBox); auto wallId = hasWallCollision(tempBox);
if (wallId) { if (wallId) {
// One way stuff. // One way stuff.
@ -356,11 +345,12 @@ struct BoxWorld {
break; break;
} }
} }
if (!properties.isPassable && wallId) { if (~properties.flags & boxPassableFlag && wallId) {
return wallId; return wallId;
} else { } else {
actor.position.y += moveSign; actor.position.y += moveSign;
move -= moveSign; move -= moveSign;
properties.gridY = actor.position.y / gridTileHeight - (actor.position.y < 0);
} }
} }
return 0; return 0;
@ -443,8 +433,8 @@ struct BoxWorld {
auto move = properties.remainder.round().toIVec(); auto move = properties.remainder.round().toIVec();
if (move.x != 0 || move.y != 0) { if (move.x != 0 || move.y != 0) {
foreach (i, ref actorProperties; actorsProperties) { foreach (i, ref actorProperties; actorsProperties) {
actorProperties.isRiding = false; actorProperties.flags &= ~boxRidingFlag;
if (!actorProperties.rideSide || actorProperties.isPassable) continue; if (!actorProperties.rideSide || actorProperties.flags & boxPassableFlag) continue;
auto rideBox = actors[i]; auto rideBox = actors[i];
final switch (actorProperties.rideSide) with (RideSide) { final switch (actorProperties.rideSide) with (RideSide) {
case none: break; case none: break;
@ -453,16 +443,17 @@ struct BoxWorld {
case right: rideBox.position.x -= 1; break; case right: rideBox.position.x -= 1; break;
case bottom: rideBox.position.y -= 1; break; case bottom: rideBox.position.y -= 1; break;
} }
actorProperties.isRiding = wall.hasIntersection(rideBox); actorProperties.flags |= wall.hasIntersection(rideBox) ? boxRidingFlag : 0x0;
} }
} }
if (move.x != 0) { if (move.x != 0) {
wall.position.x += move.x; wall.position.x += move.x;
properties.remainder.x -= move.x; properties.remainder.x -= move.x;
if (!properties.isPassable) { properties.gridX = wall.position.x / gridTileWidth - (wall.position.x < 0);
properties.isPassable = true; if (~properties.flags & boxPassableFlag) {
properties.flags |= boxPassableFlag;
foreach (i, ref actor; actors) { foreach (i, ref actor; actors) {
if (actorsProperties[i].isPassable) continue; if (actorsProperties[i].flags & boxPassableFlag) continue;
if (wall.hasIntersection(actor)) { if (wall.hasIntersection(actor)) {
// Push actor. // Push actor.
auto wallLeft = wall.position.x; auto wallLeft = wall.position.x;
@ -474,21 +465,22 @@ struct BoxWorld {
// Squish actor. // Squish actor.
squishedIdsBuffer.append(cast(BaseBoxId) (i + 1)); squishedIdsBuffer.append(cast(BaseBoxId) (i + 1));
} }
} else if (actorsProperties[i].isRiding) { } else if (actorsProperties[i].flags & boxRidingFlag) {
// Carry actor. // Carry actor.
moveActorX(cast(BaseBoxId) (i + 1), move.x); moveActorX(cast(BaseBoxId) (i + 1), move.x);
} }
} }
properties.isPassable = false; properties.flags &= ~boxPassableFlag;
} }
} }
if (move.y != 0) { if (move.y != 0) {
wall.position.y += move.y; wall.position.y += move.y;
properties.remainder.y -= move.y; properties.remainder.y -= move.y;
if (!properties.isPassable) { properties.gridY = wall.position.y / gridTileHeight - (wall.position.y < 0);
properties.isPassable = true; if (~properties.flags & boxPassableFlag) {
properties.flags |= boxPassableFlag;
foreach (i, ref actor; actors) { foreach (i, ref actor; actors) {
if (actorsProperties[i].isPassable) continue; if (actorsProperties[i].flags & boxPassableFlag) continue;
if (wall.hasIntersection(actor)) { if (wall.hasIntersection(actor)) {
// Push actor. // Push actor.
auto wallTop = wall.position.y; auto wallTop = wall.position.y;
@ -500,12 +492,12 @@ struct BoxWorld {
// Squish actor. // Squish actor.
squishedIdsBuffer.append(cast(BaseBoxId) (i + 1)); squishedIdsBuffer.append(cast(BaseBoxId) (i + 1));
} }
} else if (actorsProperties[i].isRiding) { } else if (actorsProperties[i].flags & boxRidingFlag) {
// Carry actor. // Carry actor.
moveActorY(cast(BaseBoxId) (i + 1), move.y); moveActorY(cast(BaseBoxId) (i + 1), move.y);
} }
} }
properties.isPassable = false; properties.flags &= ~boxPassableFlag;
} }
} }
return squishedIdsBuffer[]; return squishedIdsBuffer[];

View file

@ -15,7 +15,6 @@ import joka.ascii;
import joka.containers; import joka.containers;
import joka.io; import joka.io;
import joka.types; import joka.types;
import joka.unions;
@safe @nogc nothrow: @safe @nogc nothrow: