Map Base
This commit is contained in:
@@ -14,7 +14,7 @@ CubeEntity.prototype.constructor = CubeEntity;
|
|||||||
|
|
||||||
CubeEntity.prototype.update = function() {
|
CubeEntity.prototype.update = function() {
|
||||||
OverworldEntity.prototype.update.call(this);
|
OverworldEntity.prototype.update.call(this);
|
||||||
var speed = 3.0;
|
var speed = 5.0;
|
||||||
var move = Input.axis2D(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT, INPUT_ACTION_UP, INPUT_ACTION_DOWN);
|
var move = Input.axis2D(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT, INPUT_ACTION_UP, INPUT_ACTION_DOWN);
|
||||||
this.position.position.x += move.x * speed * TIME.delta;
|
this.position.position.x += move.x * speed * TIME.delta;
|
||||||
this.position.position.z += move.y * speed * TIME.delta;
|
this.position.position.z += move.y * speed * TIME.delta;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ OverworldEntity.prototype.update = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
OverworldEntity.prototype.dispose = function() {
|
OverworldEntity.prototype.dispose = function() {
|
||||||
// Nothing to dispose
|
Entity.prototype.dispose.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
module = OverworldEntity;
|
module = OverworldEntity;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
var CubeEntity = include('entities/CubeEntity.js');
|
||||||
|
|
||||||
|
function TestChunkN100(x, y, z) {
|
||||||
|
MapChunk.call(this, x, y, z);
|
||||||
|
|
||||||
|
this.cube = new CubeEntity();
|
||||||
|
this.cube.position.position = new Vec3(-16, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestChunkN100.prototype = Object.create(MapChunk.prototype);
|
||||||
|
TestChunkN100.prototype.constructor = TestChunkN100;
|
||||||
|
|
||||||
|
TestChunkN100.prototype.dispose = function() {
|
||||||
|
this.cube.dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
module = TestChunkN100;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
var CubeEntity = include('entities/CubeEntity.js');
|
||||||
|
|
||||||
|
function TestChunk000(x, y, z) {
|
||||||
|
MapChunk.call(this, x, y, z);
|
||||||
|
|
||||||
|
this.cube = new CubeEntity();
|
||||||
|
this.cube.position.position = new Vec3(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestChunk000.prototype = Object.create(MapChunk.prototype);
|
||||||
|
TestChunk000.prototype.constructor = TestChunk000;
|
||||||
|
|
||||||
|
TestChunk000.prototype.dispose = function() {
|
||||||
|
this.cube.dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
module = TestChunk000;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
var CubeEntity = include('entities/CubeEntity.js');
|
||||||
|
|
||||||
|
function TestChunk001(x, y, z) {
|
||||||
|
MapChunk.call(this, x, y, z);
|
||||||
|
|
||||||
|
this.cube = new CubeEntity();
|
||||||
|
this.cube.position.position = new Vec3(0, 0, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestChunk001.prototype = Object.create(MapChunk.prototype);
|
||||||
|
TestChunk001.prototype.constructor = TestChunk001;
|
||||||
|
|
||||||
|
TestChunk001.prototype.dispose = function() {
|
||||||
|
this.cube.dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
module = TestChunk001;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
var CubeEntity = include('entities/CubeEntity.js');
|
||||||
|
|
||||||
|
function TestChunk010(x, y, z) {
|
||||||
|
MapChunk.call(this, x, y, z);
|
||||||
|
|
||||||
|
this.cube = new CubeEntity();
|
||||||
|
this.cube.position.position = new Vec3(0, 16, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestChunk010.prototype = Object.create(MapChunk.prototype);
|
||||||
|
TestChunk010.prototype.constructor = TestChunk010;
|
||||||
|
|
||||||
|
TestChunk010.prototype.dispose = function() {
|
||||||
|
this.cube.dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
module = TestChunk010;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
var CubeEntity = include('entities/CubeEntity.js');
|
||||||
|
|
||||||
|
function TestChunk100(x, y, z) {
|
||||||
|
MapChunk.call(this, x, y, z);
|
||||||
|
|
||||||
|
this.cube = new CubeEntity();
|
||||||
|
this.cube.position.position = new Vec3(16, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestChunk100.prototype = Object.create(MapChunk.prototype);
|
||||||
|
TestChunk100.prototype.constructor = TestChunk100;
|
||||||
|
|
||||||
|
TestChunk100.prototype.dispose = function() {
|
||||||
|
this.cube.dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
module = TestChunk100;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
var CubeEntity = include('entities/CubeEntity.js');
|
||||||
|
|
||||||
|
function TestChunk200(x, y, z) {
|
||||||
|
MapChunk.call(this, x, y, z);
|
||||||
|
|
||||||
|
this.cube = new CubeEntity();
|
||||||
|
this.cube.position.position = new Vec3(32, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestChunk200.prototype = Object.create(MapChunk.prototype);
|
||||||
|
TestChunk200.prototype.constructor = TestChunk200;
|
||||||
|
|
||||||
|
TestChunk200.prototype.dispose = function() {
|
||||||
|
this.cube.dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
module = TestChunk200;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
var CubeEntity = include('entities/CubeEntity.js');
|
||||||
|
|
||||||
|
function TestChunk300(x, y, z) {
|
||||||
|
MapChunk.call(this, x, y, z);
|
||||||
|
|
||||||
|
this.cube = new CubeEntity();
|
||||||
|
this.cube.position.position = new Vec3(48, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestChunk300.prototype = Object.create(MapChunk.prototype);
|
||||||
|
TestChunk300.prototype.constructor = TestChunk300;
|
||||||
|
|
||||||
|
TestChunk300.prototype.dispose = function() {
|
||||||
|
this.cube.dispose();
|
||||||
|
};
|
||||||
|
|
||||||
|
module = TestChunk300;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
function TestMap() {
|
||||||
|
Map.call(this);
|
||||||
|
|
||||||
|
Map.setChunkSize(16, 16, 16);
|
||||||
|
Map.setPosition(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestMap.prototype = Object.create(Map.prototype);
|
||||||
|
TestMap.prototype.constructor = TestMap;
|
||||||
|
|
||||||
|
TestMap.prototype.update = function() {
|
||||||
|
};
|
||||||
|
|
||||||
|
TestMap.prototype.dispose = function() {
|
||||||
|
};
|
||||||
|
|
||||||
|
module = TestMap;
|
||||||
+13
-30
@@ -1,50 +1,33 @@
|
|||||||
var CubeEntity = include('entities/CubeEntity.js');
|
var CubeEntity = include('entities/CubeEntity.js');
|
||||||
var MoveCubeCutscene = include('cutscenes/MoveCubeCutscene.js');
|
|
||||||
|
|
||||||
function CubeScene() {
|
function CubeScene() {
|
||||||
|
Map.load('test');
|
||||||
|
|
||||||
this.cam = new Entity();
|
this.cam = new Entity();
|
||||||
this.cam.add(POSITION);
|
this.cam.add(POSITION);
|
||||||
this.cam.add(CAMERA);
|
this.cam.add(CAMERA);
|
||||||
|
|
||||||
this.cam.position.position = new Vec3(3, 3, 3);
|
this.player = new CubeEntity();
|
||||||
this.cam.position.lookAt(new Vec3(0, 0, 0));
|
this.player.position.position = new Vec3(0, 0, 0);
|
||||||
|
|
||||||
this.cube = new CubeEntity();
|
|
||||||
|
|
||||||
this.spriteEnt = new Entity();
|
|
||||||
this.spriteEnt.add(POSITION);
|
|
||||||
this.spriteEnt.position.position = new Vec3(16, 16, 0);
|
|
||||||
|
|
||||||
this.inputEnabled = false;
|
|
||||||
|
|
||||||
var scene = this;
|
|
||||||
Cutscene.play(new MoveCubeCutscene({ cube: this.cube })).then(function() {
|
|
||||||
scene.inputEnabled = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
Textbox.setText(
|
|
||||||
"Hello! This is a visual novel textbox. It automatically " +
|
|
||||||
"wraps long lines and splits into pages when the content " +
|
|
||||||
"is too tall to fit. Press advance to continue...\t" +
|
|
||||||
"This is a second paragraph on a new page."
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CubeScene.prototype = Object.create(Scene.prototype);
|
CubeScene.prototype = Object.create(Scene.prototype);
|
||||||
CubeScene.prototype.constructor = CubeScene;
|
CubeScene.prototype.constructor = CubeScene;
|
||||||
|
|
||||||
CubeScene.prototype.update = function() {
|
CubeScene.prototype.update = function() {
|
||||||
if(this.inputEnabled) {
|
this.player.update();
|
||||||
this.cube.update();
|
|
||||||
}
|
var pos = this.player.position.position;
|
||||||
|
this.cam.position.position = new Vec3(pos.x, pos.y + 8, pos.z + 8);
|
||||||
|
this.cam.position.lookAt(pos);
|
||||||
|
|
||||||
|
Map.setPosition(Math.floor(pos.x), Math.floor(pos.y), Math.floor(pos.z));
|
||||||
};
|
};
|
||||||
|
|
||||||
CubeScene.prototype.dispose = function() {
|
CubeScene.prototype.dispose = function() {
|
||||||
Cutscene.stop();
|
|
||||||
this.cam.dispose();
|
this.cam.dispose();
|
||||||
this.cube.dispose();
|
this.player.dispose();
|
||||||
this.spriteEnt.dispose();
|
Map.dispose();
|
||||||
};
|
};
|
||||||
|
|
||||||
module = CubeScene;
|
module = CubeScene;
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ add_subdirectory(system)
|
|||||||
add_subdirectory(time)
|
add_subdirectory(time)
|
||||||
add_subdirectory(ui)
|
add_subdirectory(ui)
|
||||||
add_subdirectory(network)
|
add_subdirectory(network)
|
||||||
|
add_subdirectory(overworld)
|
||||||
add_subdirectory(save)
|
add_subdirectory(save)
|
||||||
add_subdirectory(util)
|
add_subdirectory(util)
|
||||||
add_subdirectory(thread)
|
add_subdirectory(thread)
|
||||||
@@ -128,3 +128,22 @@ void entityRenderableSpriteBatchClear(
|
|||||||
);
|
);
|
||||||
r->spritebatch.spriteCount = 0;
|
r->spritebatch.spriteCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void entityRenderableDispose(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId
|
||||||
|
) {
|
||||||
|
entityrenderable_t *r = componentGetData(
|
||||||
|
entityId, componentId, COMPONENT_TYPE_RENDERABLE
|
||||||
|
);
|
||||||
|
if(
|
||||||
|
r->type == ENTITY_RENDERABLE_TYPE_CALLBACK &&
|
||||||
|
r->userFree &&
|
||||||
|
r->user
|
||||||
|
) {
|
||||||
|
r->userFree(r->user);
|
||||||
|
r->user = NULL;
|
||||||
|
}
|
||||||
|
r->mesh = NULL;
|
||||||
|
r->shader = NULL;
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,8 +16,18 @@
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
ENTITY_RENDERABLE_TYPE_MATERIAL = 0,
|
ENTITY_RENDERABLE_TYPE_MATERIAL = 0,
|
||||||
ENTITY_RENDERABLE_TYPE_SPRITEBATCH,
|
ENTITY_RENDERABLE_TYPE_SPRITEBATCH,
|
||||||
|
ENTITY_RENDERABLE_TYPE_CALLBACK,
|
||||||
} entityrenderabletype_t;
|
} entityrenderabletype_t;
|
||||||
|
|
||||||
|
typedef errorret_t (*entityrenderablecallback_t)(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId,
|
||||||
|
const mat4 view,
|
||||||
|
const mat4 proj,
|
||||||
|
const mat4 model,
|
||||||
|
void *user
|
||||||
|
);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
spritebatchsprite_t sprites[ENTITY_RENDERABLE_SPRITEBATCH_SPRITES_MAX];
|
spritebatchsprite_t sprites[ENTITY_RENDERABLE_SPRITEBATCH_SPRITES_MAX];
|
||||||
uint16_t spriteCount;
|
uint16_t spriteCount;
|
||||||
@@ -32,6 +42,11 @@ typedef struct {
|
|||||||
shadermaterial_t material;
|
shadermaterial_t material;
|
||||||
};
|
};
|
||||||
entityrenderablespritebatch_t spritebatch;
|
entityrenderablespritebatch_t spritebatch;
|
||||||
|
struct {
|
||||||
|
entityrenderablecallback_t callback;
|
||||||
|
void (*userFree)(void *user);
|
||||||
|
void *user;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
} entityrenderable_t;
|
} entityrenderable_t;
|
||||||
|
|
||||||
@@ -44,6 +59,14 @@ void entityRenderableInit(
|
|||||||
const componentid_t componentId
|
const componentid_t componentId
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes the entity renderable component, freeing any callback user data.
|
||||||
|
*/
|
||||||
|
void entityRenderableDispose(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the renderable type.
|
* Gets the renderable type.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -19,6 +19,6 @@
|
|||||||
|
|
||||||
X(POSITION, entityposition_t, position, entityPositionInit, NULL)
|
X(POSITION, entityposition_t, position, entityPositionInit, NULL)
|
||||||
X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL)
|
X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL)
|
||||||
X(RENDERABLE, entityrenderable_t, renderable, entityRenderableInit, NULL)
|
X(RENDERABLE, entityrenderable_t, renderable, entityRenderableInit, entityRenderableDispose)
|
||||||
X(PHYSICS, entityphysics_t, physics, entityPhysicsInit, entityPhysicsDispose)
|
X(PHYSICS, entityphysics_t, physics, entityPhysicsInit, entityPhysicsDispose)
|
||||||
X(TRIGGER, entitytrigger_t, trigger, entityTriggerInit, NULL)
|
X(TRIGGER, entitytrigger_t, trigger, entityTriggerInit, NULL)
|
||||||
|
|||||||
@@ -86,11 +86,12 @@ void entityDispose(const entityid_t entityId) {
|
|||||||
|
|
||||||
for(componentid_t i = 0; i < ENTITY_COMPONENT_COUNT_MAX; i++) {
|
for(componentid_t i = 0; i < ENTITY_COMPONENT_COUNT_MAX; i++) {
|
||||||
compInd = componentGetIndex(entityId, i);
|
compInd = componentGetIndex(entityId, i);
|
||||||
if(ENTITY_MANAGER.components[compInd].type == COMPONENT_TYPE_NULL) continue;
|
componenttype_t type = ENTITY_MANAGER.components[compInd].type;
|
||||||
componentDispose(entityId, i);
|
if(type == COMPONENT_TYPE_NULL) continue;
|
||||||
ENTITY_MANAGER.entitiesWithComponent[
|
ENTITY_MANAGER.entitiesWithComponent[
|
||||||
ENTITY_MANAGER.components[compInd].type * ENTITY_COUNT_MAX + entityId
|
type * ENTITY_COUNT_MAX + entityId
|
||||||
] = COMPONENT_ID_INVALID;
|
] = COMPONENT_ID_INVALID;
|
||||||
|
componentDispose(entityId, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
ent->state = 0;
|
ent->state = 0;
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Copyright (c) 2026 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
||||||
|
PUBLIC
|
||||||
|
maptypes.c
|
||||||
|
map.c
|
||||||
|
mapchunk.c
|
||||||
|
)
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "map.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
#include "asset/assetfile.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
#include "console/console.h"
|
||||||
|
#include "script/module/overworld/modulemap.h"
|
||||||
|
|
||||||
|
map_t MAP;
|
||||||
|
|
||||||
|
chunkindex_t mapChunkRelToIndex(
|
||||||
|
chunkunit_t rx,
|
||||||
|
chunkunit_t ry,
|
||||||
|
chunkunit_t rz
|
||||||
|
) {
|
||||||
|
return (chunkindex_t)(
|
||||||
|
rz * (MAP_CHUNKS_WIDE * MAP_CHUNKS_HIGH) +
|
||||||
|
ry * MAP_CHUNKS_WIDE +
|
||||||
|
rx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mapInit(void) {
|
||||||
|
memoryZero(&MAP, sizeof(map_t));
|
||||||
|
MAP.scriptRef = MAP_SCRIPT_REF_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t mapLoad(const char_t *handle) {
|
||||||
|
assertStrLenMin(handle, 1, "Map handle cannot be empty");
|
||||||
|
assertStrLenMax(handle, MAP_HANDLE_MAX - 1, "Map handle too long");
|
||||||
|
|
||||||
|
if(mapIsLoaded()) mapDispose();
|
||||||
|
|
||||||
|
memoryZero(&MAP, sizeof(map_t));
|
||||||
|
MAP.scriptRef = MAP_SCRIPT_REF_NONE;
|
||||||
|
stringCopy(MAP.handle, handle, MAP_HANDLE_MAX);
|
||||||
|
|
||||||
|
char_t path[ASSET_FILE_NAME_MAX];
|
||||||
|
stringFormat(path, sizeof(path), "maps/%s/init.js", handle);
|
||||||
|
|
||||||
|
jerry_value_t mapClass = MAP_SCRIPT_REF_NONE;
|
||||||
|
errorChain(scriptManagerExecFile(path, &mapClass));
|
||||||
|
|
||||||
|
if(!jerry_value_is_function(mapClass)) {
|
||||||
|
if(mapClass != MAP_SCRIPT_REF_NONE) jerry_value_free(mapClass);
|
||||||
|
errorThrow("Map '%s' must export a constructor function", handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_t mapObj = jerry_construct(mapClass, NULL, 0);
|
||||||
|
jerry_value_free(mapClass);
|
||||||
|
|
||||||
|
if(jerry_value_is_exception(mapObj)) {
|
||||||
|
char_t errMsg[512];
|
||||||
|
moduleBaseExceptionMessage(mapObj, errMsg, sizeof(errMsg));
|
||||||
|
jerry_value_free(mapObj);
|
||||||
|
errorThrow("Map '%s' constructor threw: %s", handle, errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
MAP.scriptRef = mapObj;
|
||||||
|
consolePrint("Map loaded: %s", handle);
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_t mapIsLoaded(void) {
|
||||||
|
return MAP.loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t mapPositionSet(tilepos_t tilePos) {
|
||||||
|
assertTrue(MAP.chunkTileWidth > 0, "chunkTileWidth not set");
|
||||||
|
assertTrue(MAP.chunkTileHeight > 0, "chunkTileHeight not set");
|
||||||
|
assertTrue(MAP.chunkTileDepth > 0, "chunkTileDepth not set");
|
||||||
|
|
||||||
|
// Convert tile position to chunk-space window origin.
|
||||||
|
chunkpos_t newPos = {
|
||||||
|
.x = (chunkunit_t)(tilePos.x / MAP.chunkTileWidth),
|
||||||
|
.y = (chunkunit_t)(tilePos.y / MAP.chunkTileHeight),
|
||||||
|
.z = (chunkunit_t)(tilePos.z / MAP.chunkTileDepth),
|
||||||
|
};
|
||||||
|
|
||||||
|
if(MAP.loaded && chunkPosEqual(MAP.chunkPosition, newPos)) errorOk();
|
||||||
|
|
||||||
|
// Categorise existing chunks as remaining or freed.
|
||||||
|
chunkindex_t remaining[MAP_CHUNKS_COUNT];
|
||||||
|
chunkindex_t freed[MAP_CHUNKS_COUNT];
|
||||||
|
chunkindex_t remainingCount = 0;
|
||||||
|
chunkindex_t freedCount = 0;
|
||||||
|
|
||||||
|
for(chunkindex_t i = 0; i < MAP_CHUNKS_COUNT; i++) {
|
||||||
|
mapchunk_t *chunk = &MAP.chunks[i];
|
||||||
|
chunkpos_t p = chunk->position;
|
||||||
|
|
||||||
|
bool_t stays = MAP.loaded &&
|
||||||
|
p.x >= newPos.x && p.x < newPos.x + MAP_CHUNKS_WIDE &&
|
||||||
|
p.y >= newPos.y && p.y < newPos.y + MAP_CHUNKS_HIGH &&
|
||||||
|
p.z >= newPos.z && p.z < newPos.z + MAP_CHUNKS_DEEP;
|
||||||
|
|
||||||
|
if(stays) {
|
||||||
|
remaining[remainingCount++] = i;
|
||||||
|
} else {
|
||||||
|
if(MAP.loaded) mapChunkUnload(chunk);
|
||||||
|
freed[freedCount++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build chunkOrder for the new window, loading into freed slots as needed.
|
||||||
|
chunkindex_t orderIndex = 0;
|
||||||
|
for(chunkunit_t zOff = 0; zOff < MAP_CHUNKS_DEEP; zOff++) {
|
||||||
|
for(chunkunit_t yOff = 0; yOff < MAP_CHUNKS_HIGH; yOff++) {
|
||||||
|
for(chunkunit_t xOff = 0; xOff < MAP_CHUNKS_WIDE; xOff++) {
|
||||||
|
chunkpos_t target = {
|
||||||
|
.x = (chunkunit_t)(newPos.x + xOff),
|
||||||
|
.y = (chunkunit_t)(newPos.y + yOff),
|
||||||
|
.z = (chunkunit_t)(newPos.z + zOff),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if the target chunk is already loaded.
|
||||||
|
chunkindex_t poolIdx = -1;
|
||||||
|
for(chunkindex_t r = 0; r < remainingCount; r++) {
|
||||||
|
if(chunkPosEqual(MAP.chunks[remaining[r]].position, target)) {
|
||||||
|
poolIdx = remaining[r];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise recycle a freed slot.
|
||||||
|
if(poolIdx == -1) {
|
||||||
|
poolIdx = freed[--freedCount];
|
||||||
|
MAP.chunks[poolIdx].position = target;
|
||||||
|
errorChain(mapChunkLoad(&MAP.chunks[poolIdx]));
|
||||||
|
}
|
||||||
|
|
||||||
|
MAP.chunkOrder[orderIndex++] = &MAP.chunks[poolIdx];
|
||||||
|
}}}
|
||||||
|
|
||||||
|
MAP.chunkPosition = newPos;
|
||||||
|
MAP.loaded = true;
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
mapchunk_t *mapGetChunkAt(chunkpos_t pos) {
|
||||||
|
if(!MAP.loaded) return NULL;
|
||||||
|
chunkpos_t p = MAP.chunkPosition;
|
||||||
|
if(
|
||||||
|
pos.x < p.x || pos.x >= p.x + MAP_CHUNKS_WIDE ||
|
||||||
|
pos.y < p.y || pos.y >= p.y + MAP_CHUNKS_HIGH ||
|
||||||
|
pos.z < p.z || pos.z >= p.z + MAP_CHUNKS_DEEP
|
||||||
|
) return NULL;
|
||||||
|
chunkindex_t idx = mapChunkRelToIndex(
|
||||||
|
pos.x - p.x, pos.y - p.y, pos.z - p.z
|
||||||
|
);
|
||||||
|
return MAP.chunkOrder[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
errorret_t mapUpdate(void) {
|
||||||
|
if(!MAP.loaded) errorOk();
|
||||||
|
errorChain(moduleMapCall("update"));
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
void mapDispose(void) {
|
||||||
|
consolePrint("Map disposing: %s", MAP.handle);
|
||||||
|
if(MAP.scriptRef != MAP_SCRIPT_REF_NONE) {
|
||||||
|
moduleMapCall("dispose");
|
||||||
|
moduleMapReset();
|
||||||
|
}
|
||||||
|
if(!MAP.loaded) return;
|
||||||
|
for(chunkindex_t i = 0; i < MAP_CHUNKS_COUNT; i++) {
|
||||||
|
mapChunkUnload(&MAP.chunks[i]);
|
||||||
|
}
|
||||||
|
MAP.loaded = false;
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "mapchunk.h"
|
||||||
|
#include "error/error.h"
|
||||||
|
#include "script/scriptmanager.h"
|
||||||
|
|
||||||
|
#define MAP_NAME_MAX 64
|
||||||
|
#define MAP_HANDLE_MAX 32
|
||||||
|
#define MAP_CHUNKS_WIDE 3
|
||||||
|
#define MAP_CHUNKS_HIGH 3
|
||||||
|
#define MAP_CHUNKS_DEEP 2
|
||||||
|
#define MAP_CHUNKS_COUNT (MAP_CHUNKS_WIDE * MAP_CHUNKS_HIGH * MAP_CHUNKS_DEEP)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char_t name[MAP_NAME_MAX];
|
||||||
|
char_t handle[MAP_HANDLE_MAX];
|
||||||
|
uint16_t chunkTileWidth;
|
||||||
|
uint16_t chunkTileHeight;
|
||||||
|
uint16_t chunkTileDepth;
|
||||||
|
mapchunk_t chunks[MAP_CHUNKS_COUNT];
|
||||||
|
mapchunk_t *chunkOrder[MAP_CHUNKS_COUNT];
|
||||||
|
chunkpos_t chunkPosition;
|
||||||
|
bool_t loaded;
|
||||||
|
jerry_value_t scriptRef;
|
||||||
|
} map_t;
|
||||||
|
|
||||||
|
/** Sentinel value meaning no map script is loaded. */
|
||||||
|
#define MAP_SCRIPT_REF_NONE ((jerry_value_t)0)
|
||||||
|
|
||||||
|
extern map_t MAP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the map, zeroing all state.
|
||||||
|
*/
|
||||||
|
void mapInit(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the map for use with the given handle. If a map is already loaded
|
||||||
|
* it is disposed first. Chunk positions are not set until mapPositionSet is
|
||||||
|
* called.
|
||||||
|
*
|
||||||
|
* @param handle Short identifier for this map (max MAP_HANDLE_MAX - 1 chars).
|
||||||
|
* @return An error code.
|
||||||
|
*/
|
||||||
|
errorret_t mapLoad(const char_t *handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if a map is currently loaded.
|
||||||
|
*
|
||||||
|
* @return true if a map is loaded, false otherwise.
|
||||||
|
*/
|
||||||
|
bool_t mapIsLoaded(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a chunk position relative to the window origin to its pool index.
|
||||||
|
*
|
||||||
|
* @param rx Relative chunk X offset within the window.
|
||||||
|
* @param ry Relative chunk Y offset within the window.
|
||||||
|
* @param rz Relative chunk Z offset within the window.
|
||||||
|
* @return The flat pool index for that relative position.
|
||||||
|
*/
|
||||||
|
chunkindex_t mapChunkRelToIndex(
|
||||||
|
chunkunit_t rx,
|
||||||
|
chunkunit_t ry,
|
||||||
|
chunkunit_t rz
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slides the loaded chunk window so its origin is the chunk that contains
|
||||||
|
* the given tile-space position. Only the delta is loaded/unloaded.
|
||||||
|
*
|
||||||
|
* @param tilePos Tile-space position of the new window origin.
|
||||||
|
* @return An error code.
|
||||||
|
*/
|
||||||
|
errorret_t mapPositionSet(tilepos_t tilePos);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the map each frame.
|
||||||
|
*
|
||||||
|
* @return An error code.
|
||||||
|
*/
|
||||||
|
errorret_t mapUpdate(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes the map, unloading all chunks.
|
||||||
|
*/
|
||||||
|
void mapDispose(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the chunk at the given absolute chunk-space position, or NULL if
|
||||||
|
* it is outside the currently loaded window.
|
||||||
|
*
|
||||||
|
* @param pos Absolute chunk-space position to look up.
|
||||||
|
* @return Pointer to the chunk, or NULL if not loaded.
|
||||||
|
*/
|
||||||
|
mapchunk_t *mapGetChunkAt(chunkpos_t pos);
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "mapchunk.h"
|
||||||
|
#include "map.h"
|
||||||
|
#include "entity/entitymanager.h"
|
||||||
|
#include "util/memory.h"
|
||||||
|
#include "util/string.h"
|
||||||
|
#include "asset/asset.h"
|
||||||
|
#include "console/console.h"
|
||||||
|
#include "script/module/overworld/modulemapchunk.h"
|
||||||
|
|
||||||
|
errorret_t mapChunkLoad(mapchunk_t *chunk) {
|
||||||
|
chunk->entityCount = 0;
|
||||||
|
chunk->scriptRef = MAP_CHUNK_SCRIPT_REF_NONE;
|
||||||
|
memoryZero(chunk->entities, sizeof(chunk->entities));
|
||||||
|
|
||||||
|
if(MAP.handle[0] == '\0') errorOk();
|
||||||
|
|
||||||
|
char_t path[ASSET_FILE_NAME_MAX];
|
||||||
|
stringFormat(
|
||||||
|
path, sizeof(path),
|
||||||
|
"maps/%s/chunks/%d_%d_%d.js",
|
||||||
|
MAP.handle,
|
||||||
|
(int)chunk->position.x,
|
||||||
|
(int)chunk->position.y,
|
||||||
|
(int)chunk->position.z
|
||||||
|
);
|
||||||
|
|
||||||
|
if(!assetFileExists(path)) errorOk();
|
||||||
|
|
||||||
|
jerry_value_t chunkClass = MAP_CHUNK_SCRIPT_REF_NONE;
|
||||||
|
errorChain(scriptManagerExecFile(path, &chunkClass));
|
||||||
|
|
||||||
|
if(!jerry_value_is_function(chunkClass)) {
|
||||||
|
if(chunkClass != MAP_CHUNK_SCRIPT_REF_NONE) jerry_value_free(chunkClass);
|
||||||
|
errorThrow("Chunk script '%s' must export a constructor", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_t args[3] = {
|
||||||
|
jerry_number((double)chunk->position.x),
|
||||||
|
jerry_number((double)chunk->position.y),
|
||||||
|
jerry_number((double)chunk->position.z),
|
||||||
|
};
|
||||||
|
jerry_value_t chunkObj = jerry_construct(chunkClass, args, 3);
|
||||||
|
jerry_value_free(chunkClass);
|
||||||
|
jerry_value_free(args[0]);
|
||||||
|
jerry_value_free(args[1]);
|
||||||
|
jerry_value_free(args[2]);
|
||||||
|
|
||||||
|
if(jerry_value_is_exception(chunkObj)) {
|
||||||
|
jerry_value_free(chunkObj);
|
||||||
|
errorThrow("Chunk script '%s' constructor threw", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk->scriptRef = chunkObj;
|
||||||
|
consolePrint(
|
||||||
|
"Chunk loaded: %s [%d,%d,%d]",
|
||||||
|
path,
|
||||||
|
(int)chunk->position.x,
|
||||||
|
(int)chunk->position.y,
|
||||||
|
(int)chunk->position.z
|
||||||
|
);
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
void mapChunkUnload(mapchunk_t *chunk) {
|
||||||
|
consolePrint(
|
||||||
|
"Chunk unloading: [%d,%d,%d]",
|
||||||
|
(int)chunk->position.x,
|
||||||
|
(int)chunk->position.y,
|
||||||
|
(int)chunk->position.z
|
||||||
|
);
|
||||||
|
if(chunk->scriptRef != MAP_CHUNK_SCRIPT_REF_NONE) {
|
||||||
|
jerry_value_t key = jerry_string_sz("dispose");
|
||||||
|
jerry_value_t fn = jerry_object_get(chunk->scriptRef, key);
|
||||||
|
jerry_value_free(key);
|
||||||
|
|
||||||
|
if(jerry_value_is_function(fn)) {
|
||||||
|
jerry_value_t result = jerry_call(fn, chunk->scriptRef, NULL, 0);
|
||||||
|
jerry_value_free(result);
|
||||||
|
}
|
||||||
|
jerry_value_free(fn);
|
||||||
|
jerry_value_free(chunk->scriptRef);
|
||||||
|
chunk->scriptRef = MAP_CHUNK_SCRIPT_REF_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(uint8_t i = 0; i < chunk->entityCount; i++) {
|
||||||
|
entityDispose(chunk->entities[i]);
|
||||||
|
}
|
||||||
|
chunk->entityCount = 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "maptypes.h"
|
||||||
|
#include "entity/entitybase.h"
|
||||||
|
#include "error/error.h"
|
||||||
|
#include "script/scriptmanager.h"
|
||||||
|
|
||||||
|
#define MAP_CHUNK_ENTITY_COUNT_MAX 64
|
||||||
|
|
||||||
|
/** Sentinel value meaning no chunk script is loaded. */
|
||||||
|
#define MAP_CHUNK_SCRIPT_REF_NONE ((jerry_value_t)0)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
chunkpos_t position;
|
||||||
|
entityid_t entities[MAP_CHUNK_ENTITY_COUNT_MAX];
|
||||||
|
uint8_t entityCount;
|
||||||
|
jerry_value_t scriptRef;
|
||||||
|
} mapchunk_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads content into a chunk at its current position.
|
||||||
|
*
|
||||||
|
* @param chunk The chunk to load.
|
||||||
|
* @return An error code.
|
||||||
|
*/
|
||||||
|
errorret_t mapChunkLoad(mapchunk_t *chunk);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes all entities owned by the chunk and resets its state.
|
||||||
|
*
|
||||||
|
* @param chunk The chunk to unload.
|
||||||
|
*/
|
||||||
|
void mapChunkUnload(mapchunk_t *chunk);
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "maptypes.h"
|
||||||
|
|
||||||
|
bool_t chunkPosEqual(chunkpos_t a, chunkpos_t b) {
|
||||||
|
return a.x == b.x && a.y == b.y && a.z == b.z;
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
|
||||||
|
typedef int16_t chunkunit_t;
|
||||||
|
typedef int16_t chunkindex_t;
|
||||||
|
typedef int32_t tileunit_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
chunkunit_t x, y, z;
|
||||||
|
} chunkpos_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
tileunit_t x, y, z;
|
||||||
|
} tilepos_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if two chunk positions are equal.
|
||||||
|
*
|
||||||
|
* @param a The first chunk position.
|
||||||
|
* @param b The second chunk position.
|
||||||
|
* @return true if the positions are equal, false otherwise.
|
||||||
|
*/
|
||||||
|
bool_t chunkPosEqual(chunkpos_t a, chunkpos_t b);
|
||||||
+14
-5
@@ -96,12 +96,19 @@ errorret_t sceneRender(void) {
|
|||||||
entityPositionGetTransform(entityId, posComp, model);
|
entityPositionGetTransform(entityId, posComp, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
shader_t *shader = r->shader;
|
|
||||||
if(!shader) continue;
|
|
||||||
|
|
||||||
switch(r->type) {
|
switch(r->type) {
|
||||||
|
case ENTITY_RENDERABLE_TYPE_CALLBACK: {
|
||||||
|
if(!r->callback) break;
|
||||||
|
errorChain(r->callback(
|
||||||
|
entityId, renderComp, view, proj, model, r->user
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case ENTITY_RENDERABLE_TYPE_MATERIAL: {
|
case ENTITY_RENDERABLE_TYPE_MATERIAL: {
|
||||||
if(!r->mesh) continue;
|
shader_t *shader = r->shader;
|
||||||
|
if(!shader) break;
|
||||||
|
if(!r->mesh) break;
|
||||||
if(shaderCurrent != shader) {
|
if(shaderCurrent != shader) {
|
||||||
shaderCurrent = shader;
|
shaderCurrent = shader;
|
||||||
errorChain(shaderBind(shaderCurrent));
|
errorChain(shaderBind(shaderCurrent));
|
||||||
@@ -115,7 +122,9 @@ errorret_t sceneRender(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case ENTITY_RENDERABLE_TYPE_SPRITEBATCH: {
|
case ENTITY_RENDERABLE_TYPE_SPRITEBATCH: {
|
||||||
if(r->spritebatch.spriteCount == 0) continue;
|
if(r->spritebatch.spriteCount == 0) break;
|
||||||
|
shader_t *shader = r->shader;
|
||||||
|
if(!shader) break;
|
||||||
if(shaderCurrent != shader) {
|
if(shaderCurrent != shader) {
|
||||||
shaderCurrent = shader;
|
shaderCurrent = shader;
|
||||||
errorChain(shaderBind(shaderCurrent));
|
errorChain(shaderBind(shaderCurrent));
|
||||||
|
|||||||
@@ -112,6 +112,68 @@ moduleBaseFunction(moduleEntityRenderableSpriteBatchClear) {
|
|||||||
return jerry_undefined();
|
return jerry_undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void jsRenderCallbackFree(void *user) {
|
||||||
|
jerry_value_t *cb = (jerry_value_t *)user;
|
||||||
|
jerry_value_free(*cb);
|
||||||
|
memoryFree(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static errorret_t jsRenderCallbackBridge(
|
||||||
|
const entityid_t entityId,
|
||||||
|
const componentid_t componentId,
|
||||||
|
const mat4 view,
|
||||||
|
const mat4 proj,
|
||||||
|
const mat4 model,
|
||||||
|
void *user
|
||||||
|
) {
|
||||||
|
(void)entityId; (void)componentId;
|
||||||
|
(void)view; (void)proj; (void)model;
|
||||||
|
jerry_value_t *cb = (jerry_value_t *)user;
|
||||||
|
jerry_value_t ret = jerry_call(*cb, jerry_undefined(), NULL, 0);
|
||||||
|
if(jerry_value_is_exception(ret)) {
|
||||||
|
jerry_value_free(ret);
|
||||||
|
errorThrow("Renderable callback threw a JS exception");
|
||||||
|
}
|
||||||
|
jerry_value_free(ret);
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleEntityRenderableSetCallback) {
|
||||||
|
componenthandle_t *h = scriptProtoGetValue(
|
||||||
|
&MODULE_ENTITY_RENDERABLE_PROTO, callInfo->this_value
|
||||||
|
);
|
||||||
|
if(!h) return jerry_undefined();
|
||||||
|
|
||||||
|
entityrenderable_t *r = (entityrenderable_t *)componentGetData(
|
||||||
|
h->eid, h->cid, COMPONENT_TYPE_RENDERABLE
|
||||||
|
);
|
||||||
|
|
||||||
|
if(
|
||||||
|
r->type == ENTITY_RENDERABLE_TYPE_CALLBACK &&
|
||||||
|
r->userFree &&
|
||||||
|
r->user
|
||||||
|
) {
|
||||||
|
r->userFree(r->user);
|
||||||
|
r->user = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(argc >= 1 && jerry_value_is_function(args[0])) {
|
||||||
|
jerry_value_t *cb = (jerry_value_t *)memoryAllocate(sizeof(jerry_value_t));
|
||||||
|
*cb = jerry_value_copy(args[0]);
|
||||||
|
r->type = ENTITY_RENDERABLE_TYPE_CALLBACK;
|
||||||
|
r->callback = jsRenderCallbackBridge;
|
||||||
|
r->userFree = jsRenderCallbackFree;
|
||||||
|
r->user = cb;
|
||||||
|
} else {
|
||||||
|
r->type = ENTITY_RENDERABLE_TYPE_CALLBACK;
|
||||||
|
r->callback = NULL;
|
||||||
|
r->userFree = NULL;
|
||||||
|
r->user = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
static void moduleEntityRENDERABLE(void) {
|
static void moduleEntityRENDERABLE(void) {
|
||||||
scriptProtoInit(
|
scriptProtoInit(
|
||||||
&MODULE_ENTITY_RENDERABLE_PROTO, NULL, sizeof(componenthandle_t), NULL
|
&MODULE_ENTITY_RENDERABLE_PROTO, NULL, sizeof(componenthandle_t), NULL
|
||||||
@@ -127,6 +189,13 @@ static void moduleEntityRENDERABLE(void) {
|
|||||||
scriptProtoDefineFunc(&MODULE_ENTITY_RENDERABLE_PROTO, "clearSprites",
|
scriptProtoDefineFunc(&MODULE_ENTITY_RENDERABLE_PROTO, "clearSprites",
|
||||||
moduleEntityRenderableSpriteBatchClear);
|
moduleEntityRenderableSpriteBatchClear);
|
||||||
|
|
||||||
moduleBaseSetInt("ENTITY_RENDERABLE_TYPE_MATERIAL", ENTITY_RENDERABLE_TYPE_MATERIAL);
|
scriptProtoDefineFunc(&MODULE_ENTITY_RENDERABLE_PROTO, "setCallback",
|
||||||
moduleBaseSetInt("ENTITY_RENDERABLE_TYPE_SPRITEBATCH", ENTITY_RENDERABLE_TYPE_SPRITEBATCH);
|
moduleEntityRenderableSetCallback);
|
||||||
|
|
||||||
|
moduleBaseSetInt("ENTITY_RENDERABLE_TYPE_MATERIAL",
|
||||||
|
ENTITY_RENDERABLE_TYPE_MATERIAL);
|
||||||
|
moduleBaseSetInt("ENTITY_RENDERABLE_TYPE_SPRITEBATCH",
|
||||||
|
ENTITY_RENDERABLE_TYPE_SPRITEBATCH);
|
||||||
|
moduleBaseSetInt("ENTITY_RENDERABLE_TYPE_CALLBACK",
|
||||||
|
ENTITY_RENDERABLE_TYPE_CALLBACK);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "script/module/ui/moduletextbox.h"
|
#include "script/module/ui/moduletextbox.h"
|
||||||
#include "script/module/ui/modulefullbox.h"
|
#include "script/module/ui/modulefullbox.h"
|
||||||
#include "script/module/save/modulesave.h"
|
#include "script/module/save/modulesave.h"
|
||||||
|
#include "script/module/overworld/modulemap.h"
|
||||||
|
|
||||||
static void moduleRegister(void) {
|
static void moduleRegister(void) {
|
||||||
moduleInclude();
|
moduleInclude();
|
||||||
@@ -53,4 +54,5 @@ static void moduleRegister(void) {
|
|||||||
moduleTextbox();
|
moduleTextbox();
|
||||||
moduleFullbox();
|
moduleFullbox();
|
||||||
moduleSave();
|
moduleSave();
|
||||||
|
moduleMap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "overworld/map.h"
|
||||||
|
#include "modulemapchunk.h"
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_MAP_PROTO;
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMapDefaultUpdate) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMapDefaultDispose) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMapDefaultConstructor) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMapLoad) {
|
||||||
|
moduleBaseRequireArgs(1); moduleBaseRequireString(0);
|
||||||
|
|
||||||
|
char_t handle[MAP_HANDLE_MAX];
|
||||||
|
moduleBaseToString(args[0], handle, sizeof(handle));
|
||||||
|
if(handle[0] == '\0') return moduleBaseThrow("Map.load: handle cannot be empty");
|
||||||
|
|
||||||
|
errorret_t err = mapLoad(handle);
|
||||||
|
if(err.code != ERROR_OK) return moduleBaseThrow("Map.load: failed to load map");
|
||||||
|
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMapIsLoaded) {
|
||||||
|
return jerry_boolean(mapIsLoaded());
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMapDispose) {
|
||||||
|
mapDispose();
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMapSetChunkSize) {
|
||||||
|
moduleBaseRequireArgs(3);
|
||||||
|
moduleBaseRequireNumber(0); moduleBaseRequireNumber(1); moduleBaseRequireNumber(2);
|
||||||
|
|
||||||
|
MAP.chunkTileWidth = (uint16_t)moduleBaseArgInt(0);
|
||||||
|
MAP.chunkTileHeight = (uint16_t)moduleBaseArgInt(1);
|
||||||
|
MAP.chunkTileDepth = (uint16_t)moduleBaseArgInt(2);
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMapSetPosition) {
|
||||||
|
moduleBaseRequireArgs(3);
|
||||||
|
moduleBaseRequireNumber(0); moduleBaseRequireNumber(1); moduleBaseRequireNumber(2);
|
||||||
|
|
||||||
|
tilepos_t pos = {
|
||||||
|
.x = (tileunit_t)moduleBaseArgInt(0),
|
||||||
|
.y = (tileunit_t)moduleBaseArgInt(1),
|
||||||
|
.z = (tileunit_t)moduleBaseArgInt(2),
|
||||||
|
};
|
||||||
|
errorret_t err = mapPositionSet(pos);
|
||||||
|
if(err.code != ERROR_OK) return moduleBaseThrow("Map.setPosition: failed");
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleMapReset(void) {
|
||||||
|
if(MAP.scriptRef != MAP_SCRIPT_REF_NONE) {
|
||||||
|
jerry_value_free(MAP.scriptRef);
|
||||||
|
MAP.scriptRef = MAP_SCRIPT_REF_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_t global = jerry_current_realm();
|
||||||
|
jerry_value_t key = jerry_string_sz("module");
|
||||||
|
jerry_value_t undef = jerry_undefined();
|
||||||
|
jerry_object_set(global, key, undef);
|
||||||
|
jerry_value_free(undef);
|
||||||
|
jerry_value_free(key);
|
||||||
|
jerry_value_free(global);
|
||||||
|
}
|
||||||
|
|
||||||
|
static errorret_t moduleMapCall(const char_t *method) {
|
||||||
|
assertStrLenMin(method, 1, "Method name cannot be empty");
|
||||||
|
|
||||||
|
if(MAP.scriptRef == MAP_SCRIPT_REF_NONE) errorOk();
|
||||||
|
|
||||||
|
jerry_value_t key = jerry_string_sz(method);
|
||||||
|
jerry_value_t fn = jerry_object_get(MAP.scriptRef, key);
|
||||||
|
jerry_value_free(key);
|
||||||
|
|
||||||
|
if(!jerry_value_is_function(fn)) {
|
||||||
|
jerry_value_free(fn);
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_t result = jerry_call(fn, MAP.scriptRef, NULL, 0);
|
||||||
|
jerry_value_free(fn);
|
||||||
|
|
||||||
|
if(jerry_value_is_exception(result)) {
|
||||||
|
char_t errMsg[512];
|
||||||
|
moduleBaseExceptionMessage(result, errMsg, sizeof(errMsg));
|
||||||
|
jerry_value_free(result);
|
||||||
|
errorThrow("Map:%s failed: %s", method, errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
jerry_value_free(result);
|
||||||
|
errorOk();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleMap(void) {
|
||||||
|
moduleMapChunk();
|
||||||
|
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_MAP_PROTO,
|
||||||
|
"Map",
|
||||||
|
sizeof(uint8_t),
|
||||||
|
moduleMapDefaultConstructor
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineFunc(&MODULE_MAP_PROTO, "update", moduleMapDefaultUpdate);
|
||||||
|
scriptProtoDefineFunc(&MODULE_MAP_PROTO, "dispose", moduleMapDefaultDispose);
|
||||||
|
|
||||||
|
scriptProtoDefineStaticFunc(&MODULE_MAP_PROTO, "load", moduleMapLoad);
|
||||||
|
scriptProtoDefineStaticFunc(&MODULE_MAP_PROTO, "dispose", moduleMapDispose);
|
||||||
|
scriptProtoDefineStaticFunc(&MODULE_MAP_PROTO, "setChunkSize", moduleMapSetChunkSize);
|
||||||
|
scriptProtoDefineStaticFunc(&MODULE_MAP_PROTO, "setPosition", moduleMapSetPosition);
|
||||||
|
scriptProtoDefineStaticProp(
|
||||||
|
&MODULE_MAP_PROTO, "isLoaded", moduleMapIsLoaded, NULL
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "script/module/modulebase.h"
|
||||||
|
#include "script/scriptproto.h"
|
||||||
|
#include "overworld/mapchunk.h"
|
||||||
|
|
||||||
|
static scriptproto_t MODULE_MAP_CHUNK_PROTO;
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMapChunkDefaultConstructor) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleBaseFunction(moduleMapChunkDefaultDispose) {
|
||||||
|
return jerry_undefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moduleMapChunk(void) {
|
||||||
|
scriptProtoInit(
|
||||||
|
&MODULE_MAP_CHUNK_PROTO,
|
||||||
|
"MapChunk",
|
||||||
|
sizeof(uint8_t),
|
||||||
|
moduleMapChunkDefaultConstructor
|
||||||
|
);
|
||||||
|
|
||||||
|
scriptProtoDefineFunc(
|
||||||
|
&MODULE_MAP_CHUNK_PROTO, "dispose", moduleMapChunkDefaultDispose
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user