diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ca7763..1b341fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,7 @@ add_library(akgl SHARED src/staticstring.c src/tilemap.c src/util.c + src/stage.c ${GAMECONTROLLERDB_H} ) diff --git a/include/akgl/SDL_GameControllerDB.h b/include/akgl/SDL_GameControllerDB.h index 1419a9f..ca04f60 100644 --- a/include/akgl/SDL_GameControllerDB.h +++ b/include/akgl/SDL_GameControllerDB.h @@ -1,9 +1,9 @@ #ifndef _SDL_GAMECONTROLLERDB_H_ #define _SDL_GAMECONTROLLERDB_H_ -// Taken from https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt on Tue Jun 2 04:45:53 PM EDT 2026 +// Taken from https://raw.githubusercontent.com/mdqinc/SDL_GameControllerDB/refs/heads/master/gamecontrollerdb.txt on Sun Jun 21 04:37:50 PM EDT 2026 -#define AKGL_SDL_GAMECONTROLLER_DB_LEN 2229 +#define AKGL_SDL_GAMECONTROLLER_DB_LEN 2241 const char *SDL_GAMECONTROLLER_DB[] = { "03000000300f00000a01000000000000,3 In 1 Conversion Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows", @@ -687,7 +687,7 @@ const char *SDL_GAMECONTROLLER_DB[] = { "03000000a306000020f6000000000000,Saitek PS2700 PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Windows", "03000000300f00001101000000000000,Saitek Rumble,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Windows", "03000000e804000000a0000000000000,Samsung EIGP20,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows", - "03000000bd12000010d1000000000000,Sanwa 4Button,a:b0,b:b1,x:b2,y:b3,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,platform:Windows", + "03000000bd12000010d1000000000000,Sanwa 4Button,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,x:b2,y:b3,platform:Windows", "03000000c01100000252000000000000,Sanwa Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows", "03000000c01100004350000000000000,Sanwa Micro Grip P3,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,x:b3,y:b2,platform:Windows", "03000000411200004550000000000000,Sanwa Micro Grip Pro,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a1,righty:a2,start:b9,x:b1,y:b3,platform:Windows", @@ -701,14 +701,14 @@ const char *SDL_GAMECONTROLLER_DB[] = { "030000004f04000028b3000000000000,Score A,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows", "03000000952e00002577000000000000,Scuf PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows", "03000000a30c00002500000000000000,Sega Genesis Mini 3B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows", - "03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,leftshoulder:b6,lefttrigger:b7,back:b8,start:b9,x:b3,y:b0,platform:Windows", + "03000000a30c00002400000000000000,Sega Mega Drive Mini 6B Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows", "03000000d804000086e6000000000000,Sega Multi Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a2,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows", "0300000000050000289b000000000000,Sega Saturn Adapter,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows", "0300000000f000000800000000000000,Sega Saturn Controller,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b7,righttrigger:b3,start:b0,x:b5,y:b6,platform:Windows", "03000000730700000601000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows", "03000000b40400000a01000000000000,Sega Saturn Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows", "030000003b07000004a1000000000000,SFX,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Windows", - "03000000632500002705000000000000,ShanWan Gamepad,a:b0,b:b1,x:b3,y:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,-lefty:-a1,+lefty:+a2,leftshoulder:b6,lefttrigger:b8,rightshoulder:b7,righttrigger:b9,start:b11,leftstick:b13,rightstick:b14,platform:Windows", + "03000000632500002705000000000000,ShanWan Gamepad,+lefty:+a2,-lefty:-a1,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,rightshoulder:b7,rightstick:b14,righttrigger:b9,start:b11,x:b3,y:b4,platform:Windows", "03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows", "03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows", "03000000140300000918000000000000,SNES Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows", @@ -737,6 +737,7 @@ const char *SDL_GAMECONTROLLER_DB[] = { "03000000120c0000160e000000000000,Steel Play Metaltech PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows", "03000000110100001914000000000000,SteelSeries,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftstick:b13,lefttrigger:b6,leftx:a0,lefty:a1,rightstick:b14,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows", "03000000381000001214000000000000,SteelSeries Free,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Windows", + "03000000381000004114000000000000,SteelSeries Nimbus Cloud,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b15,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,paddle1:b5,paddle2:b2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:b8,righttrigger:b9,platform:Windows", "03000000110100003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows", "03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows", "03000000381000003114000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows", @@ -780,7 +781,7 @@ const char *SDL_GAMECONTROLLER_DB[] = { "030000005f140000c501000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows", "03000000b80500000210000000000000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows", "030000005f1400002a01000000000000,Trust Predator GM1200,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,x:b3,y:b4,platform:Windows", - "03000000580400000a10000000000000,Trust Sight Fighter,a:b0,b:b1,x:b3,y:b4,back:b5,start:b2,leftshoulder:b6,rightshoulder:b7,leftx:a0,lefty:a1,lefttrigger:b8,righttrigger:b9,platform:Windows", + "03000000580400000a10000000000000,Trust Sight Fighter,a:b0,b:b1,back:b5,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b2,x:b3,y:b4,platform:Windows", "030000004f04000087b6000000000000,TWCS Throttle,dpdown:b8,dpleft:b9,dpright:b7,dpup:b6,leftstick:b5,lefttrigger:-a5,leftx:a0,lefty:a1,righttrigger:+a5,platform:Windows", "03000000411200000450000000000000,Twin Shock,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a4,start:b11,x:b3,y:b0,platform:Windows", "03000000d90400000200000000000000,TwinShock PS2 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows", @@ -942,6 +943,7 @@ const char *SDL_GAMECONTROLLER_DB[] = { "03000000503200000110000045010000,Atari VCS Classic,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X", "03000000503200000110000047010000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X", "03000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Mac OS X", + "03000000e30500008106000000010000,Austgame GameCube Adapter,a:b0,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:+a4,leftx:a0,lefty:a1,misc3:b5,misc4:b6,rightshoulder:b4,righttrigger:-a4,rightx:a2,righty:a3,start:b7,x:b1,y:b3,platform:Mac OS X", "030000008a3500000102000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X", "030000008a3500000201000000010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X", "030000008a3500000202000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X", @@ -1119,6 +1121,7 @@ const char *SDL_GAMECONTROLLER_DB[] = { "03000000110100002014000000000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X", "03000000110100002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X", "03000000381000002014000001000000,SteelSeries Nimbus,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,x:b2,y:b3,platform:Mac OS X", + "03000000381000004114000000010000,SteelSeries Nimbus Cloud,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b15,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,paddle1:b5,paddle2:b2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Mac OS X", "05000000484944204465766963650000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X", "050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b15,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b16,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b14,x:b2,y:b3,platform:Mac OS X", "03000000381000003014000000000000,SteelSeries Stratus Duo,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X", @@ -1250,6 +1253,9 @@ const char *SDL_GAMECONTROLLER_DB[] = { "03000000c82d00001230000011010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b2,paddle2:b5,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", "03000000c82d00001330000011010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b26,paddle1:b23,paddle2:b19,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", "05000000c82d00001230000000010000,8BitDo Ultimate,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", + "03000000c82d00001260000011010000,8BitDo Ultimate 2,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Linux", + "03000000c82d00000b31000014010000,8BitDo Ultimate 2,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux", + "05000000c82d00001260000001000000,8BitDo Ultimate 2,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,paddle1:b17,paddle2:b16,paddle3:b2,paddle4:b5,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Linux", "03000000c82d00000a31000014010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", "03000000c82d00001d30000011010000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", "05000000c82d00001b30000001000000,8BitDo Ultimate 2C,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b5,paddle2:b2,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", @@ -1277,6 +1283,7 @@ const char *SDL_GAMECONTROLLER_DB[] = { "03000000020500000913000010010000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", "03000000373500000710000010010000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", "05000000373500004610000001000000,Anbernic RG P01,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", + "19000000010000000100000000010000,Anbernic RG28XX Controller,a:b4,b:b3,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b7,misc1:b1,rightshoulder:b8,leftstick:b12,rightstick:b13,start:b10,x:b5,y:b6,platform:Linux", "03000000190e00000110000010010000,Aquaplus Piece,a:b1,b:b0,back:b3,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b2,platform:Linux", "03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux", "03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux", @@ -1349,6 +1356,7 @@ const char *SDL_GAMECONTROLLER_DB[] = { "05000000ac0500002d0200001b010000,GameSir G4s,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b33,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", "03000000ac0500007a05000011010000,GameSir G5,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", "03000000373500009710000001020000,GameSir Kaleid Flux,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", + "03000000373500005810000001020000,GameSir K1,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", "03000000ac0500001a06000011010000,GameSir T3 2.02,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", "03000000373500000410000044010000,GameSir T4 Kaleid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", "03000000bc2000005656000011010000,GameSir T4w,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux", @@ -1447,6 +1455,7 @@ const char *SDL_GAMECONTROLLER_DB[] = { "03000000242f00008a00000011010000,JYS Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux", "03000000314100001935000000010000,LeadJoy Xeno Plus,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:+a5,rightx:a4,righty:a4,start:b7,x:b2,y:b3,platform:Linux", "030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", + "030000006f0e000001f5000010010000,Logic3 Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", "030000006d040000d1ca000000000000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux", "030000006d040000d1ca000011010000,Logitech Chillstream,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux", "030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux", @@ -1537,10 +1546,10 @@ const char *SDL_GAMECONTROLLER_DB[] = { "03000000853200000706000012010000,Nacon GC100,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", "030000006b140000010c000010010000,Nacon GC400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux", "05000000853200000503000000010000,Nacon MGX Pro,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", - "0300000085320000170d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux", - "0300000085320000190d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux", - "0300000085320000180d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux", "0300000085320000160d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux", + "0300000085320000170d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux", + "0300000085320000180d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux", + "0300000085320000190d000011010000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux", "030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux", "030000004f1f00000800000011010000,NeoGeo PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux", "0300000092120000474e000000010000,NeoGeo X Arcade Stick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b3,y:b2,platform:Linux", @@ -1619,6 +1628,7 @@ const char *SDL_GAMECONTROLLER_DB[] = { "03000000d62000000520000050010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", "03000000d62000000b20000001010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", "03000000d62000000f20000001010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b7,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", + "03000000d62000006520000002010000,PowerA Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", "030000006d040000d2ca000011010000,Precision Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux", "03000000250900000017000010010000,PS SS N64 Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b5,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2~,righty:a3,start:b8,platform:Linux", "03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux", @@ -1752,7 +1762,8 @@ const char *SDL_GAMECONTROLLER_DB[] = { "030000005e0400008e02000073050000,Speedlink Torid,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", "030000005e0400008e02000020200000,SpeedLink Xeox Pro Analog,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", "03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", - "05000000381000001214000001000000,SteelSeries Free,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b12,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a2,righttrigger:a3,platform:Linux", + "05000000381000001214000001000000,SteelSeries Free,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a3,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", + "05000000381000004114000000010000,SteelSeries Nimbus Cloud,a:b0,b:b1,x:b3,y:b4,back:b10,guide:b15,start:b11,leftstick:b13,rightstick:b14,leftshoulder:b6,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,paddle1:b5,paddle2:b2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a5,righttrigger:a4,platform:Linux", "050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b10,guide:b11,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Linux", "03000000381000003014000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", "03000000381000003114000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", @@ -1798,9 +1809,10 @@ const char *SDL_GAMECONTROLLER_DB[] = { "03000000e00d00000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux", "03000000f00600000300000003000000,TRBot Virtual Joypad,a:b11,b:b12,back:b15,dpdown:b6,dpleft:b3,dpright:b4,dpup:b5,leftshoulder:b17,leftstick:b21,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b22,righttrigger:a2,rightx:a3,righty:a4,start:b16,x:b13,y:b14,platform:Linux", "030000005f140000c501000010010000,Trust Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux", + "03000000f51000001370000000000000,Turtle Beach ReactR,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", + "03000000f51000001370000000010000,Turtle Beach ReactR,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", "06000000f51000000870000003010000,Turtle Beach Recon,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux", - "03000000f51000001370000000010000,Turtle Beach ReactR,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux", - "03000000f51000001370000000000000,Turtle Beach ReactR,a:b0,b:b1,x:b2,y:b3,back:b6,guide:b8,start:b7,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Linux", + "05000000f51000008f01000001000000,Turtle Beach Rematch Nintendo Switch Controller,a:b0,b:b1,x:b2,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,lefttrigger:b6,righttrigger:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,platform:Linux", "03000000100800000100000010010000,Twin PlayStation Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,platform:Linux", "03000000c0160000e105000010010000,Ultimate Atari Fight Stick,a:b1,b:b2,back:b9,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b8,x:b0,y:b3,platform:Linux", "03000000151900005678000010010000,Uniplay U6,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux", diff --git a/include/akgl/actor.h b/include/akgl/actor.h index a5d16e9..d4008ca 100644 --- a/include/akgl/actor.h +++ b/include/akgl/actor.h @@ -59,6 +59,7 @@ extern char *AKGL_ACTOR_STATE_STRING_NAMES[AKGL_ACTOR_MAX_STATES+1]; #define AKGL_MAX_HEAP_ACTOR 64 typedef struct akgl_Actor { + uint8_t type; uint8_t refcount; char name[AKGL_ACTOR_MAX_NAME_LENGTH]; akgl_Character *basechar; @@ -70,7 +71,10 @@ typedef struct akgl_Actor { bool movement_controls_face; void *actorData; bool visible; + bool simulating; SDL_Time movetimer; + // Bounding box accounting for the actor's current real position plus physics delta + SDL_FRect bbox; // Velocity. Combined effect of all forces acting on the actor resulting // in energy along an axis. float32_t vx; @@ -100,13 +104,24 @@ typedef struct akgl_Actor { float32_t y; float32_t z; float32_t scale; + // Reference to the BSP Node currently containing this actor + aksl_TreeNode *bsphome; + // Reference to the BSP linked list node currently holding this actor + aksl_ListNode *bsplistnode; struct akgl_Actor *children[AKGL_ACTOR_MAX_CHILDREN]; struct akgl_Actor *parent; + + // method that controls logic update functions akerr_ErrorContext AKERR_NOIGNORE *(*updatefunc)(struct akgl_Actor *obj); + // method that renders the actor to the currently active renderer akerr_ErrorContext AKERR_NOIGNORE *(*renderfunc)(struct akgl_Actor *obj); + // method that changes the actor's face flags based on other state akerr_ErrorContext AKERR_NOIGNORE *(*facefunc)(struct akgl_Actor *obj); + // method that performs movement logic on the actor based on state flags akerr_ErrorContext AKERR_NOIGNORE *(*movementlogicfunc)(struct akgl_Actor *obj, float32_t dt); + // method that calculates the correct sprite frame for the actor's current state akerr_ErrorContext AKERR_NOIGNORE *(*changeframefunc)(struct akgl_Actor *obj, akgl_Sprite *curSprite, SDL_Time curtimems); + // method to add a child to this actor akerr_ErrorContext AKERR_NOIGNORE *(*addchild)(struct akgl_Actor *obj, struct akgl_Actor *child); } akgl_Actor; diff --git a/include/akgl/error.h b/include/akgl/error.h index a76608b..cdba7f5 100644 --- a/include/akgl/error.h +++ b/include/akgl/error.h @@ -12,7 +12,9 @@ #define RESTORE_GCC_WARNINGS \ _Pragma("GCC diagnostic pop") -#define AKGL_ERR_SDL AKERR_LAST_ERRNO_VALUE + 1 -#define AKGL_ERR_LOGICINTERRUPT AKERR_LAST_ERRNO_VALUE + 2 +#define AKGL_ERR_SDL AKERR_LAST_ERRNO_VALUE + 1 /** Error in the SDL library. See SDL_GetError() for more detail. */ +#define AKGL_ERR_LOGICINTERRUPT AKERR_LAST_ERRNO_VALUE + 2 /** Raised by an actor to tell the physics simulator not to process this node */ +#define AKGL_ERR_REGISTRY AKERR_LAST_ERRNO_VALUE + 3 /** The AKGL Registry was not able to fulfill the operation */ +#define AKGL_ERR_OOHEAP AKERR_LAST_ERRNO_VALUE + 4 /** The AKGL Heap in this context has been exhausted */ #endif // _ERROR_H_ diff --git a/include/akgl/heap.h b/include/akgl/heap.h index 0c56ac6..eb434be 100644 --- a/include/akgl/heap.h +++ b/include/akgl/heap.h @@ -1,11 +1,12 @@ #ifndef _AKGL_HEAP_H_ #define _AKGL_HEAP_H_ -#include "sprite.h" -#include "actor.h" -#include "character.h" -#include "staticstring.h" +#include +#include +#include +#include #include +#include #ifndef AKGL_MAX_HEAP_ACTOR #define AKGL_MAX_HEAP_ACTOR 64 @@ -23,7 +24,10 @@ #define AKGL_MAX_HEAP_STRING 256 #endif #ifndef AKGL_MAX_HEAP_LIST -#define AKGL_MAX_HEAP_LIST AKGL_MAX_HEAP_ACTOR +#define AKGL_MAX_HEAP_LIST (AKGL_MAX_HEAP_ACTOR * 2) + 256 +#endif +#ifndef AKGL_MAX_HEAP_TREE +#define AKGL_MAX_HEAP_TREE 32 #endif extern akgl_Actor HEAP_ACTOR[AKGL_MAX_HEAP_ACTOR]; @@ -32,20 +36,30 @@ extern akgl_SpriteSheet HEAP_SPRITESHEET[AKGL_MAX_HEAP_SPRITESHEET]; extern akgl_Character HEAP_CHARACTER[AKGL_MAX_HEAP_CHARACTER]; extern akgl_String HEAP_STRING[AKGL_MAX_HEAP_STRING]; extern aksl_ListNode HEAP_LIST[AKGL_MAX_HEAP_LIST]; +extern aksl_TreeNode HEAP_TREE[AKGL_MAX_HEAP_TREE]; +extern akgl_BSPLeaf HEAP_TREE_LEAVES[AKGL_MAX_HEAP_TREE]; +extern void *AKGL_LIST_SENTINEL; akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_init(); akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_init_list(); +akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_init_tree(); akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_init_actor(); akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_actor(akgl_Actor **dest); akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_sprite(akgl_Sprite **dest); akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_spritesheet(akgl_SpriteSheet **dest); akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_character(akgl_Character **dest); akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_string(akgl_String **dest); +akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_list(aksl_ListNode **dest); +akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_next_tree(aksl_TreeNode **dest); akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_release_actor(akgl_Actor *ptr); akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_release_sprite(akgl_Sprite *ptr); akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_release_spritesheet(akgl_SpriteSheet *ptr); akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_release_character(akgl_Character *ptr); akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_release_string(akgl_String *ptr); +akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_release_list(aksl_ListNode *ptr); + +akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_iter_list_release(aksl_ListNode *ptr, void *data); +akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_iter_tree_release(aksl_TreeNode *ptr, void *data); #endif //_AKGL_HEAP_H_ diff --git a/include/akgl/stage.h b/include/akgl/stage.h index e5d8c19..10a8d94 100644 --- a/include/akgl/stage.h +++ b/include/akgl/stage.h @@ -1,14 +1,35 @@ #ifndef _STAGE_H_ #define _STAGE_H_ +#include +#include + +#define AKGL_STAGE_PARTITION_HORIZONTAL 0 +#define AKGL_STAGE_PARTITION_VERTICAL 1 +#define AKGL_STAGE_PARTITION_MAXDEPTH 4 + // Stages are collections of lights and cameras in a 2D or 3D space wherein actors // and backgrounds are placed and directed for action. typedef struct akgl_Stage { aksl_TreeNode bsp; akerr_ErrorContext AKERR_NOIGNORE *(*partition)(struct akgl_Stage *self); + akerr_ErrorContext AKERR_NOIGNORE *(*partition_actor)(struct akgl_Stage *self, akgl_Actor *actor); } akgl_Stage; -// A function to divide the stage into a binary space partition tree with lists of actors in each partition -akerr_ErrorContext AKERR_NOIGNORE akgl_stage_2d_bsp(akgl_Stage *self); +typedef struct akgl_BSPContext { + aksl_TreeNode *left; + aksl_TreeNode *right; + aksl_TreeNode *parent; +} akgl_BSPContext; -#endif _STAGE_H_ +typedef struct akgl_BSPLeaf { + SDL_FRect extent; + aksl_ListNode *actors; +} + +akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d(akgl_Stage *self); + +// A function to divide the stage into a binary space partition tree with lists of actors in each partition +akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_partition(akgl_Stage *self); + +#endif // _STAGE_H_ diff --git a/include/akgl/staticstring.h b/include/akgl/staticstring.h index 704a70c..b1f5265 100644 --- a/include/akgl/staticstring.h +++ b/include/akgl/staticstring.h @@ -10,9 +10,9 @@ typedef struct { int refcount; + int length; char data[AKGL_MAX_STRING_LENGTH]; } akgl_String; -akerr_ErrorContext AKERR_NOIGNORE *akgl_string_initialize(akgl_String *obj, char *init); -akerr_ErrorContext AKERR_NOIGNORE *akgl_string_copy(akgl_String *src, akgl_String *dst, int count); +akerr_ErrorContext AKERR_NOIGNORE *akgl_string_initialize(akgl_String *obj, char *init);akerr_ErrorContext AKERR_NOIGNORE *akgl_string_copy(akgl_String *src, akgl_String *dst, int count); #endif //_STRING_H_ diff --git a/src/controller.c b/src/controller.c index dbd1ef3..2170dfd 100644 --- a/src/controller.c +++ b/src/controller.c @@ -277,7 +277,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_controller_default(int controlmapid, cha controlmap->jsid = jsid; controlmap->target = SDL_GetPointerProperty(AKGL_REGISTRY_ACTOR, actorname, NULL); - FAIL_ZERO_BREAK(errctx, controlmap->target, AKERR_REGISTRY, "Actor %s not found in registry", actorname); + FAIL_ZERO_BREAK(errctx, controlmap->target, AKGL_ERR_REGISTRY, "Actor %s not found in registry", actorname); // ---- KEYBOARD CONTROLS ---- diff --git a/src/game.c b/src/game.c index e2d1dea..d3baf78 100644 --- a/src/game.c +++ b/src/game.c @@ -50,6 +50,12 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_init() int i = 0; PREPARE_ERROR(e); + + akerr_name_for_status(AKGL_ERR_SDL, "SDL Error"); + akerr_name_for_status(AKGL_ERR_LOGICINTERRUPT, "AKGL Logic Interrupt"); + akerr_name_for_status(AKGL_ERR_REGISTRY, "AKGL Registry Error"); + akerr_name_for_status(AKGL_ERR_OOHEAP, "AKGL Heap Capacity"); + strncpy((char *)&game.libversion, AKGL_VERSION, 32); game.gameStartTime = SDL_GetTicksNS(); game.lastIterTime = game.gameStartTime; @@ -445,7 +451,8 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_game_update(akgl_Iterator *opflags) PASS(e, actor->updatefunc(actor)); } } - PASS(e, physics->simulate(physics, NULL)); + + PASS(e, physics->simulate(physics, NULL)); PASS(e, renderer->draw_world(renderer, NULL)); PASS(e, akgl_game_state_unlock()); SUCCEED_RETURN(e); diff --git a/src/heap.c b/src/heap.c index caa2eef..c06f7ff 100644 --- a/src/heap.c +++ b/src/heap.c @@ -1,3 +1,5 @@ +/** @file */ + #include #include @@ -15,8 +17,10 @@ akgl_SpriteSheet HEAP_SPRITESHEET[AKGL_MAX_HEAP_SPRITESHEET]; akgl_Character HEAP_CHARACTER[AKGL_MAX_HEAP_CHARACTER]; akgl_String HEAP_STRING[AKGL_MAX_HEAP_STRING]; aksl_ListNode HEAP_LIST[AKGL_MAX_HEAP_LIST]; +aksl_TreeNode HEAP_TREE[AKGL_MAX_HEAP_TREE]; +aksl_ListNode HEAP_TREE_LEAVES[AKGL_MAX_HEAP_TREE]; -int AKGL_LIST_SENTINEL = 65535; +void *AKGL_LIST_SENTINEL = (void *)1; /** Sentinel value used for aksl_ListNode objects to determine if they are available */ akerr_ErrorContext *akgl_heap_init() { @@ -44,7 +48,17 @@ akerr_ErrorContext *akgl_heap_init_list(void) PREPARE_ERROR(e); for ( int i = 0; i < AKGL_MAX_HEAP_LIST; i++) { memset(&HEAP_LIST[i], 0x00, sizeof(aksl_ListNode)); - HEAP_LIST[i].data = (void *)&AKGL_LIST_SENTINEL; + HEAP_LIST[i].data = AKGL_LIST_SENTINEL; + } + SUCCEED_RETURN(e); +} + +akerr_ErrorContext *akgl_heap_init_tree(void) +{ + PREPARE_ERROR(e); + for ( int i = 0; i < AKGL_MAX_HEAP_TREE; i++) { + memset(&HEAP_TREE[i], 0x00, sizeof(aksl_TreeNode)); + HEAP_TREE[i].leaf = AKGL_LIST_SENTINEL; } SUCCEED_RETURN(e); } @@ -58,20 +72,60 @@ akerr_ErrorContext *akgl_heap_init_actor(void) SUCCEED_RETURN(e); } +/** + * @brief Return the next aksl_ListNode from the heap + * + * @param[out] dest The pointer that will hold the procured aksl_ListNode + * + * @throws AKGL_ERR_OOHEAP There are no available nodes in HEAP_LIST + * @return akerr_ErrorContext* + */ + akerr_ErrorContext *akgl_heap_next_list(aksl_ListNode **dest) { PREPARE_ERROR(errctx); for (int i = 0; i < AKGL_MAX_HEAP_LIST; i++ ) { - if ( HEAP_LIST[i].data != &AKGL_LIST_SENTINEL ) { + if ( HEAP_LIST[i].data != AKGL_LIST_SENTINEL ) { continue; } *dest = &HEAP_LIST[i]; HEAP_LIST[i].data = NULL; SUCCEED_RETURN(errctx); } - FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused list on the heap"); + FAIL_RETURN(errctx, AKGL_ERR_OOHEAP, "Unable to find unused list on the heap"); } +/** + * @brief Return the next aksl_TreeNode from the heap + * + * @param[out] dest The pointer that will hold the procured tree node + * + * @throws AKGL_ERR_OOHEAP There are no available nodes in HEAP_TREE + * @return akerr_ErrorContext* + */ + +akerr_ErrorContext *akgl_heap_next_tree(aksl_TreeNode **dest) +{ + PREPARE_ERROR(errctx); + for (int i = 0; i < AKGL_MAX_HEAP_TREE; i++ ) { + if ( HEAP_TREE[i].leaf != &AKGL_LIST_SENTINEL ) { + continue; + } + *dest = &HEAP_TREE[i]; + HEAP_TREE[i].leaf = &HEAP_TREE_LEAVES[i]; + SUCCEED_RETURN(errctx); + } + FAIL_RETURN(errctx, AKGL_ERR_OOHEAP, "Unable to find unused tree on the heap"); +} + +/** + * @brief Release an aksl_ListNode that came from HEAP_TREE to make it available again + * + * @param[in] tree The aksl_ListNode to release. + * + * @return akerr_ErrorContext* + */ + akerr_ErrorContext *akgl_heap_release_list(aksl_ListNode *list) { PREPARE_ERROR(e); @@ -82,6 +136,76 @@ akerr_ErrorContext *akgl_heap_release_list(aksl_ListNode *list) SUCCEED_RETURN(e); } +/** + * @brief Release an aksl_TreeNode that came from HEAP_TREE to make it available again + * + * @param[in] tree The aksl_TreeNode to release. Ensure the leaf on this node has been released and the leaf is set to NULL. + * + * @throws AKERR_VALUE The aksl_TreeNode contains a leaf that has not been released. + * @return akerr_ErrorContext* + */ + +akerr_ErrorContext *akgl_heap_release_tree(aksl_TreeNode *tree) +{ + PREPARE_ERROR(e); + FAIL_ZERO_RETURN(e, tree, AKERR_NULLPOINTER, "tree"); + tree->left = NULL; + tree->right = NULL; + if ( tree->leaf != NULL && tree->leaf->actors != NULL ) { + FAIL_RETURN(e, AKERR_VALUE, "Can't release tree node %p until its leaf actors %p is released and leaf->actors == NULL", tree, tree->leaf->actors); + } else if ( tree->leaf != NULL ) { + memset((void *)tree->leaf, 0x00, sizeof(akgl_BSPLeaf)); + } + tree->leaf = AKGL_LIST_SENTINEL; + SUCCEED_RETURN(e); +} + +/** + * @brief Iterator callback which releases a given list item back to the heap + * + * @param[in] ptr The aksl_ListNode to free + * @param[in] data Context data that was passed to aksl_list_iterate + * + * @throws AKERR_VALUE The aksl_TreeNode contains a leaf that has not been released. + * @return akerr_ErrorContext* + */ + +akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_iter_list_release(aksl_ListNode *ptr, void *data) +{ + PREPARE_ERROR(e); + FAIL_ZERO_RETURN(e, ptr, AKERR_NULLPOINTER, "ptr)"); + PASS(e, akgl_heap_release_list(ptr)); + SUCCEED_RETURN(e); +} + +/** + * @brief Iterator callback which releases a given tree node back to the heap. Calls akgl_heap_release_list on the tree leaf as well. + * + * @param[in] ptr The aksl_TreeNode to free + * @param[in] data Context data that was passed to aksl_tree_iterate + * + * @return akerr_ErrorContext* + */ +akerr_ErrorContext AKERR_NOIGNORE *akgl_heap_iter_tree_release(aksl_TreeNode *ptr, void *data) +{ + PREPARE_ERROR(e); + FAIL_ZERO_RETURN(e, ptr, AKERR_NULLPOINTER, "ptr"); + if ( ptr->leaf != AKGL_LIST_SENTINEL && ptr->leaf != NULL ) { + PASS(e, akgl_heap_release_list(ptr->leaf)); + ptr->leaf = NULL; + } + PASS(e, akgl_heap_release_tree(ptr)); + SUCCEED_RETURN(e); +} + +/** + * @brief Return the next actor from the heap + * + * @param[out] dest The pointer that will hold the procured actor + * + * @throws AKGL_ERR_OOHEAP There are no available actors in HEAP_ACTOR + * @return akerr_ErrorContext* + */ akerr_ErrorContext *akgl_heap_next_actor(akgl_Actor **dest) { PREPARE_ERROR(errctx); @@ -92,9 +216,18 @@ akerr_ErrorContext *akgl_heap_next_actor(akgl_Actor **dest) *dest = &HEAP_ACTOR[i]; SUCCEED_RETURN(errctx); } - FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused actor on the heap"); + FAIL_RETURN(errctx, AKGL_ERR_OOHEAP, "Unable to find unused actor on the heap"); } +/** + * @brief Return the next akgl_Sprite from the heap + * + * @param[out] dest The pointer that will hold the procured sprite + * + * @throws AKGL_ERR_OOHEAP There are no available actors in HEAP_SPRITE + * @return akerr_ErrorContext* + */ + akerr_ErrorContext *akgl_heap_next_sprite(akgl_Sprite **dest) { PREPARE_ERROR(errctx); @@ -105,9 +238,17 @@ akerr_ErrorContext *akgl_heap_next_sprite(akgl_Sprite **dest) *dest = &HEAP_SPRITE[i]; SUCCEED_RETURN(errctx); } - FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused sprite on the heap"); + FAIL_RETURN(errctx, AKGL_ERR_OOHEAP, "Unable to find unused sprite on the heap"); } +/** + * @brief Return the next akgl_SpriteSheet from the heap + * + * @param[out] dest The pointer that will hold the procured spritesheet + * + * @throws AKGL_ERR_OOHEAP There are no available elements in HEAP_SPRITESHEET + * @return akerr_ErrorContext* + */ akerr_ErrorContext *akgl_heap_next_spritesheet(akgl_SpriteSheet **dest) { PREPARE_ERROR(errctx); @@ -118,9 +259,18 @@ akerr_ErrorContext *akgl_heap_next_spritesheet(akgl_SpriteSheet **dest) *dest = &HEAP_SPRITESHEET[i]; SUCCEED_RETURN(errctx); } - FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused spritesheet on the heap"); + FAIL_RETURN(errctx, AKGL_ERR_OOHEAP, "Unable to find unused spritesheet on the heap"); } +/** + * @brief Return the next akgl_Character from the heap + * + * @param[out] dest The pointer that will hold the procured character + * + * @throws AKGL_ERR_OOHEAP There are no available elements in HEAP_CHARACTER + * @return akerr_ErrorContext* + */ + akerr_ErrorContext *akgl_heap_next_character(akgl_Character **dest) { PREPARE_ERROR(errctx); @@ -131,9 +281,17 @@ akerr_ErrorContext *akgl_heap_next_character(akgl_Character **dest) *dest = &HEAP_CHARACTER[i]; SUCCEED_RETURN(errctx); } - FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused character on the heap"); + FAIL_RETURN(errctx, AKGL_ERR_OOHEAP, "Unable to find unused character on the heap"); } +/** + * @brief Return the next akgl_String from the heap + * + * @param[out] dest The pointer that will hold the procured string + * + * @throws AKGL_ERR_OOHEAP There are no available elements in HEAP_STRING + * @return akerr_ErrorContext* + */ akerr_ErrorContext *akgl_heap_next_string(akgl_String **dest) { PREPARE_ERROR(errctx); @@ -145,9 +303,16 @@ akerr_ErrorContext *akgl_heap_next_string(akgl_String **dest) HEAP_STRING[i].refcount += 1; SUCCEED_RETURN(errctx); } - FAIL_RETURN(errctx, AKERR_HEAP, "Unable to find unused string on the heap"); + FAIL_RETURN(errctx, AKGL_ERR_OOHEAP, "Unable to find unused string on the heap"); } +/** + * @brief Release an akgl_Actor from the heap to be used again + * + * @param[in] ptr The object that will be released + * + * @return akerr_ErrorContext* + */ akerr_ErrorContext *akgl_heap_release_actor(akgl_Actor *ptr) { int i = 0; @@ -168,23 +333,37 @@ akerr_ErrorContext *akgl_heap_release_actor(akgl_Actor *ptr) SUCCEED_RETURN(errctx); } -akerr_ErrorContext *akgl_heap_release_character(akgl_Character *basechar) +/** + * @brief Release an akgl_Character from the heap to be used again + * + * @param[in] ptr The object that will be released + * + * @return akerr_ErrorContext* + */ +akerr_ErrorContext *akgl_heap_release_character(akgl_Character *ptr) { PREPARE_ERROR(errctx); akgl_Iterator opflags; - FAIL_ZERO_RETURN(errctx, basechar, AKERR_NULLPOINTER, "NULL character reference"); + FAIL_ZERO_RETURN(errctx, ptr, AKERR_NULLPOINTER, "NULL character reference"); AKGL_BITMASK_CLEAR(opflags.flags); - if ( basechar->refcount > 0 ) { - basechar->refcount -= 1; + if ( ptr->refcount > 0 ) { + ptr->refcount -= 1; } - if ( basechar->refcount == 0 ) { - SDL_ClearProperty(AKGL_REGISTRY_CHARACTER, (char *)&basechar->name); - memset(basechar, 0x00, sizeof(akgl_Character)); + if ( ptr->refcount == 0 ) { + SDL_ClearProperty(AKGL_REGISTRY_CHARACTER, (char *)&ptr->name); + memset(ptr, 0x00, sizeof(akgl_Character)); } SUCCEED_RETURN(errctx); } +/** + * @brief Release an akgl_Sprite from the heap to be used again + * + * @param[in] ptr The object that will be released + * + * @return akerr_ErrorContext* + */ akerr_ErrorContext *akgl_heap_release_sprite(akgl_Sprite *ptr) { PREPARE_ERROR(errctx); @@ -199,6 +378,13 @@ akerr_ErrorContext *akgl_heap_release_sprite(akgl_Sprite *ptr) SUCCEED_RETURN(errctx); } +/** + * @brief Release an akgl_SpriteSheet from the heap to be used again + * + * @param[in] ptr The object that will be released + * + * @return akerr_ErrorContext* + */ akerr_ErrorContext *akgl_heap_release_spritesheet(akgl_SpriteSheet *ptr) { PREPARE_ERROR(errctx); @@ -217,6 +403,13 @@ akerr_ErrorContext *akgl_heap_release_spritesheet(akgl_SpriteSheet *ptr) SUCCEED_RETURN(errctx); } +/** + * @brief Release an akgl_String from the heap to be used again + * + * @param[in] ptr The object that will be released + * + * @return akerr_ErrorContext* + */ akerr_ErrorContext *akgl_heap_release_string(akgl_String *ptr) { PREPARE_ERROR(errctx); diff --git a/src/physics.c b/src/physics.c index 83d86be..12c22c4 100644 --- a/src/physics.c +++ b/src/physics.c @@ -64,10 +64,24 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_gravity(akgl_PhysicsBacke SUCCEED_RETURN(e); } +/** + * @brief Test two actors for collision + * + * @param[in] self The physics simulation to use + * @param[in] a1 actor 1 + * @param[in] a2 actor 2 + * + * @throws AKERR_NULLPOINTER on null pointer input + * + * @return akerr_ErrorContext* + */ + akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_collide(akgl_PhysicsBackend *self, akgl_Actor *a1, akgl_Actor *a2) { PREPARE_ERROR(e); FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self"); + FAIL_ZERO_RETURN(e, a1, AKERR_NULLPOINTER, "a1"); + FAIL_ZERO_RETURN(e, a2, AKERR_NULLPOINTER, "a2"); FAIL_RETURN(e, AKERR_API, "Not implemented"); SUCCEED_RETURN(e); } @@ -79,7 +93,7 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_arcade_move(struct akgl_PhysicsB FAIL_ZERO_RETURN(e, actor, AKERR_NULLPOINTER, "actor"); actor->x += actor->vx * dt; actor->y += actor->vy * dt; - actor->z += actor->vz * dt; + actor->z += actor->vz * dt; SUCCEED_RETURN(e); } @@ -116,6 +130,17 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_init_arcade(akgl_PhysicsBackend SUCCEED_RETURN(e); } +/** + * @brief Perform arcade physics simulation on all alive actors + * + * @param[in] self Physics simulator object + * @param[in] opflags Iteration operation flags (or NULL) + * + * @throws AKERR_NULLPOINTER on null pointer input + * + * @return akerr_ErrorContext* + */ + akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_simulate(akgl_PhysicsBackend *self, akgl_Iterator *opflags) { PREPARE_ERROR(e); @@ -189,34 +214,47 @@ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_simulate(akgl_PhysicsBackend *se } ATTEMPT { CATCH(e, actor->movementlogicfunc(actor, dt)); - PASS(e, self->gravity(self, actor, dt)); - - // Counteract velocity with atmospheric drag - if ( self->drag_x != 0 ) { - actor->ex -= actor->ex * self->drag_x * dt; - } - if ( self->drag_y != 0 ) { - actor->ey -= actor->ey * self->drag_y * dt; - } - if ( self->drag_z != 0 ) { - actor->ez -= actor->ez * self->drag_z * dt; - } - - actor->vx = actor->ex + actor->tx; - actor->vy = actor->ey + actor->ty; - actor->vz = actor->ez + actor->tz; - - PASS(e, self->move(self, actor, dt)); } CLEANUP { } PROCESS(e) { } HANDLE(e, AKGL_ERR_LOGICINTERRUPT) { // noop } FINISH(e, true); + + PASS(e, self->gravity(self, actor, dt)); + + // Counteract velocity with atmospheric drag + if ( self->drag_x != 0 ) { + actor->ex -= actor->ex * self->drag_x * dt; + } + if ( self->drag_y != 0 ) { + actor->ey -= actor->ey * self->drag_y * dt; + } + if ( self->drag_z != 0 ) { + actor->ez -= actor->ez * self->drag_z * dt; + } + + actor->vx = actor->ex + actor->tx; + actor->vy = actor->ey + actor->ty; + actor->vz = actor->ez + actor->tz; + + PASS(e, self->move(self, actor, dt)); + PASS(e, stage->partition_actor(stage, actor)); } self->gravity_time = curtime; SUCCEED_RETURN(e); } +/** + * @brief Initialize a physics backend according to a string type + * + * @param[out] self The physics backend to initialize + * @param[in] type The type to initialize + * + * @throws AKERR_NULLPOINTER on null pointer input + * @throws AKERR_KEY on invalid type + * + * @return akerr_ErrorContext* + */ akerr_ErrorContext AKERR_NOIGNORE *akgl_physics_factory(akgl_PhysicsBackend *self, akgl_String *type) { uint32_t hashval; diff --git a/src/stage.c b/src/stage.c new file mode 100644 index 0000000..2feef3b --- /dev/null +++ b/src/stage.c @@ -0,0 +1,349 @@ +#include +#include +#include +#include +#include + +/** + * @brief Initialize a 2D stage + * + * @param[in] self akgl_Stage * to initialize + * + * @throw AKERR_NULLPOINTER on null input pointers + * @return akerr_ErrorContext + */ +akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d(akgl_Stage *self) +{ + PREPARE_ERROR(e); + FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self"); + memset((void *)self, 0x00, sizeof(akgl_Stage)); + self->partition = akgl_stage_2d_partition; + self->partition_actor = akgl_stage_2d_bsp_move; + SUCCEED_RETURN(e); +} + +/** + * @brief Flip the current partitioning direction + * + * @param[in] One of AKGL_STAGE_PARTITION_HORIZONTAL or AKGL_STAGE_PARTITION_VERTICAL + * @param[out] dest Location to place the modified direction + * + * @throws AKERR_NULLPOINTER on null pointer inputs + * @throws AKERR_VALUE on invalid direction + * @return akerr_ErrorContext* + */ +akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_partition_switchdirection(uint8_t direction, uint8_t *dest) +{ + PREPARE_ERROR(e); + FAIL_ZERO_RETURN(e, dest, AKERR_NULLPOINTER, "dest"); + if ( direction == AKGL_STAGE_PARTITION_HORIZONTAL ) { + *dest = AKGL_STAGE_PARTITION_VERTICAL; + } else if ( direction == AKGL_STAGE_PARTITION_VERTICAL ) { + *dest = AKGL_STAGE_PARTITION_HORIZONTAL; + } else { + FAIL_RETURN(e, AKERR_VALUE, "Unknown partition direction %d", direction); + } +} + +/** + * @brief Iterator function that sorts an actor into one of three lists based on colliding rectangles + * + * This function tests which of two rectangles ("left" and "right") in the context collide with the actor. + * The actor is sorted into the appropriate linked list for each given rectangle. + * If the actor collides with neither rectangle, it is pushed onto the parent list. + * Note that the existing node is not pushed - a new node is taken from the heap and pushed. + * Otherwise we would have to pop the actor out of whatever list it was in, which would + * modify the list during iteration. + * + * @param[in] node A list node containing an akgl_Actor in ->data + * @param[in] data A pointer to akgl_BSPContext containing left, right, and parent SDL_FRect structures + * + * @throws AKERR_NULLPOINTER on null pointer inputs + * @return akerr_ErrorContext* + */ +akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_bsp_actoriter(aksl_ListNode *node, void *data) +{ + bool e1intersect; + bool e2intersect; + akgl_BSPContext *context = NULL; + akgl_Actor *actor = NULL; + aksl_ListNode *dest = NULL; + aksl_ListNode *newnode = NULL; + + PREPARE_ERROR(e); + FAIL_ZERO_RETURN(e, node, AKERR_NULLPOINTER, "node"); + FAIL_ZERO_RETURN(e, data, AKERR_NULLPOINTER, "data"); + FAIL_ZERO_RETURN(e, node->data, AKERR_NULLPOINTER, "node->data"); + FAIL_ZERO_RETURN(e, context->left, AKERR_NULLPOINTER, "left"); + FAIL_ZERO_RETURN(e, context->right, AKERR_NULLPOINTER, "right"); + FAIL_ZERO_RETURN(e, context->parent, AKERR_NULLPOINTER, "parent"); + FAIL_ZERO_RETURN(e, context->left->leaf.actors, AKERR_NULLPOINTER, "left->leaf"); + FAIL_ZERO_RETURN(e, context->right->leaf.actors, AKERR_NULLPOINTER, "right->leaf"); + FAIL_ZERO_RETURN(e, context->parent->leaf.actors, AKERR_NULLPOINTER, "parent->leaf"); + + context = (akgl_BSPContext *)data; + actor = (akgl_Actor *)node->data; + + e1intersect = SDL_HasRectIntersectionFloat(&context->left.extent, &actor->bbox); + e2intersect = SDL_HasRectIntersectionFloat(&context->right.extent, &actor->bbox); + + if ( e1intersect && e2intersect ) { + // Actors who cross left/right boundaries get left in the context of their parent, so both + // child spaces test them for collision + dest = context->parent; + } else if ( e1intersect ) { + dest = context->left; + } else if ( e2intersect ) { + dest = context->right; + } + + if ( dest != NULL ) { + PASS(e, akgl_heap_next_list(&newnode)); + newnode->data = node->data; + PASS(e, aksl_list_push(dest->leaf.actors, newnode)); + actor->bsphome = dest; + actor->bsplistnode = newnode; + } + + SUCCEED_RETURN(e); +} + +/** + * @brief Recursive function used to divide the stage into a BSP tree of actor near neighbors + * + * This is a recursive function that builds a balanced tree until the tree is maxdepth levels deep. + * It creates a list of all actors which fit within the extents of the root node, + * then successively divides that actor list down into the extents of the child nodes of the balanced tree, + * until the maximum recursion depth has been achieved. + * + * @param[in] root The root node of the current level of the tree + * @param[in] actors The list of all actors that are within the root node's extents + * @param[in] extents The {x, y, w, h} extents of this level of the tree + * @param[in] direction The AKL_STAGE_PARTITION_* direction in which the extent is being divided + * @param[in] depth The depth of the current root node. Callers should provide 1 here. + * @param[in] maxdepth The maximum depth to which we should recurse + * + * @throws AKERR_VALUE Invalid direction provided + * + * @return akerr_ErrorContext* + */ +akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_bsp(aksl_TreeNode *root, aksl_ListNode *actors, SDL_FRect extents, uint8_t direction, uint8_t depth, uint8_t maxdepth) +{ + akgl_BSPContext context; + + // Until we have reached maxdepth levels of depth + PREPARE_ERROR(e); + if ( depth > maxdepth ) { + SUCCEED_RETURN(e); + } + + // - Get a pointer to the left and right nodes of the tree representing the halves + // - Divide the current working space into two equal halves + // - Construct a SDL_FRect representing the two equal halves + // (the top half of a vertical division is "left", bottom half is "right") + if ( root->left == NULL ) { + PASS(e, akgl_heap_next_tree((aksl_TreeNode **)&root->left)); + PASS(e, akgl_heap_next_list((aksl_ListNode **)&root->left->leaf->actors)); + context.left = root->left; + root->left->parent = root; + if ( direction == AKGL_STAGE_PARTITION_HORIZONTAL ) { + context.left.extent.x = extents.x; + context.left.extent.y = extents.y; + context.left.extent.w = extents.w; + context.left.extent.h = (extents.h / 2); + } else if ( direction == AKGL_STAGE_PARTITION_VERTICAL ) { + context.left.extent.x = extents.x; + context.left.extent.y = extents.y; + context.left.extent.w = (extents.w / 2); + context.left.extent.h = extents.h; + } else { + FAIL_RETURN(e, AKERR_VALUE, "Invalid partition direction %d", direction); + } + } + if ( root->right == NULL ) { + PASS(e, akgl_heap_next_tree((aksl_TreeNode **)&root->right)); + PASS(e, akgl_heap_next_list((aksl_ListNode **)&root->right->leaf->actors)); + context.right = root->right; + root->right->parent = root; + if ( direction == AKGL_STAGE_PARTITION_HORIZONTAL ) { + context.right.extent.x = extents.x; + context.right.extent.y = extents.y + (extents.h / 2); + context.right.extent.w = extents.w; + context.right.extent.h = (extents.h / 2); + } else if ( direction == AKGL_STAGE_PARTITION_VERTICAL ) { + context.right.extent.x = extents.x + (extents.w / 2); + context.right.extent.y = extents.y; + context.right.extent.w = (extents.w / 2); + context.right.extent.h = extents.h; + } else { + FAIL_RETURN(e, AKERR_VALUE, "Invalid partition direction %d", direction); + } + } + if ( root->leaf->actors == NULL ) { + PASS(e, akgl_heap_next_list((aksl_ListNode **)&root->leaf->actors)); + context.parent = root; + } + + // - Test (SDL_HasIntersection) all actor objects in the current working space + // against the SDL_Frects for both halves. If they intersect, push the actors + // down into the leaf linked lists for each half + PASS(e, aksl_list_iterate(actors, &akgl_stage_2d_bsp_actoriter, &context)); + PASS(e, akgl_stage_2d_partition_switchdirection(direction, &direction)); + // - Recurse down into the left BSP node, changing our partitioning + // (vertical->horizontal, horizontal->vertical etc) + PASS(e, akgl_stage_2d_bsp(root->left, root->left->leaf->actors, context.left.extent, direction, depth + 1, maxdepth)); + // - Recurse down into the right BSP node + // (vertical->horizontal, horizontal->vertical etc) + PASS(e, akgl_stage_2d_bsp(root->right, root->right->leaf->actors, context.right.extent, direction, depth + 1, maxdepth)); + + SUCCEED_RETURN(e); +} + +/** + * @brief Release the stage's curent BSP tree + * + * This recursive function traverses down all nodes of the stage's BSP tree and release all the tree and list nodes + * + * @param[in] self The stage whose BSP tree should be freed + * + * @throws AKERR_NULLPOINTER on NULL pointer inputs + * + * @return akerr_ErrorContext* + */ + +akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_bspfree(akgl_Stage *self) +{ + PREPARE_ERROR(e); + FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self"); + PASS(e, aksl_tree_iterate(&self->bsp, &akgl_heap_iter_tree_release, NULL, NULL, AKSL_TREE_SEARCH_DFS, NULL, NULL)); + SUCCEED_RETURN(e); +} + +/** + * @brief Partition the stage into a BSP tree, grouping near neighbor actors together + * + * @param[in] self The stage to partition + * + * @return akerr_ErrorContext* + */ +akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_partition(akgl_Stage *self) +{ + aksl_ListNode *actors = NULL; + aksl_ListNode *curnode = NULL; + PREPARE_ERROR(e); + FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self"); + + PASS(e, akgl_stage_2d_bspfree(self)); + + // Build a linked list of all actors currently initialized + PASS(e, akgl_heap_next_list(&actors)); + curnode = actors; + for ( int i = 0; i < AKGL_MAX_HEAP_ACTOR; i++ ) { + if ( HEAP_ACTOR[i].refcount != 0 ) { + curnode->data = (void *)&HEAP_ACTOR[i]; + PASS(e, akgl_heap_next_list(&curnode->next)); + curnode = curnode->next; + } + } + + /* This could be better. + * Right now we divide the extents into 16 equal rectangles and sort actors + * into them by their bbox coordinates. This doesn't help when lots of actors are + * stacked up unequally on one area of the screen. A better solution might be to + * continue subdividing a given extent until it is small enough that only + * N number of actors fit inside of it. All of this is overkill for something like + * a simple Mario style sidescroller or even a JRPG, but for something like + * a bullet hell, it will be essential. + */ + ATTEMPT { + CATCH(e, akgl_stage_2d_bsp( + &self->bsp, + actors, + // Define the current working space as the boundaries of the entire stage + // (from the origin to the extents of the largest and/or furthest object) + // .... let's see what we get if we just cheat and use the camera extents. + // (Do we *really* want off-screen objects in the collision detection?) + (SDL_FRect){0.0, 0.0, camera->w, camera->h}, + // Define if we are partitioning horizontally or vertically + AKGL_STAGE_PARTITION_VERTICAL, + // Begin depth counter at 1 + 1, + // Maximum recursion depth + AKGL_STAGE_PARTITION_MAXDEPTH) + ); + } CLEANUP { + PREPARE_ERROR(e2); + PASS(e2, aksl_list_iterate(actors, &akgl_heap_iter_list_release, NULL)); + } PROCESS(e) { + } FINISH(e, true); + + SUCCEED_RETURN(e); +} + +/** + * @brief Move this actor to the correct part of the BSP tree + * + * @param[in] self akgl_Stage * + * @param[in] actor akgl_Actor * + * + * @throws AKERR_NULLPOINTER on null pointer inputs + * @return akerr_ErrorContext + */ +akerr_ErrorContext AKERR_NOIGNORE *akgl_stage_2d_bsp_move(akgl_Stage *self, akgl_Actor *actor) +{ + aksl_ListNode *tmp = NULL; + aksl_TreeNode *curnode = NULL; + akgl_BSPContext context; + PREPARE_ERROR(e); + FAIL_ZERO_RETURN(e, self, AKERR_NULLPOINTER, "self"); + FAIL_ZERO_RETURN(e, actor, AKERR_NULLPOINTER, "actor"); + if ( actor->bsphome == NULL ) { + // New actor that is not currently part of the BSP tree. Push it down. + PASS(e, akgl_heap_next_list(&tmp)); + tmp->data = actor; + ATTEMPT { + PASS(e, akgl_stage_2d_bsp( + &self->bsp, + tmp, + (SDL_FRect){0.0, 0.0, camera->w, camera->h}, + AKGL_STAGE_PARTITION_VERTICAL, + 1, + AKGL_STAGE_PARTITION_MAXDEPTH) + ); + } CLEANUP { + PREPARE_ERROR(e2); + PASS(e2, akgl_heap_release_list(tmp)); + } PROCESS(e) { + } FINISH(e, true); + } else { + // Actor already exists somewhere in the BSP tree + // 1. Is it still in the extents for its bsphome? If so, do nothing. + if ( SDL_HasRectIntersectionFloat(&actor->bsphome->leaf->extent, &actor->bbox) ) { + SUCCEED_RETURN(e); + } + // 2. If not, pop the actor out of its current BSP actor list, and start walking up. + PASS(e, aksl_list_pop(actor->bsplistnode)); + curnode = actor->bsphome->parent; + tmp = actor->bsplistnode; + while ( curnode != NULL ) { + // 3. Is it within the current BSP node's extents? If not, walk up and repeat. + if ( SDL_HasRectIntersectionFloat(&curnode->leaf->extent, &actor->bbox) ) { + // 4. Find which extent it matches. + // FIXME : akgl_BSPContext should be replaced with aksl_TreeNode at this point, it's fully redundant + context.left = curnode->left; + context.right = curnode->right; + context.parent = curnode->parent; + PASS(e, akgl_stage_2d_bsp_actoriter(tmp, actor->bsplistnode, &context)); + } + curnode = curnode->parent; + } + // Release the old actor node, it will have been moved to a new one by now + if ( tmp == actor->bsplistnode ) { + // Odd ... + SDL_Log("Expected actor %d to be moved to a different BSP node after moving, but it still has the same list node...\n", actor); + } else { + PASS(e, akgl_heap_release_list(tmp)); + } + } + SUCCEED_RETURN(e); +}