diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c
new file mode 100644
index 000000000..d011d6304
--- /dev/null
+++ b/examples/core/core_automation_events.c
@@ -0,0 +1,289 @@
+/*******************************************************************************************
+*
+* raylib [core] example - automation events
+*
+* Example originally created with raylib 5.0, last time updated with raylib 5.0
+*
+* Example based on 2d_camera_platformer example by arvyy (@arvyy)
+*
+* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
+* BSD-like license that allows static linking with closed source software
+*
+* Copyright (c) 2023 Ramon Santamaria (@raysan5)
+*
+********************************************************************************************/
+
+#include "raylib.h"
+#include "raymath.h"
+
+#define GRAVITY 400
+#define PLAYER_JUMP_SPD 350.0f
+#define PLAYER_HOR_SPD 200.0f
+
+#define MAX_ENVIRONMENT_ELEMENTS 5
+
+typedef struct Player {
+ Vector2 position;
+ float speed;
+ bool canJump;
+} Player;
+
+typedef struct EnvElement {
+ Rectangle rect;
+ int blocking;
+ Color color;
+} EnvElement;
+
+
+//------------------------------------------------------------------------------------
+// Program main entry point
+//------------------------------------------------------------------------------------
+int main(void)
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ InitWindow(screenWidth, screenHeight, "raylib [core] example - automation events");
+
+ // Define player
+ Player player = { 0 };
+ player.position = (Vector2){ 400, 280 };
+ player.speed = 0;
+ player.canJump = false;
+
+ // Define environment elements (platforms)
+ EnvElement envElements[MAX_ENVIRONMENT_ELEMENTS] = {
+ {{ 0, 0, 1000, 400 }, 0, LIGHTGRAY },
+ {{ 0, 400, 1000, 200 }, 1, GRAY },
+ {{ 300, 200, 400, 10 }, 1, GRAY },
+ {{ 250, 300, 100, 10 }, 1, GRAY },
+ {{ 650, 300, 100, 10 }, 1, GRAY }
+ };
+
+ // Define camera
+ Camera2D camera = { 0 };
+ camera.target = player.position;
+ camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
+ camera.rotation = 0.0f;
+ camera.zoom = 1.0f;
+
+ // Automation events
+ AutomationEventList aelist = LoadAutomationEventList(0); // Initialize list of automation events to record new events
+ SetAutomationEventList(&aelist);
+ bool eventRecording = false;
+ bool eventPlaying = false;
+
+ int frameCounter = 0;
+ int playFrameCounter = 0;
+ int currentFrame = 0;
+
+ SetTargetFPS(60);
+ //--------------------------------------------------------------------------------------
+
+ // Main game loop
+ while (!WindowShouldClose())
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ float deltaTime = GetFrameTime();
+
+ // Dropped files logic
+ //----------------------------------------------------------------------------------
+ if (IsFileDropped())
+ {
+ FilePathList droppedFiles = LoadDroppedFiles();
+
+ // Supports loading .rgs style files (text or binary) and .png style palette images
+ if (IsFileExtension(droppedFiles.paths[0], ".txt;.rae"))
+ {
+ UnloadAutomationEventList(&aelist);
+ aelist = LoadAutomationEventList(droppedFiles.paths[0]);
+
+ eventRecording = false;
+
+ // Reset scene state to play
+ eventPlaying = true;
+ playFrameCounter = 0;
+
+ player.position = (Vector2){ 400, 280 };
+ player.speed = 0;
+ player.canJump = false;
+
+ camera.target = player.position;
+ camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
+ camera.rotation = 0.0f;
+ camera.zoom = 1.0f;
+ }
+
+ UnloadDroppedFiles(droppedFiles); // Unload filepaths from memory
+ }
+ //----------------------------------------------------------------------------------
+
+ // Update player
+ //----------------------------------------------------------------------------------
+ if (IsKeyDown(KEY_LEFT)) player.position.x -= PLAYER_HOR_SPD*deltaTime;
+ if (IsKeyDown(KEY_RIGHT)) player.position.x += PLAYER_HOR_SPD*deltaTime;
+ if (IsKeyDown(KEY_SPACE) && player.canJump)
+ {
+ player.speed = -PLAYER_JUMP_SPD;
+ player.canJump = false;
+ }
+
+ int hitObstacle = 0;
+ for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++)
+ {
+ EnvElement *element = &envElements[i];
+ Vector2 *p = &(player.position);
+ if (element->blocking &&
+ element->rect.x <= p->x &&
+ element->rect.x + element->rect.width >= p->x &&
+ element->rect.y >= p->y &&
+ element->rect.y <= p->y + player.speed*deltaTime)
+ {
+ hitObstacle = 1;
+ player.speed = 0.0f;
+ p->y = element->rect.y;
+ }
+ }
+
+ if (!hitObstacle)
+ {
+ player.position.y += player.speed*deltaTime;
+ player.speed += GRAVITY*deltaTime;
+ player.canJump = false;
+ }
+ else player.canJump = true;
+
+ camera.zoom += ((float)GetMouseWheelMove()*0.05f);
+
+ if (camera.zoom > 3.0f) camera.zoom = 3.0f;
+ else if (camera.zoom < 0.25f) camera.zoom = 0.25f;
+
+ if (IsKeyPressed(KEY_R))
+ {
+ camera.zoom = 1.0f;
+ player.position = (Vector2){ 400, 280 };
+ }
+ //----------------------------------------------------------------------------------
+
+ // Update camera
+ //----------------------------------------------------------------------------------
+ camera.target = player.position;
+ camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f };
+ float minX = 1000, minY = 1000, maxX = -1000, maxY = -1000;
+
+ for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++)
+ {
+ EnvElement *element = &envElements[i];
+ minX = fminf(element->rect.x, minX);
+ maxX = fmaxf(element->rect.x + element->rect.width, maxX);
+ minY = fminf(element->rect.y, minY);
+ maxY = fmaxf(element->rect.y + element->rect.height, maxY);
+ }
+
+ Vector2 max = GetWorldToScreen2D((Vector2){ maxX, maxY }, camera);
+ Vector2 min = GetWorldToScreen2D((Vector2){ minX, minY }, camera);
+
+ if (max.x < screenWidth) camera.offset.x = screenWidth - (max.x - screenWidth/2);
+ if (max.y < screenHeight) camera.offset.y = screenHeight - (max.y - screenHeight/2);
+ if (min.x > 0) camera.offset.x = screenWidth/2 - min.x;
+ if (min.y > 0) camera.offset.y = screenHeight/2 - min.y;
+ //----------------------------------------------------------------------------------
+
+ // Toggle events recording
+ if (IsKeyPressed(KEY_ONE))
+ {
+ if (!eventPlaying)
+ {
+ if (eventRecording)
+ {
+ StopAutomationEventRecording();
+ eventRecording = false;
+
+ ExportAutomationEventList(aelist, "automation.rae");
+ }
+ else
+ {
+ StartAutomationEventRecording();
+ eventRecording = true;
+ }
+ }
+ }
+
+ if (eventPlaying)
+ {
+ if (playFrameCounter == aelist.events[currentFrame].frame)
+ {
+ PlayAutomationEvent(aelist.events[currentFrame]);
+ currentFrame++;
+
+ if (currentFrame == aelist.count)
+ {
+ eventPlaying = false;
+ currentFrame = 0;
+ playFrameCounter = 0;
+ }
+ }
+
+ playFrameCounter++;
+ }
+
+ if (eventRecording || eventPlaying) frameCounter++;
+ else frameCounter = 0;
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+
+ ClearBackground(LIGHTGRAY);
+
+ BeginMode2D(camera);
+
+ // Draw environment elements
+ for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++)
+ {
+ DrawRectangleRec(envElements[i].rect, envElements[i].color);
+ }
+
+ // Draw player rectangle
+ DrawRectangleRec((Rectangle){ player.position.x - 20, player.position.y - 40, 40, 40 }, RED);
+
+ EndMode2D();
+
+ // Draw automation events recording indicator
+ if (eventRecording)
+ {
+ if (((frameCounter/15)%2) == 1)
+ {
+ DrawCircle(GetScreenWidth() - 200, 20, 10, MAROON);
+ DrawText(TextFormat("RECORDING EVENTS... [%i]", aelist.count), GetScreenWidth() - 180, 15, 10, RED);
+ }
+ }
+ else if (eventPlaying)
+ {
+ if (((frameCounter/15)%2) == 1)
+ {
+ DrawTriangle((Vector2){ GetScreenWidth() - 200, 10 }, (Vector2){ GetScreenWidth() - 200, 30 }, (Vector2){ GetScreenWidth() - 200 + 20, 20 }, DARKGREEN);
+ DrawText(TextFormat("PLAYING EVENTS... [%i]", currentFrame), GetScreenWidth() - 170, 15, 10, LIME);
+ }
+ }
+
+ DrawText("Controls:", 20, 20, 10, BLACK);
+ DrawText("- Right/Left to move", 30, 40, 10, DARKGRAY);
+ DrawText("- Space to jump", 30, 60, 10, DARKGRAY);
+ DrawText("- Mouse Wheel to Zoom in-out, R to reset zoom", 30, 80, 10, DARKGRAY);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+}
diff --git a/projects/VS2022/examples/core_automation_events.vcxproj b/projects/VS2022/examples/core_automation_events.vcxproj
new file mode 100644
index 000000000..e70a6b1ea
--- /dev/null
+++ b/projects/VS2022/examples/core_automation_events.vcxproj
@@ -0,0 +1,390 @@
+
+
+
+
+ Debug.DLL
+ Win32
+
+
+ Debug.DLL
+ x64
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release.DLL
+ Win32
+
+
+ Release.DLL
+ x64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}
+ Win32Proj
+ core_automation_events
+ 10.0
+ core_automation_events
+
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ true
+ $(DefaultPlatformToolset)
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+ Application
+ false
+ $(DefaultPlatformToolset)
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ true
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ false
+ $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\
+ $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\
+
+
+ $(SolutionDir)..\..\examples\core
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\core
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\core
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\core
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\core
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\core
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\core
+ WindowsLocalDebugger
+
+
+ $(SolutionDir)..\..\examples\core
+ WindowsLocalDebugger
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ /FS %(AdditionalOptions)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+
+
+ Level3
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions)
+ CompileAsC
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+
+
+ Console
+ true
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+ Copy Debug DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+ Level3
+
+
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP
+ $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories)
+ CompileAsC
+ true
+
+
+ Console
+ true
+ true
+ true
+ raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
+ $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\
+
+
+ xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)"
+
+
+ Copy Release DLL to output directory
+
+
+
+
+
+
+
+
+
+
+ {e89d61ac-55de-4482-afd4-df7242ebc859}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln
index 007f796f9..167c60f3f 100644
--- a/projects/VS2022/raylib.sln
+++ b/projects/VS2022/raylib.sln
@@ -273,6 +273,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_sound_multi", "exampl
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_2d_camera_split_screen", "examples\core_2d_camera_split_screen.vcxproj", "{CC62F7DB-D089-4677-8575-CAB7A7815C43}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_automation_events", "examples\core_automation_events.vcxproj", "{7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug.DLL|x64 = Debug.DLL|x64
@@ -2297,6 +2299,22 @@ Global
{CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x64.Build.0 = Release|x64
{CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.ActiveCfg = Release|Win32
{CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.Build.0 = Release|Win32
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x64.Build.0 = Debug.DLL|x64
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x64.ActiveCfg = Debug|x64
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x64.Build.0 = Debug|x64
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x86.ActiveCfg = Debug|Win32
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x86.Build.0 = Debug|Win32
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x64.ActiveCfg = Release.DLL|x64
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x64.Build.0 = Release.DLL|x64
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x86.Build.0 = Release.DLL|Win32
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x64.ActiveCfg = Release|x64
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x64.Build.0 = Release|x64
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x86.ActiveCfg = Release|Win32
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -2435,6 +2453,7 @@ Global
{3755E9F4-CB48-4EC3-B561-3B85964EBDEF} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9}
{F81C5819-85B4-4D2E-B6DC-104A7634461B} = {CC132A4D-D081-4C26-BFB9-AB11984054F8}
{CC62F7DB-D089-4677-8575-CAB7A7815C43} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035}
+ {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29}
diff --git a/src/config.h b/src/config.h
index 7886aeeba..96fdd065f 100644
--- a/src/config.h
+++ b/src/config.h
@@ -63,7 +63,7 @@
// Support CompressData() and DecompressData() functions
#define SUPPORT_COMPRESSION_API 1
// Support automatic generated events, loading and recording of those events when required
-//#define SUPPORT_EVENTS_AUTOMATION 1
+#define SUPPORT_AUTOMATION_EVENTS 1
// Support custom frame control, only for advance users
// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents()
// Enabling this flag allows manual control of the frame processes, use at your own risk
@@ -85,6 +85,7 @@
#define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB
+#define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record
//------------------------------------------------------------------------------------
// Module: rlgl - Configuration values
diff --git a/src/raylib.h b/src/raylib.h
index b172562d0..7b3eddaee 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -506,6 +506,20 @@ typedef struct FilePathList {
char **paths; // Filepaths entries
} FilePathList;
+// Automation event (opaque struct)
+typedef struct AutomationEvent {
+ unsigned int frame; // Event frame
+ unsigned int type; // Event type (AutomationEventType)
+ int params[4]; // Event parameters (if required)
+} AutomationEvent;
+
+// Automation event list
+typedef struct AutomationEventList {
+ unsigned int capacity; // Events max entries (MAX_AUTOMATION_EVENTS)
+ unsigned int count; // Events entries count
+ AutomationEvent *events; // Events entries
+} AutomationEventList;
+
//----------------------------------------------------------------------------------
// Enumerators Definition
//----------------------------------------------------------------------------------
@@ -1114,6 +1128,15 @@ RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataS
RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree()
RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree()
+// Automation events functionality
+RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
+RLAPI void UnloadAutomationEventList(AutomationEventList *list); // Unload automation events list from file
+RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file
+RLAPI void SetAutomationEventList(AutomationEventList *list); // Set automation event list to record to
+RLAPI void StartAutomationEventRecording(void); // Start recording automation events (AutomationEventList must be set)
+RLAPI void StopAutomationEventRecording(void); // Stop recording automation events
+RLAPI void PlayAutomationEvent(AutomationEvent event); // Play a recorded automation event
+
//------------------------------------------------------------------------------------
// Input Handling Functions (Module: core)
//------------------------------------------------------------------------------------
diff --git a/src/rcore.c b/src/rcore.c
index ef3beaee3..67c79efa9 100644
--- a/src/rcore.c
+++ b/src/rcore.c
@@ -3,17 +3,17 @@
* rcore - Window/display management, Graphic device/context management and input management
*
* PLATFORMS SUPPORTED:
-* - PLATFORM_DESKTOP:
+* - PLATFORM_DESKTOP:
* > Windows (Win32, Win64)
* > Linux (X11/Wayland desktop mode)
* > macOS/OSX (x64, arm64)
* > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop)
-* - PLATFORM_WEB:
+* - PLATFORM_WEB:
* > HTML5 (WebAssembly)
-* - PLATFORM_DRM:
+* - PLATFORM_DRM:
* > Raspberry Pi 0-5
* > Linux native mode (KMS driver)
-* - PLATFORM_ANDROID:
+* - PLATFORM_ANDROID:
* > Android (ARM, ARM64)
*
* CONFIGURATION:
@@ -48,8 +48,8 @@
* provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module
* for linkage
*
-* #define SUPPORT_EVENTS_AUTOMATION
-* Support automatic generated events, loading and recording of those events when required
+* #define SUPPORT_AUTOMATION_EVENTS
+* Support automatic events recording and playing, useful for automated testing systems or AI based game playing
*
* DEPENDENCIES:
* raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion)
@@ -183,9 +183,8 @@ bool gifRecording = false; // GIF recording state
MsfGifState gifState = { 0 }; // MSGIF context state
#endif
-#if defined(SUPPORT_EVENTS_AUTOMATION)
-#define MAX_CODE_AUTOMATION_EVENTS 16384
-
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+// Automation events type
typedef enum AutomationEventType {
EVENT_NONE = 0,
// Input events
@@ -212,12 +211,12 @@ typedef enum AutomationEventType {
WINDOW_MINIMIZE, // no params
WINDOW_RESIZE, // param[0]: width, param[1]: height
// Custom events
- ACTION_TAKE_SCREENSHOT,
- ACTION_SETTARGETFPS
+ ACTION_TAKE_SCREENSHOT, // no params
+ ACTION_SETTARGETFPS // param[0]: fps
} AutomationEventType;
-// Event type
-// Used to enable events flags
+// Event type to config events flags
+// TODO: Not used at the moment
typedef enum {
EVENT_INPUT_KEYBOARD = 0,
EVENT_INPUT_MOUSE = 1,
@@ -228,6 +227,7 @@ typedef enum {
EVENT_CUSTOM = 32
} EventType;
+// Event type name strings, required for export
static const char *autoEventTypeName[] = {
"EVENT_NONE",
"INPUT_KEY_UP",
@@ -255,19 +255,19 @@ static const char *autoEventTypeName[] = {
"ACTION_SETTARGETFPS"
};
+/*
// Automation event (24 bytes)
-typedef struct AutomationEvent {
+// NOTE: Opaque struct, internal to raylib
+struct AutomationEvent {
unsigned int frame; // Event frame
unsigned int type; // Event type (AutomationEventType)
int params[4]; // Event parameters (if required)
-} AutomationEvent;
+};
+*/
-static AutomationEvent *events = NULL; // Events array
-static unsigned int eventCount = 0; // Events count
-static bool eventsPlaying = false; // Play events
-static bool eventsRecording = false; // Record events
-
-//static short eventsEnabled = 0b0000001111111111; // Events enabled for checking
+static AutomationEventList *currentEventList = NULL; // Current automation events list, set by user, keep internal pointer
+static bool automationEventRecording = false; // Recording automation events flag
+//static short automationEventEnabled = 0b0000001111111111; // TODO: Automation events enabled for recording/playing
#endif
//-----------------------------------------------------------------------------------
@@ -291,11 +291,8 @@ static void SetupViewport(int width, int height); // Set viewport for
static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories in a base path
static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories recursively from a base path
-#if defined(SUPPORT_EVENTS_AUTOMATION)
-static void LoadAutomationEvents(const char *fileName); // Load automation events from file
-static void ExportAutomationEvents(const char *fileName); // Export recorded automation events into a file
-static void RecordAutomationEvent(unsigned int frame); // Record frame events (to internal events array)
-static void PlayAutomationEvent(unsigned int frame); // Play frame events (from internal events array)
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+static void RecordAutomationEvent(void); // Record frame events (to internal events array)
#endif
#if defined(_WIN32)
@@ -304,14 +301,14 @@ void __stdcall Sleep(unsigned long msTimeout); // Required for: Wai
#endif
#if !defined(SUPPORT_MODULE_RTEXT)
-const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed'
+const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed'
#endif // !SUPPORT_MODULE_RTEXT
// Include platform-specific submodules
#if defined(PLATFORM_DESKTOP)
#include "platforms/rcore_desktop.c"
#elif defined(PLATFORM_DESKTOP_SDL)
- #include "platforms/rcore_desktop_sdl.c"
+ #include "platforms/rcore_desktop_sdl.c"
#elif defined(PLATFORM_WEB)
#include "platforms/rcore_web.c"
#elif defined(PLATFORM_DRM)
@@ -434,12 +431,12 @@ void InitWindow(int width, int height, const char *title)
CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f };
CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW;
CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN;
-
+
// Initialize platform
- //--------------------------------------------------------------
+ //--------------------------------------------------------------
InitPlatform();
//--------------------------------------------------------------
-
+
// Initialize rlgl default data (buffers and shaders)
// NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl
rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height);
@@ -485,9 +482,6 @@ void InitWindow(int width, int height, const char *title)
#endif
CORE.Time.frameCounter = 0;
-#if defined(SUPPORT_EVENTS_AUTOMATION)
- events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent));
-#endif
// Initialize random seed
SetRandomSeed((unsigned int)time(NULL));
@@ -512,14 +506,10 @@ void CloseWindow(void)
rlglClose(); // De-init rlgl
// De-initialize platform
- //--------------------------------------------------------------
+ //--------------------------------------------------------------
ClosePlatform();
//--------------------------------------------------------------
-#if defined(SUPPORT_EVENTS_AUTOMATION)
- RL_FREE(events);
-#endif
-
CORE.Window.ready = false;
TRACELOG(LOG_INFO, "Window closed successfully");
}
@@ -684,34 +674,6 @@ void EndDrawing(void)
}
#endif
-#if defined(SUPPORT_EVENTS_AUTOMATION)
- // Draw record/play indicator
- if (eventsRecording)
- {
- gifFrameCounter++;
-
- if (((gifFrameCounter/15)%2) == 1)
- {
- DrawCircle(30, CORE.Window.screen.height - 20, 10, MAROON);
- DrawText("EVENTS RECORDING", 50, CORE.Window.screen.height - 25, 10, RED);
- }
-
- rlDrawRenderBatchActive(); // Update and draw internal render batch
- }
- else if (eventsPlaying)
- {
- gifFrameCounter++;
-
- if (((gifFrameCounter/15)%2) == 1)
- {
- DrawCircle(30, CORE.Window.screen.height - 20, 10, LIME);
- DrawText("EVENTS PLAYING", 50, CORE.Window.screen.height - 25, 10, GREEN);
- }
-
- rlDrawRenderBatchActive(); // Update and draw internal render batch
- }
-#endif
-
#if !defined(SUPPORT_CUSTOM_FRAME_CONTROL)
SwapScreenBuffer(); // Copy back buffer to front buffer (screen)
@@ -775,16 +737,9 @@ void EndDrawing(void)
}
#endif // SUPPORT_SCREEN_CAPTURE
-#if defined(SUPPORT_EVENTS_AUTOMATION)
- // Events recording and playing logic
- if (eventsRecording) RecordAutomationEvent(CORE.Time.frameCounter);
- else if (eventsPlaying)
- {
- // TODO: When should we play? After/before/replace PollInputEvents()?
- if (CORE.Time.frameCounter >= eventCount) eventsPlaying = false;
- PlayAutomationEvent(CORE.Time.frameCounter);
- }
-#endif // SUPPORT_EVENTS_AUTOMATION
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+ if (automationEventRecording) RecordAutomationEvent(); // Event recording
+#endif
CORE.Time.frameCounter++;
}
@@ -1470,7 +1425,7 @@ float GetFrameTime(void)
//----------------------------------------------------------------------------------
// NOTE: Functions with a platform-specific implementation on rcore_.c
-//void SwapScreenBuffer(void);
+//void SwapScreenBuffer(void);
//void PollInputEvents(void);
// Wait for some time (stop program execution)
@@ -1481,7 +1436,7 @@ float GetFrameTime(void)
void WaitTime(double seconds)
{
if (seconds < 0) return;
-
+
#if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP)
double destinationTime = GetTime() + seconds;
#endif
@@ -2180,6 +2135,237 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize)
return decodedData;
}
+//----------------------------------------------------------------------------------
+// Module Functions Definition: Automation Events Recording and Playing
+//----------------------------------------------------------------------------------
+
+// Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS
+AutomationEventList LoadAutomationEventList(const char *fileName)
+{
+ AutomationEventList list = { 0 };
+
+ // Allocate and empty automation event list, ready to record new events
+ list.events = (AutomationEvent *)RL_CALLOC(MAX_AUTOMATION_EVENTS, sizeof(AutomationEvent));
+ list.capacity = MAX_AUTOMATION_EVENTS;
+
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+ if (fileName == NULL) TRACELOG(LOG_INFO, "AUTOMATION: New empty events list loaded successfully");
+ else
+ {
+ // Load automation events file (binary)
+ /*
+ //int dataSize = 0;
+ //unsigned char *data = LoadFileData(fileName, &dataSize);
+
+ FILE *raeFile = fopen(fileName, "rb");
+ unsigned char fileId[4] = { 0 };
+
+ fread(fileId, 1, 4, raeFile);
+
+ if ((fileId[0] == 'r') && (fileId[1] == 'A') && (fileId[2] == 'E') && (fileId[1] == ' '))
+ {
+ fread(&eventCount, sizeof(int), 1, raeFile);
+ TRACELOG(LOG_WARNING, "Events loaded: %i\n", eventCount);
+ fread(events, sizeof(AutomationEvent), eventCount, raeFile);
+ }
+
+ fclose(raeFile);
+ */
+
+ // Load events file (text)
+ //unsigned char *buffer = LoadFileText(fileName);
+ FILE *raeFile = fopen(fileName, "rt");
+
+ if (raeFile != NULL)
+ {
+ unsigned int counter = 0;
+ char buffer[256] = { 0 };
+ char eventDesc[64] = { 0 };
+
+ fgets(buffer, 256, raeFile);
+
+ while (!feof(raeFile))
+ {
+ switch (buffer[0])
+ {
+ case 'c': sscanf(buffer, "c %i", &list.count); break;
+ case 'e':
+ {
+ sscanf(buffer, "e %d %d %d %d %d %d %[^\n]s", &list.events[counter].frame, &list.events[counter].type,
+ &list.events[counter].params[0], &list.events[counter].params[1], &list.events[counter].params[2], &list.events[counter].params[3], eventDesc);
+
+ counter++;
+ } break;
+ default: break;
+ }
+
+ fgets(buffer, 256, raeFile);
+ }
+
+ if (counter != list.count)
+ {
+ TRACELOG(LOG_WARNING, "AUTOMATION: Events read from file [%i] do not mach event count specified [%i]", counter, list.count);
+ list.count = counter;
+ }
+
+ fclose(raeFile);
+
+ TRACELOG(LOG_INFO, "AUTOMATION: Events file loaded successfully");
+ }
+
+ TRACELOG(LOG_INFO, "AUTOMATION: Events loaded from file: %i", list.count);
+ }
+#endif
+ return list;
+}
+
+// Unload automation events list from file
+void UnloadAutomationEventList(AutomationEventList *list)
+{
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+ RL_FREE(list->events);
+ list->events = NULL;
+ list->count = 0;
+ list->capacity = 0;
+#endif
+}
+
+// Export automation events list as text file
+bool ExportAutomationEventList(AutomationEventList list, const char *fileName)
+{
+ bool success = false;
+
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+ // Export events as binary file
+ // TODO: Save to memory buffer and SaveFileData()
+ /*
+ unsigned char fileId[4] = "rAE ";
+ FILE *raeFile = fopen(fileName, "wb");
+ fwrite(fileId, sizeof(unsigned char), 4, raeFile);
+ fwrite(&eventCount, sizeof(int), 1, raeFile);
+ fwrite(events, sizeof(AutomationEvent), eventCount, raeFile);
+ fclose(raeFile);
+ */
+
+ // Export events as text
+ // TODO: Save to memory buffer and SaveFileText()
+ char *txtData = (char *)RL_CALLOC(256*list.count + 2048, sizeof(char)); // 256 characters per line plus some header
+
+ int byteCount = 0;
+ byteCount += sprintf(txtData + byteCount, "#\n");
+ byteCount += sprintf(txtData + byteCount, "# Automation events exporter v1.0 - raylib automation events list\n");
+ byteCount += sprintf(txtData + byteCount, "#\n");
+ byteCount += sprintf(txtData + byteCount, "# c \n");
+ byteCount += sprintf(txtData + byteCount, "# e // \n");
+ byteCount += sprintf(txtData + byteCount, "#\n");
+ byteCount += sprintf(txtData + byteCount, "# more info and bugs-report: github.com/raysan5/raylib\n");
+ byteCount += sprintf(txtData + byteCount, "# feedback and support: ray[at]raylib.com\n");
+ byteCount += sprintf(txtData + byteCount, "#\n");
+ byteCount += sprintf(txtData + byteCount, "# Copyright (c) 2023-2024 Ramon Santamaria (@raysan5)\n");
+ byteCount += sprintf(txtData + byteCount, "#\n\n");
+
+ // Add events data
+ byteCount += sprintf(txtData + byteCount, "c %i\n", list.count);
+ for (int i = 0; i < list.count; i++)
+ {
+ byteCount += sprintf(txtData + byteCount, "e %i %i %i %i %i %i // Event: %s\n", list.events[i].frame, list.events[i].type,
+ list.events[i].params[0], list.events[i].params[1], list.events[i].params[2], list.events[i].params[3], autoEventTypeName[list.events[i].type]);
+ }
+
+ // NOTE: Text data size exported is determined by '\0' (NULL) character
+ success = SaveFileText(fileName, txtData);
+
+ RL_FREE(txtData);
+#endif
+
+ return success;
+}
+
+// Setup automation event list to record to
+void SetAutomationEventList(AutomationEventList *list)
+{
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+ currentEventList = list;
+#endif
+}
+
+// Start recording automation events (AutomationEventList must be set)
+void StartAutomationEventRecording(void)
+{
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+ automationEventRecording = true;
+#endif
+}
+
+// Stop recording automation events
+void StopAutomationEventRecording(void)
+{
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+ automationEventRecording = false;
+#endif
+}
+
+// Play a recorded automation event
+void PlayAutomationEvent(AutomationEvent event)
+{
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+ // WARNING: When should event be played? After/before/replace PollInputEvents()? -> Up to the user!
+
+ if (!automationEventRecording) // TODO: Allow recording events while playing?
+ {
+ switch (event.type)
+ {
+ // Input event
+ case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[event.params[0]] = false; break; // param[0]: key
+ case INPUT_KEY_DOWN: CORE.Input.Keyboard.currentKeyState[event.params[0]] = true; break; // param[0]: key
+ case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[event.params[0]] = false; break; // param[0]: key
+ case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[event.params[0]] = true; break; // param[0]: key
+ case INPUT_MOUSE_POSITION: // param[0]: x, param[1]: y
+ {
+ CORE.Input.Mouse.currentPosition.x = (float)event.params[0];
+ CORE.Input.Mouse.currentPosition.y = (float)event.params[1];
+ } break;
+ case INPUT_MOUSE_WHEEL_MOTION: // param[0]: x delta, param[1]: y delta
+ {
+ CORE.Input.Mouse.currentWheelMove.x = (float)event.params[0]; break;
+ CORE.Input.Mouse.currentWheelMove.y = (float)event.params[1]; break;
+ } break;
+ case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[event.params[0]] = false; break; // param[0]: id
+ case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[event.params[0]] = true; break; // param[0]: id
+ case INPUT_TOUCH_POSITION: // param[0]: id, param[1]: x, param[2]: y
+ {
+ CORE.Input.Touch.position[event.params[0]].x = (float)event.params[1];
+ CORE.Input.Touch.position[event.params[0]].y = (float)event.params[2];
+ } break;
+ case INPUT_GAMEPAD_CONNECT: CORE.Input.Gamepad.ready[event.params[0]] = true; break; // param[0]: gamepad
+ case INPUT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[event.params[0]] = false; break; // param[0]: gamepad
+ case INPUT_GAMEPAD_BUTTON_UP: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = false; break; // param[0]: gamepad, param[1]: button
+ case INPUT_GAMEPAD_BUTTON_DOWN: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = true; break; // param[0]: gamepad, param[1]: button
+ case INPUT_GAMEPAD_AXIS_MOTION: // param[0]: gamepad, param[1]: axis, param[2]: delta
+ {
+ CORE.Input.Gamepad.axisState[event.params[0]][event.params[1]] = ((float)event.params[2]/32768.0f);
+ } break;
+ case INPUT_GESTURE: GESTURES.current = event.params[0]; break; // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current
+
+ // Window event
+ case WINDOW_CLOSE: CORE.Window.shouldClose = true; break;
+ case WINDOW_MAXIMIZE: MaximizeWindow(); break;
+ case WINDOW_MINIMIZE: MinimizeWindow(); break;
+ case WINDOW_RESIZE: SetWindowSize(event.params[0], event.params[1]); break;
+
+ // Custom event
+ case ACTION_TAKE_SCREENSHOT:
+ {
+ TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
+ screenshotCounter++;
+ } break;
+ case ACTION_SETTARGETFPS: SetTargetFPS(event.params[0]); break;
+ default: break;
+ }
+ }
+#endif
+}
+
//----------------------------------------------------------------------------------
// Module Functions Definition: Input Handling: Keyboard
//----------------------------------------------------------------------------------
@@ -2793,233 +2979,180 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi
else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath);
}
-#if defined(SUPPORT_EVENTS_AUTOMATION)
-// NOTE: Loading happens over AutomationEvent *events
-// TODO: This system should probably be redesigned
-static void LoadAutomationEvents(const char *fileName)
+#if defined(SUPPORT_AUTOMATION_EVENTS)
+// Automation event recording
+// NOTE: Recording is by default done at EndDrawing(), after PollInputEvents()
+static void RecordAutomationEvent(void)
{
- // Load events file (binary)
- /*
- FILE *repFile = fopen(fileName, "rb");
- unsigned char fileId[4] = { 0 };
+ // Checking events in current frame and save them into currentEventList
+ // TODO: How important is the current frame? Could it be modified?
+
+ if (currentEventList->count == currentEventList->capacity) return; // Security check
- fread(fileId, 1, 4, repFile);
-
- if ((fileId[0] == 'r') && (fileId[1] == 'E') && (fileId[2] == 'P') && (fileId[1] == ' '))
- {
- fread(&eventCount, sizeof(int), 1, repFile);
- TRACELOG(LOG_WARNING, "Events loaded: %i\n", eventCount);
- fread(events, sizeof(AutomationEvent), eventCount, repFile);
- }
-
- fclose(repFile);
- */
-
- // Load events file (text)
- FILE *repFile = fopen(fileName, "rt");
-
- if (repFile != NULL)
- {
- unsigned int count = 0;
- char buffer[256] = { 0 };
-
- fgets(buffer, 256, repFile);
-
- while (!feof(repFile))
- {
- if (buffer[0] == 'c') sscanf(buffer, "c %i", &eventCount);
- else if (buffer[0] == 'e')
- {
- sscanf(buffer, "e %d %d %d %d %d", &events[count].frame, &events[count].type,
- &events[count].params[0], &events[count].params[1], &events[count].params[2]);
-
- count++;
- }
-
- fgets(buffer, 256, repFile);
- }
-
- if (count != eventCount) TRACELOG(LOG_WARNING, "Events count provided is different than count");
-
- fclose(repFile);
- }
-
- TRACELOG(LOG_WARNING, "Events loaded: %i", eventCount);
-}
-
-// Export recorded events into a file
-static void ExportAutomationEvents(const char *fileName)
-{
- unsigned char fileId[4] = "rEP ";
-
- // Save as binary
- /*
- FILE *repFile = fopen(fileName, "wb");
- fwrite(fileId, sizeof(unsigned char), 4, repFile);
- fwrite(&eventCount, sizeof(int), 1, repFile);
- fwrite(events, sizeof(AutomationEvent), eventCount, repFile);
- fclose(repFile);
- */
-
- // Export events as text
- FILE *repFile = fopen(fileName, "wt");
-
- if (repFile != NULL)
- {
- fprintf(repFile, "# Automation events list\n");
- fprintf(repFile, "# c \n");
- fprintf(repFile, "# e // \n");
-
- fprintf(repFile, "c %i\n", eventCount);
- for (int i = 0; i < eventCount; i++)
- {
- fprintf(repFile, "e %i %i %i %i %i // %s\n", events[i].frame, events[i].type,
- events[i].params[0], events[i].params[1], events[i].params[2], autoEventTypeName[events[i].type]);
- }
-
- fclose(repFile);
- }
-}
-
-// EndDrawing() -> After PollInputEvents()
-// Check event in current frame and save into the events[i] array
-static void RecordAutomationEvent(unsigned int frame)
-{
+ // Keyboard input events recording
+ //-------------------------------------------------------------------------------------
for (int key = 0; key < MAX_KEYBOARD_KEYS; key++)
{
- // INPUT_KEY_UP (only saved once)
+ // Event type: INPUT_KEY_UP (only saved once)
if (CORE.Input.Keyboard.previousKeyState[key] && !CORE.Input.Keyboard.currentKeyState[key])
{
- events[eventCount].frame = frame;
- events[eventCount].type = INPUT_KEY_UP;
- events[eventCount].params[0] = key;
- events[eventCount].params[1] = 0;
- events[eventCount].params[2] = 0;
+ currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+ currentEventList->events[currentEventList->count].type = INPUT_KEY_UP;
+ currentEventList->events[currentEventList->count].params[0] = key;
+ currentEventList->events[currentEventList->count].params[1] = 0;
+ currentEventList->events[currentEventList->count].params[2] = 0;
- TRACELOG(LOG_INFO, "[%i] INPUT_KEY_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
- eventCount++;
+ TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+ currentEventList->count++;
}
+
+ if (currentEventList->count == currentEventList->capacity) return; // Security check
- // INPUT_KEY_DOWN
+ // Event type: INPUT_KEY_DOWN
if (CORE.Input.Keyboard.currentKeyState[key])
{
- events[eventCount].frame = frame;
- events[eventCount].type = INPUT_KEY_DOWN;
- events[eventCount].params[0] = key;
- events[eventCount].params[1] = 0;
- events[eventCount].params[2] = 0;
+ currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+ currentEventList->events[currentEventList->count].type = INPUT_KEY_DOWN;
+ currentEventList->events[currentEventList->count].params[0] = key;
+ currentEventList->events[currentEventList->count].params[1] = 0;
+ currentEventList->events[currentEventList->count].params[2] = 0;
- TRACELOG(LOG_INFO, "[%i] INPUT_KEY_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
- eventCount++;
+ TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+ currentEventList->count++;
}
+
+ if (currentEventList->count == currentEventList->capacity) return; // Security check
}
+ //-------------------------------------------------------------------------------------
+ // Mouse input currentEventList->events recording
+ //-------------------------------------------------------------------------------------
for (int button = 0; button < MAX_MOUSE_BUTTONS; button++)
{
- // INPUT_MOUSE_BUTTON_UP
+ // Event type: INPUT_MOUSE_BUTTON_UP
if (CORE.Input.Mouse.previousButtonState[button] && !CORE.Input.Mouse.currentButtonState[button])
{
- events[eventCount].frame = frame;
- events[eventCount].type = INPUT_MOUSE_BUTTON_UP;
- events[eventCount].params[0] = button;
- events[eventCount].params[1] = 0;
- events[eventCount].params[2] = 0;
+ currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+ currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_UP;
+ currentEventList->events[currentEventList->count].params[0] = button;
+ currentEventList->events[currentEventList->count].params[1] = 0;
+ currentEventList->events[currentEventList->count].params[2] = 0;
- TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_BUTTON_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
- eventCount++;
+ TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+ currentEventList->count++;
}
+
+ if (currentEventList->count == currentEventList->capacity) return; // Security check
- // INPUT_MOUSE_BUTTON_DOWN
+ // Event type: INPUT_MOUSE_BUTTON_DOWN
if (CORE.Input.Mouse.currentButtonState[button])
{
- events[eventCount].frame = frame;
- events[eventCount].type = INPUT_MOUSE_BUTTON_DOWN;
- events[eventCount].params[0] = button;
- events[eventCount].params[1] = 0;
- events[eventCount].params[2] = 0;
+ currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+ currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_DOWN;
+ currentEventList->events[currentEventList->count].params[0] = button;
+ currentEventList->events[currentEventList->count].params[1] = 0;
+ currentEventList->events[currentEventList->count].params[2] = 0;
- TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_BUTTON_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
- eventCount++;
+ TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+ currentEventList->count++;
}
+
+ if (currentEventList->count == currentEventList->capacity) return; // Security check
}
- // INPUT_MOUSE_POSITION (only saved if changed)
+ // Event type: INPUT_MOUSE_POSITION (only saved if changed)
if (((int)CORE.Input.Mouse.currentPosition.x != (int)CORE.Input.Mouse.previousPosition.x) ||
((int)CORE.Input.Mouse.currentPosition.y != (int)CORE.Input.Mouse.previousPosition.y))
{
- events[eventCount].frame = frame;
- events[eventCount].type = INPUT_MOUSE_POSITION;
- events[eventCount].params[0] = (int)CORE.Input.Mouse.currentPosition.x;
- events[eventCount].params[1] = (int)CORE.Input.Mouse.currentPosition.y;
- events[eventCount].params[2] = 0;
+ currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+ currentEventList->events[currentEventList->count].type = INPUT_MOUSE_POSITION;
+ currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentPosition.x;
+ currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentPosition.y;
+ currentEventList->events[currentEventList->count].params[2] = 0;
- TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_POSITION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
- eventCount++;
+ TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_POSITION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+ currentEventList->count++;
+
+ if (currentEventList->count == currentEventList->capacity) return; // Security check
}
- // INPUT_MOUSE_WHEEL_MOTION
+ // Event type: INPUT_MOUSE_WHEEL_MOTION
if (((int)CORE.Input.Mouse.currentWheelMove.x != (int)CORE.Input.Mouse.previousWheelMove.x) ||
((int)CORE.Input.Mouse.currentWheelMove.y != (int)CORE.Input.Mouse.previousWheelMove.y))
{
- events[eventCount].frame = frame;
- events[eventCount].type = INPUT_MOUSE_WHEEL_MOTION;
- events[eventCount].params[0] = (int)CORE.Input.Mouse.currentWheelMove.x;
- events[eventCount].params[1] = (int)CORE.Input.Mouse.currentWheelMove.y;;
- events[eventCount].params[2] = 0;
+ currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+ currentEventList->events[currentEventList->count].type = INPUT_MOUSE_WHEEL_MOTION;
+ currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentWheelMove.x;
+ currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentWheelMove.y;;
+ currentEventList->events[currentEventList->count].params[2] = 0;
- TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_WHEEL_MOTION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
- eventCount++;
+ TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_WHEEL_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+ currentEventList->count++;
+
+ if (currentEventList->count == currentEventList->capacity) return; // Security check
}
+ //-------------------------------------------------------------------------------------
+ // Touch input currentEventList->events recording
+ //-------------------------------------------------------------------------------------
for (int id = 0; id < MAX_TOUCH_POINTS; id++)
{
- // INPUT_TOUCH_UP
+ // Event type: INPUT_TOUCH_UP
if (CORE.Input.Touch.previousTouchState[id] && !CORE.Input.Touch.currentTouchState[id])
{
- events[eventCount].frame = frame;
- events[eventCount].type = INPUT_TOUCH_UP;
- events[eventCount].params[0] = id;
- events[eventCount].params[1] = 0;
- events[eventCount].params[2] = 0;
+ currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+ currentEventList->events[currentEventList->count].type = INPUT_TOUCH_UP;
+ currentEventList->events[currentEventList->count].params[0] = id;
+ currentEventList->events[currentEventList->count].params[1] = 0;
+ currentEventList->events[currentEventList->count].params[2] = 0;
- TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
- eventCount++;
+ TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+ currentEventList->count++;
}
+
+ if (currentEventList->count == currentEventList->capacity) return; // Security check
- // INPUT_TOUCH_DOWN
+ // Event type: INPUT_TOUCH_DOWN
if (CORE.Input.Touch.currentTouchState[id])
{
- events[eventCount].frame = frame;
- events[eventCount].type = INPUT_TOUCH_DOWN;
- events[eventCount].params[0] = id;
- events[eventCount].params[1] = 0;
- events[eventCount].params[2] = 0;
+ currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+ currentEventList->events[currentEventList->count].type = INPUT_TOUCH_DOWN;
+ currentEventList->events[currentEventList->count].params[0] = id;
+ currentEventList->events[currentEventList->count].params[1] = 0;
+ currentEventList->events[currentEventList->count].params[2] = 0;
- TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
- eventCount++;
+ TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+ currentEventList->count++;
}
+
+ if (currentEventList->count == currentEventList->capacity) return; // Security check
- // INPUT_TOUCH_POSITION
+ // Event type: INPUT_TOUCH_POSITION
// TODO: It requires the id!
/*
if (((int)CORE.Input.Touch.currentPosition[id].x != (int)CORE.Input.Touch.previousPosition[id].x) ||
((int)CORE.Input.Touch.currentPosition[id].y != (int)CORE.Input.Touch.previousPosition[id].y))
{
- events[eventCount].frame = frame;
- events[eventCount].type = INPUT_TOUCH_POSITION;
- events[eventCount].params[0] = id;
- events[eventCount].params[1] = (int)CORE.Input.Touch.currentPosition[id].x;
- events[eventCount].params[2] = (int)CORE.Input.Touch.currentPosition[id].y;
+ currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+ currentEventList->events[currentEventList->count].type = INPUT_TOUCH_POSITION;
+ currentEventList->events[currentEventList->count].params[0] = id;
+ currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Touch.currentPosition[id].x;
+ currentEventList->events[currentEventList->count].params[2] = (int)CORE.Input.Touch.currentPosition[id].y;
- TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_POSITION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
- eventCount++;
+ TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_POSITION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+ currentEventList->count++;
}
*/
+
+ if (currentEventList->count == currentEventList->capacity) return; // Security check
}
+ //-------------------------------------------------------------------------------------
+ // Gamepad input currentEventList->events recording
+ //-------------------------------------------------------------------------------------
for (int gamepad = 0; gamepad < MAX_GAMEPADS; gamepad++)
{
- // INPUT_GAMEPAD_CONNECT
+ // Event type: INPUT_GAMEPAD_CONNECT
/*
if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
(CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to ready
@@ -3028,7 +3161,7 @@ static void RecordAutomationEvent(unsigned int frame)
}
*/
- // INPUT_GAMEPAD_DISCONNECT
+ // Event type: INPUT_GAMEPAD_DISCONNECT
/*
if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) &&
(!CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to not-ready
@@ -3039,122 +3172,84 @@ static void RecordAutomationEvent(unsigned int frame)
for (int button = 0; button < MAX_GAMEPAD_BUTTONS; button++)
{
- // INPUT_GAMEPAD_BUTTON_UP
+ // Event type: INPUT_GAMEPAD_BUTTON_UP
if (CORE.Input.Gamepad.previousButtonState[gamepad][button] && !CORE.Input.Gamepad.currentButtonState[gamepad][button])
{
- events[eventCount].frame = frame;
- events[eventCount].type = INPUT_GAMEPAD_BUTTON_UP;
- events[eventCount].params[0] = gamepad;
- events[eventCount].params[1] = button;
- events[eventCount].params[2] = 0;
+ currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+ currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_UP;
+ currentEventList->events[currentEventList->count].params[0] = gamepad;
+ currentEventList->events[currentEventList->count].params[1] = button;
+ currentEventList->events[currentEventList->count].params[2] = 0;
- TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_BUTTON_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
- eventCount++;
+ TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+ currentEventList->count++;
}
+
+ if (currentEventList->count == currentEventList->capacity) return; // Security check
- // INPUT_GAMEPAD_BUTTON_DOWN
+ // Event type: INPUT_GAMEPAD_BUTTON_DOWN
if (CORE.Input.Gamepad.currentButtonState[gamepad][button])
{
- events[eventCount].frame = frame;
- events[eventCount].type = INPUT_GAMEPAD_BUTTON_DOWN;
- events[eventCount].params[0] = gamepad;
- events[eventCount].params[1] = button;
- events[eventCount].params[2] = 0;
+ currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+ currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_DOWN;
+ currentEventList->events[currentEventList->count].params[0] = gamepad;
+ currentEventList->events[currentEventList->count].params[1] = button;
+ currentEventList->events[currentEventList->count].params[2] = 0;
- TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_BUTTON_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
- eventCount++;
+ TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+ currentEventList->count++;
}
+
+ if (currentEventList->count == currentEventList->capacity) return; // Security check
}
for (int axis = 0; axis < MAX_GAMEPAD_AXIS; axis++)
{
- // INPUT_GAMEPAD_AXIS_MOTION
+ // Event type: INPUT_GAMEPAD_AXIS_MOTION
if (CORE.Input.Gamepad.axisState[gamepad][axis] > 0.1f)
{
- events[eventCount].frame = frame;
- events[eventCount].type = INPUT_GAMEPAD_AXIS_MOTION;
- events[eventCount].params[0] = gamepad;
- events[eventCount].params[1] = axis;
- events[eventCount].params[2] = (int)(CORE.Input.Gamepad.axisState[gamepad][axis]*32768.0f);
+ currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+ currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_AXIS_MOTION;
+ currentEventList->events[currentEventList->count].params[0] = gamepad;
+ currentEventList->events[currentEventList->count].params[1] = axis;
+ currentEventList->events[currentEventList->count].params[2] = (int)(CORE.Input.Gamepad.axisState[gamepad][axis]*32768.0f);
- TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_AXIS_MOTION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
- eventCount++;
+ TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_AXIS_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+ currentEventList->count++;
}
+
+ if (currentEventList->count == currentEventList->capacity) return; // Security check
}
}
+ //-------------------------------------------------------------------------------------
- // INPUT_GESTURE
+ // Gestures input currentEventList->events recording
+ //-------------------------------------------------------------------------------------
if (GESTURES.current != GESTURE_NONE)
{
- events[eventCount].frame = frame;
- events[eventCount].type = INPUT_GESTURE;
- events[eventCount].params[0] = GESTURES.current;
- events[eventCount].params[1] = 0;
- events[eventCount].params[2] = 0;
+ // Event type: INPUT_GESTURE
+ currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter;
+ currentEventList->events[currentEventList->count].type = INPUT_GESTURE;
+ currentEventList->events[currentEventList->count].params[0] = GESTURES.current;
+ currentEventList->events[currentEventList->count].params[1] = 0;
+ currentEventList->events[currentEventList->count].params[2] = 0;
- TRACELOG(LOG_INFO, "[%i] INPUT_GESTURE: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]);
- eventCount++;
+ TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GESTURE | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]);
+ currentEventList->count++;
+
+ if (currentEventList->count == currentEventList->capacity) return; // Security check
}
-}
+ //-------------------------------------------------------------------------------------
-// Play automation event
-static void PlayAutomationEvent(unsigned int frame)
-{
- for (unsigned int i = 0; i < eventCount; i++)
- {
- if (events[i].frame == frame)
- {
- switch (events[i].type)
- {
- // Input events
- case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[events[i].params[0]] = false; break; // param[0]: key
- case INPUT_KEY_DOWN: CORE.Input.Keyboard.currentKeyState[events[i].params[0]] = true; break; // param[0]: key
- case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[events[i].params[0]] = false; break; // param[0]: key
- case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[events[i].params[0]] = true; break; // param[0]: key
- case INPUT_MOUSE_POSITION: // param[0]: x, param[1]: y
- {
- CORE.Input.Mouse.currentPosition.x = (float)events[i].params[0];
- CORE.Input.Mouse.currentPosition.y = (float)events[i].params[1];
- } break;
- case INPUT_MOUSE_WHEEL_MOTION: // param[0]: x delta, param[1]: y delta
- {
- CORE.Input.Mouse.currentWheelMove.x = (float)events[i].params[0]; break;
- CORE.Input.Mouse.currentWheelMove.y = (float)events[i].params[1]; break;
- } break;
- case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[events[i].params[0]] = false; break; // param[0]: id
- case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[events[i].params[0]] = true; break; // param[0]: id
- case INPUT_TOUCH_POSITION: // param[0]: id, param[1]: x, param[2]: y
- {
- CORE.Input.Touch.position[events[i].params[0]].x = (float)events[i].params[1];
- CORE.Input.Touch.position[events[i].params[0]].y = (float)events[i].params[2];
- } break;
- case INPUT_GAMEPAD_CONNECT: CORE.Input.Gamepad.ready[events[i].params[0]] = true; break; // param[0]: gamepad
- case INPUT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[events[i].params[0]] = false; break; // param[0]: gamepad
- case INPUT_GAMEPAD_BUTTON_UP: CORE.Input.Gamepad.currentButtonState[events[i].params[0]][events[i].params[1]] = false; break; // param[0]: gamepad, param[1]: button
- case INPUT_GAMEPAD_BUTTON_DOWN: CORE.Input.Gamepad.currentButtonState[events[i].params[0]][events[i].params[1]] = true; break; // param[0]: gamepad, param[1]: button
- case INPUT_GAMEPAD_AXIS_MOTION: // param[0]: gamepad, param[1]: axis, param[2]: delta
- {
- CORE.Input.Gamepad.axisState[events[i].params[0]][events[i].params[1]] = ((float)events[i].params[2]/32768.0f);
- } break;
- case INPUT_GESTURE: GESTURES.current = events[i].params[0]; break; // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current
+ // Window events recording
+ //-------------------------------------------------------------------------------------
+ // TODO.
+ //-------------------------------------------------------------------------------------
- // Window events
- case WINDOW_CLOSE: CORE.Window.shouldClose = true; break;
- case WINDOW_MAXIMIZE: MaximizeWindow(); break;
- case WINDOW_MINIMIZE: MinimizeWindow(); break;
- case WINDOW_RESIZE: SetWindowSize(events[i].params[0], events[i].params[1]); break;
-
- // Custom events
- case ACTION_TAKE_SCREENSHOT:
- {
- TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter));
- screenshotCounter++;
- } break;
- case ACTION_SETTARGETFPS: SetTargetFPS(events[i].params[0]); break;
- default: break;
- }
- }
- }
+ // Custom actions events recording
+ //-------------------------------------------------------------------------------------
+ // TODO.
+ //-------------------------------------------------------------------------------------
}
#endif
diff --git a/src/rcore.h b/src/rcore.h
index 1127585a0..a6955f8cc 100644
--- a/src/rcore.h
+++ b/src/rcore.h
@@ -89,6 +89,10 @@
#define MAX_DECOMPRESSION_SIZE 64 // Maximum size allocated for decompression in MB
#endif
+#ifndef MAX_AUTOMATION_EVENTS
+ #define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record
+#endif
+
// Flags operation macros
#define FLAG_SET(n, f) ((n) |= (f))
#define FLAG_CLEAR(n, f) ((n) &= ~(f))