chore(build): use SDL3

This commit is contained in:
phaneron 2025-04-12 04:38:19 -04:00
parent 9d04a35d87
commit b3c0734a9e
3286 changed files with 866354 additions and 554996 deletions

View file

@ -0,0 +1 @@
A complete game of Snake, written in SDL.

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View file

@ -0,0 +1,350 @@
/*
* Logic implementation of the Snake game. It is designed to efficiently
* represent the state of the game in memory.
*
* This code is public domain. Feel free to use it for any purpose!
*/
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#define STEP_RATE_IN_MILLISECONDS 125
#define SNAKE_BLOCK_SIZE_IN_PIXELS 24
#define SDL_WINDOW_WIDTH (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_WIDTH)
#define SDL_WINDOW_HEIGHT (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_HEIGHT)
#define SNAKE_GAME_WIDTH 24U
#define SNAKE_GAME_HEIGHT 18U
#define SNAKE_MATRIX_SIZE (SNAKE_GAME_WIDTH * SNAKE_GAME_HEIGHT)
#define THREE_BITS 0x7U /* ~CHAR_MAX >> (CHAR_BIT - SNAKE_CELL_MAX_BITS) */
#define SHIFT(x, y) (((x) + ((y) * SNAKE_GAME_WIDTH)) * SNAKE_CELL_MAX_BITS)
typedef enum
{
SNAKE_CELL_NOTHING = 0U,
SNAKE_CELL_SRIGHT = 1U,
SNAKE_CELL_SUP = 2U,
SNAKE_CELL_SLEFT = 3U,
SNAKE_CELL_SDOWN = 4U,
SNAKE_CELL_FOOD = 5U
} SnakeCell;
#define SNAKE_CELL_MAX_BITS 3U /* floor(log2(SNAKE_CELL_FOOD)) + 1 */
typedef enum
{
SNAKE_DIR_RIGHT,
SNAKE_DIR_UP,
SNAKE_DIR_LEFT,
SNAKE_DIR_DOWN
} SnakeDirection;
typedef struct
{
unsigned char cells[(SNAKE_MATRIX_SIZE * SNAKE_CELL_MAX_BITS) / 8U];
char head_xpos;
char head_ypos;
char tail_xpos;
char tail_ypos;
char next_dir;
char inhibit_tail_step;
unsigned occupied_cells;
} SnakeContext;
typedef struct
{
SDL_Window *window;
SDL_Renderer *renderer;
SnakeContext snake_ctx;
Uint64 last_step;
} AppState;
SnakeCell snake_cell_at(const SnakeContext *ctx, char x, char y)
{
const int shift = SHIFT(x, y);
unsigned short range;
SDL_memcpy(&range, ctx->cells + (shift / 8), sizeof(range));
return (SnakeCell)((range >> (shift % 8)) & THREE_BITS);
}
static void set_rect_xy_(SDL_FRect *r, short x, short y)
{
r->x = (float)(x * SNAKE_BLOCK_SIZE_IN_PIXELS);
r->y = (float)(y * SNAKE_BLOCK_SIZE_IN_PIXELS);
}
static void put_cell_at_(SnakeContext *ctx, char x, char y, SnakeCell ct)
{
const int shift = SHIFT(x, y);
const int adjust = shift % 8;
unsigned char *const pos = ctx->cells + (shift / 8);
unsigned short range;
SDL_memcpy(&range, pos, sizeof(range));
range &= ~(THREE_BITS << adjust); /* clear bits */
range |= (ct & THREE_BITS) << adjust;
SDL_memcpy(pos, &range, sizeof(range));
}
static int are_cells_full_(SnakeContext *ctx)
{
return ctx->occupied_cells == SNAKE_GAME_WIDTH * SNAKE_GAME_HEIGHT;
}
static void new_food_pos_(SnakeContext *ctx)
{
while (true) {
const char x = (char) SDL_rand(SNAKE_GAME_WIDTH);
const char y = (char) SDL_rand(SNAKE_GAME_HEIGHT);
if (snake_cell_at(ctx, x, y) == SNAKE_CELL_NOTHING) {
put_cell_at_(ctx, x, y, SNAKE_CELL_FOOD);
break;
}
}
}
void snake_initialize(SnakeContext *ctx)
{
int i;
SDL_zeroa(ctx->cells);
ctx->head_xpos = ctx->tail_xpos = SNAKE_GAME_WIDTH / 2;
ctx->head_ypos = ctx->tail_ypos = SNAKE_GAME_HEIGHT / 2;
ctx->next_dir = SNAKE_DIR_RIGHT;
ctx->inhibit_tail_step = ctx->occupied_cells = 4;
--ctx->occupied_cells;
put_cell_at_(ctx, ctx->tail_xpos, ctx->tail_ypos, SNAKE_CELL_SRIGHT);
for (i = 0; i < 4; i++) {
new_food_pos_(ctx);
++ctx->occupied_cells;
}
}
void snake_redir(SnakeContext *ctx, SnakeDirection dir)
{
SnakeCell ct = snake_cell_at(ctx, ctx->head_xpos, ctx->head_ypos);
if ((dir == SNAKE_DIR_RIGHT && ct != SNAKE_CELL_SLEFT) ||
(dir == SNAKE_DIR_UP && ct != SNAKE_CELL_SDOWN) ||
(dir == SNAKE_DIR_LEFT && ct != SNAKE_CELL_SRIGHT) ||
(dir == SNAKE_DIR_DOWN && ct != SNAKE_CELL_SUP)) {
ctx->next_dir = dir;
}
}
static void wrap_around_(char *val, char max)
{
if (*val < 0) {
*val = max - 1;
} else if (*val > max - 1) {
*val = 0;
}
}
void snake_step(SnakeContext *ctx)
{
const SnakeCell dir_as_cell = (SnakeCell)(ctx->next_dir + 1);
SnakeCell ct;
char prev_xpos;
char prev_ypos;
/* Move tail forward */
if (--ctx->inhibit_tail_step == 0) {
++ctx->inhibit_tail_step;
ct = snake_cell_at(ctx, ctx->tail_xpos, ctx->tail_ypos);
put_cell_at_(ctx, ctx->tail_xpos, ctx->tail_ypos, SNAKE_CELL_NOTHING);
switch (ct) {
case SNAKE_CELL_SRIGHT:
ctx->tail_xpos++;
break;
case SNAKE_CELL_SUP:
ctx->tail_ypos--;
break;
case SNAKE_CELL_SLEFT:
ctx->tail_xpos--;
break;
case SNAKE_CELL_SDOWN:
ctx->tail_ypos++;
break;
default:
break;
}
wrap_around_(&ctx->tail_xpos, SNAKE_GAME_WIDTH);
wrap_around_(&ctx->tail_ypos, SNAKE_GAME_HEIGHT);
}
/* Move head forward */
prev_xpos = ctx->head_xpos;
prev_ypos = ctx->head_ypos;
switch (ctx->next_dir) {
case SNAKE_DIR_RIGHT:
++ctx->head_xpos;
break;
case SNAKE_DIR_UP:
--ctx->head_ypos;
break;
case SNAKE_DIR_LEFT:
--ctx->head_xpos;
break;
case SNAKE_DIR_DOWN:
++ctx->head_ypos;
break;
}
wrap_around_(&ctx->head_xpos, SNAKE_GAME_WIDTH);
wrap_around_(&ctx->head_ypos, SNAKE_GAME_HEIGHT);
/* Collisions */
ct = snake_cell_at(ctx, ctx->head_xpos, ctx->head_ypos);
if (ct != SNAKE_CELL_NOTHING && ct != SNAKE_CELL_FOOD) {
snake_initialize(ctx);
return;
}
put_cell_at_(ctx, prev_xpos, prev_ypos, dir_as_cell);
put_cell_at_(ctx, ctx->head_xpos, ctx->head_ypos, dir_as_cell);
if (ct == SNAKE_CELL_FOOD) {
if (are_cells_full_(ctx)) {
snake_initialize(ctx);
return;
}
new_food_pos_(ctx);
++ctx->inhibit_tail_step;
++ctx->occupied_cells;
}
}
static SDL_AppResult handle_key_event_(SnakeContext *ctx, SDL_Scancode key_code)
{
switch (key_code) {
/* Quit. */
case SDL_SCANCODE_ESCAPE:
case SDL_SCANCODE_Q:
return SDL_APP_SUCCESS;
/* Restart the game as if the program was launched. */
case SDL_SCANCODE_R:
snake_initialize(ctx);
break;
/* Decide new direction of the snake. */
case SDL_SCANCODE_RIGHT:
snake_redir(ctx, SNAKE_DIR_RIGHT);
break;
case SDL_SCANCODE_UP:
snake_redir(ctx, SNAKE_DIR_UP);
break;
case SDL_SCANCODE_LEFT:
snake_redir(ctx, SNAKE_DIR_LEFT);
break;
case SDL_SCANCODE_DOWN:
snake_redir(ctx, SNAKE_DIR_DOWN);
break;
default:
break;
}
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppIterate(void *appstate)
{
AppState *as = (AppState *)appstate;
SnakeContext *ctx = &as->snake_ctx;
const Uint64 now = SDL_GetTicks();
SDL_FRect r;
unsigned i;
unsigned j;
int ct;
// run game logic if we're at or past the time to run it.
// if we're _really_ behind the time to run it, run it
// several times.
while ((now - as->last_step) >= STEP_RATE_IN_MILLISECONDS) {
snake_step(ctx);
as->last_step += STEP_RATE_IN_MILLISECONDS;
}
r.w = r.h = SNAKE_BLOCK_SIZE_IN_PIXELS;
SDL_SetRenderDrawColor(as->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(as->renderer);
for (i = 0; i < SNAKE_GAME_WIDTH; i++) {
for (j = 0; j < SNAKE_GAME_HEIGHT; j++) {
ct = snake_cell_at(ctx, i, j);
if (ct == SNAKE_CELL_NOTHING)
continue;
set_rect_xy_(&r, i, j);
if (ct == SNAKE_CELL_FOOD)
SDL_SetRenderDrawColor(as->renderer, 80, 80, 255, SDL_ALPHA_OPAQUE);
else /* body */
SDL_SetRenderDrawColor(as->renderer, 0, 128, 0, SDL_ALPHA_OPAQUE);
SDL_RenderFillRect(as->renderer, &r);
}
}
SDL_SetRenderDrawColor(as->renderer, 255, 255, 0, SDL_ALPHA_OPAQUE); /*head*/
set_rect_xy_(&r, ctx->head_xpos, ctx->head_ypos);
SDL_RenderFillRect(as->renderer, &r);
SDL_RenderPresent(as->renderer);
return SDL_APP_CONTINUE;
}
static const struct
{
const char *key;
const char *value;
} extended_metadata[] =
{
{ SDL_PROP_APP_METADATA_URL_STRING, "https://examples.libsdl.org/SDL3/demo/01-snake/" },
{ SDL_PROP_APP_METADATA_CREATOR_STRING, "SDL team" },
{ SDL_PROP_APP_METADATA_COPYRIGHT_STRING, "Placed in the public domain" },
{ SDL_PROP_APP_METADATA_TYPE_STRING, "game" }
};
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
size_t i;
if (!SDL_SetAppMetadata("Example Snake game", "1.0", "com.example.Snake")) {
return SDL_APP_FAILURE;
}
for (i = 0; i < SDL_arraysize(extended_metadata); i++) {
if (!SDL_SetAppMetadataProperty(extended_metadata[i].key, extended_metadata[i].value)) {
return SDL_APP_FAILURE;
}
}
if (!SDL_Init(SDL_INIT_VIDEO)) {
return SDL_APP_FAILURE;
}
AppState *as = (AppState *)SDL_calloc(1, sizeof(AppState));
if (!as) {
return SDL_APP_FAILURE;
}
*appstate = as;
if (!SDL_CreateWindowAndRenderer("examples/demo/snake", SDL_WINDOW_WIDTH, SDL_WINDOW_HEIGHT, 0, &as->window, &as->renderer)) {
return SDL_APP_FAILURE;
}
snake_initialize(&as->snake_ctx);
as->last_step = SDL_GetTicks();
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
SnakeContext *ctx = &((AppState *)appstate)->snake_ctx;
switch (event->type) {
case SDL_EVENT_QUIT:
return SDL_APP_SUCCESS;
case SDL_EVENT_KEY_DOWN:
return handle_key_event_(ctx, event->key.scancode);
}
return SDL_APP_CONTINUE;
}
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
if (appstate != NULL) {
AppState *as = (AppState *)appstate;
SDL_DestroyRenderer(as->renderer);
SDL_DestroyWindow(as->window);
SDL_free(as);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -0,0 +1 @@
Minimal splitscreen FPS with multiple mouse and keyboards, drawn with the 2D Render API.

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View file

@ -0,0 +1,480 @@
/*
* This code is public domain. Feel free to use it for any purpose!
*/
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#define MAP_BOX_SCALE 16
#define MAP_BOX_EDGES_LEN (12 + MAP_BOX_SCALE * 2)
#define MAX_PLAYER_COUNT 4
#define CIRCLE_DRAW_SIDES 32
#define CIRCLE_DRAW_SIDES_LEN (CIRCLE_DRAW_SIDES + 1)
typedef struct {
SDL_MouseID mouse;
SDL_KeyboardID keyboard;
double pos[3];
double vel[3];
unsigned int yaw;
int pitch;
float radius, height;
unsigned char color[3];
unsigned char wasd;
} Player;
typedef struct {
SDL_Window *window;
SDL_Renderer *renderer;
int player_count;
Player players[MAX_PLAYER_COUNT];
float edges[MAP_BOX_EDGES_LEN][6];
} AppState;
static const struct {
const char *key;
const char *value;
} extended_metadata[] = {
{ SDL_PROP_APP_METADATA_URL_STRING, "https://examples.libsdl.org/SDL3/demo/02-woodeneye-008/" },
{ SDL_PROP_APP_METADATA_CREATOR_STRING, "SDL team" },
{ SDL_PROP_APP_METADATA_COPYRIGHT_STRING, "Placed in the public domain" },
{ SDL_PROP_APP_METADATA_TYPE_STRING, "game" }
};
static int whoseMouse(SDL_MouseID mouse, const Player players[], int players_len)
{
int i;
for (i = 0; i < players_len; i++) {
if (players[i].mouse == mouse) return i;
}
return -1;
}
static int whoseKeyboard(SDL_KeyboardID keyboard, const Player players[], int players_len)
{
int i;
for (i = 0; i < players_len; i++) {
if (players[i].keyboard == keyboard) return i;
}
return -1;
}
static void shoot(int shooter, Player players[], int players_len)
{
int i, j;
double x0 = players[shooter].pos[0];
double y0 = players[shooter].pos[1];
double z0 = players[shooter].pos[2];
double bin_rad = SDL_PI_D / 2147483648.0;
double yaw_rad = bin_rad * players[shooter].yaw;
double pitch_rad = bin_rad * players[shooter].pitch;
double cos_yaw = SDL_cos( yaw_rad);
double sin_yaw = SDL_sin( yaw_rad);
double cos_pitch = SDL_cos(pitch_rad);
double sin_pitch = SDL_sin(pitch_rad);
double vx = -sin_yaw*cos_pitch;
double vy = sin_pitch;
double vz = -cos_yaw*cos_pitch;
for (i = 0; i < players_len; i++) {
if (i == shooter) continue;
Player *target = &(players[i]);
int hit = 0;
for (j = 0; j < 2; j++) {
double r = target->radius;
double h = target->height;
double dx = target->pos[0] - x0;
double dy = target->pos[1] - y0 + (j == 0 ? 0 : r - h);
double dz = target->pos[2] - z0;
double vd = vx*dx + vy*dy + vz*dz;
double dd = dx*dx + dy*dy + dz*dz;
double vv = vx*vx + vy*vy + vz*vz;
double rr = r * r;
if (vd < 0) continue;
if (vd * vd >= vv * (dd - rr)) hit += 1;
}
if (hit) {
target->pos[0] = (double)(MAP_BOX_SCALE * (SDL_rand(256) - 128)) / 256;
target->pos[1] = (double)(MAP_BOX_SCALE * (SDL_rand(256) - 128)) / 256;
target->pos[2] = (double)(MAP_BOX_SCALE * (SDL_rand(256) - 128)) / 256;
}
}
}
static void update(Player *players, int players_len, Uint64 dt_ns)
{
int i;
for (i = 0; i < players_len; i++) {
Player *player = &players[i];
double rate = 6.0;
double time = (double)dt_ns * 1e-9;
double drag = SDL_exp(-time * rate);
double diff = 1.0 - drag;
double mult = 60.0;
double grav = 25.0;
double yaw = (double)player->yaw;
double rad = yaw * SDL_PI_D / 2147483648.0;
double cos = SDL_cos(rad);
double sin = SDL_sin(rad);
unsigned char wasd = player->wasd;
double dirX = (wasd & 8 ? 1.0 : 0.0) - (wasd & 2 ? 1.0 : 0.0);
double dirZ = (wasd & 4 ? 1.0 : 0.0) - (wasd & 1 ? 1.0 : 0.0);
double norm = dirX * dirX + dirZ * dirZ;
double accX = mult * (norm == 0 ? 0 : ( cos*dirX + sin*dirZ) / SDL_sqrt(norm));
double accZ = mult * (norm == 0 ? 0 : (-sin*dirX + cos*dirZ) / SDL_sqrt(norm));
double velX = player->vel[0];
double velY = player->vel[1];
double velZ = player->vel[2];
player->vel[0] -= velX * diff;
player->vel[1] -= grav * time;
player->vel[2] -= velZ * diff;
player->vel[0] += diff * accX / rate;
player->vel[2] += diff * accZ / rate;
player->pos[0] += (time - diff/rate) * accX / rate + diff * velX / rate;
player->pos[1] += -0.5 * grav * time * time + velY * time;
player->pos[2] += (time - diff/rate) * accZ / rate + diff * velZ / rate;
double scale = (double)MAP_BOX_SCALE;
double bound = scale - player->radius;
double posX = SDL_max(SDL_min(bound, player->pos[0]), -bound);
double posY = SDL_max(SDL_min(bound, player->pos[1]), player->height - scale);
double posZ = SDL_max(SDL_min(bound, player->pos[2]), -bound);
if (player->pos[0] != posX) player->vel[0] = 0;
if (player->pos[1] != posY) player->vel[1] = (wasd & 16) ? 8.4375 : 0;
if (player->pos[2] != posZ) player->vel[2] = 0;
player->pos[0] = posX;
player->pos[1] = posY;
player->pos[2] = posZ;
}
}
static void drawCircle(SDL_Renderer *renderer, float r, float x, float y)
{
float ang;
SDL_FPoint points[CIRCLE_DRAW_SIDES_LEN];
int i;
for (i = 0; i < CIRCLE_DRAW_SIDES_LEN; i++) {
ang = 2.0f * SDL_PI_F * (float)i / (float)CIRCLE_DRAW_SIDES;
points[i].x = x + r * SDL_cosf(ang);
points[i].y = y + r * SDL_sinf(ang);
}
SDL_RenderLines(renderer, (const SDL_FPoint*)&points, CIRCLE_DRAW_SIDES_LEN);
}
static void drawClippedSegment(
SDL_Renderer *renderer,
float ax, float ay, float az,
float bx, float by, float bz,
float x, float y, float z, float w)
{
if (az >= -w && bz >= -w) return;
float dx = ax - bx;
float dy = ay - by;
if (az > -w) {
float t = (-w - bz) / (az - bz);
ax = bx + dx * t;
ay = by + dy * t;
az = -w;
} else if (bz > -w) {
float t = (-w - az) / (bz - az);
bx = ax - dx * t;
by = ay - dy * t;
bz = -w;
}
ax = -z * ax / az;
ay = -z * ay / az;
bx = -z * bx / bz;
by = -z * by / bz;
SDL_RenderLine(renderer, x + ax, y - ay, x + bx, y - by);
}
static char debug_string[32];
static void draw(SDL_Renderer *renderer, const float (*edges)[6], const Player players[], int players_len)
{
int w, h, i, j, k;
if (!SDL_GetRenderOutputSize(renderer, &w, &h)) {
return;
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);
if (players_len > 0) {
float wf = (float)w;
float hf = (float)h;
int part_hor = players_len > 2 ? 2 : 1;
int part_ver = players_len > 1 ? 2 : 1;
float size_hor = wf / ((float)part_hor);
float size_ver = hf / ((float)part_ver);
for (i = 0; i < players_len; i++) {
const Player *player = &players[i];
float mod_x = (float)(i % part_hor);
float mod_y = (float)(i / part_hor);
float hor_origin = (mod_x + 0.5f) * size_hor;
float ver_origin = (mod_y + 0.5f) * size_ver;
float cam_origin = (float)(0.5 * SDL_sqrt(size_hor * size_hor + size_ver * size_ver));
float hor_offset = mod_x * size_hor;
float ver_offset = mod_y * size_ver;
SDL_Rect rect;
rect.x = (int)hor_offset;
rect.y = (int)ver_offset;
rect.w = (int)size_hor;
rect.h = (int)size_ver;
SDL_SetRenderClipRect(renderer, &rect);
double x0 = player->pos[0];
double y0 = player->pos[1];
double z0 = player->pos[2];
double bin_rad = SDL_PI_D / 2147483648.0;
double yaw_rad = bin_rad * player->yaw;
double pitch_rad = bin_rad * player->pitch;
double cos_yaw = SDL_cos( yaw_rad);
double sin_yaw = SDL_sin( yaw_rad);
double cos_pitch = SDL_cos(pitch_rad);
double sin_pitch = SDL_sin(pitch_rad);
double mat[9] = {
cos_yaw , 0, -sin_yaw ,
sin_yaw*sin_pitch, cos_pitch, cos_yaw*sin_pitch,
sin_yaw*cos_pitch, -sin_pitch, cos_yaw*cos_pitch
};
SDL_SetRenderDrawColor(renderer, 64, 64, 64, 255);
for (k = 0; k < MAP_BOX_EDGES_LEN; k++) {
const float *line = edges[k];
float ax = (float)(mat[0] * (line[0] - x0) + mat[1] * (line[1] - y0) + mat[2] * (line[2] - z0));
float ay = (float)(mat[3] * (line[0] - x0) + mat[4] * (line[1] - y0) + mat[5] * (line[2] - z0));
float az = (float)(mat[6] * (line[0] - x0) + mat[7] * (line[1] - y0) + mat[8] * (line[2] - z0));
float bx = (float)(mat[0] * (line[3] - x0) + mat[1] * (line[4] - y0) + mat[2] * (line[5] - z0));
float by = (float)(mat[3] * (line[3] - x0) + mat[4] * (line[4] - y0) + mat[5] * (line[5] - z0));
float bz = (float)(mat[6] * (line[3] - x0) + mat[7] * (line[4] - y0) + mat[8] * (line[5] - z0));
drawClippedSegment(renderer, ax, ay, az, bx, by, bz, hor_origin, ver_origin, cam_origin, 1);
}
for (j = 0; j < players_len; j++) {
if (i == j) continue;
const Player *target = &players[j];
SDL_SetRenderDrawColor(renderer, target->color[0], target->color[1], target->color[2], 255);
for (k = 0; k < 2; k++) {
double rx = target->pos[0] - player->pos[0];
double ry = target->pos[1] - player->pos[1] + (target->radius - target->height) * (float)k;
double rz = target->pos[2] - player->pos[2];
double dx = mat[0] * rx + mat[1] * ry + mat[2] * rz;
double dy = mat[3] * rx + mat[4] * ry + mat[5] * rz;
double dz = mat[6] * rx + mat[7] * ry + mat[8] * rz;
double r_eff = target->radius * cam_origin / dz;
if (!(dz < 0)) continue;
drawCircle(renderer, (float)(r_eff), (float)(hor_origin - cam_origin*dx/dz), (float)(ver_origin + cam_origin*dy/dz));
}
}
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderLine(renderer, hor_origin, ver_origin-10, hor_origin, ver_origin+10);
SDL_RenderLine(renderer, hor_origin-10, ver_origin, hor_origin+10, ver_origin);
}
}
SDL_SetRenderClipRect(renderer, 0);
SDL_SetRenderDrawColor(renderer, 255, 255, 255, 255);
SDL_RenderDebugText(renderer, 0, 0, debug_string);
SDL_RenderPresent(renderer);
}
static void initPlayers(Player *players, int len)
{
int i;
for (i = 0; i < len; i++) {
players[i].pos[0] = 8.0 * (i & 1 ? -1.0 : 1.0);
players[i].pos[1] = 0;
players[i].pos[2] = 8.0 * (i & 1 ? -1.0 : 1.0) * (i & 2 ? -1.0 : 1.0);
players[i].vel[0] = 0;
players[i].vel[1] = 0;
players[i].vel[2] = 0;
players[i].yaw = 0x20000000 + (i & 1 ? 0x80000000 : 0) + (i & 2 ? 0x40000000 : 0);
players[i].pitch = -0x08000000;
players[i].radius = 0.5f;
players[i].height = 1.5f;
players[i].wasd = 0;
players[i].mouse = 0;
players[i].keyboard = 0;
players[i].color[0] = (1 << (i / 2)) & 2 ? 0 : 0xff;
players[i].color[1] = (1 << (i / 2)) & 1 ? 0 : 0xff;
players[i].color[2] = (1 << (i / 2)) & 4 ? 0 : 0xff;
players[i].color[0] = (i & 1) ? players[i].color[0] : ~players[i].color[0];
players[i].color[1] = (i & 1) ? players[i].color[1] : ~players[i].color[1];
players[i].color[2] = (i & 1) ? players[i].color[2] : ~players[i].color[2];
}
}
static void initEdges(int scale, float (*edges)[6], int edges_len)
{
int i, j;
const float r = (float)scale;
const int map[24] = {
0,1 , 1,3 , 3,2 , 2,0 ,
7,6 , 6,4 , 4,5 , 5,7 ,
6,2 , 3,7 , 0,4 , 5,1
};
for(i = 0; i < 12; i++) {
for (j = 0; j < 3; j++) {
edges[i][j+0] = (map[i*2+0] & (1 << j) ? r : -r);
edges[i][j+3] = (map[i*2+1] & (1 << j) ? r : -r);
}
}
for(i = 0; i < scale; i++) {
float d = (float)(i * 2);
for (j = 0; j < 2; j++) {
edges[i+12][3*j+0] = j ? r : -r;
edges[i+12][3*j+1] = -r;
edges[i+12][3*j+2] = d-r;
edges[i+12+scale][3*j+0] = d-r;
edges[i+12+scale][3*j+1] = -r;
edges[i+12+scale][3*j+2] = j ? r : -r;
}
}
}
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
if (!SDL_SetAppMetadata("Example splitscreen shooter game", "1.0", "com.example.woodeneye-008")) {
return SDL_APP_FAILURE;
}
int i;
for (i = 0; i < SDL_arraysize(extended_metadata); i++) {
if (!SDL_SetAppMetadataProperty(extended_metadata[i].key, extended_metadata[i].value)) {
return SDL_APP_FAILURE;
}
}
AppState *as = SDL_calloc(1, sizeof(AppState));
if (!as) {
return SDL_APP_FAILURE;
} else {
*appstate = as;
}
if (!SDL_Init(SDL_INIT_VIDEO)) {
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/demo/woodeneye-008", 640, 480, 0, &as->window, &as->renderer)) {
return SDL_APP_FAILURE;
}
as->player_count = 1;
initPlayers(as->players, MAX_PLAYER_COUNT);
initEdges(MAP_BOX_SCALE, as->edges, MAP_BOX_EDGES_LEN);
debug_string[0] = 0;
SDL_SetRenderVSync(as->renderer, false);
SDL_SetWindowRelativeMouseMode(as->window, true);
SDL_SetHintWithPriority(SDL_HINT_WINDOWS_RAW_KEYBOARD, "1", SDL_HINT_OVERRIDE);
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
AppState *as = appstate;
Player *players = as->players;
int player_count = as->player_count;
int i;
switch (event->type) {
case SDL_EVENT_QUIT:
return SDL_APP_SUCCESS;
break;
case SDL_EVENT_MOUSE_REMOVED:
for (i = 0; i < player_count; i++) {
if (players[i].mouse == event->mdevice.which) {
players[i].mouse = 0;
}
}
break;
case SDL_EVENT_KEYBOARD_REMOVED:
for (i = 0; i < player_count; i++) {
if (players[i].keyboard == event->kdevice.which) {
players[i].keyboard = 0;
}
}
break;
case SDL_EVENT_MOUSE_MOTION: {
SDL_MouseID id = event->motion.which;
int index = whoseMouse(id, players, player_count);
if (index >= 0) {
players[index].yaw -= ((int)event->motion.xrel) * 0x00080000;
players[index].pitch = SDL_max(-0x40000000, SDL_min(0x40000000, players[index].pitch - ((int)event->motion.yrel) * 0x00080000));
} else if (id) {
for (i = 0; i < MAX_PLAYER_COUNT; i++) {
if (players[i].mouse == 0) {
players[i].mouse = id;
as->player_count = SDL_max(as->player_count, i + 1);
break;
}
}
}
break;
}
case SDL_EVENT_MOUSE_BUTTON_DOWN: {
SDL_MouseID id = event->button.which;
int index = whoseMouse(id, players, player_count);
if (index >= 0) {
shoot(index, players, player_count);
}
break;
}
case SDL_EVENT_KEY_DOWN: {
SDL_Keycode sym = event->key.key;
SDL_KeyboardID id = event->key.which;
int index = whoseKeyboard(id, players, player_count);
if (index >= 0) {
if (sym == SDLK_W) players[index].wasd |= 1;
if (sym == SDLK_A) players[index].wasd |= 2;
if (sym == SDLK_S) players[index].wasd |= 4;
if (sym == SDLK_D) players[index].wasd |= 8;
if (sym == SDLK_SPACE) players[index].wasd |= 16;
} else if (id) {
for (i = 0; i < MAX_PLAYER_COUNT; i++) {
if (players[i].keyboard == 0) {
players[i].keyboard = id;
as->player_count = SDL_max(as->player_count, i + 1);
break;
}
}
}
break;
}
case SDL_EVENT_KEY_UP: {
SDL_Keycode sym = event->key.key;
SDL_KeyboardID id = event->key.which;
if (sym == SDLK_ESCAPE) return SDL_APP_SUCCESS;
int index = whoseKeyboard(id, players, player_count);
if (index >= 0) {
if (sym == SDLK_W) players[index].wasd &= 30;
if (sym == SDLK_A) players[index].wasd &= 29;
if (sym == SDLK_S) players[index].wasd &= 27;
if (sym == SDLK_D) players[index].wasd &= 23;
if (sym == SDLK_SPACE) players[index].wasd &= 15;
}
break;
}
}
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppIterate(void *appstate)
{
AppState *as = appstate;
static Uint64 accu = 0;
static Uint64 last = 0;
static Uint64 past = 0;
Uint64 now = SDL_GetTicksNS();
Uint64 dt_ns = now - past;
update(as->players, as->player_count, dt_ns);
draw(as->renderer, (const float (*)[6])as->edges, as->players, as->player_count);
if (now - last > 999999999) {
last = now;
SDL_snprintf(debug_string, sizeof(debug_string), "%" SDL_PRIu64 " fps", accu);
accu = 0;
}
past = now;
accu += 1;
Uint64 elapsed = SDL_GetTicksNS() - now;
if (elapsed < 999999) {
SDL_DelayNS(999999 - elapsed);
}
return SDL_APP_CONTINUE;
}
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
SDL_free(appstate); // just free the memory, SDL will clean up the window/renderer for us.
}

View file

@ -0,0 +1,7 @@
How many monkeys does it take to write the complete works of Shakespeare?
Now you can find out!
Cheer on your favorite monkey as they bash keyboards on their way through classic literature.

View file

@ -0,0 +1,377 @@
/*
* This code is public domain. Feel free to use it for any purpose!
*/
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
/* We will use this renderer to draw into this window every frame. */
static SDL_Window *window = NULL;
static SDL_Renderer *renderer = NULL;
static char *text;
static const char *end;
static const char *progress;
static SDL_Time start_time;
static SDL_Time end_time;
typedef struct {
Uint32 *text;
int length;
} Line;
int row = 0;
int rows = 0;
int cols = 0;
static Line **lines;
static Line monkey_chars;
static int monkeys = 100;
/* The highest and lowest scancodes a monkey can hit */
#define MIN_MONKEY_SCANCODE SDL_SCANCODE_A
#define MAX_MONKEY_SCANCODE SDL_SCANCODE_SLASH
static const char *default_text =
"Jabberwocky, by Lewis Carroll\n"
"\n"
"'Twas brillig, and the slithy toves\n"
" Did gyre and gimble in the wabe:\n"
"All mimsy were the borogoves,\n"
" And the mome raths outgrabe.\n"
"\n"
"\"Beware the Jabberwock, my son!\n"
" The jaws that bite, the claws that catch!\n"
"Beware the Jubjub bird, and shun\n"
" The frumious Bandersnatch!\"\n"
"\n"
"He took his vorpal sword in hand;\n"
" Long time the manxome foe he sought-\n"
"So rested he by the Tumtum tree\n"
" And stood awhile in thought.\n"
"\n"
"And, as in uffish thought he stood,\n"
" The Jabberwock, with eyes of flame,\n"
"Came whiffling through the tulgey wood,\n"
" And burbled as it came!\n"
"\n"
"One, two! One, two! And through and through\n"
" The vorpal blade went snicker-snack!\n"
"He left it dead, and with its head\n"
" He went galumphing back.\n"
"\n"
"\"And hast thou slain the Jabberwock?\n"
" Come to my arms, my beamish boy!\n"
"O frabjous day! Callooh! Callay!\"\n"
" He chortled in his joy.\n"
"\n"
"'Twas brillig, and the slithy toves\n"
" Did gyre and gimble in the wabe:\n"
"All mimsy were the borogoves,\n"
" And the mome raths outgrabe.\n";
static void FreeLines(void)
{
int i;
if (rows > 0 && cols > 0) {
for (i = 0; i < rows; ++i) {
SDL_free(lines[i]->text);
SDL_free(lines[i]);
}
SDL_free(lines);
lines = NULL;
}
SDL_free(monkey_chars.text);
monkey_chars.text = NULL;
}
static void OnWindowSizeChanged(void)
{
int w, h;
if (!SDL_GetCurrentRenderOutputSize(renderer, &w, &h)) {
return;
}
FreeLines();
row = 0;
rows = (h / SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE) - 4;
cols = (w / SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE);
if (rows > 0 && cols > 0) {
int i;
lines = (Line **)SDL_malloc(rows * sizeof(Line *));
if (lines) {
for (i = 0; i < rows; ++i) {
lines[i] = (Line *)SDL_malloc(sizeof(Line));
if (!lines[i]) {
FreeLines();
break;
}
lines[i]->text = (Uint32 *)SDL_malloc(cols * sizeof(Uint32));
if (!lines[i]->text) {
FreeLines();
break;
}
lines[i]->length = 0;
}
}
monkey_chars.text = (Uint32 *)SDL_malloc(cols * sizeof(Uint32));
if (monkey_chars.text) {
for (i = 0; i < cols; ++i) {
monkey_chars.text[i] = ' ';
}
monkey_chars.length = cols;
}
}
}
/* This function runs once at startup. */
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
{
int arg = 1;
SDL_SetAppMetadata("Infinite Monkeys", "1.0", "com.example.infinite-monkeys");
if (!SDL_Init(SDL_INIT_VIDEO)) {
SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
if (!SDL_CreateWindowAndRenderer("examples/demo/infinite-monkeys", 640, 480, 0, &window, &renderer)) {
SDL_Log("Couldn't create window/renderer: %s", SDL_GetError());
return SDL_APP_FAILURE;
}
SDL_SetRenderVSync(renderer, 1);
if (argv[arg] && SDL_strcmp(argv[arg], "--monkeys") == 0) {
++arg;
if (argv[arg]) {
monkeys = SDL_atoi(argv[arg]);
++arg;
} else {
SDL_Log("Usage: %s [--monkeys N] [file.txt]", argv[0]);
return SDL_APP_FAILURE;
}
}
if (argv[arg]) {
const char *file = argv[arg];
size_t size;
text = (char *)SDL_LoadFile(file, &size);
if (!text) {
SDL_Log("Couldn't open %s: %s", file, SDL_GetError());
return SDL_APP_FAILURE;
}
end = text + size;
} else {
text = SDL_strdup(default_text);
end = text + SDL_strlen(text);
}
progress = text;
SDL_GetCurrentTime(&start_time);
OnWindowSizeChanged();
return SDL_APP_CONTINUE; /* carry on with the program! */
}
/* This function runs when a new event (mouse input, keypresses, etc) occurs. */
SDL_AppResult SDL_AppEvent(void *appstate, SDL_Event *event)
{
switch (event->type) {
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
OnWindowSizeChanged();
break;
case SDL_EVENT_QUIT:
return SDL_APP_SUCCESS; /* end the program, reporting success to the OS. */
}
return SDL_APP_CONTINUE; /* carry on with the program! */
}
static void DisplayLine(float x, float y, Line *line)
{
/* Allocate maximum space potentially needed for this line */
char *utf8 = (char *)SDL_malloc(line->length * 4 + 1);
if (utf8) {
char *spot = utf8;
int i;
for (i = 0; i < line->length; ++i) {
spot = SDL_UCS4ToUTF8(line->text[i], spot);
}
*spot = '\0';
SDL_RenderDebugText(renderer, x, y, utf8);
SDL_free(utf8);
}
}
static bool CanMonkeyType(Uint32 ch)
{
SDL_Keymod modstate;
SDL_Scancode scancode = SDL_GetScancodeFromKey(ch, &modstate);
if (scancode < MIN_MONKEY_SCANCODE || scancode > MAX_MONKEY_SCANCODE) {
return false;
}
/* Monkeys can hit the shift key, but nothing else */
if ((modstate & ~SDL_KMOD_SHIFT) != 0) {
return false;
}
return true;
}
static void AdvanceRow(void)
{
Line *line;
++row;
line = lines[row % rows];
line->length = 0;
}
static void AddMonkeyChar(int monkey, Uint32 ch)
{
if (monkey >= 0 && monkey_chars.text) {
monkey_chars.text[(monkey % cols)] = ch;
}
if (lines) {
if (ch == '\n') {
AdvanceRow();
} else {
Line *line = lines[row % rows];
line->text[line->length++] = ch;
if (line->length == cols) {
AdvanceRow();
}
}
}
SDL_StepUTF8(&progress, NULL);
}
static Uint32 GetNextChar(void)
{
Uint32 ch = 0;
while (progress < end) {
const char *spot = progress;
ch = SDL_StepUTF8(&spot, NULL);
if (CanMonkeyType(ch)) {
break;
} else {
/* This is a freebie, monkeys can't type this */
AddMonkeyChar(-1, ch);
}
}
return ch;
}
static Uint32 MonkeyPlay(void)
{
int count = (MAX_MONKEY_SCANCODE - MIN_MONKEY_SCANCODE + 1);
SDL_Scancode scancode = (SDL_Scancode)(MIN_MONKEY_SCANCODE + SDL_rand(count));
SDL_Keymod modstate = (SDL_rand(2) ? SDL_KMOD_SHIFT : 0);
return SDL_GetKeyFromScancode(scancode, modstate, false);
}
/* This function runs once per frame, and is the heart of the program. */
SDL_AppResult SDL_AppIterate(void *appstate)
{
int i, monkey;
Uint32 next_char = 0, ch;
float x, y;
char *caption = NULL;
SDL_Time now, elapsed;
int hours, minutes, seconds;
SDL_FRect rect;
for (monkey = 0; monkey < monkeys; ++monkey) {
if (next_char == 0) {
next_char = GetNextChar();
if (!next_char) {
/* All done! */
break;
}
}
ch = MonkeyPlay();
if (ch == next_char) {
AddMonkeyChar(monkey, ch);
next_char = 0;
}
}
/* Clear the screen */
SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderClear(renderer);
/* Show the text already decoded */
SDL_SetRenderDrawColor(renderer, 255, 255, 255, SDL_ALPHA_OPAQUE);
x = 0.0f;
y = 0.0f;
if (lines) {
int row_offset = row - rows + 1;
if (row_offset < 0) {
row_offset = 0;
}
for (i = 0; i < rows; ++i) {
Line *line = lines[(row_offset + i) % rows];
DisplayLine(x, y, line);
y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
}
/* Show the caption */
y = (float)((rows + 1) * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE);
if (progress == end) {
if (!end_time) {
SDL_GetCurrentTime(&end_time);
}
now = end_time;
} else {
SDL_GetCurrentTime(&now);
}
elapsed = (now - start_time);
elapsed /= SDL_NS_PER_SECOND;
seconds = (int)(elapsed % 60);
elapsed /= 60;
minutes = (int)(elapsed % 60);
elapsed /= 60;
hours = (int)elapsed;
SDL_asprintf(&caption, "Monkeys: %d - %dH:%dM:%dS", monkeys, hours, minutes, seconds);
if (caption) {
SDL_RenderDebugText(renderer, x, y, caption);
SDL_free(caption);
}
y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
/* Show the characters currently typed */
DisplayLine(x, y, &monkey_chars);
y += SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
}
/* Show the current progress */
SDL_SetRenderDrawColor(renderer, 0, 255, 0, SDL_ALPHA_OPAQUE);
rect.x = x;
rect.y = y;
rect.w = ((float)(progress - text) / (end - text)) * (cols * SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE);
rect.h = (float)SDL_DEBUG_TEXT_FONT_CHARACTER_SIZE;
SDL_RenderFillRect(renderer, &rect);
SDL_RenderPresent(renderer);
return SDL_APP_CONTINUE; /* carry on with the program! */
}
/* This function runs once at shutdown. */
void SDL_AppQuit(void *appstate, SDL_AppResult result)
{
/* SDL will clean up the window/renderer for us. */
FreeLines();
SDL_free(text);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View file

@ -0,0 +1,4 @@
An implementation of the BytePusher VM
For example programs and more information about BytePusher, see
https://esolangs.org/wiki/BytePusher

View file

@ -0,0 +1,416 @@
/*
* An implementation of the BytePusher VM.
*
* For example programs and more information about BytePusher, see
* https://esolangs.org/wiki/BytePusher
*
* This code is public domain. Feel free to use it for any purpose!
*/
#define SDL_MAIN_USE_CALLBACKS
#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <stdarg.h>
#define SCREEN_W 256
#define SCREEN_H 256
#define RAM_SIZE 0x1000000
#define FRAMES_PER_SECOND 60
#define SAMPLES_PER_FRAME 256
#define NS_PER_SECOND (Uint64)SDL_NS_PER_SECOND
#define MAX_AUDIO_LATENCY_FRAMES 5
#define IO_KEYBOARD 0
#define IO_PC 2
#define IO_SCREEN_PAGE 5
#define IO_AUDIO_BANK 6
typedef struct {
Uint8 ram[RAM_SIZE + 8];
Uint8 screenbuf[SCREEN_W * SCREEN_H];
Uint64 last_tick;
Uint64 tick_acc;
SDL_Window* window;
SDL_Renderer* renderer;
SDL_Surface* screen;
SDL_Texture* screentex;
SDL_Texture* rendertarget; /* we need this render target for text to look good */
SDL_AudioStream* audiostream;
char status[SCREEN_W / 8];
int status_ticks;
Uint16 keystate;
bool display_help;
bool positional_input;
} BytePusher;
static const struct {
const char *key;
const char *value;
} extended_metadata[] = {
{ SDL_PROP_APP_METADATA_URL_STRING, "https://examples.libsdl.org/SDL3/demo/04-bytepusher/" },
{ SDL_PROP_APP_METADATA_CREATOR_STRING, "SDL team" },
{ SDL_PROP_APP_METADATA_COPYRIGHT_STRING, "Placed in the public domain" },
{ SDL_PROP_APP_METADATA_TYPE_STRING, "game" }
};
static inline Uint16 read_u16(const BytePusher* vm, Uint32 addr) {
const Uint8* ptr = &vm->ram[addr];
return ((Uint16)ptr[0] << 8) | ((Uint16)ptr[1]);
}
static inline Uint32 read_u24(const BytePusher* vm, Uint32 addr) {
const Uint8* ptr = &vm->ram[addr];
return ((Uint32)ptr[0] << 16) | ((Uint32)ptr[1] << 8) | ((Uint32)ptr[2]);
}
static void set_status(BytePusher* vm, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
SDL_vsnprintf(vm->status, sizeof(vm->status), fmt, args);
va_end(args);
vm->status[sizeof(vm->status) - 1] = 0;
vm->status_ticks = FRAMES_PER_SECOND * 3;
}
static bool load(BytePusher* vm, SDL_IOStream* stream, bool closeio) {
size_t bytes_read = 0;
bool ok = true;
SDL_memset(vm->ram, 0, RAM_SIZE);
if (!stream) {
return false;
}
while (bytes_read < RAM_SIZE) {
size_t read = SDL_ReadIO(stream, &vm->ram[bytes_read], RAM_SIZE - bytes_read);
bytes_read += read;
if (read == 0) {
ok = SDL_GetIOStatus(stream) == SDL_IO_STATUS_EOF;
break;
}
}
if (closeio) {
SDL_CloseIO(stream);
}
SDL_ClearAudioStream(vm->audiostream);
vm->display_help = !ok;
return ok;
}
static const char* filename(const char* path) {
size_t i = SDL_strlen(path) + 1;
while (i > 0) {
i -= 1;
if (path[i] == '/' || path[i] == '\\') {
return path + i + 1;
}
}
return path;
}
static bool load_file(BytePusher* vm, const char* path) {
if (load(vm, SDL_IOFromFile(path, "rb"), true)) {
set_status(vm, "loaded %s", filename(path));
return true;
} else {
set_status(vm, "load failed: %s", filename(path));
return false;
}
}
static void print(BytePusher* vm, int x, int y, const char* str) {
SDL_SetRenderDrawColor(vm->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
SDL_RenderDebugText(vm->renderer, (float)(x + 1), (float)(y + 1), str);
SDL_SetRenderDrawColor(vm->renderer, 0xff, 0xff, 0xff, SDL_ALPHA_OPAQUE);
SDL_RenderDebugText(vm->renderer, (float)x, (float)y, str);
SDL_SetRenderDrawColor(vm->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
}
SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[]) {
BytePusher* vm;
SDL_Palette* palette;
SDL_Rect usable_bounds;
SDL_AudioSpec audiospec = { SDL_AUDIO_S8, 1, SAMPLES_PER_FRAME * FRAMES_PER_SECOND };
SDL_DisplayID primary_display;
SDL_PropertiesID texprops;
int zoom = 2;
int i;
Uint8 r, g, b;
(void)argc;
(void)argv;
if (!SDL_SetAppMetadata("SDL 3 BytePusher", "1.0", "com.example.SDL3BytePusher")) {
return SDL_APP_FAILURE;
}
for (i = 0; i < (int)SDL_arraysize(extended_metadata); i++) {
if (!SDL_SetAppMetadataProperty(extended_metadata[i].key, extended_metadata[i].value)) {
return SDL_APP_FAILURE;
}
}
if (!SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO)) {
return SDL_APP_FAILURE;
}
if (!(vm = (BytePusher *)SDL_calloc(1, sizeof(*vm)))) {
return SDL_APP_FAILURE;
}
*(BytePusher**)appstate = vm;
vm->display_help = true;
primary_display = SDL_GetPrimaryDisplay();
if (SDL_GetDisplayUsableBounds(primary_display, &usable_bounds)) {
int zoom_w = (usable_bounds.w - usable_bounds.x) * 2 / 3 / SCREEN_W;
int zoom_h = (usable_bounds.h - usable_bounds.y) * 2 / 3 / SCREEN_H;
zoom = zoom_w < zoom_h ? zoom_w : zoom_h;
if (zoom < 1) {
zoom = 1;
}
}
if (!SDL_CreateWindowAndRenderer("SDL 3 BytePusher",
SCREEN_W * zoom, SCREEN_H * zoom, SDL_WINDOW_RESIZABLE,
&vm->window, &vm->renderer
)) {
return SDL_APP_FAILURE;
}
if (!SDL_SetRenderLogicalPresentation(
vm->renderer, SCREEN_W, SCREEN_H, SDL_LOGICAL_PRESENTATION_INTEGER_SCALE
)) {
return SDL_APP_FAILURE;
}
if (!(vm->screen = SDL_CreateSurfaceFrom(
SCREEN_W, SCREEN_H, SDL_PIXELFORMAT_INDEX8, vm->screenbuf, SCREEN_W
))) {
return SDL_APP_FAILURE;
}
if (!(palette = SDL_CreateSurfacePalette(vm->screen))) {
return SDL_APP_FAILURE;
}
i = 0;
for (r = 0; r < 6; ++r) {
for (g = 0; g < 6; ++g) {
for (b = 0; b < 6; ++b, ++i) {
SDL_Color color = { (Uint8)(r * 0x33), (Uint8)(g * 0x33), (Uint8)(b * 0x33), SDL_ALPHA_OPAQUE };
palette->colors[i] = color;
}
}
}
for (; i < 256; ++i) {
SDL_Color color = { 0, 0, 0, SDL_ALPHA_OPAQUE };
palette->colors[i] = color;
}
texprops = SDL_CreateProperties();
SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_STREAMING);
SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER, SCREEN_W);
SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER, SCREEN_H);
vm->screentex = SDL_CreateTextureWithProperties(vm->renderer, texprops);
SDL_SetNumberProperty(texprops, SDL_PROP_TEXTURE_CREATE_ACCESS_NUMBER, SDL_TEXTUREACCESS_TARGET);
vm->rendertarget = SDL_CreateTextureWithProperties(vm->renderer, texprops);
SDL_DestroyProperties(texprops);
if (!vm->screentex || !vm->rendertarget) {
return SDL_APP_FAILURE;
}
SDL_SetTextureScaleMode(vm->screentex, SDL_SCALEMODE_NEAREST);
SDL_SetTextureScaleMode(vm->rendertarget, SDL_SCALEMODE_NEAREST);
if (!(vm->audiostream = SDL_OpenAudioDeviceStream(
SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &audiospec, NULL, NULL
))) {
return SDL_APP_FAILURE;
}
SDL_SetAudioStreamGain(vm->audiostream, 0.1f); /* examples are loud! */
SDL_ResumeAudioStreamDevice(vm->audiostream);
set_status(vm, "renderer: %s", SDL_GetRendererName(vm->renderer));
vm->last_tick = SDL_GetTicksNS();
vm->tick_acc = NS_PER_SECOND;
return SDL_APP_CONTINUE;
}
SDL_AppResult SDL_AppIterate(void* appstate) {
BytePusher* vm = (BytePusher*)appstate;
Uint64 tick = SDL_GetTicksNS();
Uint64 delta = tick - vm->last_tick;
bool updated, skip_audio;
vm->last_tick = tick;
vm->tick_acc += delta * FRAMES_PER_SECOND;
updated = vm->tick_acc >= NS_PER_SECOND;
skip_audio = vm->tick_acc >= MAX_AUDIO_LATENCY_FRAMES * NS_PER_SECOND;
if (skip_audio) {
// don't let audio fall too far behind
SDL_ClearAudioStream(vm->audiostream);
}
while (vm->tick_acc >= NS_PER_SECOND) {
Uint32 pc;
int i;
vm->tick_acc -= NS_PER_SECOND;
vm->ram[IO_KEYBOARD] = (Uint8)(vm->keystate >> 8);
vm->ram[IO_KEYBOARD + 1] = (Uint8)(vm->keystate);
pc = read_u24(vm, IO_PC);
for (i = 0; i < SCREEN_W * SCREEN_H; ++i) {
Uint32 src = read_u24(vm, pc);
Uint32 dst = read_u24(vm, pc + 3);
vm->ram[dst] = vm->ram[src];
pc = read_u24(vm, pc + 6);
}
if (!skip_audio || vm->tick_acc < NS_PER_SECOND) {
SDL_PutAudioStreamData(
vm->audiostream,
&vm->ram[(Uint32)read_u16(vm, IO_AUDIO_BANK) << 8],
SAMPLES_PER_FRAME
);
}
}
if (updated) {
SDL_Surface *tex;
SDL_SetRenderTarget(vm->renderer, vm->rendertarget);
if (!SDL_LockTextureToSurface(vm->screentex, NULL, &tex)) {
return SDL_APP_FAILURE;
}
vm->screen->pixels = &vm->ram[(Uint32)vm->ram[IO_SCREEN_PAGE] << 16];
SDL_BlitSurface(vm->screen, NULL, tex, NULL);
SDL_UnlockTexture(vm->screentex);
SDL_RenderTexture(vm->renderer, vm->screentex, NULL, NULL);
}
if (vm->display_help) {
print(vm, 4, 4, "Drop a BytePusher file in this");
print(vm, 8, 12, "window to load and run it!");
print(vm, 4, 28, "Press ENTER to switch between");
print(vm, 8, 36, "positional and symbolic input.");
}
if (vm->status_ticks > 0) {
vm->status_ticks -= 1;
print(vm, 4, SCREEN_H - 12, vm->status);
}
SDL_SetRenderTarget(vm->renderer, NULL);
SDL_RenderClear(vm->renderer);
SDL_RenderTexture(vm->renderer, vm->rendertarget, NULL, NULL);
SDL_RenderPresent(vm->renderer);
return SDL_APP_CONTINUE;
}
static Uint16 keycode_mask(SDL_Keycode key) {
int index;
if (key >= SDLK_0 && key <= SDLK_9) {
index = key - SDLK_0;
} else if (key >= SDLK_A && key <= SDLK_F) {
index = key - SDLK_A + 10;
} else {
return 0;
}
return (Uint16)1 << index;
}
static Uint16 scancode_mask(SDL_Scancode scancode) {
int index;
switch (scancode) {
case SDL_SCANCODE_1: index = 0x1; break;
case SDL_SCANCODE_2: index = 0x2; break;
case SDL_SCANCODE_3: index = 0x3; break;
case SDL_SCANCODE_4: index = 0xc; break;
case SDL_SCANCODE_Q: index = 0x4; break;
case SDL_SCANCODE_W: index = 0x5; break;
case SDL_SCANCODE_E: index = 0x6; break;
case SDL_SCANCODE_R: index = 0xd; break;
case SDL_SCANCODE_A: index = 0x7; break;
case SDL_SCANCODE_S: index = 0x8; break;
case SDL_SCANCODE_D: index = 0x9; break;
case SDL_SCANCODE_F: index = 0xe; break;
case SDL_SCANCODE_Z: index = 0xa; break;
case SDL_SCANCODE_X: index = 0x0; break;
case SDL_SCANCODE_C: index = 0xb; break;
case SDL_SCANCODE_V: index = 0xf; break;
default: return 0;
}
return (Uint16)1 << index;
}
SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {
BytePusher* vm = (BytePusher*)appstate;
switch (event->type) {
case SDL_EVENT_QUIT:
return SDL_APP_SUCCESS;
case SDL_EVENT_DROP_FILE:
load_file(vm, event->drop.data);
break;
case SDL_EVENT_KEY_DOWN:
#ifndef __EMSCRIPTEN__
if (event->key.key == SDLK_ESCAPE) {
return SDL_APP_SUCCESS;
}
#endif
if (event->key.key == SDLK_RETURN) {
vm->positional_input = !vm->positional_input;
vm->keystate = 0;
if (vm->positional_input) {
set_status(vm, "switched to positional input");
} else {
set_status(vm, "switched to symbolic input");
}
}
if (vm->positional_input) {
vm->keystate |= scancode_mask(event->key.scancode);
} else {
vm->keystate |= keycode_mask(event->key.key);
}
break;
case SDL_EVENT_KEY_UP:
if (vm->positional_input) {
vm->keystate &= ~scancode_mask(event->key.scancode);
} else {
vm->keystate &= ~keycode_mask(event->key.key);
}
break;
}
return SDL_APP_CONTINUE;
}
void SDL_AppQuit(void* appstate, SDL_AppResult result) {
if (result == SDL_APP_FAILURE) {
SDL_Log("Error: %s", SDL_GetError());
}
if (appstate) {
BytePusher* vm = (BytePusher*)appstate;
SDL_DestroyAudioStream(vm->audiostream);
SDL_DestroyTexture(vm->rendertarget);
SDL_DestroyTexture(vm->screentex);
SDL_DestroySurface(vm->screen);
SDL_DestroyRenderer(vm->renderer);
SDL_DestroyWindow(vm->window);
SDL_free(vm);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -0,0 +1 @@
Full game and app demos