// quantumde1 developed software, licensed under BSD-0-Clause license. module scripts.lua; import bindbc.lua; import raylib; import variables; import graphics.effects; import std.conv; import system.abstraction; import system.config; import std.string; import graphics.engine; import graphics.playback; import std.file; import std.array; import std.algorithm; /* * This module provides Lua bindings for various engine functionalities. * Functions are built on top of engine built-in functions for execution from scripts. * Not all engine functions usable for scripting are yet implemented. */ string hint; extern (C) nothrow int luaL_showHint(lua_State* L) { hint = "" ~ to!string(luaL_checkstring(L, 1)); hintNeeded = true; return 0; } /* text window */ extern (C) nothrow int luaL_dialogBox(lua_State* L) { showDialog = true; // parse table with text pages luaL_checktype(L, 1, LUA_TTABLE); int textTableLength = cast(int) lua_objlen(L, 1); messageGlobal = new string[](textTableLength); for (int i = 0; i < textTableLength; i++) { lua_rawgeti(L, 1, i + 1); messageGlobal[i] = luaL_checkstring(L, -1).to!string; backlogText ~= messageGlobal[i]; lua_pop(L, 1); } //parse table with choices luaL_checktype(L, 2, LUA_TTABLE); int choicesLength = cast(int) lua_objlen(L, 2); choices = new string[choicesLength]; for (int i = 0; i < choicesLength; i++) { lua_rawgeti(L, 2, i + 1); choices[i] = luaL_checkstring(L, -1).to!string; lua_pop(L, 1); } //if provided, get page on which choices must be shown if (lua_gettop(L) == 3) { choicePage = cast(int)luaL_checkinteger(L, 3); } //if provided, change speed of showing text if (lua_gettop(L) == 4) { typingSpeed = cast(float) luaL_checknumber(L, 7); } return 0; } extern (C) nothrow int luaL_getAnswerValue(lua_State* L) { lua_pushinteger(L, selectedChoice); return 1; } extern (C) nothrow int luaL_isDialogExecuted(lua_State *L) { lua_pushboolean(L, showDialog); return 1; } extern (C) nothrow int luaL_dialogAnswerValue(lua_State* L) { lua_pushinteger(L, selectedChoice); return 1; } /* background drawing and loading */ extern (C) nothrow int luaL_load2Dbackground(lua_State* L) { try { int index = cast(int) luaL_checkinteger(L, 2); //if index too big, extending array if (index >= backgrounds.length) { backgrounds.length = index + 1; } // if texture with same Index already loaded, unloading it if (index < backgrounds.length && backgrounds[index].id != 0) { UnloadTexture(backgrounds[index]); } backgrounds[index] = LoadTexture(luaL_checkstring(L, 1)); } catch (Exception e) { debugWriteln(e.msg); } return 0; } extern (C) nothrow int luaL_draw2Dbackground(lua_State* L) { try { backgroundTexture = backgrounds[luaL_checkinteger(L, 1)]; neededDraw2D = true; } catch (Exception e) { debugWriteln(e.msg); } return 0; } extern (C) nothrow int luaL_stopDraw2Dbackground(lua_State* L) { try { backgroundTexture = Texture2D(); neededDraw2D = true; } catch (Exception e) { debugWriteln(e.msg); } return 0; } extern (C) nothrow int luaL_unload2Dbackground(lua_State* L) { UnloadTexture(backgrounds[cast(int) luaL_checkinteger(L, 1)]); return 0; } /* character textures */ extern (C) nothrow int luaL_load2Dcharacter(lua_State *L) { try { int count = cast(int) luaL_checkinteger(L, 2); if (count >= characterTextures.length) { characterTextures.length = count + 1; } characterTextures[count].texture = LoadTexture(luaL_checkstring(L, 1)); characterTextures[count].width = characterTextures[count].texture.width; characterTextures[count].height = characterTextures[count].texture.height; characterTextures[count].drawTexture = false; } catch (Exception e) { debugWriteln(e.msg); } return 0; } extern (C) nothrow int luaL_draw2Dcharacter(lua_State* L) { try { //configuring needed parameters in characterTextures like coordinates, scale and drawTexture(its a boolean value which checks need this texture to be drawn or not) int count = to!int(luaL_checkinteger(L, 4)); characterTextures[count].scale = luaL_checknumber(L, 3); characterTextures[count].y = cast(int) luaL_checkinteger(L, 2); characterTextures[count].x = cast(int) luaL_checkinteger(L, 1); characterTextures[count].drawTexture = true; debugWriteln("Count: ", count, " drawTexture cond: ", characterTextures[count].drawTexture); } catch (Exception e) { debugWriteln(e.msg); } return 0; } extern (C) nothrow int luaL_stopDraw2Dcharacter(lua_State* L) { int count = cast(int) luaL_checkinteger(L, 1); characterTextures[count].drawTexture = false; return 0; } extern (C) nothrow int luaL_unload2Dcharacter(lua_State *L) { int count = cast(int) luaL_checkinteger(L, 1); UnloadTexture(characterTextures[count].texture); return 0; } /* music and video */ extern (C) nothrow int luaL_loadMusic(lua_State* L) { try { musicPath = cast(char*) luaL_checkstring(L, 1); music = LoadMusicStream(musicPath); } catch (Exception e) { debugWriteln(e.msg); } return 0; } extern (C) nothrow int luaL_playMusic(lua_State* L) { PlayMusicStream(music); return 0; } extern (C) nothrow int luaL_stopMusic(lua_State* L) { StopMusicStream(music); return 0; } extern (C) nothrow int luaL_unloadMusic(lua_State* L) { UnloadMusicStream(music); return 0; } extern (C) nothrow int luaL_playSfx(lua_State *L) { try { playSfx(to!string(luaL_checkstring(L, 1))); } catch (Exception e) { debugWriteln(e.msg); } return 0; } extern (C) nothrow int luaL_stopSfx(lua_State *L) { StopSound(sfx); return 0; } extern (C) nothrow int luaL_playVideo(lua_State* L) { try { videoFinished = false; playVideo(luaL_checkstring(L, 1).to!string); } catch (Exception e) { debugWriteln(e.msg); } return 0; } /* ui animations */ extern (C) nothrow int luaL_moveCamera(lua_State *L) { try { float targetX = cast(float) luaL_checknumber(L, 1); float targetY = cast(float) luaL_checknumber(L, 2); float zoom = cast(float) luaL_optnumber(L, 3, 1.0f); float speed = cast(float) luaL_optnumber(L, 4, 5.0f); cameraTargetX = targetX; cameraTargetY = targetY; cameraTargetZoom = zoom; cameraMoveSpeed = speed; isCameraMoving = true; } catch (Exception e) { debugWriteln(e.msg); } return 0; } extern (C) nothrow int luaL_isCameraMoving(lua_State *L) { lua_pushboolean(L, isCameraMoving); return 1; } extern (C) nothrow int luaL_loadUIAnimation(lua_State *L) { try { //loads from uifx folder HPFF files, in which png textures are stored framesUI = loadAnimationFramesUI(to!string(luaL_checkstring(L, 1)), to!string(luaL_checkstring(L, 2))); if (lua_gettop(L) == 3) { frameDuration = luaL_checknumber(L, 3); debug debugWriteln("frameDuration: ", frameDuration); } } catch (Exception e) { debugWriteln(e.msg); } return 0; } extern (C) nothrow int luaL_playUIAnimation(lua_State *L) { debug debugWriteln("Animation UI start"); try { playAnimation = true; } catch (Exception e) { debugWriteln(e.msg); } return 0; } extern (C) nothrow int luaL_stopUIAnimation(lua_State *L) { playAnimation = false; debug debugWriteln("Animation UI stop"); frameDuration = 0.016f; currentFrame = 0; return 0; } extern (C) nothrow int luaL_unloadUIAnimation(lua_State *L) { try { for (int i = 0; i < framesUI.length; i++) { UnloadTexture(framesUI[i]); } } catch (Exception e) { debugWriteln(e.msg); } return 0; } /* system */ extern (C) nothrow int luaL_getScreenWidth(lua_State* L) { lua_pushinteger(L, GetScreenWidth()); return 1; } extern (C) nothrow int luaL_getScreenHeight(lua_State* L) { lua_pushinteger(L, GetScreenHeight()); return 1; } extern (C) nothrow int luaL_getUsedLanguage(lua_State* L) { lua_pushstring(L, usedLang.toStringz()); return 1; } extern (C) nothrow int luaL_2dModeEnable(lua_State* L) { neededDraw2D = true; return 0; } extern (C) nothrow int luaL_2dModeDisable(lua_State* L) { neededDraw2D = false; return 0; } extern (C) nothrow int luaL_setGameFont(lua_State* L) { const char* x = luaL_checkstring(L, 1); debugWriteln("Setting custom font: ", x.to!string); int[512] codepoints = 0; //configuring both cyrillic and latin fonts if available foreach (i; 0 .. 95) { codepoints[i] = 32 + i; } foreach (i; 0 .. 255) { codepoints[96 + i] = 0x400 + i; } textFont = LoadFontEx(x, 40, codepoints.ptr, codepoints.length); return 0; } extern (C) nothrow int luaL_getTime(lua_State* L) { //getTime() returns current time. lua_pushnumber(L, GetTime()); return 1; } extern (C) nothrow int luaL_isKeyPressed(lua_State* L) { try { int keyCode = cast(int)luaL_checkinteger(L, 1); lua_pushboolean(L, IsKeyPressed(keyCode).to!bool); return 1; } catch (Exception e) { debugWriteln(e.msg); return 1; } } extern (C) nothrow int luaL_loadScript(lua_State* L) { for (int i = cast(int)characterTextures.length; i < characterTextures.length; i++) { UnloadTexture(characterTextures[i].texture); } for (int i = cast(int)backgrounds.length; i < backgrounds.length; i++) { UnloadTexture(backgrounds[i]); } try { luaExec = luaL_checkstring(L, 1).to!string; resetAllScriptValues(); } catch (Exception e) { debugWriteln(e.msg); } luaReload = true; return 0; } extern (C) nothrow int luaL_setGameState(lua_State *L) { currentGameState = cast(int)luaL_checkinteger(L, 1); return 0; } /* raylib direct bindings for graphics */ /* basic */ extern (C) nothrow int luaL_loadTexture(lua_State *L) { const char* fileName = luaL_checkstring(L, 1); Texture2D texture = LoadTexture(fileName); Texture2D* texturePtr = cast(Texture2D*)lua_newuserdata(L, Texture2D.sizeof); *texturePtr = texture; if (luaL_newmetatable(L, "Texture")) { lua_pushcfunction(L, &luaL_textureGC); lua_setfield(L, -2, "__gc"); } lua_setmetatable(L, -2); return 1; } extern (C) nothrow int luaL_textureGC(lua_State *L) { Texture2D* texture = cast(Texture2D*)luaL_checkudata(L, 1, "Texture"); UnloadTexture(*texture); return 0; } extern (C) nothrow int luaL_drawTexture(lua_State *L) { Texture2D* texture = cast(Texture2D*)luaL_checkudata(L, 1, "Texture"); int x = cast(int)luaL_checkinteger(L, 2); int y = cast(int)luaL_checkinteger(L, 3); Color color = Colors.WHITE; if (lua_gettop(L) >= 4 && lua_istable(L, 4)) { lua_getfield(L, 4, "r"); color.r = cast(ubyte)lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, 4, "g"); color.g = cast(ubyte)lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, 4, "b"); color.b = cast(ubyte)lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, 4, "a"); color.a = cast(ubyte)lua_tointeger(L, -1); lua_pop(L, 1); } DrawTexture(*texture, x, y, color); return 0; } extern (C) nothrow int luaL_getTextureWidth(lua_State *L) { Texture2D* texture = cast(Texture2D*)luaL_checkudata(L, 1, "Texture"); lua_pushinteger(L, texture.width); return 1; } extern (C) nothrow int luaL_getTextureHeight(lua_State *L) { Texture2D* texture = cast(Texture2D*)luaL_checkudata(L, 1, "Texture"); lua_pushinteger(L, texture.height); return 1; } extern (C) nothrow int luaL_drawText(lua_State *L) { const char* text = luaL_checkstring(L, 1); int x = cast(int)luaL_checkinteger(L, 2); int y = cast(int)luaL_checkinteger(L, 3); int fontSize = cast(int)luaL_optinteger(L, 4, 20); Color color = Colors.WHITE; if (lua_istable(L, 5)) { lua_getfield(L, 5, "r"); color.r = cast(ubyte)lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, 5, "g"); color.g = cast(ubyte)lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, 5, "b"); color.b = cast(ubyte)lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, 5, "a"); color.a = cast(ubyte)lua_tointeger(L, -1); lua_pop(L, 1); } DrawTextEx(textFont, text, Vector2(x, y), fontSize, 1.0f, color); return 0; } extern (C) nothrow int luaL_measureTextX(lua_State *L) { lua_pushinteger(L, cast(int)MeasureTextEx(textFont, luaL_checkstring(L, 1), cast(int)luaL_checkinteger(L, 2), 1.0f).x); return 1; } extern (C) nothrow int luaL_measureTextY(lua_State *L) { lua_pushinteger(L, cast(int)MeasureTextEx(textFont, luaL_checkstring(L, 1), cast(int)luaL_checkinteger(L, 2), 1.0f).y); return 1; } /* extended */ extern (C) nothrow int luaL_drawTextureEx(lua_State *L) { Texture2D* texture = cast(Texture2D*)luaL_checkudata(L, 1, "Texture"); float x = luaL_checknumber(L, 2); float y = luaL_checknumber(L, 3); float rotation = luaL_optnumber(L, 4, 0); float scale = luaL_optnumber(L, 5, 1); Color color = Colors.WHITE; if (lua_istable(L, 6)) { lua_getfield(L, 6, "r"); color.r = cast(ubyte)lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, 6, "g"); color.g = cast(ubyte)lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, 6, "b"); color.b = cast(ubyte)lua_tointeger(L, -1); lua_pop(L, 1); lua_getfield(L, 6, "a"); color.a = cast(ubyte)lua_tointeger(L, -1); lua_pop(L, 1); } DrawTextureEx(*texture, Vector2(x, y), rotation, scale, color); return 0; } /* Register functions */ extern (C) nothrow void luaL_loader(lua_State* L) { lua_register(L, "dialogBox", &luaL_dialogBox); lua_register(L, "dialogAnswerValue", &luaL_dialogAnswerValue); lua_register(L, "isDialogExecuted", &luaL_isDialogExecuted); lua_register(L, "getAnswerValue", &luaL_getAnswerValue); lua_register(L, "loadAnimationUI", &luaL_loadUIAnimation); lua_register(L, "playAnimationUI", &luaL_playUIAnimation); lua_register(L, "stopAnimationUI", &luaL_stopUIAnimation); lua_register(L, "unloadAnimationUI", &luaL_unloadUIAnimation); lua_register(L, "moveCamera", &luaL_moveCamera); lua_register(L, "isCameraMoving", &luaL_isCameraMoving); lua_register(L, "playVideo", &luaL_playVideo); lua_register(L, "loadMusic", &luaL_loadMusic); lua_register(L, "playMusic", &luaL_playMusic); lua_register(L, "stopMusic", &luaL_stopMusic); lua_register(L, "unloadMusic", &luaL_unloadMusic); lua_register(L, "playSfx", &luaL_playSfx); lua_register(L, "stopSfx", &luaL_stopSfx); lua_register(L, "Begin2D", &luaL_2dModeEnable); lua_register(L, "End2D", &luaL_2dModeDisable); lua_register(L, "load2Dcharacter", &luaL_load2Dcharacter); lua_register(L, "draw2Dcharacter", &luaL_draw2Dcharacter); lua_register(L, "stopDraw2Dcharacter", &luaL_stopDraw2Dcharacter); lua_register(L, "unload2Dcharacter", &luaL_unload2Dcharacter); lua_register(L, "load2Dtexture", &luaL_load2Dbackground); lua_register(L, "draw2Dtexture", &luaL_draw2Dbackground); lua_register(L, "stopDraw2Dtexture", &luaL_stopDraw2Dbackground); lua_register(L, "unload2Dtexture", &luaL_unload2Dbackground); lua_register(L, "getTime", &luaL_getTime); lua_register(L, "loadScript", &luaL_loadScript); lua_register(L, "setFont", &luaL_setGameFont); lua_register(L, "getScreenHeight", &luaL_getScreenHeight); lua_register(L, "getScreenWidth", &luaL_getScreenWidth); lua_register(L, "isKeyPressed", &luaL_isKeyPressed); lua_register(L, "getLanguage", &luaL_getUsedLanguage); lua_register(L, "setGameState", &luaL_setGameState); //raylib direct bindings lua_register(L, "loadTexture", &luaL_loadTexture); lua_register(L, "drawTexture", &luaL_drawTexture); lua_register(L, "drawTextureEx", &luaL_drawTextureEx); lua_register(L, "drawText", &luaL_drawText); lua_register(L, "measureTextX", &luaL_measureTextX); lua_register(L, "measureTextY", &luaL_measureTextY); lua_register(L, "getTextureWidth", &luaL_getTextureWidth); lua_register(L, "getTextureHeight", &luaL_getTextureHeight); } int luaInit(string luaExec) { debugWriteln("loading Lua"); L = luaL_newstate(); luaL_openlibs(L); luaL_loader(L); debugWriteln("Executing next Lua file: ", luaExec); if (std.file.exists(luaExec) == false) { debugWriteln("Script file not found! Exiting."); return EngineExitCodes.EXIT_FILE_NOT_FOUND; } if (luaL_dofile(L, toStringz(luaExec)) != LUA_OK) { debugWriteln("Lua error: ", to!string(lua_tostring(L, -1))); return EngineExitCodes.EXIT_SCRIPT_ERROR; } return EngineExitCodes.EXIT_OK; } void luaEventLoop() { lua_getglobal(L, "EventLoop"); if (lua_pcall(L, 0, 0, 0) != LUA_OK) { debug debugWriteln("Error in EventLoop: ", to!string(lua_tostring(L, -1))); } lua_pop(L, 0); }