diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..aab7135 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.associations": { + "*.h": "c", + "xstring": "cpp" + } +} \ No newline at end of file diff --git a/main.cpp b/main.cpp index 481d0f7..7ee0db7 100644 --- a/main.cpp +++ b/main.cpp @@ -1,9 +1,22 @@ -#include "raylib.h" -#include -#include -#include #include -#include +#include +#include +#include +#include "raylib.h" +#include "angelscript.h" +#include "scriptstdstring.h" +#include +#include +#include +namespace fs = std::filesystem; + +asIScriptEngine* engine = nullptr; +std::string scriptFile = "scripts/test.as"; +std::time_t lastWriteTime = 0; + +// Cached script functions +asIScriptFunction* updateFunc = nullptr; +asIScriptFunction* drawFunc = nullptr; // ------------------------- // Functions exposed to AngelScript @@ -12,6 +25,19 @@ void Print(const std::string &msg) { std::cout << "[Script] " << msg << std::endl; } +Color ColorFromUInt(unsigned int c) { + Color col; + col.r = (c >> 24) & 0xFF; + col.g = (c >> 16) & 0xFF; + col.b = (c >> 8) & 0xFF; + col.a = c & 0xFF; + return col; +} + +void AS_DrawText(const std::string &text, int x, int y, int fontSize, unsigned int color) { + DrawText(text.c_str(), x, y, fontSize, ColorFromUInt(color)); +} + // ------------------------- // AngelScript message callback // ------------------------- @@ -19,6 +45,74 @@ void AngelScriptMessageCallback(const asSMessageInfo* msg, void* param) { std::cout << msg->section << " (" << msg->row << "): " << msg->message << std::endl; } +// ------------------------- +// Utility to read script files +// ------------------------- +std::string ReadFile(const std::string &filename) { + std::ifstream file(filename); + if (!file) return ""; + std::stringstream ss; + ss << file.rdbuf(); + return ss.str(); +} + +// ------------------------- +// Compile script and cache Update/Draw +// ------------------------- +bool compileScript(const std::string& filename) { + engine->GarbageCollect(); + asIScriptModule* mod = engine->GetModule("main", asGM_ALWAYS_CREATE); + + std::string code = ReadFile(filename); + if (code.empty()) { + std::cerr << "Failed to read script file: " << filename << "\n"; + return false; + } + + int r = mod->AddScriptSection(filename.c_str(), code.c_str()); + if (r < 0) return false; + + r = mod->Build(); + if (r < 0) return false; + + // Cache Update(float dt) and Draw() functions if they exist + updateFunc = mod->GetFunctionByName("Update"); + drawFunc = mod->GetFunctionByName("Draw"); + + std::cout << "Script compiled and cached: " << filename << "\n"; + return true; +} + +// ------------------------- +// Hot reload check +// ------------------------- +void checkHotReload() { + auto ftime = fs::last_write_time(scriptFile); + auto sctp = std::chrono::time_point_cast(ftime - fs::file_time_type::clock::now() + std::chrono::system_clock::now()); + std::time_t t = std::chrono::system_clock::to_time_t(sctp); + if (t != lastWriteTime) { + lastWriteTime = t; + compileScript(scriptFile); + } +} + +// ------------------------- +// Call a cached script function +// ------------------------- +void callScriptFunction(asIScriptFunction* func, float dt = 0.0f) { + if (!func) return; + + asIScriptContext* ctx = engine->CreateContext(); + ctx->Prepare(func); + + if (func->GetParamCount() == 1) { + ctx->SetArgFloat(0, dt); + } + + ctx->Execute(); + ctx->Release(); +} + // ------------------------- // Main // ------------------------- @@ -28,45 +122,41 @@ int main() { SetTargetFPS(60); // Initialize AngelScript - asIScriptEngine* engine = asCreateScriptEngine(); + engine = asCreateScriptEngine(); assert(engine); RegisterStdString(engine); + engine->RegisterGlobalFunction("void DrawText(const string &in, int, int, int, uint)", + asFUNCTION(AS_DrawText), asCALL_CDECL); - // Register the message callback int r = engine->SetMessageCallback(asFUNCTION(AngelScriptMessageCallback), nullptr, asCALL_CDECL); assert(r >= 0); - // Register the Print function for scripts r = engine->RegisterGlobalFunction("void Print(const string &in)", asFUNCTION(Print), asCALL_CDECL); assert(r >= 0); - // Create a module and add a simple script - asIScriptModule* mod = engine->GetModule("Game", asGM_ALWAYS_CREATE); - const char* scriptCode = - "void main() { " - " Print('Hello from AngelScript!'); " - "}"; - mod->AddScriptSection("test", scriptCode); - r = mod->Build(); - if (r < 0) std::cerr << "Failed to build script.\n"; - - // Prepare and execute the script - asIScriptFunction* func = mod->GetFunctionByDecl("void main()"); - asIScriptContext* ctx = engine->CreateContext(); - ctx->Prepare(func); - ctx->Execute(); - ctx->Release(); + compileScript(scriptFile); // Main loop while (!WindowShouldClose()) { + float dt = GetFrameTime(); + + checkHotReload(); + + callScriptFunction(updateFunc, dt); + BeginDrawing(); ClearBackground(RAYWHITE); - DrawText("Raylib + AngelScript running!", 180, 280, 20, DARKGRAY); + + DrawText("Modify scripts/test.as to hot-reload!", 50, 50, 20, DARKGRAY); + + callScriptFunction(drawFunc); + EndDrawing(); + + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } - // Clean up CloseWindow(); - engine->Release(); + engine->ShutDownAndRelease(); } diff --git a/scripts/test.as b/scripts/test.as index 89c461d..077151a 100644 --- a/scripts/test.as +++ b/scripts/test.as @@ -1,3 +1,11 @@ -void main() { - Print("Hello from file script!"); -} \ No newline at end of file +float x = 50; +float y = 100; + +void Update(float dt) { + x += 50 * dt; // move text horizontally + if (x > 800) x = 0; +} + +void Draw() { + DrawText("Hello from AngelScript!", int(x), int(y), 20, 0xFF0000FF); +}