2 Commits

Author SHA1 Message Date
YourWishes ca6cbf41c1 Lua test. 2026-04-14 18:50:04 -05:00
YourWishes 2a9667feca Module updating 2026-04-14 16:36:50 -05:00
107 changed files with 2044 additions and 2267 deletions
-16
View File
@@ -53,22 +53,6 @@ jobs:
path: ./git-artifcats/Dusk
if-no-files-found: error
build-vita:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Docker
uses: docker/setup-docker-action@v5
- name: Build Vita
run: ./scripts/build-vita-docker.sh
- name: Upload Vita binary
uses: actions/upload-artifact@v6
with:
name: dusk-vita
path: build-vita/Dusk.vpk
if-no-files-found: error
build-knulli:
runs-on: ubuntu-latest
steps:
+14
View File
@@ -0,0 +1,14 @@
module('entity')
module('entityposition')
module('entitymaterial')
module('glm')
module('color')
-- Position
local posComp = entityAddComponent(ENTITY_ID, COMPONENT_TYPE_POSITION)
entityPositionSetPosition(ENTITY_ID, posComp, vec3(1, 2, 3))
-- Material
local matComp = entityAddComponent(ENTITY_ID, COMPONENT_TYPE_MATERIAL)
local material = entityMaterialGetShaderMaterial(ENTITY_ID, matComp)
material.unlit.color = colorBlue()
+2 -2
View File
@@ -9,8 +9,8 @@ if PSP then
inputBind("down", INPUT_ACTION_DOWN)
inputBind("left", INPUT_ACTION_LEFT)
inputBind("right", INPUT_ACTION_RIGHT)
inputBind("accept", INPUT_ACTION_ACCEPT)
inputBind("cancel", INPUT_ACTION_CANCEL)
inputBind("circle", INPUT_ACTION_CANCEL)
inputBind("cross", INPUT_ACTION_ACCEPT)
inputBind("select", INPUT_ACTION_RAGEQUIT)
inputBind("lstick_up", INPUT_ACTION_UP)
inputBind("lstick_down", INPUT_ACTION_DOWN)
-5
View File
@@ -3,8 +3,3 @@ include(cmake/targets/dolphin.cmake)
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_GAMECUBE
)
# Link libraries
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
# bba
)
-4
View File
@@ -1,7 +1,6 @@
# Find link platform-specific libraries
find_package(SDL2 REQUIRED)
find_package(OpenGL REQUIRED)
# find_package(CURL REQUIRED)
# Setup endianess at compile time to optimize.
include(TestBigEndian)
@@ -23,7 +22,6 @@ target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
OpenGL::GL
GL
m
# CURL::libcurl
)
# Define platform-specific macros.
@@ -40,6 +38,4 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_INPUT_POINTER
DUSK_INPUT_GAMEPAD
DUSK_TIME_DYNAMIC
DUSK_NETWORK_IPV6
THREAD_PTHREAD=1
)
-6
View File
@@ -24,11 +24,6 @@ target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
pspvfpu
pspvram
psphprm
pspnet
pspnet_inet
pspnet_apctl
psphttp
pspssl
)
target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
@@ -44,7 +39,6 @@ target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_OPENGL_LEGACY
DUSK_DISPLAY_WIDTH=480
DUSK_DISPLAY_HEIGHT=272
THREAD_PTHREAD=1
)
# Postbuild, create .pbp file for PSP.
-110
View File
@@ -1,110 +0,0 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
if(NOT DEFINED ENV{VITASDK})
message(FATAL_ERROR "VITASDK environment variable is not set.")
endif()
include("$ENV{VITASDK}/share/vita.cmake" REQUIRED)
set(VITA_APP_NAME "Dusk")
set(VITA_TITLEID "DUSK00001")
set(VITA_VERSION "01.00")
find_package(SDL2 REQUIRED)
# Custom flags for cglm
set(CGLM_SHARED OFF CACHE BOOL "Build cglm shared" FORCE)
set(CGLM_STATIC ON CACHE BOOL "Build cglm static" FORCE)
find_package(cglm REQUIRED)
# Compile lua
include(FetchContent)
FetchContent_Declare(
liblua
URL https://www.lua.org/ftp/lua-5.5.0.tar.gz
)
FetchContent_MakeAvailable(liblua)
set(LUA_SRC_DIR "${liblua_SOURCE_DIR}/src")
set(LUA_C_FILES
lapi.c lauxlib.c lbaselib.c lcode.c lcorolib.c lctype.c ldblib.c ldebug.c
ldo.c ldump.c lfunc.c lgc.c linit.c liolib.c llex.c lmathlib.c lmem.c
loadlib.c lobject.c lopcodes.c loslib.c lparser.c lstate.c lstring.c
lstrlib.c ltable.c ltablib.c ltm.c lundump.c lutf8lib.c lvm.c lzio.c
)
list(TRANSFORM LUA_C_FILES PREPEND "${LUA_SRC_DIR}/")
add_library(liblua STATIC ${LUA_C_FILES})
target_include_directories(liblua PUBLIC "${LUA_SRC_DIR}")
target_compile_definitions(liblua PRIVATE LUA_USE_C89)
add_library(lua::lua ALIAS liblua)
set(Lua_FOUND TRUE CACHE BOOL "Lua found" FORCE)
# Link libraries
target_link_libraries(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
${SDL2_LIBRARIES}
liblua
cglm
SDL2
SDL2main
zip
bz2
z
zstd
crypto
lzma
m
pthread
stdc++
vitaGL
mathneon
vitashark
kubridge_stub
SceAppMgr_stub
SceAudio_stub
SceCtrl_stub
SceCommonDialog_stub
SceDisplay_stub
SceKernelDmacMgr_stub
SceGxm_stub
SceShaccCg_stub
SceSysmodule_stub
ScePower_stub
SceTouch_stub
SceVshBridge_stub
SceIofilemgr_stub
SceShaccCgExt
libtaihen_stub.a
# SceKernel_stub
SceAppUtil_stub
SceHid_stub
SceRtc_stub
)
target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PRIVATE
${SDL2_INCLUDE_DIRS}
)
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
DUSK_SDL2
DUSK_OPENGL
DUSK_VITA
DUSK_INPUT_GAMEPAD
DUSK_PLATFORM_ENDIAN_LITTLE
DUSK_OPENGL_LEGACY
DUSK_DISPLAY_WIDTH=960
DUSK_DISPLAY_HEIGHT=544
)
# Post-build: create SELF from the ELF binary (UNSAFE = homebrew, no signing)
vita_create_self(${DUSK_BINARY_TARGET_NAME}.self ${DUSK_BINARY_TARGET_NAME} UNSAFE)
# Post-build: package SELF + assets into a .vpk installable on the Vita
vita_create_vpk(${DUSK_BINARY_TARGET_NAME}.vpk ${VITA_TITLEID} ${DUSK_BINARY_TARGET_NAME}.self
VERSION ${VITA_VERSION}
NAME ${VITA_APP_NAME}
FILE ${DUSK_ASSETS_ZIP} dusk.dsk
)
-13
View File
@@ -1,13 +0,0 @@
FROM vitasdk/vitasdk:latest
WORKDIR /workdir
# Install vitaGL and its dependencies (vitashark, SceShaccCg) via vdpm
RUN which vdpm
# Install Python (needed for Dusk code generation tools)
RUN apk add --no-cache \
python3 \
py3-pip \
py3-dotenv
VOLUME ["/workdir"]
-3
View File
@@ -1,3 +0,0 @@
#!/bin/bash
docker build -t dusk-vita -f docker/vita/Dockerfile .
docker run --rm -v $(pwd):/workdir dusk-vita /bin/bash -c "./scripts/build-vita.sh"
-13
View File
@@ -1,13 +0,0 @@
#!/bin/bash
if [ -z "$VITASDK" ]; then
echo "VITASDK environment variable is not set. Please set it to the path of your VitaSDK installation."
exit 1
fi
mkdir -p build-vita
cd build-vita
cmake \
-DCMAKE_TOOLCHAIN_FILE=$VITASDK/share/vita.toolchain.cmake \
-DDUSK_TARGET_SYSTEM=vita \
..
make -j$(nproc)
+1 -6
View File
@@ -16,12 +16,7 @@ elseif(DUSK_TARGET_SYSTEM STREQUAL "psp")
add_subdirectory(dusksdl2)
add_subdirectory(duskgl)
elseif(DUSK_TARGET_SYSTEM STREQUAL "vita")
add_subdirectory(duskvita)
add_subdirectory(dusksdl2)
add_subdirectory(duskgl)
elseif(DUSK_TARGET_SYSTEM STREQUAL "wii" OR DUSK_TARGET_SYSTEM STREQUAL "gamecube")
elseif(DUSK_TARGET_SYSTEM STREQUAL "gamecube" OR DUSK_TARGET_SYSTEM STREQUAL "wii")
add_subdirectory(duskdolphin)
endif()
+3 -2
View File
@@ -72,9 +72,10 @@ add_subdirectory(locale)
add_subdirectory(physics)
add_subdirectory(scene)
add_subdirectory(script)
add_subdirectory(system)
add_subdirectory(time)
add_subdirectory(ui)
add_subdirectory(network)
add_subdirectory(util)
# if(DUSK_TARGET_SYSTEM STREQUAL "linux" OR DUSK_TARGET_SYSTEM STREQUAL "psp")
# add_subdirectory(thread)
# endif()
+4 -1
View File
@@ -64,7 +64,7 @@ errorret_t displayInit(void) {
glm_perspective(
glm_rad(45.0f),
SCREEN.aspect,
(float_t)SCREEN.width / (float_t)SCREEN.height,
0.1f,
100.0f,
proj
@@ -101,6 +101,9 @@ errorret_t displayUpdate(void) {
errorChain(sceneRender());
// Render UI
// uiRender();
// Finish up
screenUnbind();
screenRender();
@@ -40,17 +40,6 @@ uint32_t frameBufferGetHeight(const framebuffer_t *framebuffer) {
return frameBufferPlatformGetHeight(framebuffer);
}
float_t frameBufferGetAspect(const framebuffer_t *framebuffer) {
#ifdef frameBufferPlatformGetAspect
return frameBufferPlatformGetAspect(framebuffer);
#endif
uint32_t width = frameBufferGetWidth(framebuffer);
uint32_t height = frameBufferGetHeight(framebuffer);
if(height == 0) return 1.0f; // Avoid divide by zero, just return 1:1 aspect.
return (float_t)width / (float_t)height;
}
void frameBufferClear(const uint8_t flags, const color_t color) {
frameBufferPlatformClear(flags, color);
}
@@ -58,16 +58,6 @@ uint32_t frameBufferGetWidth(const framebuffer_t *framebuffer);
*/
uint32_t frameBufferGetHeight(const framebuffer_t *framebuffer);
/**
* Returns the aspect ratio of the framebuffer. This is ALMOST always just
* the width / height, however some platforms may choose to override this if
* they have stretched styled back buffers, e.g. 640x480 stretched.
*
* @param framebuffer The framebuffer to get the aspect ratio of.
* @return The aspect ratio of the framebuffer.
*/
float_t frameBufferGetAspect(const framebuffer_t *framebuffer);
/**
* Binds the framebuffer for rendering, or the backbuffer if the framebuffer
* provided is NULL.
+4 -4
View File
@@ -52,7 +52,7 @@ errorret_t screenBind() {
// Screen mode backbuffer uses the full display size
SCREEN.width = frameBufferGetWidth(FRAMEBUFFER_BOUND);
SCREEN.height = frameBufferGetHeight(FRAMEBUFFER_BOUND);
SCREEN.aspect = frameBufferGetAspect(FRAMEBUFFER_BOUND);
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
// No needd for a framebuffer.
#ifdef DUSK_DISPLAY_SIZE_DYNAMIC
@@ -100,7 +100,8 @@ errorret_t screenBind() {
int32_t fbWidth, fbHeight;
fbWidth = frameBufferGetWidth(FRAMEBUFFER_BOUND);
fbHeight = frameBufferGetHeight(FRAMEBUFFER_BOUND);
float_t currentAspect = frameBufferGetAspect(FRAMEBUFFER_BOUND);
float_t currentAspect = (float_t)fbWidth / (float_t)fbHeight;
if(currentAspect == SCREEN.aspectRatio.ratio) {
// No need to use framebuffer.
SCREEN.width = fbWidth;
@@ -128,14 +129,13 @@ errorret_t screenBind() {
if(SCREEN.framebufferReady) {
// Is current framebuffer the correct size?
int32_t curFbWidth, curFbHeight;
float_t curFbAspect = frameBufferGetAspect(&SCREEN.framebuffer);
curFbWidth = frameBufferGetWidth(&SCREEN.framebuffer);
curFbHeight = frameBufferGetHeight(&SCREEN.framebuffer);
if(curFbWidth == newFbWidth && curFbHeight == newFbHeight) {
// Correct size, nothing to do.
SCREEN.width = newFbWidth;
SCREEN.height = newFbHeight;
SCREEN.aspect = curFbAspect;
SCREEN.aspect = (float_t)SCREEN.width / (float_t)SCREEN.height;
errorChain(frameBufferBind(&SCREEN.framebuffer));
errorOk();
}
+2 -5
View File
@@ -70,7 +70,9 @@ errorret_t textDraw(
const float_t x,
const float_t y,
const char_t *text,
#if MESH_ENABLE_COLOR
const color_t color,
#endif
const tileset_t *tileset,
texture_t *texture
) {
@@ -81,11 +83,6 @@ errorret_t textDraw(
errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_TEXTURE, texture));
#if MESH_ENABLE_COLOR
#else
errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, color));
#endif
// errorChain(spriteBatchPush(
// // texture,
// posX, posY,
+2
View File
@@ -66,7 +66,9 @@ errorret_t textDraw(
const float_t x,
const float_t y,
const char_t *text,
#if MESH_ENABLE_COLOR
const color_t color,
#endif
const tileset_t *tileset,
texture_t *texture
);
+11 -100
View File
@@ -20,77 +20,14 @@
#include "entity/component/physics/entityphysics.h"
#include "game/game.h"
#include "physics/physicsmanager.h"
#include "network/network.h"
#include "network/networkinfo.h"
#include "system/system.h"
#include "display/mesh/cube.h"
#include "display/mesh/plane.h"
engine_t ENGINE;
entityid_t phBoxEnt;
componentid_t phBoxPhys;
float_t onlineSwapTime = FLT_MAX;
void goOnline();
void goOffline();
void onNetworkConnected(void *user) {
onlineSwapTime = TIME.time + 3.0f;
networkinfo_t info = networkGetInfo();
if(info.type == NETWORK_TYPE_IPV4) {
sceneLog(
"Connected to network with IPv4 address: " NETWORK_INFO_FORMAT_IPV4 "\n",
info.ipv4.ip[0], info.ipv4.ip[1], info.ipv4.ip[2], info.ipv4.ip[3]
);
#ifdef DUSK_NETWORK_IPV6
} else if(info.type == NETWORK_TYPE_IPV6) {
sceneLog(
"Connected to network with IPv6 address: " NETWORK_INFO_FORMAT_IPV6 "\n",
info.ipv6.ip[0], info.ipv6.ip[1], info.ipv6.ip[2], info.ipv6.ip[3],
info.ipv6.ip[4], info.ipv6.ip[5], info.ipv6.ip[6], info.ipv6.ip[7],
info.ipv6.ip[8], info.ipv6.ip[9], info.ipv6.ip[10], info.ipv6.ip[11],
info.ipv6.ip[12], info.ipv6.ip[13], info.ipv6.ip[14], info.ipv6.ip[15]
);
#endif
}
sceneLog("Network connected, I will disconnect at: %.2f1.\n", onlineSwapTime);
}
void onNetworkFailed(errorret_t error, void *user) {
onlineSwapTime = TIME.time + 3.0f;
sceneLog("Failed to connect to network, will try again at %.2f1.\n", onlineSwapTime);
}
void onNetworkDisconnected(errorret_t error, void *user) {
onlineSwapTime = TIME.time + 3.0f;
sceneLog("Network disconnected, will go online at %.2f1.\n", onlineSwapTime);
errorCatch(errorPrint(error));
}
void onNetworkDisconnectFinished(void *user) {
onlineSwapTime = TIME.time + 3.0f;
sceneLog("Finished disconnecting from network, will go online at %.2f1.\n", onlineSwapTime);
}
void goOnline() {
sceneLog("Going online...\n");
networkRequestConnection(
onNetworkConnected,
onNetworkFailed,
onNetworkDisconnected,
NULL
);
}
void goOffline() {
sceneLog("Going offline...\n");
networkRequestDisconnection(onNetworkDisconnectFinished, NULL);
}
/* Kept module-level only because engineUpdate needs them for the reset. */
static entityid_t phBoxEnt;
static componentid_t phBoxPhys;
errorret_t engineInit(const int32_t argc, const char_t **argv) {
memoryZero(&ENGINE, sizeof(engine_t));
@@ -99,7 +36,6 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
ENGINE.argv = argv;
// Init systems. Order is important.
errorChain(systemInit());
timeInit();
errorChain(inputInit());
errorChain(assetInit());
@@ -110,13 +46,9 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
errorChain(sceneInit());
entityManagerInit();
physicsManagerInit();
// errorChain(networkInit());
errorChain(gameInit());
sceneLog("Init done, going to queue online in 3 seconds...\n");
onlineSwapTime = TIME.time + 3.0f;
// Camera
/* ---- Camera ---- */
entityid_t cam = entityManagerAdd();
componentid_t camPos = entityAddComponent(cam, COMPONENT_TYPE_POSITION);
float_t distance = 6.0f;
@@ -127,9 +59,9 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
(vec3){ distance, distance, distance }
);
componentid_t camCam = entityAddComponent(cam, COMPONENT_TYPE_CAMERA);
entityCameraSetZFar(cam, camCam, 100.0f);
entityCameraSetZFar(cam, camCam, distance * 6.0f);
// Floor
/* ---- Static floor (visual + physics) ---- */
entityid_t floorEnt = entityManagerAdd();
componentid_t floorPos = entityAddComponent(floorEnt, COMPONENT_TYPE_POSITION);
componentid_t floorMesh = entityAddComponent(floorEnt, COMPONENT_TYPE_MESH);
@@ -149,16 +81,14 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
floorPhysData->shape.data.plane.normal[2] = 0.0f;
floorPhysData->shape.data.plane.distance = 0.0f;
// Box
// Test Box
phBoxEnt = entityManagerAdd();
componentid_t boxPos = entityAddComponent(phBoxEnt, COMPONENT_TYPE_POSITION);
componentid_t boxMesh = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MESH);
componentid_t boxMat = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MATERIAL);
phBoxPhys = entityAddComponent(phBoxEnt, COMPONENT_TYPE_PHYSICS);
// componentid_t boxMat = entityAddComponent(phBoxEnt, COMPONENT_TYPE_MATERIAL);
entityMeshSetMesh(phBoxEnt, boxMesh, &CUBE_MESH_SIMPLE);
entityMaterialGetShaderMaterial(phBoxEnt, boxMat)->unlit.color = COLOR_RED;
entityPositionSetPosition(phBoxEnt, boxPos, (vec3){ 0.0f, 4.0f, 0.0f });
// entityMaterialGetShaderMaterial(phBoxEnt, boxMat)->unlit.color = COLOR_RED;
componentid_t boxScript = entityAddComponent(phBoxEnt, COMPONENT_TYPE_SCRIPT);
errorChain(entityScriptExecAsset(phBoxEnt, boxScript, "entity/test.lua"));
/* Run the init script. */
scriptcontext_t ctx;
@@ -170,21 +100,12 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
}
errorret_t engineUpdate(void) {
// errorChain(networkUpdate());
timeUpdate();
inputUpdate();
uiUpdate();
errorChain(sceneUpdate());
/* Reset the box to its start position on demand. */
if(inputIsDown(INPUT_ACTION_ACCEPT)) {
componentid_t posComp = entityGetComponent(phBoxEnt, COMPONENT_TYPE_POSITION);
entityPositionSetPosition(phBoxEnt, posComp, (vec3){ 0.0f, 4.0f, 0.0f });
entityPhysicsSetVelocity(phBoxEnt, phBoxPhys, (vec3){ 0.0f, 0.0f, 0.0f });
}
/* Step physics: positions are updated directly on POSITION components. */
physicsManagerUpdate();
@@ -193,15 +114,6 @@ errorret_t engineUpdate(void) {
if(inputPressed(INPUT_ACTION_RAGEQUIT)) ENGINE.running = false;
if(TIME.time >= onlineSwapTime) {
onlineSwapTime = FLT_MAX;
if(NETWORK.state == NETWORK_STATE_CONNECTED) {
goOffline();
} else {
goOnline();
}
}
errorOk();
}
@@ -210,7 +122,6 @@ void engineExit(void) {
}
errorret_t engineDispose(void) {
// errorChain(networkDispose());
sceneDispose();
errorChain(gameDispose());
entityManagerDispose();
+1
View File
@@ -5,3 +5,4 @@
add_subdirectory(display)
add_subdirectory(physics)
add_subdirectory(script)
@@ -55,6 +55,65 @@ void entityCameraSetZFar(
cam->farClip = zFar;
}
entitycameraprojectiontype_t entityCameraGetProjType(
const entityid_t ent,
const componentid_t comp
) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
return cam->projType;
}
void entityCameraSetProjType(
const entityid_t ent,
const componentid_t comp,
const entitycameraprojectiontype_t type
) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
cam->projType = type;
}
float_t entityCameraGetFov(
const entityid_t ent,
const componentid_t comp
) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
return cam->perspective.fov;
}
void entityCameraSetFov(
const entityid_t ent,
const componentid_t comp,
const float_t fov
) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
cam->perspective.fov = fov;
}
void entityCameraSetOrthographic(
const entityid_t ent,
const componentid_t comp,
const float_t left,
const float_t right,
const float_t top,
const float_t bottom
) {
entitycamera_t *cam = (entitycamera_t *)componentGetData(
ent, comp, COMPONENT_TYPE_CAMERA
);
cam->orthographic.left = left;
cam->orthographic.right = right;
cam->orthographic.top = top;
cam->orthographic.bottom = bottom;
}
void entityCameraGetProjection(
const entityid_t ent,
const componentid_t comp,
@@ -97,3 +97,72 @@ void entityCameraSetZFar(
const componentid_t comp,
const float_t zFar
);
/**
* Gets the projection type of a camera.
*
* @param ent The entity ID.
* @param comp The component ID.
* @return The projection type.
*/
entitycameraprojectiontype_t entityCameraGetProjType(
const entityid_t ent,
const componentid_t comp
);
/**
* Sets the projection type of a camera.
*
* @param ent The entity ID.
* @param comp The component ID.
* @param type The projection type.
*/
void entityCameraSetProjType(
const entityid_t ent,
const componentid_t comp,
const entitycameraprojectiontype_t type
);
/**
* Gets the field of view (in radians) of a perspective camera.
*
* @param ent The entity ID.
* @param comp The component ID.
* @return The field of view in radians.
*/
float_t entityCameraGetFov(
const entityid_t ent,
const componentid_t comp
);
/**
* Sets the field of view (in radians) of a perspective camera.
*
* @param ent The entity ID.
* @param comp The component ID.
* @param fov The field of view in radians.
*/
void entityCameraSetFov(
const entityid_t ent,
const componentid_t comp,
const float_t fov
);
/**
* Sets the orthographic projection bounds of a camera.
*
* @param ent The entity ID.
* @param comp The component ID.
* @param left Left bound.
* @param right Right bound.
* @param top Top bound.
* @param bottom Bottom bound.
*/
void entityCameraSetOrthographic(
const entityid_t ent,
const componentid_t comp,
const float_t left,
const float_t right,
const float_t top,
const float_t bottom
);
@@ -100,6 +100,44 @@ bool_t entityPhysicsIsOnGround(
return phys->onGround;
}
physicsbodytype_t entityPhysicsGetBodyType(
const entityid_t entityId,
const componentid_t componentId
) {
entityphysics_t *phys = entityPhysicsGet(entityId, componentId);
assertNotNull(phys, "Failed to get physics component data");
return phys->type;
}
void entityPhysicsSetBodyType(
const entityid_t entityId,
const componentid_t componentId,
const physicsbodytype_t type
) {
entityphysics_t *phys = entityPhysicsGet(entityId, componentId);
assertNotNull(phys, "Failed to get physics component data");
phys->type = type;
}
float_t entityPhysicsGetGravityScale(
const entityid_t entityId,
const componentid_t componentId
) {
entityphysics_t *phys = entityPhysicsGet(entityId, componentId);
assertNotNull(phys, "Failed to get physics component data");
return phys->gravityScale;
}
void entityPhysicsSetGravityScale(
const entityid_t entityId,
const componentid_t componentId,
const float_t scale
) {
entityphysics_t *phys = entityPhysicsGet(entityId, componentId);
assertNotNull(phys, "Failed to get physics component data");
phys->gravityScale = scale;
}
void entityPhysicsDispose(
const entityid_t entityId,
const componentid_t componentId
@@ -122,6 +122,56 @@ bool_t entityPhysicsIsOnGround(
const componentid_t componentId
);
/**
* Gets the body type of the entity's physics body.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @return The body type (static, dynamic, kinematic).
*/
physicsbodytype_t entityPhysicsGetBodyType(
const entityid_t entityId,
const componentid_t componentId
);
/**
* Sets the body type of the entity's physics body.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @param type The new body type.
*/
void entityPhysicsSetBodyType(
const entityid_t entityId,
const componentid_t componentId,
const physicsbodytype_t type
);
/**
* Gets the gravity scale of the entity's physics body.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @return The gravity scale factor.
*/
float_t entityPhysicsGetGravityScale(
const entityid_t entityId,
const componentid_t componentId
);
/**
* Sets the gravity scale of the entity's physics body.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @param scale The new gravity scale factor.
*/
void entityPhysicsSetGravityScale(
const entityid_t entityId,
const componentid_t componentId,
const float_t scale
);
/**
* Releases the body slot back to PHYSICS_WORLD. Called automatically when
* the component is disposed via the component system.
@@ -6,5 +6,5 @@
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
assetvita.c
entityscript.c
)
@@ -0,0 +1,61 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entityscript.h"
#include "entity/component.h"
#include "assert/assert.h"
void entityScriptInit(
const entityid_t entityId,
const componentid_t componentId
) {
entityscript_t *script = (entityscript_t*)componentGetData(
entityId, componentId, COMPONENT_TYPE_SCRIPT
);
scriptContextInit(&script->scriptContext);
// Define script globals.
char_t strScript[64];
snprintf(strScript, sizeof(strScript), "ENTITY_ID = %d\n", entityId);
errorret_t ret = scriptContextExec(&script->scriptContext, strScript);
if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret));
assertUnreachable("Failed to set up script globals");
}
}
errorret_t entityScriptExec(
const entityid_t entityId,
const componentid_t componentId,
const char_t *script
) {
entityscript_t *entityScript = (entityscript_t*)componentGetData(
entityId, componentId, COMPONENT_TYPE_SCRIPT
);
return scriptContextExec(&entityScript->scriptContext, script);
}
errorret_t entityScriptExecAsset(
const entityid_t entityId,
const componentid_t componentId,
const char_t *assetName
) {
entityscript_t *entityScript = (entityscript_t*)componentGetData(
entityId, componentId, COMPONENT_TYPE_SCRIPT
);
return scriptContextExecFile(&entityScript->scriptContext, assetName);
}
void entityScriptDispose(
const entityid_t entityId,
const componentid_t componentId
) {
entityscript_t *script = (entityscript_t*)componentGetData(
entityId, componentId, COMPONENT_TYPE_SCRIPT
);
scriptContextDispose(&script->scriptContext);
}
@@ -0,0 +1,64 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "entity/entitybase.h"
#include "script/scriptcontext.h"
typedef struct {
scriptcontext_t scriptContext;
} entityscript_t;
/**
* Initializes the script entity component.
*
* @param entityId The entity ID.
* @param componentId The component ID.
*/
void entityScriptInit(
const entityid_t entityId,
const componentid_t componentId
);
/**
* Executes a script on the entity's script component.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @param script The script to execute.
* @return The error return value.
*/
errorret_t entityScriptExec(
const entityid_t entityId,
const componentid_t componentId,
const char_t *script
);
/**
* Executes a script from an asset on the entity's script component.
*
* @param entityId The entity ID.
* @param componentId The component ID.
* @param assetName The name of the script asset to execute.
* @return The error return value.
*/
errorret_t entityScriptExecAsset(
const entityid_t entityId,
const componentid_t componentId,
const char_t *assetName
);
/**
* Disposes of the script entity component.
*
* @param entityId The entity ID.
* @param componentId The component ID.
*/
void entityScriptDispose(
const entityid_t entityId,
const componentid_t componentId
);
+2
View File
@@ -10,9 +10,11 @@
#include "entity/component/display/entitymesh.h"
#include "entity/component/display/entitymaterial.h"
#include "entity/component/physics/entityphysics.h"
#include "entity/component/script/entityscript.h"
X(POSITION, entityposition_t, position, entityPositionInit, NULL)
X(CAMERA, entitycamera_t, camera, entityCameraInit, NULL)
X(MESH, entitymesh_t, mesh, entityMeshInit, NULL)
X(MATERIAL, entitymaterial_t, material, entityMaterialInit, NULL)
X(PHYSICS, entityphysics_t, physics, entityPhysicsInit, entityPhysicsDispose)
X(SCRIPT, entityscript_t, script, entityScriptInit, entityScriptDispose)
+2 -2
View File
@@ -8,8 +8,8 @@
#pragma once
#include "dusk.h"
#define ENTITY_COUNT_MAX 6
#define ENTITY_COMPONENT_COUNT_MAX 6
#define ENTITY_COUNT_MAX 64
#define ENTITY_COMPONENT_COUNT_MAX 16
typedef uint8_t entityid_t;
typedef uint8_t componentid_t;
+2 -3
View File
@@ -8,7 +8,6 @@
#include "entitymanager.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "scene/scene.h"
entitymanager_t ENTITY_MANAGER;
@@ -19,8 +18,8 @@ void entityManagerInit(void) {
sizeof(entityid_t) * COMPONENT_TYPE_COUNT * ENTITY_COUNT_MAX
);
sceneLog(
"Entity Manager size: %zu bytes (%.2f KB)\n",
printf(
"Entity Manager size is currently: %zu bytes (%.2f KB)\n",
sizeof(entitymanager_t),
sizeof(entitymanager_t) / 1024.0f
);
-10
View File
@@ -1,10 +0,0 @@
# 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
network.c
networkinfo.c
)
-116
View File
@@ -1,116 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "network.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "log/log.h"
network_t NETWORK;
errorret_t networkInit() {
memoryZero(&NETWORK, sizeof(network_t));
NETWORK.errorState.code = ERROR_OK;
NETWORK.onDisconnect = NULL;
return networkPlatformInit();
}
errorret_t networkUpdate() {
errorChain(networkPlatformUpdate());
if(NETWORK.state == NETWORK_STATE_CONNECTED && !networkIsConnected()) {
NETWORK.state = NETWORK_STATE_DISCONNECTED;
if(NETWORK.onDisconnect) {
errorret_t ret;
if(NETWORK.errorState.code == ERROR_OK) {
ret = errorThrowImpl(
&NETWORK.errorState,
ERROR_NOT_OK,
__FILE__, __func__, __LINE__,
"Network connection lost"
);
} else {
ret.code = NETWORK.errorState.code;
ret.state = &NETWORK.errorState;
}
NETWORK.onDisconnect(ret, NETWORK.disconnectUser);
}
}
errorOk();
}
bool_t networkIsConnected() {
return networkPlatformIsConnected();
}
void networkRequestConnection(
void (*onConnected)(void *user),
void (*onFailed)(errorret_t error, void *user),
void (*onDisconnect)(errorret_t error, void *user),
void *user
) {
assertNotNull(onConnected, "onConnected callback must not be null");
assertNotNull(onFailed, "onFailed callback must not be null");
assertNotNull(onDisconnect, "onDisconnect callback must not be null");
NETWORK.state = NETWORK_STATE_CONNECTING;
NETWORK.onDisconnect = onDisconnect;
NETWORK.disconnectUser = user;
#ifndef networkPlatformRequestConnection
// This is a platform cannot be requested to go online, this would basically
// be for platforms like Linux or Windows where the OS is responsible for
// maintaining the network connection.
if(networkIsConnected()) {
NETWORK.state = NETWORK_STATE_CONNECTED;
onConnected(user);
} else {
errorret_t ret = errorThrowImpl(
&NETWORK.errorState,
ERROR_NOT_OK,
__FILE__, __func__, __LINE__,
"No network connection available"
);
onFailed(ret, user);
}
#else
networkPlatformRequestConnection(onConnected, onFailed, onDisconnect, user);
#endif
}
void networkRequestDisconnection(
void (*onComplete)(void *user),
void *user
) {
assertNotNull(onComplete, "onComplete callback must not be null");
NETWORK.state = NETWORK_STATE_DISCONNECTING;
#ifndef networkPlatformRequestDisconnection
NETWORK.state = NETWORK_STATE_DISCONNECTED;
onComplete(user);
#else
networkPlatformRequestDisconnection(onComplete, user);
#endif
}
void networkDisconnectedDuringDispose(void *u) {
logDebug("Network disconnected during dispose\n");
}
errorret_t networkDispose() {
if(NETWORK.state == NETWORK_STATE_CONNECTED) {
networkRequestDisconnection(networkDisconnectedDuringDispose, NULL);
}
errorChain(networkPlatformDispose());
errorOk();
}
-126
View File
@@ -1,126 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "network/networkplatform.h"
#ifndef networkPlatformInit
#error "networkPlatformInit must be defined"
#endif
#ifndef networkPlatformUpdate
#error "networkPlatformUpdate must be defined"
#endif
#ifndef networkPlatformDispose
#error "networkPlatformDispose must be defined"
#endif
#ifndef networkPlatformIsConnected
#error "networkPlatformIsConnected must be defined"
#endif
typedef enum {
NETWORK_STATE_DISCONNECTED,
NETWORK_STATE_CONNECTING,
NETWORK_STATE_CONNECTED,
NETWORK_STATE_DISCONNECTING,
} networkstate_t;
typedef struct {
networkplatform_t platform;
errorstate_t errorState;
networkstate_t state;
void (*onDisconnect)(errorret_t error, void *user);
void *disconnectUser;
} network_t;
extern network_t NETWORK;
/**
* Initializes the network system. This will NOT connect to the network.
*
* @return An error code indicating success or failure.
*/
errorret_t networkInit();
/**
* Updates the network manager, dispatching any completed async request
* callbacks on the main thread.
*
* @return An error code indicating success or failure.
*/
errorret_t networkUpdate();
/**
* Disposes of the network manager. This will NOT disconnect from the network.
*
* @return An error code indicating success or failure.
*/
errorret_t networkDispose();
/**
* Returns true if the system is connected to AN network, this doesn't mean that
* requests will succeed, doesn't mean there's internet, just that we could
* possibly make network requests. If this is false, this usually means
* something like;
* - A network cable is not connnected
* - No Wi-Fi Connection has been established
* - No IP Address has been assigned
*
* That kinda stuff.
*
* In future I will probably have REASONS for why it's not connected, for
* example;
* - On PSP you need to "request" network access
* - On GameCube, network settings need to be defined, including DHCP, etc.
* - On Windows, this may require additional permissions
*
* @return True if some network connection is detected.
*/
bool_t networkIsConnected();
/**
* See networkIsConnected for a bit more info, but this is for some
* platforms (mainly PSP) to request the system to start doing networking.
*
* You should only call this once and assume that it is "pending" until either
* onComplete or onFailed is invoked. If you call this twice it is undefined
* behavior.
*
* onDisconnect must be provided, and is called whenever the network is lost
* after a successful connection. This will NOT be called if disconnect is
* manually triggered, but WILL if an error occurs, or a network stack bug, etc.
*
* @param onConnected Callback to invoke when the network is connected.
* @param onFailed Callback to invoke if the network connection fails.
* @param onDisconnect Called after a successful connection, when disconnected.
* @param user User data to pass to the callbacks.
*/
void networkRequestConnection(
void (*onConnected)(void *user),
void (*onFailed)(errorret_t error, void *user),
void (*onDisconnect)(errorret_t error, void *user),
void *user
);
/**
* Requests the system to disconnect from the network. This is basically just
* for PSP, but I guess it could be used on other platforms in future if they
* have some kind of "network connection mode" that needs to be exited.
*
* You should only call this once and assume that it is "pending" until
* onComplete is invoked. If you call this twice it is undefined behavior.
*
* If it fails, you still get onComplete called, but may fail if you try to
* reconnect later unfortunately.
*
* @param onComplete Callback to invoke when the network is disconnected.
* @param user User data to pass to the callback.
*/
void networkRequestDisconnection(
void (*onComplete)(void *user),
void *user
);
-24
View File
@@ -1,24 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "network.h"
#include "networkinfo.h"
#include "network/networkplatform.h"
#include "assert/assert.h"
#ifndef networkPlatformGetInfo
#error "networkPlatformGetInfo must be defined"
#endif
networkinfo_t networkGetInfo() {
assertTrue(
NETWORK.state == NETWORK_STATE_CONNECTED,
"networkGetInfo called when not connected"
);
return networkPlatformGetInfo();
}
-57
View File
@@ -1,57 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#define NETWORK_INFO_IPV4_DNS_COUNT_MAX 2
#define NETWORK_INFO_IPV4_OCTET_COUNT 4
#define NETWORK_INFO_IPV6_OCTET_COUNT 16
#define NETWORK_INFO_IPV6_DNS_COUNT_MAX 2
#define NETWORK_INFO_FORMAT_IPV4 "%u.%u.%u.%u"
#define NETWORK_INFO_FORMAT_IPV6 \
"%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x"
typedef enum {
NETWORK_TYPE_IPV4,
#ifdef DUSK_NETWORK_IPV6
NETWORK_TYPE_IPV6,
#endif
} networktype_t;
typedef struct {
uint8_t ip[NETWORK_INFO_IPV4_OCTET_COUNT];
// uint8_t subnet[NETWORK_INFO_IPV4_OCTET_COUNT];
// uint8_t gateway[NETWORK_INFO_IPV4_OCTET_COUNT];
// uint8_t dns[NETWORK_INFO_IPV4_OCTET_COUNT][NETWORK_INFO_IPV4_DNS_COUNT_MAX];
} networkinfoipv4_t;
#ifdef DUSK_NETWORK_IPV6
typedef struct {
uint8_t ip[NETWORK_INFO_IPV6_OCTET_COUNT];
// uint8_t subnet[NETWORK_INFO_IPV6_OCTET_COUNT];
// uint8_t gateway[NETWORK_INFO_IPV6_OCTET_COUNT];
// uint8_t dns[NETWORK_INFO_IPV6_OCTET_COUNT][NETWORK_INFO_IPV6_DNS_COUNT_MAX];
} networkinfoipv6_t;
#endif
typedef struct {
networktype_t type;
union {
networkinfoipv4_t ipv4;
#ifdef DUSK_NETWORK_IPV6
networkinfoipv6_t ipv6;
#endif
};
} networkinfo_t;
/**
* Returns the network information for the currently active network connection.
*
* @return Network information for the currently active network connection.
*/
networkinfo_t networkGetInfo();
-79
View File
@@ -12,59 +12,12 @@
#include "entity/entitymanager.h"
#include "display/shader/shaderunlit.h"
#include "display/mesh/cube.h"
#include "display/spritebatch/spritebatch.h"
#include "display/text/text.h"
#include "display/screen/screen.h"
scene_t SCENE;
char_t SCENE_LOG[SCENE_LOG_SIZE];
void sceneLog(const char *fmt, ...) {
char temp[512];
// 1. Format input like printf
va_list args;
va_start(args, fmt);
vsnprintf(temp, sizeof(temp), fmt, args);
va_end(args);
printf("%s", temp);
// 2. Split into lines
char *lines[64];
int line_count = 0;
char *ptr = temp;
while (*ptr && line_count < 64) {
lines[line_count++] = ptr;
char *nl = strchr(ptr, '\n');
if (!nl) break;
*nl = '\0';
ptr = nl + 1;
}
// 3. Prepend lines in reverse order (so final order is correct)
for (int i = 0; i < line_count; i++) {
char new_log[SCENE_LOG_SIZE];
snprintf(new_log, sizeof(new_log), "%s\n%s", lines[i], SCENE_LOG);
// Copy back safely
strncpy(SCENE_LOG, new_log, SCENE_LOG_SIZE - 1);
SCENE_LOG[SCENE_LOG_SIZE - 1] = '\0';
}
}
errorret_t sceneInit(void) {
memoryZero(&SCENE, sizeof(scene_t));
memoryZero(SCENE_LOG, sizeof(SCENE_LOG));
sceneLog("Init\n");
errorOk();
}
@@ -99,7 +52,6 @@ errorret_t sceneRender(void) {
mat4 view, proj, model;
errorChain(shaderBind(&SHADER_UNLIT));
// For each camera.
for(entityid_t camIndex = 0; camIndex < camCount; camIndex++) {
entityid_t camEnt = camEnts[camIndex];
componentid_t camComp = camComps[camIndex];
@@ -158,37 +110,6 @@ errorret_t sceneRender(void) {
}
}
// Here is where UI will go
glm_ortho(
0.0f, SCREEN.width,
SCREEN.height, 0.0f,
0.1f, 100.0f,
proj
);
glm_lookat(
(vec3){ 0.0f, 0.0f, 1.0f },
(vec3){ 0.0f, 0.0f, 0.0f },
(vec3){ 0.0f, 1.0f, 0.0f },
view
);
glm_mat4_identity(model);
errorChain(shaderBind(&SHADER_UNLIT));
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_PROJECTION, proj));
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_VIEW, view));
errorChain(shaderSetMatrix(&SHADER_UNLIT, SHADER_UNLIT_MODEL, model));
// errorChain(shaderSetTexture(&SHADER_UNLIT, SHADER_UNLIT_0TEXTURE, &DEFAULT_FONT_TEXTURE));
// errorChain(shaderSetColor(&SHADER_UNLIT, SHADER_UNLIT_COLOR, COLOR_WHITE));
errorChain(textDraw(
32, 32,
// "Hello World",
SCENE_LOG,
COLOR_WHITE,
&DEFAULT_FONT_TILESET,
&DEFAULT_FONT_TEXTURE
));
errorChain(spriteBatchFlush());
errorOk();
}
-4
View File
@@ -14,10 +14,6 @@ typedef struct {
extern scene_t SCENE;
#define SCENE_LOG_SIZE 1024
extern char_t SCENE_LOG[SCENE_LOG_SIZE];
void sceneLog(const char *fmt, ...);
/**
* Initialize the scene subsystem.
*
+1
View File
@@ -12,3 +12,4 @@ add_subdirectory(system)
add_subdirectory(scene)
add_subdirectory(time)
add_subdirectory(ui)
add_subdirectory(entity)
@@ -52,7 +52,9 @@ int moduleTextDraw(lua_State *L) {
x,
y,
text,
#if MESH_ENABLE_COLOR
color == NULL ? COLOR_WHITE : *color,
#endif
&DEFAULT_FONT_TILESET,
&DEFAULT_FONT_TEXTURE
);
@@ -6,5 +6,11 @@
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
inputvita.c
moduleentity.c
moduleentityposition.c
moduleentitycamera.c
moduleentitymesh.c
moduleentitymaterial.c
moduleentityphysics.c
moduleentityscript.c
)
@@ -0,0 +1,108 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "moduleentity.h"
#include "assert/assert.h"
#include "entity/entitymanager.h"
#include "entity/entity.h"
#include "entity/component.h"
void moduleEntity(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL");
lua_State *l = ctx->luaState;
// Component type constants
lua_pushnumber(l, COMPONENT_TYPE_POSITION);
lua_setglobal(l, "COMPONENT_TYPE_POSITION");
lua_pushnumber(l, COMPONENT_TYPE_CAMERA);
lua_setglobal(l, "COMPONENT_TYPE_CAMERA");
lua_pushnumber(l, COMPONENT_TYPE_MESH);
lua_setglobal(l, "COMPONENT_TYPE_MESH");
lua_pushnumber(l, COMPONENT_TYPE_MATERIAL);
lua_setglobal(l, "COMPONENT_TYPE_MATERIAL");
lua_pushnumber(l, COMPONENT_TYPE_PHYSICS);
lua_setglobal(l, "COMPONENT_TYPE_PHYSICS");
lua_pushnumber(l, COMPONENT_TYPE_SCRIPT);
lua_setglobal(l, "COMPONENT_TYPE_SCRIPT");
// Entity functions
lua_register(l, "entityCreate", moduleEntityCreate);
lua_register(l, "entityAddComponent", moduleEntityAddComponent);
lua_register(l, "entityGetComponent", moduleEntityGetComponent);
lua_register(l, "entityDispose", moduleEntityDispose);
}
int moduleEntityCreate(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
entityid_t id = entityManagerAdd();
entityInit(id);
lua_pushnumber(L, id);
return 1;
}
int moduleEntityAddComponent(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityAddComponent: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityAddComponent: componentType must be a number");
return 0;
}
entityid_t entityId = (entityid_t)lua_tonumber(L, 1);
componenttype_t type = (componenttype_t)lua_tonumber(L, 2);
componentid_t compId = entityAddComponent(entityId, type);
lua_pushnumber(L, compId);
return 1;
}
int moduleEntityGetComponent(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityGetComponent: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityGetComponent: componentType must be a number");
return 0;
}
entityid_t entityId = (entityid_t)lua_tonumber(L, 1);
componenttype_t type = (componenttype_t)lua_tonumber(L, 2);
componentid_t compId = entityGetComponent(entityId, type);
if(compId == 0xFF) {
lua_pushnil(L);
} else {
lua_pushnumber(L, compId);
}
return 1;
}
int moduleEntityDispose(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityDispose: entityId must be a number");
return 0;
}
entityid_t entityId = (entityid_t)lua_tonumber(L, 1);
entityDispose(entityId);
return 0;
}
@@ -0,0 +1,44 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
/**
* Registers the entity module within the given script context.
* Provides: entityCreate, entityAddComponent, entityGetComponent, entityDispose
* and all COMPONENT_TYPE_* constants.
*
* @param ctx The script context to register the module in.
*/
void moduleEntity(scriptcontext_t *ctx);
/**
* Lua binding: reserves and initializes a new entity.
* Returns: entityId (integer)
*/
int moduleEntityCreate(lua_State *L);
/**
* Lua binding: adds a component of the given type to an entity.
* Args: entityId (integer), componentType (integer)
* Returns: componentId (integer)
*/
int moduleEntityAddComponent(lua_State *L);
/**
* Lua binding: gets the component ID for the given type on an entity.
* Args: entityId (integer), componentType (integer)
* Returns: componentId (integer), or nil if the entity lacks the component
*/
int moduleEntityGetComponent(lua_State *L);
/**
* Lua binding: disposes of the entity with the given ID.
* Args: entityId (integer)
*/
int moduleEntityDispose(lua_State *L);
@@ -0,0 +1,228 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "moduleentitycamera.h"
#include "assert/assert.h"
#include "entity/component/display/entitycamera.h"
void moduleEntityCamera(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL");
lua_State *l = ctx->luaState;
// Projection type constants
lua_pushnumber(l, ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE);
lua_setglobal(l, "ENTITY_CAMERA_PROJECTION_PERSPECTIVE");
lua_pushnumber(l, ENTITY_CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED);
lua_setglobal(l, "ENTITY_CAMERA_PROJECTION_PERSPECTIVE_FLIPPED");
lua_pushnumber(l, ENTITY_CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC);
lua_setglobal(l, "ENTITY_CAMERA_PROJECTION_ORTHOGRAPHIC");
// Camera functions
lua_register(l, "entityCameraGetZNear", moduleEntityCameraGetZNear);
lua_register(l, "entityCameraSetZNear", moduleEntityCameraSetZNear);
lua_register(l, "entityCameraGetZFar", moduleEntityCameraGetZFar);
lua_register(l, "entityCameraSetZFar", moduleEntityCameraSetZFar);
lua_register(l, "entityCameraGetProjType", moduleEntityCameraGetProjType);
lua_register(l, "entityCameraSetProjType", moduleEntityCameraSetProjType);
lua_register(l, "entityCameraGetFov", moduleEntityCameraGetFov);
lua_register(l, "entityCameraSetFov", moduleEntityCameraSetFov);
lua_register(l, "entityCameraSetOrthographic", moduleEntityCameraSetOrthographic);
}
int moduleEntityCameraGetZNear(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraGetZNear: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraGetZNear: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
lua_pushnumber(L, entityCameraGetZNear(eid, cid));
return 1;
}
int moduleEntityCameraSetZNear(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraSetZNear: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraSetZNear: componentId must be a number");
return 0;
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "entityCameraSetZNear: zNear must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
float_t zNear = (float_t)lua_tonumber(L, 3);
entityCameraSetZNear(eid, cid, zNear);
return 0;
}
int moduleEntityCameraGetZFar(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraGetZFar: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraGetZFar: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
lua_pushnumber(L, entityCameraGetZFar(eid, cid));
return 1;
}
int moduleEntityCameraSetZFar(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraSetZFar: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraSetZFar: componentId must be a number");
return 0;
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "entityCameraSetZFar: zFar must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
float_t zFar = (float_t)lua_tonumber(L, 3);
entityCameraSetZFar(eid, cid, zFar);
return 0;
}
int moduleEntityCameraGetProjType(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraGetProjType: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraGetProjType: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
lua_pushnumber(L, entityCameraGetProjType(eid, cid));
return 1;
}
int moduleEntityCameraSetProjType(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraSetProjType: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraSetProjType: componentId must be a number");
return 0;
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "entityCameraSetProjType: projType must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
entitycameraprojectiontype_t type = (entitycameraprojectiontype_t)lua_tonumber(L, 3);
entityCameraSetProjType(eid, cid, type);
return 0;
}
int moduleEntityCameraGetFov(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraGetFov: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraGetFov: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
lua_pushnumber(L, entityCameraGetFov(eid, cid));
return 1;
}
int moduleEntityCameraSetFov(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraSetFov: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraSetFov: componentId must be a number");
return 0;
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "entityCameraSetFov: fov must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
float_t fov = (float_t)lua_tonumber(L, 3);
entityCameraSetFov(eid, cid, fov);
return 0;
}
int moduleEntityCameraSetOrthographic(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityCameraSetOrthographic: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityCameraSetOrthographic: componentId must be a number");
return 0;
}
if(!lua_isnumber(L, 3) || !lua_isnumber(L, 4) ||
!lua_isnumber(L, 5) || !lua_isnumber(L, 6)) {
luaL_error(L, "entityCameraSetOrthographic: left/right/top/bottom must be numbers");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
float_t left = (float_t)lua_tonumber(L, 3);
float_t right = (float_t)lua_tonumber(L, 4);
float_t top = (float_t)lua_tonumber(L, 5);
float_t bottom = (float_t)lua_tonumber(L, 6);
entityCameraSetOrthographic(eid, cid, left, right, top, bottom);
return 0;
}
@@ -0,0 +1,62 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
/**
* Registers the entitycamera module within the given script context.
* Provides get/set functions for znear, zfar, and projection type constants.
*
* @param ctx The script context to register the module in.
*/
void moduleEntityCamera(scriptcontext_t *ctx);
/**
* Lua binding: entityCameraGetZNear(entityId, componentId) -> number
*/
int moduleEntityCameraGetZNear(lua_State *L);
/**
* Lua binding: entityCameraSetZNear(entityId, componentId, number)
*/
int moduleEntityCameraSetZNear(lua_State *L);
/**
* Lua binding: entityCameraGetZFar(entityId, componentId) -> number
*/
int moduleEntityCameraGetZFar(lua_State *L);
/**
* Lua binding: entityCameraSetZFar(entityId, componentId, number)
*/
int moduleEntityCameraSetZFar(lua_State *L);
/**
* Lua binding: entityCameraGetProjType(entityId, componentId) -> number
*/
int moduleEntityCameraGetProjType(lua_State *L);
/**
* Lua binding: entityCameraSetProjType(entityId, componentId, number)
*/
int moduleEntityCameraSetProjType(lua_State *L);
/**
* Lua binding: entityCameraGetFov(entityId, componentId) -> number (radians)
*/
int moduleEntityCameraGetFov(lua_State *L);
/**
* Lua binding: entityCameraSetFov(entityId, componentId, number)
*/
int moduleEntityCameraSetFov(lua_State *L);
/**
* Lua binding: entityCameraSetOrthographic(entityId, componentId, left, right, top, bottom)
*/
int moduleEntityCameraSetOrthographic(lua_State *L);
@@ -0,0 +1,209 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "moduleentitymaterial.h"
#include "assert/assert.h"
#include "entity/component/display/entitymaterial.h"
#include "display/shader/shaderunlit.h"
#include "util/string.h"
void moduleEntityMaterial(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL");
lua_State *l = ctx->luaState;
// shadermaterial_mt: wraps shadermaterial_t* pointer
if(luaL_newmetatable(l, "shadermaterial_mt")) {
lua_pushcfunction(l, moduleEntityMaterialShaderMaterialIndex);
lua_setfield(l, -2, "__index");
}
lua_pop(l, 1);
// shaderunlitmaterial_mt: wraps shaderunlitmaterial_t* pointer
if(luaL_newmetatable(l, "shaderunlitmaterial_mt")) {
lua_pushcfunction(l, moduleEntityMaterialUnlitIndex);
lua_setfield(l, -2, "__index");
lua_pushcfunction(l, moduleEntityMaterialUnlitNewIndex);
lua_setfield(l, -2, "__newindex");
}
lua_pop(l, 1);
lua_register(l, "entityMaterialGetShader", moduleEntityMaterialGetShader);
lua_register(l, "entityMaterialSetShader", moduleEntityMaterialSetShader);
lua_register(l, "entityMaterialGetShaderMaterial", moduleEntityMaterialGetShaderMaterial);
}
int moduleEntityMaterialGetShader(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "Entity ID must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "Component ID must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
shader_t *shader = entityMaterialGetShader(eid, cid);
if(shader == NULL) {
lua_pushnil(L);
} else {
lua_pushlightuserdata(L, shader);
}
return 1;
}
int moduleEntityMaterialSetShader(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "Entity ID must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "Component ID must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
shader_t *shader = NULL;
if(!lua_isnil(L, 3)) {
if(!lua_isuserdata(L, 3)) {
luaL_error(L, "Shader must be a userdata or nil");
return 0;
}
shader = (shader_t *)lua_touserdata(L, 3);
}
entityMaterialSetShader(eid, cid, shader);
return 0;
}
int moduleEntityMaterialGetShaderMaterial(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityMaterialGetShaderMaterial: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityMaterialGetShaderMaterial: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
shadermaterial_t *mat = entityMaterialGetShaderMaterial(eid, cid);
if(mat == NULL) {
lua_pushnil(L);
return 1;
}
shadermaterial_t **pmat = (shadermaterial_t **)lua_newuserdata(
L, sizeof(shadermaterial_t *)
);
*pmat = mat;
luaL_getmetatable(L, "shadermaterial_mt");
lua_setmetatable(L, -2);
return 1;
}
int moduleEntityMaterialShaderMaterialIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
shadermaterial_t *mat = *(shadermaterial_t **)luaL_checkudata(
L, 1, "shadermaterial_mt"
);
assertNotNull(mat, "Shader material pointer cannot be NULL");
const char_t *key = lua_tostring(L, 2);
if(key != NULL && stringCompare(key, "unlit") == 0) {
shaderunlitmaterial_t **punlit = (shaderunlitmaterial_t **)lua_newuserdata(
L, sizeof(shaderunlitmaterial_t *)
);
*punlit = &(mat->unlit);
luaL_getmetatable(L, "shaderunlitmaterial_mt");
lua_setmetatable(L, -2);
return 1;
}
lua_pushnil(L);
return 1;
}
int moduleEntityMaterialUnlitIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
shaderunlitmaterial_t *unlit = *(shaderunlitmaterial_t **)luaL_checkudata(
L, 1, "shaderunlitmaterial_mt"
);
assertNotNull(unlit, "Unlit material pointer cannot be NULL");
const char_t *key = lua_tostring(L, 2);
if(key == NULL) {
lua_pushnil(L);
return 1;
}
if(stringCompare(key, "color") == 0) {
color_t *color = (color_t *)lua_newuserdata(L, sizeof(color_t));
*color = unlit->color;
luaL_getmetatable(L, "color_mt");
lua_setmetatable(L, -2);
return 1;
}
if(stringCompare(key, "texture") == 0) {
if(unlit->texture == NULL) {
lua_pushnil(L);
} else {
lua_pushlightuserdata(L, unlit->texture);
}
return 1;
}
lua_pushnil(L);
return 1;
}
int moduleEntityMaterialUnlitNewIndex(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
shaderunlitmaterial_t *unlit = *(shaderunlitmaterial_t **)luaL_checkudata(
L, 1, "shaderunlitmaterial_mt"
);
assertNotNull(unlit, "Unlit material pointer cannot be NULL");
const char_t *key = lua_tostring(L, 2);
if(key == NULL) return 0;
if(stringCompare(key, "color") == 0) {
color_t *color = (color_t *)luaL_checkudata(L, 3, "color_mt");
unlit->color = *color;
return 0;
}
if(stringCompare(key, "texture") == 0) {
if(lua_isnil(L, 3)) {
unlit->texture = NULL;
} else if(lua_isuserdata(L, 3)) {
unlit->texture = (texture_t *)lua_touserdata(L, 3);
} else {
luaL_error(L, "shaderunlitmaterial texture must be a userdata or nil");
}
return 0;
}
return 0;
}
@@ -0,0 +1,52 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
/**
* Registers the entitymaterial module within the given script context.
* Provides get/set functions for the shader pointer (lightuserdata).
*
* @param ctx The script context to register the module in.
*/
void moduleEntityMaterial(scriptcontext_t *ctx);
/**
* Lua binding: entityMaterialGetShader(entityId, componentId) -> lightuserdata or nil
*/
int moduleEntityMaterialGetShader(lua_State *L);
/**
* Lua binding: entityMaterialSetShader(entityId, componentId, shader_lightuserdata|nil)
*/
int moduleEntityMaterialSetShader(lua_State *L);
/**
* Lua binding: entityMaterialGetShaderMaterial(entityId, componentId)
* Returns a shadermaterial userdata (shadermaterial_mt) holding a pointer
* into the entity's material component, or nil on failure.
*/
int moduleEntityMaterialGetShaderMaterial(lua_State *L);
/**
* __index metamethod for shadermaterial_mt userdata.
* Supports field: "unlit" -> shaderunlitmaterial_mt userdata.
*/
int moduleEntityMaterialShaderMaterialIndex(lua_State *L);
/**
* __index metamethod for shaderunlitmaterial_mt userdata.
* Supports fields: "color" -> color_mt copy, "texture" -> lightuserdata or nil.
*/
int moduleEntityMaterialUnlitIndex(lua_State *L);
/**
* __newindex metamethod for shaderunlitmaterial_mt userdata.
* Supports fields: "color" <- color_mt, "texture" <- lightuserdata or nil.
*/
int moduleEntityMaterialUnlitNewIndex(lua_State *L);
@@ -0,0 +1,70 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "moduleentitymesh.h"
#include "assert/assert.h"
#include "entity/component/display/entitymesh.h"
void moduleEntityMesh(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL");
lua_State *l = ctx->luaState;
lua_register(l, "entityMeshGetMesh", moduleEntityMeshGetMesh);
lua_register(l, "entityMeshSetMesh", moduleEntityMeshSetMesh);
}
int moduleEntityMeshGetMesh(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityMeshGetMesh: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityMeshGetMesh: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
mesh_t *mesh = entityMeshGetMesh(eid, cid);
if(mesh == NULL) {
lua_pushnil(L);
} else {
lua_pushlightuserdata(L, mesh);
}
return 1;
}
int moduleEntityMeshSetMesh(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityMeshSetMesh: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityMeshSetMesh: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
mesh_t *mesh = NULL;
if(!lua_isnil(L, 3)) {
if(!lua_isuserdata(L, 3)) {
luaL_error(L, "entityMeshSetMesh: mesh must be a userdata or nil");
return 0;
}
mesh = (mesh_t *)lua_touserdata(L, 3);
}
entityMeshSetMesh(eid, cid, mesh);
return 0;
}
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
/**
* Registers the entitymesh module within the given script context.
* Provides get/set functions for the mesh pointer (lightuserdata).
*
* @param ctx The script context to register the module in.
*/
void moduleEntityMesh(scriptcontext_t *ctx);
/**
* Lua binding: entityMeshGetMesh(entityId, componentId) -> lightuserdata or nil
*/
int moduleEntityMeshGetMesh(lua_State *L);
/**
* Lua binding: entityMeshSetMesh(entityId, componentId, mesh_lightuserdata|nil)
*/
int moduleEntityMeshSetMesh(lua_State *L);
@@ -0,0 +1,383 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "moduleentityphysics.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "entity/component/physics/entityphysics.h"
void moduleEntityPhysics(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL");
lua_State *l = ctx->luaState;
// Body type constants
lua_pushnumber(l, PHYSICS_BODY_STATIC);
lua_setglobal(l, "PHYSICS_BODY_STATIC");
lua_pushnumber(l, PHYSICS_BODY_DYNAMIC);
lua_setglobal(l, "PHYSICS_BODY_DYNAMIC");
lua_pushnumber(l, PHYSICS_BODY_KINEMATIC);
lua_setglobal(l, "PHYSICS_BODY_KINEMATIC");
// Shape type constants
lua_pushnumber(l, PHYSICS_SHAPE_CUBE);
lua_setglobal(l, "PHYSICS_SHAPE_CUBE");
lua_pushnumber(l, PHYSICS_SHAPE_SPHERE);
lua_setglobal(l, "PHYSICS_SHAPE_SPHERE");
lua_pushnumber(l, PHYSICS_SHAPE_CAPSULE);
lua_setglobal(l, "PHYSICS_SHAPE_CAPSULE");
lua_pushnumber(l, PHYSICS_SHAPE_PLANE);
lua_setglobal(l, "PHYSICS_SHAPE_PLANE");
// Physics shape metatable
if(luaL_newmetatable(l, "physicsshape_mt")) {
lua_pushcfunction(l, moduleEntityPhysicsShapeToString);
lua_setfield(l, -2, "__tostring");
}
lua_pop(l, 1);
// Shape constructors
lua_register(l, "physicsShapeCube", modulePhysicsShapeCube);
lua_register(l, "physicsShapeSphere", modulePhysicsShapeSphere);
lua_register(l, "physicsShapeCapsule", modulePhysicsShapeCapsule);
lua_register(l, "physicsShapePlane", modulePhysicsShapePlane);
// Entity physics functions
lua_register(l, "entityPhysicsGetVelocity", moduleEntityPhysicsGetVelocity);
lua_register(l, "entityPhysicsSetVelocity", moduleEntityPhysicsSetVelocity);
lua_register(l, "entityPhysicsApplyImpulse", moduleEntityPhysicsApplyImpulse);
lua_register(l, "entityPhysicsIsOnGround", moduleEntityPhysicsIsOnGround);
lua_register(l, "entityPhysicsGetShape", moduleEntityPhysicsGetShape);
lua_register(l, "entityPhysicsSetShape", moduleEntityPhysicsSetShape);
lua_register(l, "entityPhysicsGetBodyType", moduleEntityPhysicsGetBodyType);
lua_register(l, "entityPhysicsSetBodyType", moduleEntityPhysicsSetBodyType);
lua_register(l, "entityPhysicsGetGravityScale", moduleEntityPhysicsGetGravityScale);
lua_register(l, "entityPhysicsSetGravityScale", moduleEntityPhysicsSetGravityScale);
}
int moduleEntityPhysicsShapeToString(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
physicsshape_t *shape = (physicsshape_t *)luaL_checkudata(L, 1, "physicsshape_mt");
assertNotNull(shape, "Physics shape pointer cannot be NULL");
const char_t *typeName = "unknown";
switch(shape->type) {
case PHYSICS_SHAPE_CUBE: typeName = "cube"; break;
case PHYSICS_SHAPE_SPHERE: typeName = "sphere"; break;
case PHYSICS_SHAPE_CAPSULE: typeName = "capsule"; break;
case PHYSICS_SHAPE_PLANE: typeName = "plane"; break;
}
lua_pushfstring(L, "physicsshape(%s)", typeName);
return 1;
}
int moduleEntityPhysicsGetVelocity(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsGetVelocity: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsGetVelocity: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)lua_newuserdata(L, sizeof(vec3));
memoryZero(v, sizeof(vec3));
entityPhysicsGetVelocity(eid, cid, *v);
luaL_getmetatable(L, "vec3_mt");
lua_setmetatable(L, -2);
return 1;
}
int moduleEntityPhysicsSetVelocity(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsSetVelocity: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsSetVelocity: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)luaL_checkudata(L, 3, "vec3_mt");
entityPhysicsSetVelocity(eid, cid, *v);
return 0;
}
int moduleEntityPhysicsApplyImpulse(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsApplyImpulse: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsApplyImpulse: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)luaL_checkudata(L, 3, "vec3_mt");
entityPhysicsApplyImpulse(eid, cid, *v);
return 0;
}
int moduleEntityPhysicsIsOnGround(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsIsOnGround: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsIsOnGround: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
lua_pushboolean(L, entityPhysicsIsOnGround(eid, cid) ? 1 : 0);
return 1;
}
int moduleEntityPhysicsSetShape(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsSetShape: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsSetShape: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
physicsshape_t *shape = (physicsshape_t *)luaL_checkudata(L, 3, "physicsshape_mt");
entityPhysicsSetShape(eid, cid, *shape);
return 0;
}
int moduleEntityPhysicsGetShape(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsGetShape: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsGetShape: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
physicsshape_t *shape = (physicsshape_t *)lua_newuserdata(L, sizeof(physicsshape_t));
*shape = entityPhysicsGetShape(eid, cid);
luaL_getmetatable(L, "physicsshape_mt");
lua_setmetatable(L, -2);
return 1;
}
int moduleEntityPhysicsGetBodyType(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsGetBodyType: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsGetBodyType: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
lua_pushnumber(L, entityPhysicsGetBodyType(eid, cid));
return 1;
}
int moduleEntityPhysicsSetBodyType(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsSetBodyType: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsSetBodyType: componentId must be a number");
return 0;
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "entityPhysicsSetBodyType: bodyType must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
physicsbodytype_t type = (physicsbodytype_t)lua_tonumber(L, 3);
entityPhysicsSetBodyType(eid, cid, type);
return 0;
}
int moduleEntityPhysicsGetGravityScale(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsGetGravityScale: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsGetGravityScale: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
lua_pushnumber(L, entityPhysicsGetGravityScale(eid, cid));
return 1;
}
int moduleEntityPhysicsSetGravityScale(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPhysicsSetGravityScale: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPhysicsSetGravityScale: componentId must be a number");
return 0;
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "entityPhysicsSetGravityScale: scale must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
float_t scale = (float_t)lua_tonumber(L, 3);
entityPhysicsSetGravityScale(eid, cid, scale);
return 0;
}
int modulePhysicsShapeCube(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
float_t hx = lua_isnumber(L, 1) ? (float_t)lua_tonumber(L, 1) : 0.5f;
float_t hy = lua_isnumber(L, 2) ? (float_t)lua_tonumber(L, 2) : 0.5f;
float_t hz = lua_isnumber(L, 3) ? (float_t)lua_tonumber(L, 3) : 0.5f;
physicsshape_t *shape = (physicsshape_t *)lua_newuserdata(L, sizeof(physicsshape_t));
memoryZero(shape, sizeof(physicsshape_t));
shape->type = PHYSICS_SHAPE_CUBE;
shape->data.cube.halfExtents[0] = hx;
shape->data.cube.halfExtents[1] = hy;
shape->data.cube.halfExtents[2] = hz;
luaL_getmetatable(L, "physicsshape_mt");
lua_setmetatable(L, -2);
return 1;
}
int modulePhysicsShapeSphere(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "physicsShapeSphere: radius must be a number");
return 0;
}
float_t radius = (float_t)lua_tonumber(L, 1);
physicsshape_t *shape = (physicsshape_t *)lua_newuserdata(L, sizeof(physicsshape_t));
memoryZero(shape, sizeof(physicsshape_t));
shape->type = PHYSICS_SHAPE_SPHERE;
shape->data.sphere.radius = radius;
luaL_getmetatable(L, "physicsshape_mt");
lua_setmetatable(L, -2);
return 1;
}
int modulePhysicsShapeCapsule(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "physicsShapeCapsule: radius must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "physicsShapeCapsule: halfHeight must be a number");
return 0;
}
float_t radius = (float_t)lua_tonumber(L, 1);
float_t halfHeight = (float_t)lua_tonumber(L, 2);
physicsshape_t *shape = (physicsshape_t *)lua_newuserdata(L, sizeof(physicsshape_t));
memoryZero(shape, sizeof(physicsshape_t));
shape->type = PHYSICS_SHAPE_CAPSULE;
shape->data.capsule.radius = radius;
shape->data.capsule.halfHeight = halfHeight;
luaL_getmetatable(L, "physicsshape_mt");
lua_setmetatable(L, -2);
return 1;
}
int modulePhysicsShapePlane(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "physicsShapePlane: normalX must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "physicsShapePlane: normalY must be a number");
return 0;
}
if(!lua_isnumber(L, 3)) {
luaL_error(L, "physicsShapePlane: normalZ must be a number");
return 0;
}
if(!lua_isnumber(L, 4)) {
luaL_error(L, "physicsShapePlane: distance must be a number");
return 0;
}
float_t nx = (float_t)lua_tonumber(L, 1);
float_t ny = (float_t)lua_tonumber(L, 2);
float_t nz = (float_t)lua_tonumber(L, 3);
float_t distance = (float_t)lua_tonumber(L, 4);
physicsshape_t *shape = (physicsshape_t *)lua_newuserdata(L, sizeof(physicsshape_t));
memoryZero(shape, sizeof(physicsshape_t));
shape->type = PHYSICS_SHAPE_PLANE;
shape->data.plane.normal[0] = nx;
shape->data.plane.normal[1] = ny;
shape->data.plane.normal[2] = nz;
shape->data.plane.distance = distance;
luaL_getmetatable(L, "physicsshape_mt");
lua_setmetatable(L, -2);
return 1;
}
@@ -0,0 +1,64 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
/**
* Registers the entityphysics module within the given script context.
* Provides velocity, impulse, onGround, and shape functions, plus body type
* and shape type constants. Shape constructors: physicsShapeCube,
* physicsShapeSphere, physicsShapeCapsule, physicsShapePlane.
*
* @param ctx The script context to register the module in.
*/
void moduleEntityPhysics(scriptcontext_t *ctx);
/** Lua binding: entityPhysicsGetVelocity(entityId, componentId) -> vec3 */
int moduleEntityPhysicsGetVelocity(lua_State *L);
/** Lua binding: entityPhysicsSetVelocity(entityId, componentId, vec3) */
int moduleEntityPhysicsSetVelocity(lua_State *L);
/** Lua binding: entityPhysicsApplyImpulse(entityId, componentId, vec3) */
int moduleEntityPhysicsApplyImpulse(lua_State *L);
/** Lua binding: entityPhysicsIsOnGround(entityId, componentId) -> boolean */
int moduleEntityPhysicsIsOnGround(lua_State *L);
/** Lua binding: entityPhysicsSetShape(entityId, componentId, physicsshape) */
int moduleEntityPhysicsSetShape(lua_State *L);
/** Lua binding: physicsShapeCube(halfX, halfY, halfZ) -> physicsshape */
int modulePhysicsShapeCube(lua_State *L);
/** Lua binding: physicsShapeSphere(radius) -> physicsshape */
int modulePhysicsShapeSphere(lua_State *L);
/** Lua binding: physicsShapeCapsule(radius, halfHeight) -> physicsshape */
int modulePhysicsShapeCapsule(lua_State *L);
/** Lua binding: physicsShapePlane(normalX, normalY, normalZ, distance) -> physicsshape */
int modulePhysicsShapePlane(lua_State *L);
/** Lua binding: entityPhysicsGetShape(entityId, componentId) -> physicsshape */
int moduleEntityPhysicsGetShape(lua_State *L);
/** Lua binding: entityPhysicsGetBodyType(entityId, componentId) -> number */
int moduleEntityPhysicsGetBodyType(lua_State *L);
/** Lua binding: entityPhysicsSetBodyType(entityId, componentId, number) */
int moduleEntityPhysicsSetBodyType(lua_State *L);
/** Lua binding: entityPhysicsGetGravityScale(entityId, componentId) -> number */
int moduleEntityPhysicsGetGravityScale(lua_State *L);
/** Lua binding: entityPhysicsSetGravityScale(entityId, componentId, number) */
int moduleEntityPhysicsSetGravityScale(lua_State *L);
/** Metatable __tostring for physicsshape userdata */
int moduleEntityPhysicsShapeToString(lua_State *L);
@@ -0,0 +1,172 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "moduleentityposition.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "entity/component/display/entityposition.h"
void moduleEntityPosition(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL");
lua_State *l = ctx->luaState;
lua_register(l, "entityPositionGetPosition", moduleEntityPositionGetPosition);
lua_register(l, "entityPositionSetPosition", moduleEntityPositionSetPosition);
lua_register(l, "entityPositionGetRotation", moduleEntityPositionGetRotation);
lua_register(l, "entityPositionSetRotation", moduleEntityPositionSetRotation);
lua_register(l, "entityPositionGetScale", moduleEntityPositionGetScale);
lua_register(l, "entityPositionSetScale", moduleEntityPositionSetScale);
lua_register(l, "entityPositionLookAt", moduleEntityPositionLookAt);
}
int moduleEntityPositionGetPosition(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPositionGetPosition: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPositionGetPosition: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)lua_newuserdata(L, sizeof(vec3));
memoryZero(v, sizeof(vec3));
entityPositionGetPosition(eid, cid, *v);
luaL_getmetatable(L, "vec3_mt");
lua_setmetatable(L, -2);
return 1;
}
int moduleEntityPositionSetPosition(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPositionSetPosition: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPositionSetPosition: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)luaL_checkudata(L, 3, "vec3_mt");
entityPositionSetPosition(eid, cid, *v);
return 0;
}
int moduleEntityPositionGetRotation(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPositionGetRotation: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPositionGetRotation: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)lua_newuserdata(L, sizeof(vec3));
memoryZero(v, sizeof(vec3));
entityPositionGetRotation(eid, cid, *v);
luaL_getmetatable(L, "vec3_mt");
lua_setmetatable(L, -2);
return 1;
}
int moduleEntityPositionSetRotation(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPositionSetRotation: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPositionSetRotation: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)luaL_checkudata(L, 3, "vec3_mt");
entityPositionSetRotation(eid, cid, *v);
return 0;
}
int moduleEntityPositionGetScale(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPositionGetScale: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPositionGetScale: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)lua_newuserdata(L, sizeof(vec3));
memoryZero(v, sizeof(vec3));
entityPositionGetScale(eid, cid, *v);
luaL_getmetatable(L, "vec3_mt");
lua_setmetatable(L, -2);
return 1;
}
int moduleEntityPositionSetScale(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPositionSetScale: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPositionSetScale: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *v = (vec3 *)luaL_checkudata(L, 3, "vec3_mt");
entityPositionSetScale(eid, cid, *v);
return 0;
}
int moduleEntityPositionLookAt(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityPositionLookAt: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityPositionLookAt: componentId must be a number");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
vec3 *target = (vec3 *)luaL_checkudata(L, 3, "vec3_mt");
vec3 *up = (vec3 *)luaL_checkudata(L, 4, "vec3_mt");
vec3 *eye = (vec3 *)luaL_checkudata(L, 5, "vec3_mt");
entityPositionLookAt(eid, cid, *target, *up, *eye);
return 0;
}
@@ -0,0 +1,53 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
/**
* Registers the entityposition module within the given script context.
* Provides get/set functions for position, rotation, scale, and lookAt.
*
* @param ctx The script context to register the module in.
*/
void moduleEntityPosition(scriptcontext_t *ctx);
/**
* Lua binding: entityPositionGetPosition(entityId, componentId) -> vec3
*/
int moduleEntityPositionGetPosition(lua_State *L);
/**
* Lua binding: entityPositionSetPosition(entityId, componentId, vec3)
*/
int moduleEntityPositionSetPosition(lua_State *L);
/**
* Lua binding: entityPositionGetRotation(entityId, componentId) -> vec3 (radians)
*/
int moduleEntityPositionGetRotation(lua_State *L);
/**
* Lua binding: entityPositionSetRotation(entityId, componentId, vec3) (radians)
*/
int moduleEntityPositionSetRotation(lua_State *L);
/**
* Lua binding: entityPositionGetScale(entityId, componentId) -> vec3
*/
int moduleEntityPositionGetScale(lua_State *L);
/**
* Lua binding: entityPositionSetScale(entityId, componentId, vec3)
*/
int moduleEntityPositionSetScale(lua_State *L);
/**
* Lua binding: entityPositionLookAt(entityId, componentId, target, up, eye)
* All vec3 arguments.
*/
int moduleEntityPositionLookAt(lua_State *L);
@@ -0,0 +1,77 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "moduleentityscript.h"
#include "assert/assert.h"
#include "error/error.h"
#include "entity/component/script/entityscript.h"
void moduleEntityScript(scriptcontext_t *ctx) {
assertNotNull(ctx, "Script context cannot be NULL");
lua_State *l = ctx->luaState;
lua_register(l, "entityScriptExec", moduleEntityScriptExec);
lua_register(l, "entityScriptExecAsset", moduleEntityScriptExecAsset);
}
int moduleEntityScriptExec(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityScriptExec: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityScriptExec: componentId must be a number");
return 0;
}
if(!lua_isstring(L, 3)) {
luaL_error(L, "entityScriptExec: script must be a string");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
const char_t *script = lua_tostring(L, 3);
errorret_t ret = entityScriptExec(eid, cid, script);
if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret));
luaL_error(L, "entityScriptExec failed");
return 0;
}
return 0;
}
int moduleEntityScriptExecAsset(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isnumber(L, 1)) {
luaL_error(L, "entityScriptExecAsset: entityId must be a number");
return 0;
}
if(!lua_isnumber(L, 2)) {
luaL_error(L, "entityScriptExecAsset: componentId must be a number");
return 0;
}
if(!lua_isstring(L, 3)) {
luaL_error(L, "entityScriptExecAsset: assetName must be a string");
return 0;
}
entityid_t eid = (entityid_t)lua_tonumber(L, 1);
componentid_t cid = (componentid_t)lua_tonumber(L, 2);
const char_t *assetName = lua_tostring(L, 3);
errorret_t ret = entityScriptExecAsset(eid, cid, assetName);
if(ret.code != ERROR_OK) {
errorCatch(errorPrint(ret));
luaL_error(L, "entityScriptExecAsset failed");
return 0;
}
return 0;
}
@@ -0,0 +1,29 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
/**
* Registers the entityscript module within the given script context.
* Provides functions to execute Lua code on another entity's script component.
*
* @param ctx The script context to register the module in.
*/
void moduleEntityScript(scriptcontext_t *ctx);
/**
* Lua binding: entityScriptExec(entityId, componentId, scriptString)
* Executes an inline Lua string on the target entity's script component.
*/
int moduleEntityScriptExec(lua_State *L);
/**
* Lua binding: entityScriptExecAsset(entityId, componentId, assetName)
* Executes a Lua script file from assets on the target entity's script component.
*/
int moduleEntityScriptExecAsset(lua_State *L);
+14
View File
@@ -22,6 +22,13 @@
#include "script/module/display/modulescreen.h"
#include "script/module/display/moduletexture.h"
#include "script/module/display/moduletileset.h"
#include "script/module/entity/moduleentity.h"
#include "script/module/entity/moduleentityposition.h"
#include "script/module/entity/moduleentitycamera.h"
#include "script/module/entity/moduleentitymesh.h"
#include "script/module/entity/moduleentitymaterial.h"
#include "script/module/entity/moduleentityphysics.h"
#include "script/module/entity/moduleentityscript.h"
#include "script/scriptgame.h"
#include "util/string.h"
@@ -42,6 +49,13 @@ const scriptmodule_t SCRIPT_MODULE_LIST[] = {
{ .name = "texture", .callback = moduleTexture },
{ .name = "tileset", .callback = moduleTileset },
{ .name = "shader", .callback = moduleShader },
{ .name = "entity", .callback = moduleEntity },
{ .name = "entityposition", .callback = moduleEntityPosition },
{ .name = "entitycamera", .callback = moduleEntityCamera },
{ .name = "entitymesh", .callback = moduleEntityMesh },
{ .name = "entitymaterial", .callback = moduleEntityMaterial },
{ .name = "entityphysics", .callback = moduleEntityPhysics },
{ .name = "entityscript", .callback = moduleEntityScript },
#ifdef SCRIPT_GAME_LIST
SCRIPT_GAME_LIST
-9
View File
@@ -1,9 +0,0 @@
# 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
system.c
)
-25
View File
@@ -1,25 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "system.h"
#include "system/systemplatform.h"
#ifndef systemGetActiveDialogTypePlatform
#error "systemGetActiveDialogTypePlatform is not defined"
#endif
#ifndef systemInitPlatform
#error "systemInitPlatform is not defined"
#endif
errorret_t systemInit() {
return systemInitPlatform();
}
systemdialogtype_t systemGetActiveDialogType() {
return systemGetActiveDialogTypePlatform();
}
-38
View File
@@ -1,38 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
typedef enum {
SYSTEM_DIALOG_TYPE_NONE,
SYSTEM_DIALOG_TYPE_RENDER_BLOCKING,
SYSTEM_DIALOG_TYPE_TICK_BLOCKING
} systemdialogtype_t;
/**
* Initializes the system module. This is called really early in the init
* process of the engine.
*
* @return Error code indicating success or failure.
*/
errorret_t systemInit(void);
/**
* Basically this is only used on a few system types, it is to ask the plaform,
* e.g. PSP "What dialog is currently open?" and then the engine will change the
* behavior of the main loop to accomodate.
*
* For example, PSP can show dialogs for things like, save files, wifi, system
* information, etc. When these are open, the engine really can't do much and
* the system needs to finish processing.
*
* For most systems this will go unused.
*
* @return Dialog type currently open.
*/
systemdialogtype_t systemGetActiveDialogType();
-2
View File
@@ -19,5 +19,3 @@ add_subdirectory(asset)
add_subdirectory(log)
add_subdirectory(display)
add_subdirectory(input)
add_subdirectory(network)
add_subdirectory(system)
+1 -1
View File
@@ -76,7 +76,7 @@ errorret_t displayInitDolphin(void) {
);
// Setup cull modes
GX_SetCullMode(GX_CULL_NONE);
GX_SetCullMode(GX_CULL_BACK);
GX_SetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
GX_SetZMode(GX_TRUE, GX_ALWAYS, GX_FALSE);
GX_SetDispCopyGamma(GX_GM_1_0);
@@ -8,7 +8,6 @@
#include "display/framebuffer/framebuffer.h"
#include "display/display.h"
#include "assert/assert.h"
#include "system/systemdolphin.h"
errorret_t frameBufferInitBackBufferDolphin(void) {
errorOk();
@@ -16,27 +15,14 @@ errorret_t frameBufferInitBackBufferDolphin(void) {
uint32_t frameBufferGetWidthDolphin(const framebufferdolphin_t *framebuffer) {
assertNotNull(framebuffer, "Cannot get width of NULL framebuffer.");
return DISPLAY.screenMode->fbWidth;
}
uint32_t frameBufferGetHeightDolphin(const framebufferdolphin_t *framebuffer) {
assertNotNull(framebuffer, "Cannot get height of NULL framebuffer.");
return DISPLAY.screenMode->efbHeight;
}
float_t frameBufferGetAspectDolphin(const framebufferdolphin_t *framebuffer) {
assertNotNull(framebuffer, "Cannot get aspect of NULL framebuffer.");
switch(systemGetAspectRatioDolphin()) {
case CONF_ASPECT_16_9:
return 16.0f / 9.0f;
case CONF_ASPECT_4_3:
return 4.0f / 3.0f;
default:
return (
(float_t)DISPLAY.screenMode->fbWidth /
(float_t)DISPLAY.screenMode->efbHeight
);
}
return DISPLAY.screenMode->efbHeight;
}
errorret_t frameBufferBindDolphin(framebufferdolphin_t *framebuffer) {
@@ -38,15 +38,6 @@ uint32_t frameBufferGetWidthDolphin(const framebufferdolphin_t *framebuffer);
*/
uint32_t frameBufferGetHeightDolphin(const framebufferdolphin_t *framebuffer);
/**
* Gets the aspect ratio of the framebuffer. (Dolphin implementation). Taking
* the Wii aspect setting into consideration.
*
* @param framebuffer The framebuffer to get the aspect ratio of.
* @return The aspect ratio of the framebuffer.
*/
float_t frameBufferGetAspectDolphin(const framebufferdolphin_t *framebuffer);
/**
* Binds the framebuffer for rendering. (Dolphin implementation).
*
@@ -13,6 +13,5 @@ typedef framebufferdolphin_t framebufferplatform_t;
#define frameBufferPlatformInitBackBuffer frameBufferInitBackBufferDolphin
#define frameBufferPlatformGetWidth frameBufferGetWidthDolphin
#define frameBufferPlatformGetHeight frameBufferGetHeightDolphin
#define frameBufferPlatformGetAspect frameBufferGetAspectDolphin
#define frameBufferPlatformBind frameBufferBindDolphin
#define frameBufferPlatformClear frameBufferClearDolphin
@@ -52,7 +52,6 @@ errorret_t meshDrawDolphin(
assertTrue(offsetof(meshvertex_t, pos) == 8, "pos offset wrong");
#endif
// Flush vertex data to GPU. This is required before drawing with GX.
DCFlushRange(
(void*)&mesh->vertices[vertexOffset],
sizeof(meshvertex_t) * vertexCount
+16 -13
View File
@@ -43,10 +43,10 @@ errorret_t shaderBindDolphin(shaderdolphin_t *shader) {
GX_LoadProjectionMtx(
shader->dolphinProj,
shader->matrixProjection,
shader->isProjectionPerspective ? GX_PERSPECTIVE : GX_ORTHOGRAPHIC
);
GX_LoadPosMtxImm(shader->dolphinModelView, GX_PNMTX0);
GX_LoadPosMtxImm(shader->matrixModelView, GX_PNMTX0);
errorOk();
}
@@ -193,13 +193,13 @@ errorret_t shaderUpdateMVPDolphin() {
// Need to update projection?
if((SHADER_BOUND->dirtyMatrix & SHADER_DOLPHIN_DIRTY_PROJ) != 0) {
shaderMat4ToMtx44(SHADER_BOUND->proj, SHADER_BOUND->dolphinProj);
shaderMat4ToMtx44(SHADER_BOUND->proj, SHADER_BOUND->matrixProjection);
// Fix projection Z mapping between GLM and GX.
float A = SHADER_BOUND->dolphinProj[2][2];
float B = SHADER_BOUND->dolphinProj[2][3];
SHADER_BOUND->dolphinProj[2][2] = 0.5f * (A + 1.0f);
SHADER_BOUND->dolphinProj[2][3] = 0.5f * B;
float A = SHADER_BOUND->matrixProjection[2][2];
float B = SHADER_BOUND->matrixProjection[2][3];
SHADER_BOUND->matrixProjection[2][2] = 0.5f * (A + 1.0f);
SHADER_BOUND->matrixProjection[2][3] = 0.5f * B;
// Is this perspective or ortho originally? Dolphin cares for some reason.
const float_t epsilon = 0.0001f;
@@ -209,7 +209,7 @@ errorret_t shaderUpdateMVPDolphin() {
);
GX_LoadProjectionMtx(
SHADER_BOUND->dolphinProj,
SHADER_BOUND->matrixProjection,
SHADER_BOUND->isProjectionPerspective ? GX_PERSPECTIVE : GX_ORTHOGRAPHIC
);
}
@@ -217,18 +217,23 @@ errorret_t shaderUpdateMVPDolphin() {
// Need to update view or model?
bool_t mvDirt = false;
if((SHADER_BOUND->dirtyMatrix & SHADER_DOLPHIN_DIRTY_VIEW) != 0) {
shaderMat4ToMtx(SHADER_BOUND->view, SHADER_BOUND->matrixView);
mvDirt = true;
}
if((SHADER_BOUND->dirtyMatrix & SHADER_DOLPHIN_DIRTY_MODEL) != 0) {
shaderMat4ToMtx(SHADER_BOUND->model, SHADER_BOUND->matrixModel);
mvDirt = true;
}
// Set Model/View Matrix
if(mvDirt) {
glm_mat4_mul(SHADER_BOUND->view, SHADER_BOUND->model, SHADER_BOUND->modelView);
shaderMat4ToMtx(SHADER_BOUND->modelView, SHADER_BOUND->dolphinModelView);
GX_LoadPosMtxImm(SHADER_BOUND->dolphinModelView, GX_PNMTX0);
guMtxConcat(
SHADER_BOUND->matrixView,
SHADER_BOUND->matrixModel,
SHADER_BOUND->matrixModelView
);
GX_LoadPosMtxImm(SHADER_BOUND->matrixModelView, GX_PNMTX0);
}
SHADER_BOUND->dirtyMatrix = 0;
@@ -250,8 +255,6 @@ void shaderMat4ToMtx(const mat4 inGlmMatrix, Mtx outGXMatrix) {
assertNotNull(inGlmMatrix, "Input matrix cannot be null");
assertNotNull(outGXMatrix, "Output matrix cannot be null");
guMtxIdentity(outGXMatrix);
for(int row = 0; row < 3; ++row) {
for(int col = 0; col < 4; ++col) {
outGXMatrix[row][col] = inGlmMatrix[col][row];
@@ -24,12 +24,13 @@ typedef struct shaderdolphin_s {
mat4 view;
mat4 proj;
mat4 model;
mat4 modelView;
Mtx dolphinProj;
Mtx dolphinModelView;
bool_t isProjectionPerspective;
Mtx44 matrixProjection;
Mtx matrixView;
Mtx matrixModel;
Mtx matrixModelView;
uint_fast8_t dirtyMatrix;
} shaderdolphin_t;
-8
View File
@@ -9,11 +9,3 @@
#include <ogcsys.h>
#include <gccore.h>
#include <malloc.h>
#ifdef DUSK_GAMECUBE
#define CONF_ASPECT_4_3 0
#define CONF_ASPECT_16_9 1
#define CONF_GetAspectRatio() CONF_ASPECT_4_3
#define CONF_GetLanguage() SYS_GetLanguage()
#endif
-9
View File
@@ -1,9 +0,0 @@
# 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
networkdolphin.c
)
-118
View File
@@ -1,118 +0,0 @@
// /**
// * Copyright (c) 2026 Dominic Masters
// *
// * This software is released under the MIT License.
// * https://opensource.org/licenses/MIT
// */
#include "network/network.h"
#include "util/memory.h"
#include "assert/assert.h"
errorret_t networkDolphinInit() {
// s32 ret = net_init();
// if(ret < 0) errorThrow("Failed to init network stack: %d", ret);
errorOk();
}
errorret_t networkDolphinUpdate() {
errorOk();
}
errorret_t networkDolphinDispose() {
// #ifdef DUSK_WII
// net_deinit();
// #endif
errorOk();
}
bool_t networkDolphinIsConnected() {
return NETWORK.state == NETWORK_STATE_CONNECTED;
}
void networkDolphinRequestConnection(
void (*onConnected)(void *user),
void (*onFailed)(errorret_t error, void *user),
void (*onDisconnect)(errorret_t error, void *user),
void *user
) {
assertTrue(
NETWORK.state == NETWORK_STATE_CONNECTING,
"Network host should be in a connecting state."
);
NETWORK.platform.onConnected = onConnected;
NETWORK.platform.onFailed = onFailed;
NETWORK.platform.onConnectedUser = user;
memoryZero(NETWORK.platform.ip, sizeof(NETWORK.platform.ip));
memoryZero(NETWORK.platform.netmask, sizeof(NETWORK.platform.netmask));
memoryZero(NETWORK.platform.gateway, sizeof(NETWORK.platform.gateway));
// Negotiate DHCP using the Wi-Fi settings saved in Wii System Menu.
// This call blocks until the interface is configured or times out.
#ifdef DUSK_WII
s32 ret = if_config(
NETWORK.platform.ip,
NETWORK.platform.netmask,
NETWORK.platform.gateway,
true,
20
);
#else
s32 ret = -1;
#endif
if(ret >= 0) {
NETWORK.state = NETWORK_STATE_CONNECTED;
assertNotNull(
NETWORK.platform.onConnected,
"Network platform onConnected callback should be set."
);
NETWORK.platform.onConnected(NETWORK.platform.onConnectedUser);
} else {
NETWORK.state = NETWORK_STATE_DISCONNECTED;
assertNotNull(
NETWORK.platform.onFailed,
"Network platform onFailed callback should be set."
);
errorret_t error = errorThrowImpl(
&NETWORK.errorState,
ERROR_NOT_OK,
__FILE__, __func__, __LINE__,
"Failed to connect to network"
);
NETWORK.platform.onFailed(error, NETWORK.platform.onConnectedUser);
}
}
void networkDolphinRequestDisconnection(
void (*onComplete)(void *user),
void *user
) {
assertTrue(
NETWORK.state == NETWORK_STATE_DISCONNECTING,
"Network host should be in a disconnecting state."
);
NETWORK.state = NETWORK_STATE_DISCONNECTED;
onComplete(user);
}
networkinfo_t networkDolphinGetInfo() {
networkinfo_t info;
memoryZero(&info, sizeof(networkinfo_t));
info.type = NETWORK_TYPE_IPV4;
int ret = sscanf(
NETWORK.platform.ip,
"%hhu.%hhu.%hhu.%hhu",
&info.ipv4.ip[0],
&info.ipv4.ip[1],
&info.ipv4.ip[2],
&info.ipv4.ip[3]
);
assertTrue(ret == 4, "Failed to parse IP address");
return info;
}
-86
View File
@@ -1,86 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "network/networkinfo.h"
#include <network.h>
#define NETWORK_DOLPHIN_IP_MAX 16
typedef struct {
char_t ip[NETWORK_DOLPHIN_IP_MAX];
char_t netmask[NETWORK_DOLPHIN_IP_MAX];
char_t gateway[NETWORK_DOLPHIN_IP_MAX];
void *onConnectedUser;
void (*onConnected)(void *user);
void (*onFailed)(errorret_t error, void *user);
} networkdolphin_t;
/**
* Initializes the Wii network stack via IOS. Does not connect; use
* networkDolphinRequestConnection for that.
*
* @return Error state (if any).
*/
errorret_t networkDolphinInit();
/**
* Called each frame. No-op on Wii since connection is synchronous.
*
* @return Error state (if any).
*/
errorret_t networkDolphinUpdate();
/**
* Disposes the Wii network stack.
*
* @return Error state (if any).
*/
errorret_t networkDolphinDispose();
/**
* Returns true if the Wii is connected to a network.
*
* @return True if connected.
*/
bool_t networkDolphinIsConnected();
/**
* Requests the Wii to connect to the network using the Wi-Fi settings saved
* in the Wii System Menu. Blocks until connected or failed.
*
* @param onConnected Callback on successful connection.
* @param onFailed Callback if connection fails.
* @param onDisconnect Callback when connection is later lost.
* @param user User data passed to all callbacks.
*/
void networkDolphinRequestConnection(
void (*onConnected)(void *user),
void (*onFailed)(errorret_t error, void *user),
void (*onDisconnect)(errorret_t error, void *user),
void *user
);
/**
* Requests the Wii to disconnect from the network.
*
* @param onComplete Callback when disconnection is complete.
* @param user User data passed to the callback.
*/
void networkDolphinRequestDisconnection(
void (*onComplete)(void *user),
void *user
);
/**
* Returns the current network IP information.
*
* @return Network info for the active connection.
*/
networkinfo_t networkDolphinGetInfo();
-19
View File
@@ -1,19 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "networkdolphin.h"
#define networkPlatformInit networkDolphinInit
#define networkPlatformUpdate networkDolphinUpdate
#define networkPlatformDispose networkDolphinDispose
#define networkPlatformIsConnected networkDolphinIsConnected
#define networkPlatformRequestConnection networkDolphinRequestConnection
#define networkPlatformRequestDisconnection networkDolphinRequestDisconnection
#define networkPlatformGetInfo networkDolphinGetInfo
typedef networkdolphin_t networkplatform_t;
-9
View File
@@ -1,9 +0,0 @@
# 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
systemdolphin.c
)
-27
View File
@@ -1,27 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "systemdolphin.h"
#include "input/input.h"
#include "util/string.h"
#include "assert/assert.h"
errorret_t systemInitDolphin(void) {
errorOk();
}
systemdialogtype_t systemGetActiveDialogTypeDolphin(void) {
return SYSTEM_DIALOG_TYPE_NONE;
}
int32_t systemGetAspectRatioDolphin(void) {
return CONF_GetAspectRatio();
}
int32_t systemGetLanguageDolphin(void) {
return CONF_GetLanguage();
}
-51
View File
@@ -1,51 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "system/system.h"
/**
* Initializes the Dolphin system module.
*
* @return Error code indicating success or failure.
*/
errorret_t systemInitDolphin(void);
/**
* Returns which Dolphin system dialog is currently open (if any).
*
* @return Currently open system dialog type.
*/
systemdialogtype_t systemGetActiveDialogTypeDolphin(void);
/**
* Returns either CONF_ASPECT_4_3 or CONF_ASPECT_16_9 depending on the aspect
* ratio of the system. I do believe that Gamecube will only ever return 4:3.
*
* Refer to;
* https://github.com/devkitPro/libogc/blob/20d90e944b83c8991538e88b00b1e5f309428e85/gc/ogc/conf.h#L190
*
* @return Aspect ratio of the system.
*/
int32_t systemGetAspectRatioDolphin(void);
/**
* Returns the language the system is set to. This is used for things like
* locale management, to try to match the system language if possible.
*
* Refer to;
* https://github.com/devkitPro/libogc/blob/20d90e944b83c8991538e88b00b1e5f309428e85/gc/ogc/conf.h#L190
*
* On gamecube, refer to;
* https://libogc.devkitpro.org/system_8h.html
*
* @return System language.
*/
int32_t systemGetLanguageDolphin(void);
// There's actually a tonne more things Wii can return, this is it for now
// though.
-12
View File
@@ -1,12 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "system/systemdolphin.h"
#define systemInitPlatform systemInitDolphin
#define systemGetActiveDialogTypePlatform systemGetActiveDialogTypeDolphin
-5
View File
@@ -19,12 +19,7 @@
#define GL_COLOR_ATTACHMENT0_EXT GL_COLOR_ATTACHMENT0
#define glClearDepth(depth) glClearDepthf(depth)
#else
// For some platforms (Vita) we do not include GL extensions.
#ifndef GL_GLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#else
#include <GL/gl.h>
#endif
#endif
-2
View File
@@ -13,5 +13,3 @@ target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
add_subdirectory(asset)
add_subdirectory(log)
add_subdirectory(input)
add_subdirectory(network)
add_subdirectory(system)
-9
View File
@@ -1,9 +0,0 @@
# 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
networklinux.c
)
-120
View File
@@ -1,120 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "networklinux.h"
#include "util/memory.h"
#include "util/string.h"
#include "assert/assert.h"
errorret_t networkLinuxInit() {
errorOk();
}
errorret_t networkLinuxUpdate() {
errorOk();
}
bool_t networkLinuxIsConnected() {
// Call the OS network stack to check for connectivity.
struct ifaddrs *ifaddr, *ifa;
if(getifaddrs(&ifaddr) == -1) {
return false;
}
// Check if any non loopback interfaces have running flag set.
bool_t connected = false;
for(ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (!ifa->ifa_name || !ifa->ifa_flags) continue;
// Skip loopback (localhost)
if(ifa->ifa_flags & IFF_LOOPBACK) continue;
// Is interface up and running?
if(!(ifa->ifa_flags & IFF_UP)) continue;
if(!(ifa->ifa_flags & IFF_RUNNING)) continue;
connected = true;
break;
}
// Free the linked list of interfaces
freeifaddrs(ifaddr);
return connected;
}
errorret_t networkLinuxDispose() {
errorOk();
}
networkinfo_t networkLinuxGetInfo() {
networkinfo_t info;
memset(&info, 0, sizeof(networkinfo_t));
bool_t found = false;
struct ifaddrs *ifaddr, *ifa;
if(getifaddrs(&ifaddr) == -1) {
assertUnreachable("getifaddrs failed");
}
// Find the first non-loopback interface with an IP address
for(ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (!ifa->ifa_name || !ifa->ifa_flags) continue;
// Skip loopback (localhost)
if(ifa->ifa_flags & IFF_LOOPBACK) continue;
// Is interface up and running?
if(!(ifa->ifa_flags & IFF_UP)) continue;
if(!(ifa->ifa_flags & IFF_RUNNING)) continue;
if(ifa->ifa_addr == NULL) continue;
// Check for IPv4 address
if(ifa->ifa_addr->sa_family == AF_INET) {
info.type = NETWORK_TYPE_IPV4;
struct sockaddr_in *sa = (struct sockaddr_in *)ifa->ifa_addr;
memoryCopy(
info.ipv4.ip,
&sa->sin_addr,
NETWORK_INFO_IPV4_OCTET_COUNT
);
found = true;
// sa = (struct sockaddr_in *)ifa->ifa_netmask;
// memoryCopy(
// info.ipv4.subnet,
// &sa->sin_addr,
// NETWORK_INFO_IPV4_OCTET_COUNT
// );
break;
}
// Check for IPv6 address
#ifdef DUSK_NETWORK_IPV6
if(ifa->ifa_addr->sa_family == AF_INET6) {
info.type = NETWORK_TYPE_IPV6;
struct sockaddr_in6 *sa = (struct sockaddr_in6 *)ifa->ifa_addr;
memoryCopy(
info.ipv6.ip,
&sa->sin6_addr,
NETWORK_INFO_IPV6_OCTET_COUNT
);
found = true;
break;
}
#endif
}
freeifaddrs(ifaddr);
assertTrue(found, "No active network interface found?");
return info;
}
-54
View File
@@ -1,54 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "network/networkinfo.h"
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
typedef struct {
void *nothing;
} networklinux_t;
/**
* Initializes the network manager. Must be called before any other network
* functions.
*
* @return Any error that occurs.
*/
errorret_t networkLinuxInit();
/**
* Updates the network manager, called once per frame to handle completed
* HTTP requests.
*
* @return Any error that occurs.
*/
errorret_t networkLinuxUpdate();
/**
* Returns true — Linux uses the OS network stack which is always available.
*
* @return true
*/
bool_t networkLinuxIsConnected();
/**
* Disposes the network manager.
*
* @return Any error that occurs.
*/
errorret_t networkLinuxDispose();
/**
* Gets the network information for the currently active network connection.
*
* @return Network information for the currently active network connection.
*/
networkinfo_t networkLinuxGetInfo();
-17
View File
@@ -1,17 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "networklinux.h"
#define networkPlatformInit networkLinuxInit
#define networkPlatformUpdate networkLinuxUpdate
#define networkPlatformDispose networkLinuxDispose
#define networkPlatformIsConnected networkLinuxIsConnected
#define networkPlatformGetInfo networkLinuxGetInfo
typedef networklinux_t networkplatform_t;
-9
View File
@@ -1,9 +0,0 @@
# 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
systemlinux.c
)
-16
View File
@@ -1,16 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "systemlinux.h"
errorret_t systemInitLinux() {
errorOk();
}
systemdialogtype_t systemGetActiveDialogTypeLinux() {
return SYSTEM_DIALOG_TYPE_NONE;
}
-21
View File
@@ -1,21 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "system/system.h"
/**
* Initializes the Linux system module.
*/
errorret_t systemInitLinux(void);
/**
* Currently just returns SYSTEM_DIALOG_TYPE_NONE.
*
* @return Currently open system dialog type.
*/
systemdialogtype_t systemGetActiveDialogTypeLinux();
-12
View File
@@ -1,12 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "system/systemlinux.h"
#define systemInitPlatform systemInitLinux
#define systemGetActiveDialogTypePlatform systemGetActiveDialogTypeLinux
-2
View File
@@ -18,5 +18,3 @@ target_sources(${DUSK_BINARY_TARGET_NAME}
add_subdirectory(asset)
add_subdirectory(input)
add_subdirectory(log)
add_subdirectory(network)
add_subdirectory(system)
-7
View File
@@ -7,9 +7,6 @@
#include "input/input.h"
// #define INPUT_PSP_GAMEPAD_BUTTON_ACCEPT INPUT_SDL2_GAMEPAD_BUTTON_CUSTOM
// #define INPUT_PSP_GAMEPAD_BUTTON_CANCEL INPUT_SDL2_GAMEPAD_BUTTON_CUSTOM
inputbuttondata_t INPUT_BUTTON_DATA[] = {
{ .name = "triangle", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_Y } },
{ .name = "cross", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_A } },
@@ -24,10 +21,6 @@ inputbuttondata_t INPUT_BUTTON_DATA[] = {
{ .name = "l", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_LEFTSHOULDER } },
{ .name = "r", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER } },
// Refer to systempsp.c for some extra info.
{ .name = "accept", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_A } },
{ .name = "cancel", { .type = INPUT_BUTTON_TYPE_GAMEPAD, .gpButton = SDL_CONTROLLER_BUTTON_B } },
{ .name = "lstick_down", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = SDL_CONTROLLER_AXIS_LEFTY, .positive = true } } },
{ .name = "lstick_up", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = SDL_CONTROLLER_AXIS_LEFTY, .positive = false } } },
{ .name = "lstick_right", { .type = INPUT_BUTTON_TYPE_GAMEPAD_AXIS, .gpAxis = { .axis = SDL_CONTROLLER_AXIS_LEFTX, .positive = true } } },
-9
View File
@@ -1,9 +0,0 @@
# 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
networkpsp.c
)
-19
View File
@@ -1,19 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "networkpsp.h"
#define networkPlatformInit networkPSPInit
#define networkPlatformUpdate networkPSPUpdate
#define networkPlatformDispose networkPSPDispose
#define networkPlatformIsConnected networkPSPIsConnected
#define networkPlatformRequestConnection networkPSPRequestConnection
#define networkPlatformRequestDisconnection networkPSPRequestDisconnection
#define networkPlatformGetInfo networkPSPGetInfo
typedef networkpsp_t networkplatform_t;
-259
View File
@@ -1,259 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "network/network.h"
#include "util/memory.h"
#include "util/string.h"
#include "assert/assert.h"
#include "display/displaysdl2.h"
errorret_t networkPSPInit() {
// Requests the PSP to load the network modules.
int ret;
ret = sceUtilityLoadNetModule(PSP_NET_MODULE_COMMON);
if(ret < 0) errorThrow("Failed to init NET COMMON: 0x%08X", ret);
ret = sceUtilityLoadNetModule(PSP_NET_MODULE_INET);
if(ret < 0) errorThrow("Failed to init NET INET: 0x%08X", ret);
// ret = sceUtilityLoadNetModule(PSP_NET_MODULE_PARSEURI);
// if(ret < 0) errorThrow("Failed to init NET PARSEURI: 0x%08X", ret);
// ret = sceUtilityLoadNetModule(PSP_NET_MODULE_PARSEHTTP);
// if(ret < 0) errorThrow("Failed to init NET PARSEHTTP: 0x%08X", ret);
// ret = sceUtilityLoadNetModule(PSP_NET_MODULE_SSL);
// if(ret < 0) errorThrow("Failed to init NET SSL: 0x%08X", ret);
// ret = sceUtilityLoadNetModule(PSP_NET_MODULE_HTTP);
// if(ret < 0) errorThrow("Failed to init NET HTTP: 0x%08X", ret);
// ret = sceSslInit(0x28000);
// if(ret < 0) errorThrow("sceSslInit failed: 0x%08X", ret);
// ret = sceHttpInit(0x25800);
// if(ret < 0) errorThrow("sceHttpInit failed: 0x%08X", ret);
// ret = sceHttpsInit(0, 0, 0, 0);
// if(ret < 0) errorThrow("sceHttpsInit failed: 0x%08X", ret);
// ret = sceHttpsLoadDefaultCert(0, 0);
// if(ret < 0) errorThrow("sceHttpsLoadDefaultCert failed: 0x%08X", ret);
// ret = sceHttpLoadSystemCookie();
// if(ret < 0) errorThrow("sceHttpLoadSystemCookie failed: 0x%08X", ret);
// All good.
errorOk();
}
errorret_t networkPSPUpdate() {
int ret;
if(NETWORK.state == NETWORK_STATE_CONNECTING) {
switch(sceUtilityNetconfGetStatus()) {
case PSP_UTILITY_DIALOG_INIT:
break;
case PSP_UTILITY_DIALOG_NONE:
NETWORK.state = NETWORK_STATE_DISCONNECTED;
errorThrow("PSP Netconf dialog disappeared without result");
break;
case PSP_UTILITY_DIALOG_VISIBLE:
// 1 is mandatory?
ret = sceUtilityNetconfUpdate(1);
if(ret != 0) {
errorThrow("sceUtilityNetconfUpdate failed: 0x%08X", ret);
}
break;
case PSP_UTILITY_DIALOG_QUIT:
ret = sceUtilityNetconfShutdownStart();
if(ret != 0) {
errorThrow("sceUtilityNetconfShutdownStart failed: 0x%08X", ret);
}
break;
case PSP_UTILITY_DIALOG_FINISHED:
// Did we connect?
int apState = 0;
sceNetApctlGetState(&apState);
if(apState == PSP_NET_APCTL_STATE_GOT_IP) {
NETWORK.state = NETWORK_STATE_CONNECTED;
assertNotNull(
NETWORK.platform.onConnected,
"Network platform onConnected callback should be set."
);
NETWORK.platform.onConnected(NETWORK.platform.onConnectedUser);
} else {
NETWORK.state = NETWORK_STATE_DISCONNECTED;
assertNotNull(
NETWORK.platform.onFailed,
"Network platform onFailed callback should be set."
);
// Kill the PSP network stack.
errorret_t err = networkPSPTerm();
if(err.code != ERROR_OK) {
errorCatch(errorPrint(err));
}
errorret_t error = errorThrowImpl(
&NETWORK.errorState,
ERROR_NOT_OK,
__FILE__, __func__, __LINE__,
"Failed to connect to network"
);
NETWORK.platform.onFailed(error, NETWORK.platform.onConnectedUser);
}
break;
default:
errorThrow("Unknown PSP Netconf dialog status: %d", sceUtilityNetconfGetStatus());
}
}
errorOk();
}
errorret_t networkPSPDispose() {
sceUtilityNetconfGetStatus();
errorCatch(errorPrint(networkPSPTerm()));
sceUtilityUnloadNetModule(PSP_NET_MODULE_HTTP);
sceUtilityUnloadNetModule(PSP_NET_MODULE_INET);
sceUtilityUnloadNetModule(PSP_NET_MODULE_COMMON);
errorOk();
}
bool_t networkPSPIsConnected() {
return NETWORK.state == NETWORK_STATE_CONNECTED;
}
void networkPSPRequestConnection(
void (*onConnected)(void *user),
void (*onFailed)(errorret_t error, void *user),
void (*onDisconnect)(errorret_t error, void *user),
void *user
) {
assertTrue(
NETWORK.state == NETWORK_STATE_CONNECTING,
"Network host should be in a connecting state."
);
NETWORK.platform.onConnected = onConnected;
NETWORK.platform.onFailed = onFailed;
NETWORK.platform.onConnectedUser = user;
int ret;
// Init the PSP network stack.
ret = sceNetInit(0x20000, 0x20, 4096, 0x20, 4096);
assertTrue(ret >= 0, "Failed to init net: 0x%08X");
ret = sceNetInetInit();
assertTrue(ret >= 0, "Failed to init net inet: 0x%08X");
ret = sceNetResolverInit();
assertTrue(ret >= 0, "Failed to init net resolver: 0x%08X");
ret = sceNetApctlInit(0x1800, 0x30);
assertTrue(ret >= 0, "Failed to init net apctl: 0x%08X");
// This is all related to getting the PSP online, refer to;
// https://github.com/joel16/CMFileManager-PSP/blob/00dab16c64cd48bf6452fc274a3b898d77c39a8d/app/source/net.cpp#L97
// since I follow this implementation closely.
memoryZero(&NETWORK.platform.dialogData, sizeof(NETWORK.platform.dialogData));
memoryZero(&NETWORK.platform.dialogAdhoc, sizeof(NETWORK.platform.dialogAdhoc));
NETWORK.platform.dialogData.base.size = sizeof(pspUtilityNetconfData);
NETWORK.platform.dialogData.base.language = systemPSPGetLanguage();
NETWORK.platform.dialogData.base.buttonSwap = systemPSPGetCrossButtonSetting();
NETWORK.platform.dialogData.base.graphicsThread = 17;
NETWORK.platform.dialogData.base.accessThread = 19;
NETWORK.platform.dialogData.base.fontThread = 18;
NETWORK.platform.dialogData.base.soundThread = 16;
NETWORK.platform.dialogData.action = PSP_NETCONF_ACTION_CONNECTAP;
NETWORK.platform.dialogData.adhocparam = (
&NETWORK.platform.dialogAdhoc
);
ret = sceUtilityNetconfInitStart(&NETWORK.platform.dialogData);
assertTrue(ret >= 0, "Failed to init netconf");
}
void networkPSPRequestDisconnection(
void (*onComplete)(void *user),
void *user
) {
assertTrue(
NETWORK.state == NETWORK_STATE_DISCONNECTING,
"Network host should be in a disconnecting state."
);
errorret_t err = networkPSPTerm();
if(err.code != ERROR_OK) {
errorCatch(errorPrint(err));
}
NETWORK.state = NETWORK_STATE_DISCONNECTED;
onComplete(user);
}
errorret_t networkPSPTerm() {
int ret;
ret = sceNetApctlTerm();
if(ret < 0) {
errorThrow("Failed to terminate netctl: 0x%08X", ret);
}
ret = sceNetResolverTerm();
if(ret < 0) {
errorThrow("Failed to terminate net resolver: 0x%08X", ret);
}
ret = sceNetInetTerm();
if(ret < 0) {
errorThrow("Failed to terminate net inet: 0x%08X", ret);
}
ret = sceNetTerm();
if(ret < 0) {
errorThrow("Failed to terminate net: 0x%08X", ret);
}
errorOk();
}
networkinfo_t networkPSPGetInfo() {
networkinfo_t netInfo;
memoryZero(&netInfo, sizeof(networkinfo_t));
// Get the IP address of the current connection.
union SceNetApctlInfo info;
int ret = sceNetApctlGetInfo(PSP_NET_APCTL_INFO_IP, &info);
assertTrue(ret >= 0, "Failed to get IP address");
// Parse the IP address string into octets.
netInfo.type = NETWORK_TYPE_IPV4;
ret = sscanf(
info.ip,
"%hhu.%hhu.%hhu.%hhu",
&netInfo.ipv4.ip[0],
&netInfo.ipv4.ip[1],
&netInfo.ipv4.ip[2],
&netInfo.ipv4.ip[3]
);
assertTrue(ret == 4, "Failed to parse IP address");
return netInfo;
}
-110
View File
@@ -1,110 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "network/networkinfo.h"
#include "system/systempsp.h"
#include <psphttp.h>
#include <pspnet.h>
#include <pspnet_inet.h>
#include <pspnet_apctl.h>
#include <pspssl.h>
#include <pspnet_resolver.h>
#include <psphttp.h>
// #define NETWORK_HTTP_PENDING_MAX 4
// #define NETWORK_HTTP_URL_MAX 512
// #define NETWORK_HTTP_BODY_MAX 2048
// #define NETWORK_HTTP_RESPONSE_MAX 16384
// #define NETWORK_HTTP_HEADER_MAX 8
// #define NETWORK_HTTP_HEADER_KEY_MAX 64
// #define NETWORK_HTTP_HEADER_VAL_MAX 256
// #define NETWORK_ERROR_MESSAGE_MAX 256
// #define NETWORK_PSP_AGENT "DuskEngine/1.0"
typedef struct {
pspUtilityNetconfData dialogData;
struct pspUtilityNetconfAdhoc dialogAdhoc;
// Used during establishing connection
void *onConnectedUser;
void (*onConnected)(void *user);
void (*onFailed)(errorret_t error, void *user);
} networkpsp_t;
/**
* Initializes the PSP Network manager. This will NOT do network connecting,
* only prep it for being able to connect in future.
*
* @return Error state (if any).
*/
errorret_t networkPSPInit();
/**
* Called each frame for handling PSP requests, basically this is where all
* communication between the HTTP thread and the main thread happens.
*
* @return Error state (if any).
*/
errorret_t networkPSPUpdate();
/**
* Disposes the PSP Network manager, this will clean all resources and, if the
* network is connected, it will disconnect it safely.
*
* @return Error state (if any).
*/
errorret_t networkPSPDispose();
/**
* Checks if the PSP network is connected.
*
* @return True if the PSP is connected to a network, false otherwise.
*/
bool_t networkPSPIsConnected();
/**
* Requests the PSP to connect to a network (Shows the Wi-Fi connected).
*
* @param onConnected Callback connected successfully.
* @param onFailed Callback if the connection failed.
* @param onDisconnect Callback when connection is lost.
* @param user User data to pass to the callbacks.
*/
void networkPSPRequestConnection(
void (*onConnected)(void *user),
void (*onFailed)(errorret_t error, void *user),
void (*onDisconnect)(errorret_t error, void *user),
void *user
);
/**
* Requests the PSP to disconnect from the network (Shows the Wi-Fi disconnecting).
*
* @param onComplete Callback when disconnection is complete.
* @param user User data to pass to the callback.
*/
void networkPSPRequestDisconnection(
void (*onComplete)(void *user),
void *user
);
/**
* Disposes the PSP sce net libraries, doesn't unload the modules and won't
* term the dialog if it's active.
*
* @return Error state (if any).
*/
errorret_t networkPSPTerm();
/**
* Gets the network information for the currently active network connection.
*
* @return Network information for the currently active network connection.
*/
networkinfo_t networkPSPGetInfo();
-9
View File
@@ -1,9 +0,0 @@
# 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
systempsp.c
)
-12
View File
@@ -1,12 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "system/systempsp.h"
#define systemInitPlatform systemInitPSP
#define systemGetActiveDialogTypePlatform systemGetActiveDialogTypePSP
-64
View File
@@ -1,64 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "systempsp.h"
#include "input/input.h"
#include "util/string.h"
#include "assert/assert.h"
errorret_t systemInitPSP() {
// Bind ACCEPT and CANCEL binds.
inputbuttondata_t *buttonCross, *buttonCircle, *buttonAccept, *buttonCancel;
inputbuttondata_t *i = INPUT_BUTTON_DATA;
while(i->name) {
if(stringCompare(i->name, "cross") == 0) {
buttonCross = i;
} else if(stringCompare(i->name, "circle") == 0) {
buttonCircle = i;
} else if(stringCompare(i->name, "accept") == 0) {
buttonAccept = i;
} else if(stringCompare(i->name, "cancel") == 0) {
buttonCancel = i;
}
i++;
}
assertNotNull(buttonCross, "Cross button not found!");
assertNotNull(buttonCircle, "Circle button not found!");
assertNotNull(buttonAccept, "Accept button not found!");
assertNotNull(buttonCancel, "Cancel button not found!");
if(systemPSPGetCrossButtonSetting() == PSP_UTILITY_ACCEPT_CROSS) {
buttonAccept->button.gpButton = buttonCross->button.gpButton;
buttonCancel->button.gpButton = buttonCircle->button.gpButton;
} else {
buttonAccept->button.gpButton = buttonCircle->button.gpButton;
buttonCancel->button.gpButton = buttonCross->button.gpButton;
}
errorOk();
}
systemdialogtype_t systemGetActiveDialogTypePSP() {
return SYSTEM_DIALOG_TYPE_NONE;
}
int systemPSPGetLanguage() {
int ret;
sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_LANGUAGE, &ret);
return ret;
}
int systemPSPGetCrossButtonSetting() {
int ret;
// See: https://pspdev.github.io/pspsdk/psputility__sysparam_8h.html#ab588fd5a14adc025f065e09325ffe729
sceUtilityGetSystemParamInt(PSP_SYSTEMPARAM_ID_INT_UNKNOWN, &ret);
return (
ret == 1 ? PSP_UTILITY_ACCEPT_CROSS : PSP_UTILITY_ACCEPT_CIRCLE
);
}
-40
View File
@@ -1,40 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "system/system.h"
#include <psputility.h>
/**
* Initializes the PSP system module.
*
* @return Error code indicating success or failure.
*/
errorret_t systemInitPSP(void);
/**
* Returns which PSP system dialog is currently open (if any).
*
* @return Currently open system dialog type.
*/
systemdialogtype_t systemGetActiveDialogTypePSP();
/**
* Returns the PSP_SYSTEMPARAM language for the current system.
*
* @return PSP_SYSTEMPARAM language value.
*/
int systemPSPGetLanguage();
/**
* Returns the user's setting for the PSP "Cross Button" configuration, which
* determines whether the "Cross" or "Circle" button is used for "Accept"
* actions in system dialogs.
*
* @return PSP_UTILITY_ACCEPT_CROSS or PSP_UTILITY_ACCEPT_CIRCLE.
*/
int systemPSPGetCrossButtonSetting();
-5
View File
@@ -73,11 +73,6 @@ float_t inputButtonGetValueSDL2(const inputbutton_t button) {
#ifdef DUSK_INPUT_GAMEPAD
case INPUT_BUTTON_TYPE_GAMEPAD: {
assertTrue(
button.gpButton < SDL_CONTROLLER_BUTTON_MAX,
"Gamepad button out of range"
);
if(SDL_GameControllerGetButton(
INPUT.platform.controller, button.gpButton
)) {
-20
View File
@@ -1,20 +0,0 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Includes
target_include_directories(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# Sources
target_sources(${DUSK_BINARY_TARGET_NAME}
PUBLIC
)
# Subdirs
add_subdirectory(asset)
add_subdirectory(input)
add_subdirectory(log)
-13
View File
@@ -1,13 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "assetvita.h"
typedef assetvita_t assetplatform_t;
#define assetInitPlatform assetInitVita
#define assetDisposePlatform assetDisposeVita
-22
View File
@@ -1,22 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "asset/asset.h"
#include "assert/assert.h"
errorret_t assetInitVita(void) {
int32_t error;
ASSET.zip = zip_open(ASSET_VITA_DSK_PATH, ZIP_RDONLY, &error);
if(ASSET.zip == NULL) {
errorThrow("Failed to open asset file: " ASSET_VITA_DSK_PATH);
}
errorOk();
}
errorret_t assetDisposeVita(void) {
errorOk();
}
-30
View File
@@ -1,30 +0,0 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
// dusk.dsk is packaged at the root of the VPK and accessible via app0:/
#define ASSET_VITA_DSK_PATH "app0:/" ASSET_FILE_NAME
typedef struct {
uint8_t _unused;
} assetvita_t;
/**
* Initializes the Vita asset system, opening dusk.dsk from the VPK mount.
*
* @returns An errorret_t indicating success or failure.
*/
errorret_t assetInitVita(void);
/**
* Disposes the Vita asset system.
*
* @returns An errorret_t indicating success or failure.
*/
errorret_t assetDisposeVita(void);

Some files were not shown because too many files have changed in this diff Show More