Add in-game level editor with auto-discovered tile/entity palettes
Implements a full level editor that runs inside the game engine as an alternative mode, accessible via --edit flag or E key during gameplay. The editor auto-discovers available tiles from the tileset texture and entities from a new central registry, so adding new game content automatically appears in the editor without any editor-specific changes. Editor features: tile painting (pencil/eraser/flood fill) across 3 layers, entity placement with drag-to-move, player spawn point tool, camera pan/zoom, grid overlay, .lvl save/load, map resize, and test play (P to play, ESC to return to editor). Supporting changes: - Entity registry centralizes spawn functions (replaces strcmp chain) - Mouse input + raw keyboard access added to input system - Camera zoom support for editor overview - Zoom-aware rendering in tilemap, renderer, and sprite systems - Powerup and drone sprites/animations wired up (were defined but unused) - Bitmap font renderer for editor UI (4x6 pixel glyphs, no dependencies)
This commit is contained in:
@@ -14,6 +14,19 @@ static bool s_latched_released[ACTION_COUNT];
|
||||
|
||||
static bool s_quit_requested;
|
||||
|
||||
/* ── Mouse state ──────────────────────────────────── */
|
||||
static int s_mouse_x, s_mouse_y;
|
||||
static bool s_mouse_current[MOUSE_BUTTON_COUNT];
|
||||
static bool s_mouse_previous[MOUSE_BUTTON_COUNT];
|
||||
static bool s_mouse_latched_pressed[MOUSE_BUTTON_COUNT];
|
||||
static bool s_mouse_latched_released[MOUSE_BUTTON_COUNT];
|
||||
static int s_mouse_scroll;
|
||||
|
||||
/* ── Raw keyboard state ───────────────────────────── */
|
||||
static const Uint8 *s_key_state = NULL;
|
||||
static Uint8 s_prev_keys[SDL_NUM_SCANCODES];
|
||||
static Uint8 s_latched_keys[SDL_NUM_SCANCODES];
|
||||
|
||||
/* Default key bindings (primary + alternate) */
|
||||
static SDL_Scancode s_bindings[ACTION_COUNT] = {
|
||||
[ACTION_LEFT] = SDL_SCANCODE_LEFT,
|
||||
@@ -36,13 +49,28 @@ void input_init(void) {
|
||||
memset(s_previous, 0, sizeof(s_previous));
|
||||
memset(s_latched_pressed, 0, sizeof(s_latched_pressed));
|
||||
memset(s_latched_released, 0, sizeof(s_latched_released));
|
||||
memset(s_mouse_current, 0, sizeof(s_mouse_current));
|
||||
memset(s_mouse_previous, 0, sizeof(s_mouse_previous));
|
||||
memset(s_mouse_latched_pressed, 0, sizeof(s_mouse_latched_pressed));
|
||||
memset(s_mouse_latched_released, 0, sizeof(s_mouse_latched_released));
|
||||
memset(s_prev_keys, 0, sizeof(s_prev_keys));
|
||||
memset(s_latched_keys, 0, sizeof(s_latched_keys));
|
||||
s_mouse_x = s_mouse_y = 0;
|
||||
s_mouse_scroll = 0;
|
||||
s_quit_requested = false;
|
||||
}
|
||||
|
||||
void input_poll(void) {
|
||||
/* Save previous state */
|
||||
memcpy(s_previous, s_current, sizeof(s_current));
|
||||
memcpy(s_mouse_previous, s_mouse_current, sizeof(s_mouse_current));
|
||||
s_quit_requested = false;
|
||||
s_mouse_scroll = 0;
|
||||
|
||||
/* Save previous raw key state */
|
||||
if (s_key_state) {
|
||||
memcpy(s_prev_keys, s_key_state, SDL_NUM_SCANCODES);
|
||||
}
|
||||
|
||||
/* Process SDL events */
|
||||
SDL_Event event;
|
||||
@@ -51,14 +79,17 @@ void input_poll(void) {
|
||||
case SDL_QUIT:
|
||||
s_quit_requested = true;
|
||||
break;
|
||||
case SDL_MOUSEWHEEL:
|
||||
s_mouse_scroll += event.wheel.y;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read keyboard state */
|
||||
const Uint8 *keys = SDL_GetKeyboardState(NULL);
|
||||
s_key_state = SDL_GetKeyboardState(NULL);
|
||||
for (int i = 0; i < ACTION_COUNT; i++) {
|
||||
s_current[i] = keys[s_bindings[i]];
|
||||
if (s_alt_bindings[i] && keys[s_alt_bindings[i]]) {
|
||||
s_current[i] = s_key_state[s_bindings[i]];
|
||||
if (s_alt_bindings[i] && s_key_state[s_alt_bindings[i]]) {
|
||||
s_current[i] = true;
|
||||
}
|
||||
|
||||
@@ -70,12 +101,50 @@ void input_poll(void) {
|
||||
s_latched_released[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Latch raw key edges */
|
||||
if (s_key_state) {
|
||||
for (int i = 0; i < SDL_NUM_SCANCODES; i++) {
|
||||
if (s_key_state[i] && !s_prev_keys[i]) {
|
||||
s_latched_keys[i] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Read mouse state */
|
||||
Uint32 buttons = SDL_GetMouseState(&s_mouse_x, &s_mouse_y);
|
||||
|
||||
/* Convert window coords to logical coords (SDL handles this via
|
||||
SDL_RenderSetLogicalSize, but GetMouseState returns window coords) */
|
||||
SDL_Renderer *r = SDL_GetRenderer(SDL_GetMouseFocus());
|
||||
if (r) {
|
||||
float lx, ly;
|
||||
SDL_RenderWindowToLogical(r, s_mouse_x, s_mouse_y, &lx, &ly);
|
||||
s_mouse_x = (int)lx;
|
||||
s_mouse_y = (int)ly;
|
||||
}
|
||||
|
||||
s_mouse_current[MOUSE_LEFT] = (buttons & SDL_BUTTON_LMASK) != 0;
|
||||
s_mouse_current[MOUSE_MIDDLE] = (buttons & SDL_BUTTON_MMASK) != 0;
|
||||
s_mouse_current[MOUSE_RIGHT] = (buttons & SDL_BUTTON_RMASK) != 0;
|
||||
|
||||
for (int i = 0; i < MOUSE_BUTTON_COUNT; i++) {
|
||||
if (s_mouse_current[i] && !s_mouse_previous[i]) {
|
||||
s_mouse_latched_pressed[i] = true;
|
||||
}
|
||||
if (!s_mouse_current[i] && s_mouse_previous[i]) {
|
||||
s_mouse_latched_released[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void input_consume(void) {
|
||||
/* Clear latched states after an update tick has read them */
|
||||
memset(s_latched_pressed, 0, sizeof(s_latched_pressed));
|
||||
memset(s_latched_released, 0, sizeof(s_latched_released));
|
||||
memset(s_mouse_latched_pressed, 0, sizeof(s_mouse_latched_pressed));
|
||||
memset(s_mouse_latched_released, 0, sizeof(s_mouse_latched_released));
|
||||
memset(s_latched_keys, 0, sizeof(s_latched_keys));
|
||||
}
|
||||
|
||||
bool input_pressed(Action a) {
|
||||
@@ -94,6 +163,44 @@ bool input_quit_requested(void) {
|
||||
return s_quit_requested;
|
||||
}
|
||||
|
||||
/* ── Mouse queries ────────────────────────────────── */
|
||||
|
||||
void input_mouse_pos(int *x, int *y) {
|
||||
if (x) *x = s_mouse_x;
|
||||
if (y) *y = s_mouse_y;
|
||||
}
|
||||
|
||||
bool input_mouse_pressed(MouseButton btn) {
|
||||
if (btn < 0 || btn >= MOUSE_BUTTON_COUNT) return false;
|
||||
return s_mouse_latched_pressed[btn];
|
||||
}
|
||||
|
||||
bool input_mouse_held(MouseButton btn) {
|
||||
if (btn < 0 || btn >= MOUSE_BUTTON_COUNT) return false;
|
||||
return s_mouse_current[btn];
|
||||
}
|
||||
|
||||
bool input_mouse_released(MouseButton btn) {
|
||||
if (btn < 0 || btn >= MOUSE_BUTTON_COUNT) return false;
|
||||
return s_mouse_latched_released[btn];
|
||||
}
|
||||
|
||||
int input_mouse_scroll(void) {
|
||||
return s_mouse_scroll;
|
||||
}
|
||||
|
||||
/* ── Raw keyboard queries ─────────────────────────── */
|
||||
|
||||
bool input_key_pressed(SDL_Scancode key) {
|
||||
if (key < 0 || key >= SDL_NUM_SCANCODES) return false;
|
||||
return s_latched_keys[key] != 0;
|
||||
}
|
||||
|
||||
bool input_key_held(SDL_Scancode key) {
|
||||
if (key < 0 || key >= SDL_NUM_SCANCODES) return false;
|
||||
return s_key_state && s_key_state[key];
|
||||
}
|
||||
|
||||
void input_shutdown(void) {
|
||||
/* Nothing to clean up */
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user