diff options
Diffstat (limited to 'contrib/SDL-3.2.8/examples/demo')
17 files changed, 1637 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/examples/demo/01-snake/README.txt b/contrib/SDL-3.2.8/examples/demo/01-snake/README.txt new file mode 100644 index 0000000..1cf9784 --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/01-snake/README.txt | |||
| @@ -0,0 +1 @@ | |||
| A complete game of Snake, written in SDL. | |||
diff --git a/contrib/SDL-3.2.8/examples/demo/01-snake/onmouseover.webp b/contrib/SDL-3.2.8/examples/demo/01-snake/onmouseover.webp new file mode 100644 index 0000000..1757202 --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/01-snake/onmouseover.webp | |||
| Binary files differ | |||
diff --git a/contrib/SDL-3.2.8/examples/demo/01-snake/snake.c b/contrib/SDL-3.2.8/examples/demo/01-snake/snake.c new file mode 100644 index 0000000..0aca862 --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/01-snake/snake.c | |||
| @@ -0,0 +1,350 @@ | |||
| 1 | /* | ||
| 2 | * Logic implementation of the Snake game. It is designed to efficiently | ||
| 3 | * represent the state of the game in memory. | ||
| 4 | * | ||
| 5 | * This code is public domain. Feel free to use it for any purpose! | ||
| 6 | */ | ||
| 7 | |||
| 8 | #define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ | ||
| 9 | #include <SDL3/SDL.h> | ||
| 10 | #include <SDL3/SDL_main.h> | ||
| 11 | |||
| 12 | #define STEP_RATE_IN_MILLISECONDS 125 | ||
| 13 | #define SNAKE_BLOCK_SIZE_IN_PIXELS 24 | ||
| 14 | #define SDL_WINDOW_WIDTH (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_WIDTH) | ||
| 15 | #define SDL_WINDOW_HEIGHT (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_HEIGHT) | ||
| 16 | |||
| 17 | #define SNAKE_GAME_WIDTH 24U | ||
| 18 | #define SNAKE_GAME_HEIGHT 18U | ||
| 19 | #define SNAKE_MATRIX_SIZE (SNAKE_GAME_WIDTH * SNAKE_GAME_HEIGHT) | ||
| 20 | |||
| 21 | #define THREE_BITS 0x7U /* ~CHAR_MAX >> (CHAR_BIT - SNAKE_CELL_MAX_BITS) */ | ||
| 22 | #define SHIFT(x, y) (((x) + ((y) * SNAKE_GAME_WIDTH)) * SNAKE_CELL_MAX_BITS) | ||
| 23 | |||
| 24 | typedef enum | ||
| 25 | { | ||
| 26 | SNAKE_CELL_NOTHING = 0U, | ||
| 27 | SNAKE_CELL_SRIGHT = 1U, | ||
| 28 | SNAKE_CELL_SUP = 2U, | ||
| 29 | SNAKE_CELL_SLEFT = 3U, | ||
| 30 | SNAKE_CELL_SDOWN = 4U, | ||
| 31 | SNAKE_CELL_FOOD = 5U | ||
| 32 | } SnakeCell; | ||
| 33 | |||
| 34 | #define SNAKE_CELL_MAX_BITS 3U /* floor(log2(SNAKE_CELL_FOOD)) + 1 */ | ||
| 35 | |||
| 36 | typedef enum | ||
| 37 | { | ||
| 38 | SNAKE_DIR_RIGHT, | ||
| 39 | SNAKE_DIR_UP, | ||
| 40 | SNAKE_DIR_LEFT, | ||
| 41 | SNAKE_DIR_DOWN | ||
| 42 | } SnakeDirection; | ||
| 43 | |||
| 44 | typedef struct | ||
| 45 | { | ||
| 46 | unsigned char cells[(SNAKE_MATRIX_SIZE * SNAKE_CELL_MAX_BITS) / 8U]; | ||
| 47 | char head_xpos; | ||
| 48 | char head_ypos; | ||
| 49 | char tail_xpos; | ||
| 50 | char tail_ypos; | ||
| 51 | char next_dir; | ||
| 52 | char inhibit_tail_step; | ||
| 53 | unsigned occupied_cells; | ||
| 54 | } SnakeContext; | ||
| 55 | |||
| 56 | typedef struct | ||
| 57 | { | ||
| 58 | SDL_Window *window; | ||
| 59 | SDL_Renderer *renderer; | ||
| 60 | SnakeContext snake_ctx; | ||
| 61 | Uint64 last_step; | ||
| 62 | } AppState; | ||
| 63 | |||
| 64 | SnakeCell snake_cell_at(const SnakeContext *ctx, char x, char y) | ||
| 65 | { | ||
| 66 | const int shift = SHIFT(x, y); | ||
| 67 | unsigned short range; | ||
| 68 | SDL_memcpy(&range, ctx->cells + (shift / 8), sizeof(range)); | ||
| 69 | return (SnakeCell)((range >> (shift % 8)) & THREE_BITS); | ||
| 70 | } | ||
| 71 | |||
| 72 | static void set_rect_xy_(SDL_FRect *r, short x, short y) | ||
| 73 | { | ||
| 74 | r->x = (float)(x * SNAKE_BLOCK_SIZE_IN_PIXELS); | ||
| 75 | r->y = (float)(y * SNAKE_BLOCK_SIZE_IN_PIXELS); | ||
| 76 | } | ||
| 77 | |||
| 78 | static void put_cell_at_(SnakeContext *ctx, char x, char y, SnakeCell ct) | ||
| 79 | { | ||
| 80 | const int shift = SHIFT(x, y); | ||
| 81 | const int adjust = shift % 8; | ||
| 82 | unsigned char *const pos = ctx->cells + (shift / 8); | ||
| 83 | unsigned short range; | ||
| 84 | SDL_memcpy(&range, pos, sizeof(range)); | ||
| 85 | range &= ~(THREE_BITS << adjust); /* clear bits */ | ||
| 86 | range |= (ct & THREE_BITS) << adjust; | ||
| 87 | SDL_memcpy(pos, &range, sizeof(range)); | ||
| 88 | } | ||
| 89 | |||
| 90 | static int are_cells_full_(SnakeContext *ctx) | ||
| 91 | { | ||
| 92 | return ctx->occupied_cells == SNAKE_GAME_WIDTH * SNAKE_GAME_HEIGHT; | ||
| 93 | } | ||
| 94 | |||
| 95 | static void new_food_pos_(SnakeContext *ctx) | ||
| 96 | { | ||
| 97 | while (true) { | ||
| 98 | const char x = (char) SDL_rand(SNAKE_GAME_WIDTH); | ||
| 99 | const char y = (char) SDL_rand(SNAKE_GAME_HEIGHT); | ||
| 100 | if (snake_cell_at(ctx, x, y) == SNAKE_CELL_NOTHING) { | ||
| 101 | put_cell_at_(ctx, x, y, SNAKE_CELL_FOOD); | ||
| 102 | break; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | void snake_initialize(SnakeContext *ctx) | ||
| 108 | { | ||
| 109 | int i; | ||
| 110 | SDL_zeroa(ctx->cells); | ||
| 111 | ctx->head_xpos = ctx->tail_xpos = SNAKE_GAME_WIDTH / 2; | ||
| 112 | ctx->head_ypos = ctx->tail_ypos = SNAKE_GAME_HEIGHT / 2; | ||
| 113 | ctx->next_dir = SNAKE_DIR_RIGHT; | ||
| 114 | ctx->inhibit_tail_step = ctx->occupied_cells = 4; | ||
| 115 | --ctx->occupied_cells; | ||
| 116 | put_cell_at_(ctx, ctx->tail_xpos, ctx->tail_ypos, SNAKE_CELL_SRIGHT); | ||
| 117 | for (i = 0; i < 4; i++) { | ||
| 118 | new_food_pos_(ctx); | ||
| 119 | ++ctx->occupied_cells; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 123 | void snake_redir(SnakeContext *ctx, SnakeDirection dir) | ||
| 124 | { | ||
| 125 | SnakeCell ct = snake_cell_at(ctx, ctx->head_xpos, ctx->head_ypos); | ||
| 126 | if ((dir == SNAKE_DIR_RIGHT && ct != SNAKE_CELL_SLEFT) || | ||
| 127 | (dir == SNAKE_DIR_UP && ct != SNAKE_CELL_SDOWN) || | ||
| 128 | (dir == SNAKE_DIR_LEFT && ct != SNAKE_CELL_SRIGHT) || | ||
| 129 | (dir == SNAKE_DIR_DOWN && ct != SNAKE_CELL_SUP)) { | ||
| 130 | ctx->next_dir = dir; | ||
| 131 | } | ||
| 132 | } | ||
| 133 | |||
| 134 | static void wrap_around_(char *val, char max) | ||
| 135 | { | ||
| 136 | if (*val < 0) { | ||
| 137 | *val = max - 1; | ||
| 138 | } else if (*val > max - 1) { | ||
| 139 | *val = 0; | ||
| 140 | } | ||
| 141 | } | ||
| 142 | |||
| 143 | void snake_step(SnakeContext *ctx) | ||
| 144 | { | ||
| 145 | const SnakeCell dir_as_cell = (SnakeCell)(ctx->next_dir + 1); | ||
| 146 | SnakeCell ct; | ||
| 147 | char prev_xpos; | ||
| 148 | char prev_ypos; | ||
| 149 | /* Move tail forward */ | ||
| 150 | if (--ctx->inhibit_tail_step == 0) { | ||
| 151 | ++ctx->inhibit_tail_step; | ||
| 152 | ct = snake_cell_at(ctx, ctx->tail_xpos, ctx->tail_ypos); | ||
| 153 | put_cell_at_(ctx, ctx->tail_xpos, ctx->tail_ypos, SNAKE_CELL_NOTHING); | ||
| 154 | switch (ct) { | ||
| 155 | case SNAKE_CELL_SRIGHT: | ||
| 156 | ctx->tail_xpos++; | ||
| 157 | break; | ||
| 158 | case SNAKE_CELL_SUP: | ||
| 159 | ctx->tail_ypos--; | ||
| 160 | break; | ||
| 161 | case SNAKE_CELL_SLEFT: | ||
| 162 | ctx->tail_xpos--; | ||
| 163 | break; | ||
| 164 | case SNAKE_CELL_SDOWN: | ||
| 165 | ctx->tail_ypos++; | ||
| 166 | break; | ||
| 167 | default: | ||
| 168 | break; | ||
| 169 | } | ||
| 170 | wrap_around_(&ctx->tail_xpos, SNAKE_GAME_WIDTH); | ||
| 171 | wrap_around_(&ctx->tail_ypos, SNAKE_GAME_HEIGHT); | ||
| 172 | } | ||
| 173 | /* Move head forward */ | ||
| 174 | prev_xpos = ctx->head_xpos; | ||
| 175 | prev_ypos = ctx->head_ypos; | ||
| 176 | switch (ctx->next_dir) { | ||
| 177 | case SNAKE_DIR_RIGHT: | ||
| 178 | ++ctx->head_xpos; | ||
| 179 | break; | ||
| 180 | case SNAKE_DIR_UP: | ||
| 181 | --ctx->head_ypos; | ||
| 182 | break; | ||
| 183 | case SNAKE_DIR_LEFT: | ||
| 184 | --ctx->head_xpos; | ||
| 185 | break; | ||
| 186 | case SNAKE_DIR_DOWN: | ||
| 187 | ++ctx->head_ypos; | ||
| 188 | break; | ||
| 189 | } | ||
| 190 | wrap_around_(&ctx->head_xpos, SNAKE_GAME_WIDTH); | ||
| 191 | wrap_around_(&ctx->head_ypos, SNAKE_GAME_HEIGHT); | ||
| 192 | /* Collisions */ | ||
| 193 | ct = snake_cell_at(ctx, ctx->head_xpos, ctx->head_ypos); | ||
| 194 | if (ct != SNAKE_CELL_NOTHING && ct != SNAKE_CELL_FOOD) { | ||
| 195 | snake_initialize(ctx); | ||
| 196 | return; | ||
| 197 | } | ||
| 198 | put_cell_at_(ctx, prev_xpos, prev_ypos, dir_as_cell); | ||
| 199 | put_cell_at_(ctx, ctx->head_xpos, ctx->head_ypos, dir_as_cell); | ||
| 200 | if (ct == SNAKE_CELL_FOOD) { | ||
| 201 | if (are_cells_full_(ctx)) { | ||
| 202 | snake_initialize(ctx); | ||
| 203 | return; | ||
| 204 | } | ||
| 205 | new_food_pos_(ctx); | ||
| 206 | ++ctx->inhibit_tail_step; | ||
| 207 | ++ctx->occupied_cells; | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | static SDL_AppResult handle_key_event_(SnakeContext *ctx, SDL_Scancode key_code) | ||
| 212 | { | ||
| 213 | switch (key_code) { | ||
| 214 | /* Quit. */ | ||
| 215 | case SDL_SCANCODE_ESCAPE: | ||
| 216 | case SDL_SCANCODE_Q: | ||
| 217 | return SDL_APP_SUCCESS; | ||
| 218 | /* Restart the game as if the program was launched. */ | ||
| 219 | case SDL_SCANCODE_R: | ||
| 220 | snake_initialize(ctx); | ||
| 221 | break; | ||
| 222 | /* Decide new direction of the snake. */ | ||
| 223 | case SDL_SCANCODE_RIGHT: | ||
| 224 | snake_redir(ctx, SNAKE_DIR_RIGHT); | ||
| 225 | break; | ||
| 226 | case SDL_SCANCODE_UP: | ||
| 227 | snake_redir(ctx, SNAKE_DIR_UP); | ||
| 228 | break; | ||
| 229 | case SDL_SCANCODE_LEFT: | ||
| 230 | snake_redir(ctx, SNAKE_DIR_LEFT); | ||
| 231 | break; | ||
| 232 | case SDL_SCANCODE_DOWN: | ||
| 233 | snake_redir(ctx, SNAKE_DIR_DOWN); | ||
| 234 | break; | ||
| 235 | default: | ||
| 236 | break; | ||
| 237 | } | ||
| 238 | return SDL_APP_CONTINUE; | ||
| 239 | } | ||
| 240 | |||
| 241 | SDL_AppResult SDL_AppIterate(void *appstate) | ||
| 242 | { | ||
| 243 | AppState *as = (AppState *)appstate; | ||
| 244 | SnakeContext *ctx = &as->snake_ctx; | ||
| 245 | const Uint64 now = SDL_GetTicks(); | ||
| 246 | SDL_FRect r; | ||
| 247 | unsigned i; | ||
| 248 | unsigned j; | ||
| 249 | int ct; | ||
| 250 | |||
| 251 | // run game logic if we're at or past the time to run it. | ||
| 252 | // if we're _really_ behind the time to run it, run it | ||
| 253 | // several times. | ||
| 254 | while ((now - as->last_step) >= STEP_RATE_IN_MILLISECONDS) { | ||
| 255 | snake_step(ctx); | ||
| 256 | as->last_step += STEP_RATE_IN_MILLISECONDS; | ||
| 257 | } | ||
| 258 | |||
| 259 | r.w = r.h = SNAKE_BLOCK_SIZE_IN_PIXELS; | ||
| 260 | SDL_SetRenderDrawColor(as->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); | ||
| 261 | SDL_RenderClear(as->renderer); | ||
| 262 | for (i = 0; i < SNAKE_GAME_WIDTH; i++) { | ||
| 263 | for (j = 0; j < SNAKE_GAME_HEIGHT; j++) { | ||
| 264 | ct = snake_cell_at(ctx, i, j); | ||
| 265 | if (ct == SNAKE_CELL_NOTHING) | ||
| 266 | continue; | ||
| 267 | set_rect_xy_(&r, i, j); | ||
| 268 | if (ct == SNAKE_CELL_FOOD) | ||
| 269 | SDL_SetRenderDrawColor(as->renderer, 80, 80, 255, SDL_ALPHA_OPAQUE); | ||
| 270 | else /* body */ | ||
| 271 | SDL_SetRenderDrawColor(as->renderer, 0, 128, 0, SDL_ALPHA_OPAQUE); | ||
| 272 | SDL_RenderFillRect(as->renderer, &r); | ||
| 273 | } | ||
| 274 | } | ||
| 275 | SDL_SetRenderDrawColor(as->renderer, 255, 255, 0, SDL_ALPHA_OPAQUE); /*head*/ | ||
| 276 | set_rect_xy_(&r, ctx->head_xpos, ctx->head_ypos); | ||
| 277 | SDL_RenderFillRect(as->renderer, &r); | ||
| 278 | SDL_RenderPresent(as->renderer); | ||
| 279 | return SDL_APP_CONTINUE; | ||
| 280 | } | ||
| 281 | |||
| 282 | static const struct | ||
| 283 | { | ||
| 284 | const char *key; | ||
| 285 | const char *value; | ||
| 286 | } extended_metadata[] = | ||
| 287 | { | ||
| 288 | { SDL_PROP_APP_METADATA_URL_STRING, "https://examples.libsdl.org/SDL3/demo/01-snake/" }, | ||
| 289 | { SDL_PROP_APP_METADATA_CREATOR_STRING, "SDL team" }, | ||
| 290 | { SDL_PROP_APP_METADATA_COPYRIGHT_STRING, "Placed in the public domain" }, | ||
| 291 | { SDL_PROP_APP_METADATA_TYPE_STRING, "game" } | ||
| 292 | }; | ||
| 293 | |||
| 294 | SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) | ||
| 295 | { | ||
| 296 | size_t i; | ||
| 297 | |||
| 298 | if (!SDL_SetAppMetadata("Example Snake game", "1.0", "com.example.Snake")) { | ||
| 299 | return SDL_APP_FAILURE; | ||
| 300 | } | ||
| 301 | |||
| 302 | for (i = 0; i < SDL_arraysize(extended_metadata); i++) { | ||
| 303 | if (!SDL_SetAppMetadataProperty(extended_metadata[i].key, extended_metadata[i].value)) { | ||
| 304 | return SDL_APP_FAILURE; | ||
| 305 | } | ||
| 306 | } | ||
| 307 | |||
| 308 | if (!SDL_Init(SDL_INIT_VIDEO)) { | ||
| 309 | return SDL_APP_FAILURE; | ||
| 310 | } | ||
| 311 | |||
| 312 | AppState *as = (AppState *)SDL_calloc(1, sizeof(AppState)); | ||
| 313 | if (!as) { | ||
| 314 | return SDL_APP_FAILURE; | ||
| 315 | } | ||
| 316 | |||
| 317 | *appstate = as; | ||
| 318 | |||
| 319 | if (!SDL_CreateWindowAndRenderer("examples/demo/snake", SDL_WINDOW_WIDTH, SDL_WINDOW_HEIGHT, 0, &as->window, &as->renderer)) { | ||
| 320 | return SDL_APP_FAILURE; | ||
| 321 | } | ||
| 322 | |||
| 323 | snake_initialize(&as->snake_ctx); | ||
| 324 | |||
| 325 | as->last_step = SDL_GetTicks(); | ||
| 326 | |||
| 327 | return SDL_APP_CONTINUE; | ||
| 328 | } | ||
| 329 | |||
| 330 | SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) | ||
| 331 | { | ||
| 332 | SnakeContext *ctx = &((AppState *)appstate)->snake_ctx; | ||
| 333 | switch (event->type) { | ||
| 334 | case SDL_EVENT_QUIT: | ||
| 335 | return SDL_APP_SUCCESS; | ||
| 336 | case SDL_EVENT_KEY_DOWN: | ||
| 337 | return handle_key_event_(ctx, event->key.scancode); | ||
| 338 | } | ||
| 339 | return SDL_APP_CONTINUE; | ||
| 340 | } | ||
| 341 | |||
| 342 | void SDL_AppQuit(void *appstate, SDL_AppResult result) | ||
| 343 | { | ||
| 344 | if (appstate != NULL) { | ||
| 345 | AppState *as = (AppState *)appstate; | ||
| 346 | SDL_DestroyRenderer(as->renderer); | ||
| 347 | SDL_DestroyWindow(as->window); | ||
| 348 | SDL_free(as); | ||
| 349 | } | ||
| 350 | } | ||
diff --git a/contrib/SDL-3.2.8/examples/demo/01-snake/thumbnail.png b/contrib/SDL-3.2.8/examples/demo/01-snake/thumbnail.png new file mode 100644 index 0000000..f0e27c5 --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/01-snake/thumbnail.png | |||
| Binary files differ | |||
diff --git a/contrib/SDL-3.2.8/examples/demo/02-woodeneye-008/README.txt b/contrib/SDL-3.2.8/examples/demo/02-woodeneye-008/README.txt new file mode 100644 index 0000000..83de3e3 --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/02-woodeneye-008/README.txt | |||
| @@ -0,0 +1 @@ | |||
| Minimal splitscreen FPS with multiple mouse and keyboards, drawn with the 2D Render API. \ No newline at end of file | |||
diff --git a/contrib/SDL-3.2.8/examples/demo/02-woodeneye-008/onmouseover.webp b/contrib/SDL-3.2.8/examples/demo/02-woodeneye-008/onmouseover.webp new file mode 100644 index 0000000..2e7f44f --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/02-woodeneye-008/onmouseover.webp | |||
| Binary files differ | |||
diff --git a/contrib/SDL-3.2.8/examples/demo/02-woodeneye-008/thumbnail.png b/contrib/SDL-3.2.8/examples/demo/02-woodeneye-008/thumbnail.png new file mode 100644 index 0000000..c8d1efa --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/02-woodeneye-008/thumbnail.png | |||
| Binary files differ | |||
diff --git a/contrib/SDL-3.2.8/examples/demo/02-woodeneye-008/woodeneye-008.c b/contrib/SDL-3.2.8/examples/demo/02-woodeneye-008/woodeneye-008.c new file mode 100644 index 0000000..b97b06a --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/02-woodeneye-008/woodeneye-008.c | |||
| @@ -0,0 +1,480 @@ | |||
| 1 | /* | ||
| 2 | * This code is public domain. Feel free to use it for any purpose! | ||
| 3 | */ | ||
| 4 | |||
| 5 | #define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ | ||
| 6 | #include <SDL3/SDL.h> | ||
| 7 | #include <SDL3/SDL_main.h> | ||
| 8 | |||
| 9 | #define MAP_BOX_SCALE 16 | ||
| 10 | #define MAP_BOX_EDGES_LEN (12 + MAP_BOX_SCALE * 2) | ||
| 11 | #define MAX_PLAYER_COUNT 4 | ||
| 12 | #define CIRCLE_DRAW_SIDES 32 | ||
| 13 | #define CIRCLE_DRAW_SIDES_LEN (CIRCLE_DRAW_SIDES + 1) | ||
| 14 | |||
| 15 | typedef struct { | ||
| 16 | SDL_MouseID mouse; | ||
| 17 | SDL_KeyboardID keyboard; | ||
| 18 | double pos[3]; | ||
| 19 | double vel[3]; | ||
| 20 | unsigned int yaw; | ||
| 21 | int pitch; | ||
| 22 | float radius, height; | ||
| 23 | unsigned char color[3]; | ||
| 24 | unsigned char wasd; | ||
| 25 | } Player; | ||
| 26 | |||
| 27 | typedef struct { | ||
| 28 | SDL_Window *window; | ||
| 29 | SDL_Renderer *renderer; | ||
| 30 | int player_count; | ||
| 31 | Player players[MAX_PLAYER_COUNT]; | ||
| 32 | float edges[MAP_BOX_EDGES_LEN][6]; | ||
| 33 | } AppState; | ||
| 34 | |||
| 35 | static const struct { | ||
| 36 | const char *key; | ||
| 37 | const char *value; | ||
| 38 | } extended_metadata[] = { | ||
| 39 | { SDL_PROP_APP_METADATA_URL_STRING, "https://examples.libsdl.org/SDL3/demo/02-woodeneye-008/" }, | ||
| 40 | { SDL_PROP_APP_METADATA_CREATOR_STRING, "SDL team" }, | ||
| 41 | { SDL_PROP_APP_METADATA_COPYRIGHT_STRING, "Placed in the public domain" }, | ||
| 42 | { SDL_PROP_APP_METADATA_TYPE_STRING, "game" } | ||
| 43 | }; | ||
| 44 | |||
| 45 | static int whoseMouse(SDL_MouseID mouse, const Player players[], int players_len) | ||
| 46 | { | ||
| 47 | int i; | ||
| 48 | for (i = 0; i < players_len; i++) { | ||
| 49 | if (players[i].mouse == mouse) return i; | ||
| 50 | } | ||
| 51 | return -1; | ||
| 52 | } | ||
| 53 | |||
| 54 | static int whoseKeyboard(SDL_KeyboardID keyboard, const Player players[], int players_len) | ||
| 55 | { | ||
| 56 | int i; | ||
| 57 | for (i = 0; i < players_len; i++) { | ||
| 58 | if (players[i].keyboard == keyboard) return i; | ||
| 59 | } | ||
| 60 | return -1; | ||
| 61 | } | ||
| 62 | |||
| 63 | static void shoot(int shooter, Player players[], int players_len) | ||
| 64 | { | ||
| 65 | int i, j; | ||
| 66 | double x0 = players[shooter].pos[0]; | ||
| 67 | double y0 = players[shooter].pos[1]; | ||
| 68 | double z0 = players[shooter].pos[2]; | ||
| 69 | double bin_rad = SDL_PI_D / 2147483648.0; | ||
| 70 | double yaw_rad = bin_rad * players[shooter].yaw; | ||
| 71 | double pitch_rad = bin_rad * players[shooter].pitch; | ||
| 72 | double cos_yaw = SDL_cos( yaw_rad); | ||
| 73 | double sin_yaw = SDL_sin( yaw_rad); | ||
| 74 | double cos_pitch = SDL_cos(pitch_rad); | ||
| 75 | double sin_pitch = SDL_sin(pitch_rad); | ||
| 76 | double vx = -sin_yaw*cos_pitch; | ||
| 77 | double vy = sin_pitch; | ||
| 78 | double vz = -cos_yaw*cos_pitch; | ||
| 79 | for (i = 0; i < players_len; i++) { | ||
| 80 | if (i == shooter) continue; | ||
| 81 | Player *target = &(players[i]); | ||
| 82 | int hit = 0; | ||
| 83 | for (j = 0; j < 2; j++) { | ||
| 84 | double r = target->radius; | ||
| 85 | double h = target->height; | ||
| 86 | double dx = target->pos[0] - x0; | ||
| 87 | double dy = target->pos[1] - y0 + (j == 0 ? 0 : r - h); | ||
| 88 | double dz = target->pos[2] - z0; | ||
| 89 | double vd = vx*dx + vy*dy + vz*dz; | ||
| 90 | double dd = dx*dx + dy*dy + dz*dz; | ||
| 91 | double vv = vx*vx + vy*vy + vz*vz; | ||
| 92 | double rr = r * r; | ||
| 93 | if (vd < 0) continue; | ||
| 94 | if (vd * vd >= vv * (dd - rr)) hit += 1; | ||
| 95 | } | ||
| 96 | if (hit) { | ||
| 97 | target->pos[0] = (double)(MAP_BOX_SCALE * (SDL_rand(256) - 128)) / 256; | ||
| 98 | target->pos[1] = (double)(MAP_BOX_SCALE * (SDL_rand(256) - 128)) / 256; | ||
| 99 | target->pos[2] = (double)(MAP_BOX_SCALE * (SDL_rand(256) - 128)) / 256; | ||
| 100 | } | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | static void update(Player *players, int players_len, Uint64 dt_ns) | ||
| 105 | { | ||
| 106 | int i; | ||
| 107 | for (i = 0; i < players_len; i++) { | ||
| 108 | Player *player = &players[i]; | ||
| 109 | double rate = 6.0; | ||
| 110 | double time = (double)dt_ns * 1e-9; | ||
| 111 | double drag = SDL_exp(-time * rate); | ||
| 112 | double diff = 1.0 - drag; | ||
| 113 | double mult = 60.0; | ||
| 114 | double grav = 25.0; | ||
| 115 | double yaw = (double)player->yaw; | ||
| 116 | double rad = yaw * SDL_PI_D / 2147483648.0; | ||
| 117 | double cos = SDL_cos(rad); | ||
| 118 | double sin = SDL_sin(rad); | ||
| 119 | unsigned char wasd = player->wasd; | ||
| 120 | double dirX = (wasd & 8 ? 1.0 : 0.0) - (wasd & 2 ? 1.0 : 0.0); | ||
| 121 | double dirZ = (wasd & 4 ? 1.0 : 0.0) - (wasd & 1 ? 1.0 : 0.0); | ||
| 122 | double norm = dirX * dirX + dirZ * dirZ; | ||
| 123 | double accX = mult * (norm == 0 ? 0 : ( cos*dirX + sin*dirZ) / SDL_sqrt(norm)); | ||
| 124 | double accZ = mult * (norm == 0 ? 0 : (-sin*dirX + cos*dirZ) / SDL_sqrt(norm)); | ||
| 125 | double velX = player->vel[0]; | ||
| 126 | double velY = player->vel[1]; | ||
| 127 | double velZ = player->vel[2]; | ||
| 128 | player->vel[0] -= velX * diff; | ||
| 129 | player->vel[1] -= grav * time; | ||
| 130 | player->vel[2] -= velZ * diff; | ||
| 131 | player->vel[0] += diff * accX / rate; | ||
| 132 | player->vel[2] += diff * accZ / rate; | ||
| 133 | player->pos[0] += (time - diff/rate) * accX / rate + diff * velX / rate; | ||
| 134 | player->pos[1] += -0.5 * grav * time * time + velY * time; | ||
| 135 | player->pos[2] += (time - diff/rate) * accZ / rate + diff * velZ / rate; | ||
| 136 | double scale = (double)MAP_BOX_SCALE; | ||
| 137 | double bound = scale - player->radius; | ||
| 138 | double posX = SDL_max(SDL_min(bound, player->pos[0]), -bound); | ||
| 139 | double posY = SDL_max(SDL_min(bound, player->pos[1]), player->height - scale); | ||
| 140 | double posZ = SDL_max(SDL_min(bound, player->pos[2]), -bound); | ||
| 141 | if (player->pos[0] != posX) player->vel[0] = 0; | ||
| 142 | if (player->pos[1] != posY) player->vel[1] = (wasd & 16) ? 8.4375 : 0; | ||
| 143 | if (player->pos[2] != posZ) player->vel[2] = 0; | ||
| 144 | player->pos[0] = posX; | ||
| 145 | player->pos[1] = posY; | ||
| 146 | player->pos[2] = posZ; | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | static void drawCircle(SDL_Renderer *renderer, float r, float x, float y) | ||
| 151 | { | ||
| 152 | float ang; | ||
| 153 | SDL_FPoint points[CIRCLE_DRAW_SIDES_LEN]; | ||
| 154 | int i; | ||
| 155 | for (i = 0; i < CIRCLE_DRAW_SIDES_LEN; i++) { | ||
| 156 | ang = 2.0f * SDL_PI_F * (float)i / (float)CIRCLE_DRAW_SIDES; | ||
| 157 | points[i].x = x + r * SDL_cosf(ang); | ||
| 158 | points[i].y = y + r * SDL_sinf(ang); | ||
| 159 | } | ||
| 160 | SDL_RenderLines(renderer, (const SDL_FPoint*)&points, CIRCLE_DRAW_SIDES_LEN); | ||
| 161 | } | ||
| 162 | |||
| 163 | static void drawClippedSegment( | ||
| 164 | SDL_Renderer *renderer, | ||
| 165 | float ax, float ay, float az, | ||
| 166 | float bx, float by, float bz, | ||
| 167 | float x, float y, float z, float w) | ||
| 168 | { | ||
| 169 | if (az >= -w && bz >= -w) return; | ||
| 170 | float dx = ax - bx; | ||
| 171 | float dy = ay - by; | ||
| 172 | if (az > -w) { | ||
| 173 | float t = (-w - bz) / (az - bz); | ||
| 174 | ax = bx + dx * t; | ||
| 175 | ay = by + dy * t; | ||
| 176 | az = -w; | ||
| 177 | } else if (bz > -w) { | ||
| 178 | float t = (-w - az) / (bz - az); | ||
| 179 | bx = ax - dx * t; | ||
| 180 | by = ay - dy * t; | ||
| 181 | bz = -w; | ||
| 182 | } | ||
| 183 | ax = -z * ax / az; | ||
| 184 | ay = -z * ay / az; | ||
| 185 | bx = -z * bx / bz; | ||
| 186 | by = -z * by / bz; | ||
| 187 | SDL_RenderLine(renderer, x + ax, y - ay, x + bx, y - by); | ||
| 188 | } | ||
| 189 | |||
| 190 | static char debug_string[32]; | ||
| 191 | static void draw(SDL_Renderer *renderer, const float (*edges)[6], const Player players[], int players_len) | ||
| 192 | { | ||
| 193 | int w, h, i, j, k; | ||
| 194 | if (!SDL_GetRenderOutputSize(renderer, &w, &h)) { | ||
| 195 | return; | ||
| 196 | } | ||
| 197 | SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); | ||
| 198 | SDL_RenderClear(renderer); | ||
| 199 | if (players_len > 0) { | ||
| 200 | float wf = (float)w; | ||
| 201 | float hf = (float)h; | ||
| 202 | int part_hor = players_len > 2 ? 2 : 1; | ||
| 203 | int part_ver = players_len > 1 ? 2 : 1; | ||
| 204 | float size_hor = wf / ((float)part_hor); | ||
| 205 | float size_ver = hf / ((float)part_ver); | ||
| 206 | for (i = 0; i < players_len; i++) { | ||
| 207 | const Player *player = &players[i]; | ||
| 208 | float mod_x = (float)(i % part_hor); | ||
| 209 | float mod_y = (float)(i / part_hor); | ||
| 210 | float hor_origin = (mod_x + 0.5f) * size_hor; | ||
| 211 | float ver_origin = (mod_y + 0.5f) * size_ver; | ||
| 212 | float cam_origin = (float)(0.5 * SDL_sqrt(size_hor * size_hor + size_ver * size_ver)); | ||
| 213 | float hor_offset = mod_x * size_hor; | ||
| 214 | float ver_offset = mod_y * size_ver; | ||
| 215 | SDL_Rect rect; | ||
| 216 | rect.x = (int)hor_offset; | ||
| 217 | rect.y = (int)ver_offset; | ||
| 218 | rect.w = (int)size_hor; | ||
| 219 | rect.h = (int)size_ver; | ||
| 220 | SDL_SetRenderClipRect(renderer, &rect); | ||
| 221 | double x0 = player->pos[0]; | ||
| 222 | double y0 = player->pos[1]; | ||
| 223 | double z0 = player->pos[2]; | ||
| 224 | double bin_rad = SDL_PI_D / 2147483648.0; | ||
| 225 | double yaw_rad = bin_rad * player->yaw; | ||
| 226 | double pitch_rad = bin_rad * player->pitch; | ||
| 227 | double cos_yaw = SDL_cos( yaw_rad); | ||
| 228 | double sin_yaw = SDL_sin( yaw_rad); | ||
| 229 | double cos_pitch = SDL_cos(pitch_rad); | ||
| 230 | double sin_pitch = SDL_sin(pitch_rad); | ||
| 231 | double mat[9] = { | ||
| 232 | cos_yaw , 0, -sin_yaw , | ||
| 233 | sin_yaw*sin_pitch, cos_pitch, cos_yaw*sin_pitch, | ||
| 234 | sin_yaw*cos_pitch, -sin_pitch, cos_yaw*cos_pitch | ||
| 235 | }; | ||
| 236 | SDL_SetRenderDrawColor(renderer, 64, 64, 64, 255); | ||
| 237 | for (k = 0; k < MAP_BOX_EDGES_LEN; k++) { | ||
| 238 | const float *line = edges[k]; | ||
| 239 | float ax = (float)(mat[0] * (line[0] - x0) + mat[1] * (line[1] - y0) + mat[2] * (line[2] - z0)); | ||
| 240 | float ay = (float)(mat[3] * (line[0] - x0) + mat[4] * (line[1] - y0) + mat[5] * (line[2] - z0)); | ||
| 241 | float az = (float)(mat[6] * (line[0] - x0) + mat[7] * (line[1] - y0) + mat[8] * (line[2] - z0)); | ||
| 242 | float bx = (float)(mat[0] * (line[3] - x0) + mat[1] * (line[4] - y0) + mat[2] * (line[5] - z0)); | ||
| 243 | float by = (float)(mat[3] * (line[3] - x0) + mat[4] * (line[4] - y0) + mat[5] * (line[5] - z0)); | ||
| 244 | float bz = (float)(mat[6] * (line[3] - x0) + mat[7] * (line[4] - y0) + mat[8] * (line[5] - z0)); | ||
| 245 | drawClippedSegment(renderer, ax, ay, az, bx, by, bz, hor_origin, ver_origin, cam_origin, 1); | ||
| 246 | } | ||
| 247 | for (j = 0; j < players_len; j++) { | ||
| 248 | if (i == j) continue; | ||
| 249 | const Player *target = &players[j]; | ||
| 250 | SDL_SetRenderDrawColor(renderer, target->color[0], target->color[1], target->color[2], 255); | ||
| 251 | for (k = 0; k < 2; k++) { | ||
| 252 | double rx = target->pos[0] - player->pos[0]; | ||
| 253 | double ry = target->pos[1] - player->pos[1] + (target->radius - target->height) * (float)k; | ||
| 254 | double rz = target->pos[2] - player->pos[2]; | ||
| 255 | double dx = mat[0] * rx + mat[1] * ry + mat[2] * rz; | ||
| 256 | double dy = mat[3] * rx + mat[4] * ry + mat[5] * rz; | ||
| 257 | double dz = mat[6] * rx + mat[7] * ry + mat[8] * rz; | ||
| 258 | double r_eff = target->radius * cam_origin / dz; | ||
| 259 | if (!(dz < 0)) continue; | ||
| 260 | drawCircle(renderer, (float)(r_eff), (float)(hor_origin - cam_origin*dx/dz), (float)(ver_origin + cam_origin*dy/dz)); | ||
| 261 | } | ||
| 262 | } | ||
| 263 | SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); | ||
| 264 | SDL_RenderLine(renderer, hor_origin, ver_origin-10, hor_origin, ver_origin+10); | ||
| 265 | SDL_RenderLine(renderer, hor_origin-10, ver_origin, hor_origin+10, ver_origin); | ||
| 266 | } | ||
| 267 | } | ||
| 268 | SDL_SetRenderClipRect(renderer, 0); | ||
| 269 | SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255); | ||
| 270 | SDL_RenderDebugText(renderer, 0, 0, debug_string); | ||
| 271 | SDL_RenderPresent(renderer); | ||
| 272 | } | ||
| 273 | |||
| 274 | static void initPlayers(Player *players, int len) | ||
| 275 | { | ||
| 276 | int i; | ||
| 277 | for (i = 0; i < len; i++) { | ||
| 278 | players[i].pos[0] = 8.0 * (i & 1 ? -1.0 : 1.0); | ||
| 279 | players[i].pos[1] = 0; | ||
| 280 | players[i].pos[2] = 8.0 * (i & 1 ? -1.0 : 1.0) * (i & 2 ? -1.0 : 1.0); | ||
| 281 | players[i].vel[0] = 0; | ||
| 282 | players[i].vel[1] = 0; | ||
| 283 | players[i].vel[2] = 0; | ||
| 284 | players[i].yaw = 0x20000000 + (i & 1 ? 0x80000000 : 0) + (i & 2 ? 0x40000000 : 0); | ||
| 285 | players[i].pitch = -0x08000000; | ||
| 286 | players[i].radius = 0.5f; | ||
| 287 | players[i].height = 1.5f; | ||
| 288 | players[i].wasd = 0; | ||
| 289 | players[i].mouse = 0; | ||
| 290 | players[i].keyboard = 0; | ||
| 291 | players[i].color[0] = (1 << (i / 2)) & 2 ? 0 : 0xff; | ||
| 292 | players[i].color[1] = (1 << (i / 2)) & 1 ? 0 : 0xff; | ||
| 293 | players[i].color[2] = (1 << (i / 2)) & 4 ? 0 : 0xff; | ||
| 294 | players[i].color[0] = (i & 1) ? players[i].color[0] : ~players[i].color[0]; | ||
| 295 | players[i].color[1] = (i & 1) ? players[i].color[1] : ~players[i].color[1]; | ||
| 296 | players[i].color[2] = (i & 1) ? players[i].color[2] : ~players[i].color[2]; | ||
| 297 | } | ||
| 298 | } | ||
| 299 | |||
| 300 | static void initEdges(int scale, float (*edges)[6], int edges_len) | ||
| 301 | { | ||
| 302 | int i, j; | ||
| 303 | const float r = (float)scale; | ||
| 304 | const int map[24] = { | ||
| 305 | 0,1 , 1,3 , 3,2 , 2,0 , | ||
| 306 | 7,6 , 6,4 , 4,5 , 5,7 , | ||
| 307 | 6,2 , 3,7 , 0,4 , 5,1 | ||
| 308 | }; | ||
| 309 | for(i = 0; i < 12; i++) { | ||
| 310 | for (j = 0; j < 3; j++) { | ||
| 311 | edges[i][j+0] = (map[i*2+0] & (1 << j) ? r : -r); | ||
| 312 | edges[i][j+3] = (map[i*2+1] & (1 << j) ? r : -r); | ||
| 313 | } | ||
| 314 | } | ||
| 315 | for(i = 0; i < scale; i++) { | ||
| 316 | float d = (float)(i * 2); | ||
| 317 | for (j = 0; j < 2; j++) { | ||
| 318 | edges[i+12][3*j+0] = j ? r : -r; | ||
| 319 | edges[i+12][3*j+1] = -r; | ||
| 320 | edges[i+12][3*j+2] = d-r; | ||
| 321 | edges[i+12+scale][3*j+0] = d-r; | ||
| 322 | edges[i+12+scale][3*j+1] = -r; | ||
| 323 | edges[i+12+scale][3*j+2] = j ? r : -r; | ||
| 324 | } | ||
| 325 | } | ||
| 326 | } | ||
| 327 | |||
| 328 | SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) | ||
| 329 | { | ||
| 330 | if (!SDL_SetAppMetadata("Example splitscreen shooter game", "1.0", "com.example.woodeneye-008")) { | ||
| 331 | return SDL_APP_FAILURE; | ||
| 332 | } | ||
| 333 | int i; | ||
| 334 | for (i = 0; i < SDL_arraysize(extended_metadata); i++) { | ||
| 335 | if (!SDL_SetAppMetadataProperty(extended_metadata[i].key, extended_metadata[i].value)) { | ||
| 336 | return SDL_APP_FAILURE; | ||
| 337 | } | ||
| 338 | } | ||
| 339 | |||
| 340 | AppState *as = SDL_calloc(1, sizeof(AppState)); | ||
| 341 | if (!as) { | ||
| 342 | return SDL_APP_FAILURE; | ||
| 343 | } else { | ||
| 344 | *appstate = as; | ||
| 345 | } | ||
| 346 | |||
| 347 | if (!SDL_Init(SDL_INIT_VIDEO)) { | ||
| 348 | return SDL_APP_FAILURE; | ||
| 349 | } | ||
| 350 | if (!SDL_CreateWindowAndRenderer("examples/demo/woodeneye-008", 640, 480, 0, &as->window, &as->renderer)) { | ||
| 351 | return SDL_APP_FAILURE; | ||
| 352 | } | ||
| 353 | |||
| 354 | as->player_count = 1; | ||
| 355 | initPlayers(as->players, MAX_PLAYER_COUNT); | ||
| 356 | initEdges(MAP_BOX_SCALE, as->edges, MAP_BOX_EDGES_LEN); | ||
| 357 | debug_string[0] = 0; | ||
| 358 | |||
| 359 | SDL_SetRenderVSync(as->renderer, false); | ||
| 360 | SDL_SetWindowRelativeMouseMode(as->window, true); | ||
| 361 | SDL_SetHintWithPriority(SDL_HINT_WINDOWS_RAW_KEYBOARD, "1", SDL_HINT_OVERRIDE); | ||
| 362 | return SDL_APP_CONTINUE; | ||
| 363 | } | ||
| 364 | |||
| 365 | SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) | ||
| 366 | { | ||
| 367 | AppState *as = appstate; | ||
| 368 | Player *players = as->players; | ||
| 369 | int player_count = as->player_count; | ||
| 370 | int i; | ||
| 371 | switch (event->type) { | ||
| 372 | case SDL_EVENT_QUIT: | ||
| 373 | return SDL_APP_SUCCESS; | ||
| 374 | break; | ||
| 375 | case SDL_EVENT_MOUSE_REMOVED: | ||
| 376 | for (i = 0; i < player_count; i++) { | ||
| 377 | if (players[i].mouse == event->mdevice.which) { | ||
| 378 | players[i].mouse = 0; | ||
| 379 | } | ||
| 380 | } | ||
| 381 | break; | ||
| 382 | case SDL_EVENT_KEYBOARD_REMOVED: | ||
| 383 | for (i = 0; i < player_count; i++) { | ||
| 384 | if (players[i].keyboard == event->kdevice.which) { | ||
| 385 | players[i].keyboard = 0; | ||
| 386 | } | ||
| 387 | } | ||
| 388 | break; | ||
| 389 | case SDL_EVENT_MOUSE_MOTION: { | ||
| 390 | SDL_MouseID id = event->motion.which; | ||
| 391 | int index = whoseMouse(id, players, player_count); | ||
| 392 | if (index >= 0) { | ||
| 393 | players[index].yaw -= ((int)event->motion.xrel) * 0x00080000; | ||
| 394 | players[index].pitch = SDL_max(-0x40000000, SDL_min(0x40000000, players[index].pitch - ((int)event->motion.yrel) * 0x00080000)); | ||
| 395 | } else if (id) { | ||
| 396 | for (i = 0; i < MAX_PLAYER_COUNT; i++) { | ||
| 397 | if (players[i].mouse == 0) { | ||
| 398 | players[i].mouse = id; | ||
| 399 | as->player_count = SDL_max(as->player_count, i + 1); | ||
| 400 | break; | ||
| 401 | } | ||
| 402 | } | ||
| 403 | } | ||
| 404 | break; | ||
| 405 | } | ||
| 406 | case SDL_EVENT_MOUSE_BUTTON_DOWN: { | ||
| 407 | SDL_MouseID id = event->button.which; | ||
| 408 | int index = whoseMouse(id, players, player_count); | ||
| 409 | if (index >= 0) { | ||
| 410 | shoot(index, players, player_count); | ||
| 411 | } | ||
| 412 | break; | ||
| 413 | } | ||
| 414 | case SDL_EVENT_KEY_DOWN: { | ||
| 415 | SDL_Keycode sym = event->key.key; | ||
| 416 | SDL_KeyboardID id = event->key.which; | ||
| 417 | int index = whoseKeyboard(id, players, player_count); | ||
| 418 | if (index >= 0) { | ||
| 419 | if (sym == SDLK_W) players[index].wasd |= 1; | ||
| 420 | if (sym == SDLK_A) players[index].wasd |= 2; | ||
| 421 | if (sym == SDLK_S) players[index].wasd |= 4; | ||
| 422 | if (sym == SDLK_D) players[index].wasd |= 8; | ||
| 423 | if (sym == SDLK_SPACE) players[index].wasd |= 16; | ||
| 424 | } else if (id) { | ||
| 425 | for (i = 0; i < MAX_PLAYER_COUNT; i++) { | ||
| 426 | if (players[i].keyboard == 0) { | ||
| 427 | players[i].keyboard = id; | ||
| 428 | as->player_count = SDL_max(as->player_count, i + 1); | ||
| 429 | break; | ||
| 430 | } | ||
| 431 | } | ||
| 432 | } | ||
| 433 | break; | ||
| 434 | } | ||
| 435 | case SDL_EVENT_KEY_UP: { | ||
| 436 | SDL_Keycode sym = event->key.key; | ||
| 437 | SDL_KeyboardID id = event->key.which; | ||
| 438 | if (sym == SDLK_ESCAPE) return SDL_APP_SUCCESS; | ||
| 439 | int index = whoseKeyboard(id, players, player_count); | ||
| 440 | if (index >= 0) { | ||
| 441 | if (sym == SDLK_W) players[index].wasd &= 30; | ||
| 442 | if (sym == SDLK_A) players[index].wasd &= 29; | ||
| 443 | if (sym == SDLK_S) players[index].wasd &= 27; | ||
| 444 | if (sym == SDLK_D) players[index].wasd &= 23; | ||
| 445 | if (sym == SDLK_SPACE) players[index].wasd &= 15; | ||
| 446 | } | ||
| 447 | break; | ||
| 448 | } | ||
| 449 | } | ||
| 450 | return SDL_APP_CONTINUE; | ||
| 451 | } | ||
| 452 | |||
| 453 | SDL_AppResult SDL_AppIterate(void *appstate) | ||
| 454 | { | ||
| 455 | AppState *as = appstate; | ||
| 456 | static Uint64 accu = 0; | ||
| 457 | static Uint64 last = 0; | ||
| 458 | static Uint64 past = 0; | ||
| 459 | Uint64 now = SDL_GetTicksNS(); | ||
| 460 | Uint64 dt_ns = now - past; | ||
| 461 | update(as->players, as->player_count, dt_ns); | ||
| 462 | draw(as->renderer, (const float (*)[6])as->edges, as->players, as->player_count); | ||
| 463 | if (now - last > 999999999) { | ||
| 464 | last = now; | ||
| 465 | SDL_snprintf(debug_string, sizeof(debug_string), "%" SDL_PRIu64 " fps", accu); | ||
| 466 | accu = 0; | ||
| 467 | } | ||
| 468 | past = now; | ||
| 469 | accu += 1; | ||
| 470 | Uint64 elapsed = SDL_GetTicksNS() - now; | ||
| 471 | if (elapsed < 999999) { | ||
| 472 | SDL_DelayNS(999999 - elapsed); | ||
| 473 | } | ||
| 474 | return SDL_APP_CONTINUE; | ||
| 475 | } | ||
| 476 | |||
| 477 | void SDL_AppQuit(void *appstate, SDL_AppResult result) | ||
| 478 | { | ||
| 479 | SDL_free(appstate); // just free the memory, SDL will clean up the window/renderer for us. | ||
| 480 | } \ No newline at end of file | ||
diff --git a/contrib/SDL-3.2.8/examples/demo/03-infinite-monkeys/README.txt b/contrib/SDL-3.2.8/examples/demo/03-infinite-monkeys/README.txt new file mode 100644 index 0000000..3a80b69 --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/03-infinite-monkeys/README.txt | |||
| @@ -0,0 +1,7 @@ | |||
| 1 | |||
| 2 | How many monkeys does it take to write the complete works of Shakespeare? | ||
| 3 | |||
| 4 | Now you can find out! | ||
| 5 | |||
| 6 | Cheer on your favorite monkey as they bash keyboards on their way through classic literature. | ||
| 7 | |||
diff --git a/contrib/SDL-3.2.8/examples/demo/03-infinite-monkeys/infinite-monkeys.c b/contrib/SDL-3.2.8/examples/demo/03-infinite-monkeys/infinite-monkeys.c new file mode 100644 index 0000000..a8bfad1 --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/03-infinite-monkeys/infinite-monkeys.c | |||
| @@ -0,0 +1,377 @@ | |||
| 1 | /* | ||
| 2 | * This code is public domain. Feel free to use it for any purpose! | ||
| 3 | */ | ||
| 4 | |||
| 5 | #define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */ | ||
| 6 | #include <SDL3/SDL.h> | ||
| 7 | #include <SDL3/SDL_main.h> | ||
| 8 | |||
| 9 | /* We will use this renderer to draw into this window every frame. */ | ||
| 10 | static SDL_Window *window = NULL; | ||
| 11 | static SDL_Renderer *renderer = NULL; | ||
| 12 | static char *text; | ||
| 13 | static const char *end; | ||
| 14 | static const char *progress; | ||
| 15 | static SDL_Time start_time; | ||
| 16 | static SDL_Time end_time; | ||
| 17 | typedef struct { | ||
| 18 | Uint32 *text; | ||
| 19 | int length; | ||
| 20 | } Line; | ||
| 21 | int row = 0; | ||
| 22 | int rows = 0; | ||
| 23 | int cols = 0; | ||
| 24 | static Line **lines; | ||
| 25 | static Line monkey_chars; | ||
| 26 | static int monkeys = 100; | ||
| 27 | |||
| 28 | /* The highest and lowest scancodes a monkey can hit */ | ||
| 29 | #define MIN_MONKEY_SCANCODE SDL_SCANCODE_A | ||
| 30 | #define MAX_MONKEY_SCANCODE SDL_SCANCODE_SLASH | ||
| 31 | |||
| 32 | static const char *default_text = | ||
| 33 | "Jabberwocky, by Lewis Carroll\n" | ||
| 34 | "\n" | ||
| 35 | "'Twas brillig, and the slithy toves\n" | ||
| 36 | " Did gyre and gimble in the wabe:\n" | ||
| 37 | "All mimsy were the borogoves,\n" | ||
| 38 | " And the mome raths outgrabe.\n" | ||
| 39 | "\n" | ||
| 40 | "\"Beware the Jabberwock, my son!\n" | ||
| 41 | " The jaws that bite, the claws that catch!\n" | ||
| 42 | "Beware the Jubjub bird, and shun\n" | ||
| 43 | " The frumious Bandersnatch!\"\n" | ||
| 44 | "\n" | ||
| 45 | "He took his vorpal sword in hand;\n" | ||
| 46 | " Long time the manxome foe he sought-\n" | ||
| 47 | "So rested he by the Tumtum tree\n" | ||
| 48 | " And stood awhile in thought.\n" | ||
| 49 | "\n" | ||
| 50 | "And, as in uffish thought he stood,\n" | ||
| 51 | " The Jabberwock, with eyes of flame,\n" | ||
| 52 | "Came whiffling through the tulgey wood,\n" | ||
| 53 | " And burbled as it came!\n" | ||
| 54 | "\n" | ||
| 55 | "One, two! One, two! And through and through\n" | ||
| 56 | " The vorpal blade went snicker-snack!\n" | ||
| 57 | "He left it dead, and with its head\n" | ||
| 58 | " He went galumphing back.\n" | ||
| 59 | "\n" | ||
| 60 | "\"And hast thou slain the Jabberwock?\n" | ||
| 61 | " Come to my arms, my beamish boy!\n" | ||
| 62 | "O frabjous day! Callooh! Callay!\"\n" | ||
| 63 | " He chortled in his joy.\n" | ||
| 64 | "\n" | ||
| 65 | "'Twas brillig, and the slithy toves\n" | ||
| 66 | " Did gyre and gimble in the wabe:\n" | ||
| 67 | "All mimsy were the borogoves,\n" | ||
| 68 | " And the mome raths outgrabe.\n"; | ||
| 69 | |||
| 70 | |||
| 71 | static void FreeLines(void) | ||
| 72 | { | ||
| 73 | int i; | ||
| 74 | |||
| 75 | if (rows > 0 && cols > 0) { | ||
| 76 | for (i = 0; i < rows; ++i) { | ||
| 77 | SDL_free(lines[i]->text); | ||
| 78 | SDL_free(lines[i]); | ||
| 79 | } | ||
| 80 | SDL_free(lines); | ||
| 81 | lines = NULL; | ||
| 82 | } | ||
| 83 | SDL_free(monkey_chars.text); | ||
| 84 | monkey_chars.text = NULL; | ||
| 85 | } | ||
| 86 | |||
| 87 | static void OnWindowSizeChanged(void) | ||
| 88 | { | ||
| 89 | int w, h; | ||
| 90 | |||
| 91 | if (!SDL_GetCurrentRenderOutputSize(renderer, &w, &h)) { | ||
| 92 | return; | ||
| 93 | } | ||
| 94 | |||
| 95 | FreeLines(); | ||
| 96 | |||
| 97 | row = 0; | ||
| 98 | rows = (h / SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) - 4; | ||
| 99 | cols = (w / SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE); | ||
| 100 | if (rows > 0 && cols > 0) { | ||
| 101 | int i; | ||
| 102 | |||
| 103 | lines = (Line **)SDL_malloc(rows * sizeof(Line *)); | ||
| 104 | if (lines) { | ||
| 105 | for (i = 0; i < rows; ++i) { | ||
| 106 | lines[i] = (Line *)SDL_malloc(sizeof(Line)); | ||
| 107 | if (!lines[i]) { | ||
| 108 | FreeLines(); | ||
| 109 | break; | ||
| 110 | } | ||
| 111 | lines[i]->text = (Uint32 *)SDL_malloc(cols * sizeof(Uint32)); | ||
| 112 | if (!lines[i]->text) { | ||
| 113 | FreeLines(); | ||
| 114 | break; | ||
| 115 | } | ||
| 116 | lines[i]->length = 0; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | monkey_chars.text = (Uint32 *)SDL_malloc(cols * sizeof(Uint32)); | ||
| 121 | if (monkey_chars.text) { | ||
| 122 | for (i = 0; i < cols; ++i) { | ||
| 123 | monkey_chars.text[i] = ' '; | ||
| 124 | } | ||
| 125 | monkey_chars.length = cols; | ||
| 126 | } | ||
| 127 | } | ||
| 128 | } | ||
| 129 | |||
| 130 | /* This function runs once at startup. */ | ||
| 131 | SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[]) | ||
| 132 | { | ||
| 133 | int arg = 1; | ||
| 134 | |||
| 135 | SDL_SetAppMetadata("Infinite Monkeys", "1.0", "com.example.infinite-monkeys"); | ||
| 136 | |||
| 137 | if (!SDL_Init(SDL_INIT_VIDEO)) { | ||
| 138 | SDL_Log("Couldn't initialize SDL: %s", SDL_GetError()); | ||
| 139 | return SDL_APP_FAILURE; | ||
| 140 | } | ||
| 141 | |||
| 142 | if (!SDL_CreateWindowAndRenderer("examples/demo/infinite-monkeys", 640, 480, 0, &window, &renderer)) { | ||
| 143 | SDL_Log("Couldn't create window/renderer: %s", SDL_GetError()); | ||
| 144 | return SDL_APP_FAILURE; | ||
| 145 | } | ||
| 146 | SDL_SetRenderVSync(renderer, 1); | ||
| 147 | |||
| 148 | if (argv[arg] && SDL_strcmp(argv[arg], "--monkeys") == 0) { | ||
| 149 | ++arg; | ||
| 150 | if (argv[arg]) { | ||
| 151 | monkeys = SDL_atoi(argv[arg]); | ||
| 152 | ++arg; | ||
| 153 | } else { | ||
| 154 | SDL_Log("Usage: %s [--monkeys N] [file.txt]", argv[0]); | ||
| 155 | return SDL_APP_FAILURE; | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | if (argv[arg]) { | ||
| 160 | const char *file = argv[arg]; | ||
| 161 | size_t size; | ||
| 162 | text = (char *)SDL_LoadFile(file, &size); | ||
| 163 | if (!text) { | ||
| 164 | SDL_Log("Couldn't open %s: %s", file, SDL_GetError()); | ||
| 165 | return SDL_APP_FAILURE; | ||
| 166 | } | ||
| 167 | end = text + size; | ||
| 168 | } else { | ||
| 169 | text = SDL_strdup(default_text); | ||
| 170 | end = text + SDL_strlen(text); | ||
| 171 | } | ||
| 172 | progress = text; | ||
| 173 | |||
| 174 | SDL_GetCurrentTime(&start_time); | ||
| 175 | |||
| 176 | OnWindowSizeChanged(); | ||
| 177 | |||
| 178 | return SDL_APP_CONTINUE; /* carry on with the program! */ | ||
| 179 | } | ||
| 180 | |||
| 181 | /* This function runs when a new event (mouse input, keypresses, etc) occurs. */ | ||
| 182 | SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event) | ||
| 183 | { | ||
| 184 | switch (event->type) { | ||
| 185 | case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED: | ||
| 186 | OnWindowSizeChanged(); | ||
| 187 | break; | ||
| 188 | case SDL_EVENT_QUIT: | ||
| 189 | return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */ | ||
| 190 | } | ||
| 191 | return SDL_APP_CONTINUE; /* carry on with the program! */ | ||
| 192 | } | ||
| 193 | |||
| 194 | static void DisplayLine(float x, float y, Line *line) | ||
| 195 | { | ||
| 196 | /* Allocate maximum space potentially needed for this line */ | ||
| 197 | char *utf8 = (char *)SDL_malloc(line->length * 4 + 1); | ||
| 198 | if (utf8) { | ||
| 199 | char *spot = utf8; | ||
| 200 | int i; | ||
| 201 | |||
| 202 | for (i = 0; i < line->length; ++i) { | ||
| 203 | spot = SDL_UCS4ToUTF8(line->text[i], spot); | ||
| 204 | } | ||
| 205 | *spot = '\0'; | ||
| 206 | |||
| 207 | SDL_RenderDebugText(renderer, x, y, utf8); | ||
| 208 | SDL_free(utf8); | ||
| 209 | } | ||
| 210 | } | ||
| 211 | |||
| 212 | static bool CanMonkeyType(Uint32 ch) | ||
| 213 | { | ||
| 214 | SDL_Keymod modstate; | ||
| 215 | SDL_Scancode scancode = SDL_GetScancodeFromKey(ch, &modstate); | ||
| 216 | if (scancode < MIN_MONKEY_SCANCODE || scancode > MAX_MONKEY_SCANCODE) { | ||
| 217 | return false; | ||
| 218 | } | ||
| 219 | /* Monkeys can hit the shift key, but nothing else */ | ||
| 220 | if ((modstate & ~SDL_KMOD_SHIFT) != 0) { | ||
| 221 | return false; | ||
| 222 | } | ||
| 223 | return true; | ||
| 224 | } | ||
| 225 | |||
| 226 | static void AdvanceRow(void) | ||
| 227 | { | ||
| 228 | Line *line; | ||
| 229 | |||
| 230 | ++row; | ||
| 231 | line = lines[row % rows]; | ||
| 232 | line->length = 0; | ||
| 233 | } | ||
| 234 | |||
| 235 | static void AddMonkeyChar(int monkey, Uint32 ch) | ||
| 236 | { | ||
| 237 | if (monkey >= 0 && monkey_chars.text) { | ||
| 238 | monkey_chars.text[(monkey % cols)] = ch; | ||
| 239 | } | ||
| 240 | |||
| 241 | if (lines) { | ||
| 242 | if (ch == '\n') { | ||
| 243 | AdvanceRow(); | ||
| 244 | } else { | ||
| 245 | Line *line = lines[row % rows]; | ||
| 246 | line->text[line->length++] = ch; | ||
| 247 | if (line->length == cols) { | ||
| 248 | AdvanceRow(); | ||
| 249 | } | ||
| 250 | } | ||
| 251 | } | ||
| 252 | |||
| 253 | SDL_StepUTF8(&progress, NULL); | ||
| 254 | } | ||
| 255 | |||
| 256 | static Uint32 GetNextChar(void) | ||
| 257 | { | ||
| 258 | Uint32 ch = 0; | ||
| 259 | while (progress < end) { | ||
| 260 | const char *spot = progress; | ||
| 261 | ch = SDL_StepUTF8(&spot, NULL); | ||
| 262 | if (CanMonkeyType(ch)) { | ||
| 263 | break; | ||
| 264 | } else { | ||
| 265 | /* This is a freebie, monkeys can't type this */ | ||
| 266 | AddMonkeyChar(-1, ch); | ||
| 267 | } | ||
| 268 | } | ||
| 269 | return ch; | ||
| 270 | } | ||
| 271 | |||
| 272 | static Uint32 MonkeyPlay(void) | ||
| 273 | { | ||
| 274 | int count = (MAX_MONKEY_SCANCODE - MIN_MONKEY_SCANCODE + 1); | ||
| 275 | SDL_Scancode scancode = (SDL_Scancode)(MIN_MONKEY_SCANCODE + SDL_rand(count)); | ||
| 276 | SDL_Keymod modstate = (SDL_rand(2) ? SDL_KMOD_SHIFT : 0); | ||
| 277 | |||
| 278 | return SDL_GetKeyFromScancode(scancode, modstate, false); | ||
| 279 | } | ||
| 280 | |||
| 281 | /* This function runs once per frame, and is the heart of the program. */ | ||
| 282 | SDL_AppResult SDL_AppIterate(void *appstate) | ||
| 283 | { | ||
| 284 | int i, monkey; | ||
| 285 | Uint32 next_char = 0, ch; | ||
| 286 | float x, y; | ||
| 287 | char *caption = NULL; | ||
| 288 | SDL_Time now, elapsed; | ||
| 289 | int hours, minutes, seconds; | ||
| 290 | SDL_FRect rect; | ||
| 291 | |||
| 292 | for (monkey = 0; monkey < monkeys; ++monkey) { | ||
| 293 | if (next_char == 0) { | ||
| 294 | next_char = GetNextChar(); | ||
| 295 | if (!next_char) { | ||
| 296 | /* All done! */ | ||
| 297 | break; | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | ch = MonkeyPlay(); | ||
| 302 | if (ch == next_char) { | ||
| 303 | AddMonkeyChar(monkey, ch); | ||
| 304 | next_char = 0; | ||
| 305 | } | ||
| 306 | } | ||
| 307 | |||
| 308 | /* Clear the screen */ | ||
| 309 | SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); | ||
| 310 | SDL_RenderClear(renderer); | ||
| 311 | |||
| 312 | /* Show the text already decoded */ | ||
| 313 | SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE); | ||
| 314 | x = 0.0f; | ||
| 315 | y = 0.0f; | ||
| 316 | if (lines) { | ||
| 317 | int row_offset = row - rows + 1; | ||
| 318 | if (row_offset < 0) { | ||
| 319 | row_offset = 0; | ||
| 320 | } | ||
| 321 | for (i = 0; i < rows; ++i) { | ||
| 322 | Line *line = lines[(row_offset + i) % rows]; | ||
| 323 | DisplayLine(x, y, line); | ||
| 324 | y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; | ||
| 325 | } | ||
| 326 | |||
| 327 | /* Show the caption */ | ||
| 328 | y = (float)((rows + 1) * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE); | ||
| 329 | if (progress == end) { | ||
| 330 | if (!end_time) { | ||
| 331 | SDL_GetCurrentTime(&end_time); | ||
| 332 | } | ||
| 333 | now = end_time; | ||
| 334 | } else { | ||
| 335 | SDL_GetCurrentTime(&now); | ||
| 336 | } | ||
| 337 | elapsed = (now - start_time); | ||
| 338 | elapsed /= SDL_NS_PER_SECOND; | ||
| 339 | seconds = (int)(elapsed % 60); | ||
| 340 | elapsed /= 60; | ||
| 341 | minutes = (int)(elapsed % 60); | ||
| 342 | elapsed /= 60; | ||
| 343 | hours = (int)elapsed; | ||
| 344 | SDL_asprintf(&caption, "Monkeys: %d - %dH:%dM:%dS", monkeys, hours, minutes, seconds); | ||
| 345 | if (caption) { | ||
| 346 | SDL_RenderDebugText(renderer, x, y, caption); | ||
| 347 | SDL_free(caption); | ||
| 348 | } | ||
| 349 | y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; | ||
| 350 | |||
| 351 | /* Show the characters currently typed */ | ||
| 352 | DisplayLine(x, y, &monkey_chars); | ||
| 353 | y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; | ||
| 354 | } | ||
| 355 | |||
| 356 | /* Show the current progress */ | ||
| 357 | SDL_SetRenderDrawColor(renderer, 0, 255, 0, SDL_ALPHA_OPAQUE); | ||
| 358 | rect.x = x; | ||
| 359 | rect.y = y; | ||
| 360 | rect.w = ((float)(progress - text) / (end - text)) * (cols * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE); | ||
| 361 | rect.h = (float)SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE; | ||
| 362 | SDL_RenderFillRect(renderer, &rect); | ||
| 363 | |||
| 364 | SDL_RenderPresent(renderer); | ||
| 365 | |||
| 366 | return SDL_APP_CONTINUE; /* carry on with the program! */ | ||
| 367 | } | ||
| 368 | |||
| 369 | /* This function runs once at shutdown. */ | ||
| 370 | void SDL_AppQuit(void *appstate, SDL_AppResult result) | ||
| 371 | { | ||
| 372 | /* SDL will clean up the window/renderer for us. */ | ||
| 373 | |||
| 374 | FreeLines(); | ||
| 375 | SDL_free(text); | ||
| 376 | } | ||
| 377 | |||
diff --git a/contrib/SDL-3.2.8/examples/demo/03-infinite-monkeys/onmouseover.webp b/contrib/SDL-3.2.8/examples/demo/03-infinite-monkeys/onmouseover.webp new file mode 100644 index 0000000..f522974 --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/03-infinite-monkeys/onmouseover.webp | |||
| Binary files differ | |||
diff --git a/contrib/SDL-3.2.8/examples/demo/03-infinite-monkeys/thumbnail.png b/contrib/SDL-3.2.8/examples/demo/03-infinite-monkeys/thumbnail.png new file mode 100644 index 0000000..418390b --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/03-infinite-monkeys/thumbnail.png | |||
| Binary files differ | |||
diff --git a/contrib/SDL-3.2.8/examples/demo/04-bytepusher/README.txt b/contrib/SDL-3.2.8/examples/demo/04-bytepusher/README.txt new file mode 100644 index 0000000..dbee4ce --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/04-bytepusher/README.txt | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | An implementation of the BytePusher VM | ||
| 2 | |||
| 3 | For example programs and more information about BytePusher, see | ||
| 4 | https://esolangs.org/wiki/BytePusher | ||
diff --git a/contrib/SDL-3.2.8/examples/demo/04-bytepusher/bytepusher.c b/contrib/SDL-3.2.8/examples/demo/04-bytepusher/bytepusher.c new file mode 100644 index 0000000..acb2ea4 --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/04-bytepusher/bytepusher.c | |||
| @@ -0,0 +1,416 @@ | |||
| 1 | /* | ||
| 2 | * An implementation of the BytePusher VM. | ||
| 3 | * | ||
| 4 | * For example programs and more information about BytePusher, see | ||
| 5 | * https://esolangs.org/wiki/BytePusher | ||
| 6 | * | ||
| 7 | * This code is public domain. Feel free to use it for any purpose! | ||
| 8 | */ | ||
| 9 | |||
| 10 | #define SDL_MAIN_USE_CALLBACKS | ||
| 11 | #include <SDL3/SDL.h> | ||
| 12 | #include <SDL3/SDL_main.h> | ||
| 13 | #include <stdarg.h> | ||
| 14 | |||
| 15 | #define SCREEN_W 256 | ||
| 16 | #define SCREEN_H 256 | ||
| 17 | #define RAM_SIZE 0x1000000 | ||
| 18 | #define FRAMES_PER_SECOND 60 | ||
| 19 | #define SAMPLES_PER_FRAME 256 | ||
| 20 | #define NS_PER_SECOND (Uint64)SDL_NS_PER_SECOND | ||
| 21 | #define MAX_AUDIO_LATENCY_FRAMES 5 | ||
| 22 | |||
| 23 | #define IO_KEYBOARD 0 | ||
| 24 | #define IO_PC 2 | ||
| 25 | #define IO_SCREEN_PAGE 5 | ||
| 26 | #define IO_AUDIO_BANK 6 | ||
| 27 | |||
| 28 | typedef struct { | ||
| 29 | Uint8 ram[RAM_SIZE + 8]; | ||
| 30 | Uint8 screenbuf[SCREEN_W * SCREEN_H]; | ||
| 31 | Uint64 last_tick; | ||
| 32 | Uint64 tick_acc; | ||
| 33 | SDL_Window* window; | ||
| 34 | SDL_Renderer* renderer; | ||
| 35 | SDL_Surface* screen; | ||
| 36 | SDL_Texture* screentex; | ||
| 37 | SDL_Texture* rendertarget; /* we need this render target for text to look good */ | ||
| 38 | SDL_AudioStream* audiostream; | ||
| 39 | char status[SCREEN_W / 8]; | ||
| 40 | int status_ticks; | ||
| 41 | Uint16 keystate; | ||
| 42 | bool display_help; | ||
| 43 | bool positional_input; | ||
| 44 | } BytePusher; | ||
| 45 | |||
| 46 | static const struct { | ||
| 47 | const char *key; | ||
| 48 | const char *value; | ||
| 49 | } extended_metadata[] = { | ||
| 50 | { SDL_PROP_APP_METADATA_URL_STRING, "https://examples.libsdl.org/SDL3/demo/04-bytepusher/" }, | ||
| 51 | { SDL_PROP_APP_METADATA_CREATOR_STRING, "SDL team" }, | ||
| 52 | { SDL_PROP_APP_METADATA_COPYRIGHT_STRING, "Placed in the public domain" }, | ||
| 53 | { SDL_PROP_APP_METADATA_TYPE_STRING, "game" } | ||
| 54 | }; | ||
| 55 | |||
| 56 | static inline Uint16 read_u16(const BytePusher* vm, Uint32 addr) { | ||
| 57 | const Uint8* ptr = &vm->ram[addr]; | ||
| 58 | return ((Uint16)ptr[0] << 8) | ((Uint16)ptr[1]); | ||
| 59 | } | ||
| 60 | |||
| 61 | static inline Uint32 read_u24(const BytePusher* vm, Uint32 addr) { | ||
| 62 | const Uint8* ptr = &vm->ram[addr]; | ||
| 63 | return ((Uint32)ptr[0] << 16) | ((Uint32)ptr[1] << 8) | ((Uint32)ptr[2]); | ||
| 64 | } | ||
| 65 | |||
| 66 | static void set_status(BytePusher* vm, const char* fmt, ...) { | ||
| 67 | va_list args; | ||
| 68 | va_start(args, fmt); | ||
| 69 | SDL_vsnprintf(vm->status, sizeof(vm->status), fmt, args); | ||
| 70 | va_end(args); | ||
| 71 | vm->status[sizeof(vm->status) - 1] = 0; | ||
| 72 | vm->status_ticks = FRAMES_PER_SECOND * 3; | ||
| 73 | } | ||
| 74 | |||
| 75 | static bool load(BytePusher* vm, SDL_IOStream* stream, bool closeio) { | ||
| 76 | size_t bytes_read = 0; | ||
| 77 | bool ok = true; | ||
| 78 | |||
| 79 | SDL_memset(vm->ram, 0, RAM_SIZE); | ||
| 80 | |||
| 81 | if (!stream) { | ||
| 82 | return false; | ||
| 83 | } | ||
| 84 | |||
| 85 | while (bytes_read < RAM_SIZE) { | ||
| 86 | size_t read = SDL_ReadIO(stream, &vm->ram[bytes_read], RAM_SIZE - bytes_read); | ||
| 87 | bytes_read += read; | ||
| 88 | if (read == 0) { | ||
| 89 | ok = SDL_GetIOStatus(stream) == SDL_IO_STATUS_EOF; | ||
| 90 | break; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | if (closeio) { | ||
| 94 | SDL_CloseIO(stream); | ||
| 95 | } | ||
| 96 | |||
| 97 | SDL_ClearAudioStream(vm->audiostream); | ||
| 98 | |||
| 99 | vm->display_help = !ok; | ||
| 100 | return ok; | ||
| 101 | } | ||
| 102 | |||
| 103 | static const char* filename(const char* path) { | ||
| 104 | size_t i = SDL_strlen(path) + 1; | ||
| 105 | while (i > 0) { | ||
| 106 | i -= 1; | ||
| 107 | if (path[i] == '/' || path[i] == '\\') { | ||
| 108 | return path + i + 1; | ||
| 109 | } | ||
| 110 | } | ||
| 111 | return path; | ||
| 112 | } | ||
| 113 | |||
| 114 | static bool load_file(BytePusher* vm, const char* path) { | ||
| 115 | if (load(vm, SDL_IOFromFile(path, "rb"), true)) { | ||
| 116 | set_status(vm, "loaded %s", filename(path)); | ||
| 117 | return true; | ||
| 118 | } else { | ||
| 119 | set_status(vm, "load failed: %s", filename(path)); | ||
| 120 | return false; | ||
| 121 | } | ||
| 122 | } | ||
| 123 | |||
| 124 | static void print(BytePusher* vm, int x, int y, const char* str) { | ||
| 125 | SDL_SetRenderDrawColor(vm->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); | ||
| 126 | SDL_RenderDebugText(vm->renderer, (float)(x + 1), (float)(y + 1), str); | ||
| 127 | SDL_SetRenderDrawColor(vm->renderer, 0xff, 0xff, 0xff, SDL_ALPHA_OPAQUE); | ||
| 128 | SDL_RenderDebugText(vm->renderer, (float)x, (float)y, str); | ||
| 129 | SDL_SetRenderDrawColor(vm->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE); | ||
| 130 | } | ||
| 131 | |||
| 132 | SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) { | ||
| 133 | BytePusher* vm; | ||
| 134 | SDL_Palette* palette; | ||
| 135 | SDL_Rect usable_bounds; | ||
| 136 | SDL_AudioSpec audiospec = { SDL_AUDIO_S8, 1, SAMPLES_PER_FRAME * FRAMES_PER_SECOND }; | ||
| 137 | SDL_DisplayID primary_display; | ||
| 138 | SDL_PropertiesID texprops; | ||
| 139 | int zoom = 2; | ||
| 140 | int i; | ||
| 141 | Uint8 r, g, b; | ||
| 142 | (void)argc; | ||
| 143 | (void)argv; | ||
| 144 | |||
| 145 | if (!SDL_SetAppMetadata("SDL 3 BytePusher", "1.0", "com.example.SDL3BytePusher")) { | ||
| 146 | return SDL_APP_FAILURE; | ||
| 147 | } | ||
| 148 | |||
| 149 | for (i = 0; i < (int)SDL_arraysize(extended_metadata); i++) { | ||
| 150 | if (!SDL_SetAppMetadataProperty(extended_metadata[i].key, extended_metadata[i].value)) { | ||
| 151 | return SDL_APP_FAILURE; | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | if (!SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO)) { | ||
| 156 | return SDL_APP_FAILURE; | ||
| 157 | } | ||
| 158 | |||
| 159 | if (!(vm = (BytePusher *)SDL_calloc(1, sizeof(*vm)))) { | ||
| 160 | return SDL_APP_FAILURE; | ||
| 161 | } | ||
| 162 | *(BytePusher**)appstate = vm; | ||
| 163 | |||
| 164 | vm->display_help = true; | ||
| 165 | |||
| 166 | primary_display = SDL_GetPrimaryDisplay(); | ||
| 167 | if (SDL_GetDisplayUsableBounds(primary_display, &usable_bounds)) { | ||
| 168 | int zoom_w = (usable_bounds.w - usable_bounds.x) * 2 / 3 / SCREEN_W; | ||
| 169 | int zoom_h = (usable_bounds.h - usable_bounds.y) * 2 / 3 / SCREEN_H; | ||
| 170 | zoom = zoom_w < zoom_h ? zoom_w : zoom_h; | ||
| 171 | if (zoom < 1) { | ||
| 172 | zoom = 1; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | if (!SDL_CreateWindowAndRenderer("SDL 3 BytePusher", | ||
| 177 | SCREEN_W * zoom, SCREEN_H * zoom, SDL_WINDOW_RESIZABLE, | ||
| 178 | &vm->window, &vm->renderer | ||
| 179 | )) { | ||
| 180 | return SDL_APP_FAILURE; | ||
| 181 | } | ||
| 182 | |||
| 183 | if (!SDL_SetRenderLogicalPresentation( | ||
| 184 | vm->renderer, SCREEN_W, SCREEN_H, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE | ||
| 185 | )) { | ||
| 186 | return SDL_APP_FAILURE; | ||
| 187 | } | ||
| 188 | |||
| 189 | if (!(vm->screen = SDL_CreateSurfaceFrom( | ||
| 190 | SCREEN_W, SCREEN_H, SDL_PIXELFORMAT_INDEX8, vm->screenbuf, SCREEN_W | ||
| 191 | ))) { | ||
| 192 | return SDL_APP_FAILURE; | ||
| 193 | } | ||
| 194 | |||
| 195 | if (!(palette = SDL_CreateSurfacePalette(vm->screen))) { | ||
| 196 | return SDL_APP_FAILURE; | ||
| 197 | } | ||
| 198 | i = 0; | ||
| 199 | for (r = 0; r < 6; ++r) { | ||
| 200 | for (g = 0; g < 6; ++g) { | ||
| 201 | for (b = 0; b < 6; ++b, ++i) { | ||
| 202 | SDL_Color color = { (Uint8)(r * 0x33), (Uint8)(g * 0x33), (Uint8)(b * 0x33), SDL_ALPHA_OPAQUE }; | ||
| 203 | palette->colors[i] = color; | ||
| 204 | } | ||
| 205 | } | ||
| 206 | } | ||
| 207 | for (; i < 256; ++i) { | ||
| 208 | SDL_Color color = { 0, 0, 0, SDL_ALPHA_OPAQUE }; | ||
| 209 | palette->colors[i] = color; | ||
| 210 | } | ||
| 211 | |||
| 212 | texprops = SDL_CreateProperties(); | ||
| 213 | SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STREAMING); | ||
| 214 | SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, SCREEN_W); | ||
| 215 | SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, SCREEN_H); | ||
| 216 | vm->screentex = SDL_CreateTextureWithProperties(vm->renderer, texprops); | ||
| 217 | SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_TARGET); | ||
| 218 | vm->rendertarget = SDL_CreateTextureWithProperties(vm->renderer, texprops); | ||
| 219 | SDL_DestroyProperties(texprops); | ||
| 220 | if (!vm->screentex || !vm->rendertarget) { | ||
| 221 | return SDL_APP_FAILURE; | ||
| 222 | } | ||
| 223 | SDL_SetTextureScaleMode(vm->screentex, SDL_SCALEMODE_NEAREST); | ||
| 224 | SDL_SetTextureScaleMode(vm->rendertarget, SDL_SCALEMODE_NEAREST); | ||
| 225 | |||
| 226 | if (!(vm->audiostream = SDL_OpenAudioDeviceStream( | ||
| 227 | SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audiospec, NULL, NULL | ||
| 228 | ))) { | ||
| 229 | return SDL_APP_FAILURE; | ||
| 230 | } | ||
| 231 | SDL_SetAudioStreamGain(vm->audiostream, 0.1f); /* examples are loud! */ | ||
| 232 | SDL_ResumeAudioStreamDevice(vm->audiostream); | ||
| 233 | |||
| 234 | set_status(vm, "renderer: %s", SDL_GetRendererName(vm->renderer)); | ||
| 235 | |||
| 236 | vm->last_tick = SDL_GetTicksNS(); | ||
| 237 | vm->tick_acc = NS_PER_SECOND; | ||
| 238 | |||
| 239 | return SDL_APP_CONTINUE; | ||
| 240 | } | ||
| 241 | |||
| 242 | SDL_AppResult SDL_AppIterate(void* appstate) { | ||
| 243 | BytePusher* vm = (BytePusher*)appstate; | ||
| 244 | |||
| 245 | Uint64 tick = SDL_GetTicksNS(); | ||
| 246 | Uint64 delta = tick - vm->last_tick; | ||
| 247 | bool updated, skip_audio; | ||
| 248 | |||
| 249 | vm->last_tick = tick; | ||
| 250 | |||
| 251 | vm->tick_acc += delta * FRAMES_PER_SECOND; | ||
| 252 | updated = vm->tick_acc >= NS_PER_SECOND; | ||
| 253 | skip_audio = vm->tick_acc >= MAX_AUDIO_LATENCY_FRAMES * NS_PER_SECOND; | ||
| 254 | |||
| 255 | if (skip_audio) { | ||
| 256 | // don't let audio fall too far behind | ||
| 257 | SDL_ClearAudioStream(vm->audiostream); | ||
| 258 | } | ||
| 259 | |||
| 260 | while (vm->tick_acc >= NS_PER_SECOND) { | ||
| 261 | Uint32 pc; | ||
| 262 | int i; | ||
| 263 | |||
| 264 | vm->tick_acc -= NS_PER_SECOND; | ||
| 265 | |||
| 266 | vm->ram[IO_KEYBOARD] = (Uint8)(vm->keystate >> 8); | ||
| 267 | vm->ram[IO_KEYBOARD + 1] = (Uint8)(vm->keystate); | ||
| 268 | |||
| 269 | pc = read_u24(vm, IO_PC); | ||
| 270 | for (i = 0; i < SCREEN_W * SCREEN_H; ++i) { | ||
| 271 | Uint32 src = read_u24(vm, pc); | ||
| 272 | Uint32 dst = read_u24(vm, pc + 3); | ||
| 273 | vm->ram[dst] = vm->ram[src]; | ||
| 274 | pc = read_u24(vm, pc + 6); | ||
| 275 | } | ||
| 276 | |||
| 277 | if (!skip_audio || vm->tick_acc < NS_PER_SECOND) { | ||
| 278 | SDL_PutAudioStreamData( | ||
| 279 | vm->audiostream, | ||
| 280 | &vm->ram[(Uint32)read_u16(vm, IO_AUDIO_BANK) << 8], | ||
| 281 | SAMPLES_PER_FRAME | ||
| 282 | ); | ||
| 283 | } | ||
| 284 | } | ||
| 285 | |||
| 286 | if (updated) { | ||
| 287 | SDL_Surface *tex; | ||
| 288 | |||
| 289 | SDL_SetRenderTarget(vm->renderer, vm->rendertarget); | ||
| 290 | |||
| 291 | if (!SDL_LockTextureToSurface(vm->screentex, NULL, &tex)) { | ||
| 292 | return SDL_APP_FAILURE; | ||
| 293 | } | ||
| 294 | vm->screen->pixels = &vm->ram[(Uint32)vm->ram[IO_SCREEN_PAGE] << 16]; | ||
| 295 | SDL_BlitSurface(vm->screen, NULL, tex, NULL); | ||
| 296 | SDL_UnlockTexture(vm->screentex); | ||
| 297 | |||
| 298 | SDL_RenderTexture(vm->renderer, vm->screentex, NULL, NULL); | ||
| 299 | } | ||
| 300 | |||
| 301 | if (vm->display_help) { | ||
| 302 | print(vm, 4, 4, "Drop a BytePusher file in this"); | ||
| 303 | print(vm, 8, 12, "window to load and run it!"); | ||
| 304 | print(vm, 4, 28, "Press ENTER to switch between"); | ||
| 305 | print(vm, 8, 36, "positional and symbolic input."); | ||
| 306 | } | ||
| 307 | |||
| 308 | if (vm->status_ticks > 0) { | ||
| 309 | vm->status_ticks -= 1; | ||
| 310 | print(vm, 4, SCREEN_H - 12, vm->status); | ||
| 311 | } | ||
| 312 | |||
| 313 | SDL_SetRenderTarget(vm->renderer, NULL); | ||
| 314 | SDL_RenderClear(vm->renderer); | ||
| 315 | SDL_RenderTexture(vm->renderer, vm->rendertarget, NULL, NULL); | ||
| 316 | SDL_RenderPresent(vm->renderer); | ||
| 317 | |||
| 318 | return SDL_APP_CONTINUE; | ||
| 319 | } | ||
| 320 | |||
| 321 | static Uint16 keycode_mask(SDL_Keycode key) { | ||
| 322 | int index; | ||
| 323 | if (key >= SDLK_0 && key <= SDLK_9) { | ||
| 324 | index = key - SDLK_0; | ||
| 325 | } else if (key >= SDLK_A && key <= SDLK_F) { | ||
| 326 | index = key - SDLK_A + 10; | ||
| 327 | } else { | ||
| 328 | return 0; | ||
| 329 | } | ||
| 330 | return (Uint16)1 << index; | ||
| 331 | } | ||
| 332 | |||
| 333 | static Uint16 scancode_mask(SDL_Scancode scancode) { | ||
| 334 | int index; | ||
| 335 | switch (scancode) { | ||
| 336 | case SDL_SCANCODE_1: index = 0x1; break; | ||
| 337 | case SDL_SCANCODE_2: index = 0x2; break; | ||
| 338 | case SDL_SCANCODE_3: index = 0x3; break; | ||
| 339 | case SDL_SCANCODE_4: index = 0xc; break; | ||
| 340 | case SDL_SCANCODE_Q: index = 0x4; break; | ||
| 341 | case SDL_SCANCODE_W: index = 0x5; break; | ||
| 342 | case SDL_SCANCODE_E: index = 0x6; break; | ||
| 343 | case SDL_SCANCODE_R: index = 0xd; break; | ||
| 344 | case SDL_SCANCODE_A: index = 0x7; break; | ||
| 345 | case SDL_SCANCODE_S: index = 0x8; break; | ||
| 346 | case SDL_SCANCODE_D: index = 0x9; break; | ||
| 347 | case SDL_SCANCODE_F: index = 0xe; break; | ||
| 348 | case SDL_SCANCODE_Z: index = 0xa; break; | ||
| 349 | case SDL_SCANCODE_X: index = 0x0; break; | ||
| 350 | case SDL_SCANCODE_C: index = 0xb; break; | ||
| 351 | case SDL_SCANCODE_V: index = 0xf; break; | ||
| 352 | default: return 0; | ||
| 353 | } | ||
| 354 | return (Uint16)1 << index; | ||
| 355 | } | ||
| 356 | |||
| 357 | SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) { | ||
| 358 | BytePusher* vm = (BytePusher*)appstate; | ||
| 359 | |||
| 360 | switch (event->type) { | ||
| 361 | case SDL_EVENT_QUIT: | ||
| 362 | return SDL_APP_SUCCESS; | ||
| 363 | |||
| 364 | case SDL_EVENT_DROP_FILE: | ||
| 365 | load_file(vm, event->drop.data); | ||
| 366 | break; | ||
| 367 | |||
| 368 | case SDL_EVENT_KEY_DOWN: | ||
| 369 | #ifndef __EMSCRIPTEN__ | ||
| 370 | if (event->key.key == SDLK_ESCAPE) { | ||
| 371 | return SDL_APP_SUCCESS; | ||
| 372 | } | ||
| 373 | #endif | ||
| 374 | if (event->key.key == SDLK_RETURN) { | ||
| 375 | vm->positional_input = !vm->positional_input; | ||
| 376 | vm->keystate = 0; | ||
| 377 | if (vm->positional_input) { | ||
| 378 | set_status(vm, "switched to positional input"); | ||
| 379 | } else { | ||
| 380 | set_status(vm, "switched to symbolic input"); | ||
| 381 | } | ||
| 382 | } | ||
| 383 | if (vm->positional_input) { | ||
| 384 | vm->keystate |= scancode_mask(event->key.scancode); | ||
| 385 | } else { | ||
| 386 | vm->keystate |= keycode_mask(event->key.key); | ||
| 387 | } | ||
| 388 | break; | ||
| 389 | |||
| 390 | case SDL_EVENT_KEY_UP: | ||
| 391 | if (vm->positional_input) { | ||
| 392 | vm->keystate &= ~scancode_mask(event->key.scancode); | ||
| 393 | } else { | ||
| 394 | vm->keystate &= ~keycode_mask(event->key.key); | ||
| 395 | } | ||
| 396 | break; | ||
| 397 | } | ||
| 398 | |||
| 399 | return SDL_APP_CONTINUE; | ||
| 400 | } | ||
| 401 | |||
| 402 | void SDL_AppQuit(void* appstate, SDL_AppResult result) { | ||
| 403 | if (result == SDL_APP_FAILURE) { | ||
| 404 | SDL_Log("Error: %s", SDL_GetError()); | ||
| 405 | } | ||
| 406 | if (appstate) { | ||
| 407 | BytePusher* vm = (BytePusher*)appstate; | ||
| 408 | SDL_DestroyAudioStream(vm->audiostream); | ||
| 409 | SDL_DestroyTexture(vm->rendertarget); | ||
| 410 | SDL_DestroyTexture(vm->screentex); | ||
| 411 | SDL_DestroySurface(vm->screen); | ||
| 412 | SDL_DestroyRenderer(vm->renderer); | ||
| 413 | SDL_DestroyWindow(vm->window); | ||
| 414 | SDL_free(vm); | ||
| 415 | } | ||
| 416 | } | ||
diff --git a/contrib/SDL-3.2.8/examples/demo/04-bytepusher/onmouseover.webp b/contrib/SDL-3.2.8/examples/demo/04-bytepusher/onmouseover.webp new file mode 100644 index 0000000..b99e7dd --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/04-bytepusher/onmouseover.webp | |||
| Binary files differ | |||
diff --git a/contrib/SDL-3.2.8/examples/demo/04-bytepusher/thumbnail.png b/contrib/SDL-3.2.8/examples/demo/04-bytepusher/thumbnail.png new file mode 100644 index 0000000..891aa8f --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/04-bytepusher/thumbnail.png | |||
| Binary files differ | |||
diff --git a/contrib/SDL-3.2.8/examples/demo/description.txt b/contrib/SDL-3.2.8/examples/demo/description.txt new file mode 100644 index 0000000..77bcbb7 --- /dev/null +++ b/contrib/SDL-3.2.8/examples/demo/description.txt | |||
| @@ -0,0 +1 @@ | |||
| Full game and app demos \ No newline at end of file | |||
