forked from tas/major_tom
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:
108
src/game/entity_registry.c
Normal file
108
src/game/entity_registry.c
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "game/entity_registry.h"
|
||||
#include "game/player.h"
|
||||
#include "game/enemy.h"
|
||||
#include "game/projectile.h"
|
||||
#include "game/hazards.h"
|
||||
#include "game/powerup.h"
|
||||
#include "game/drone.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
EntityRegistry g_entity_registry = {0};
|
||||
|
||||
/* ── Wrapper spawn functions ──────────────────────
|
||||
* Some spawn functions need extra args beyond (em, pos).
|
||||
* We wrap them to match the EntitySpawnFn signature.
|
||||
* ──────────────────────────────────────────────────── */
|
||||
|
||||
static Entity *spawn_platform_v(EntityManager *em, Vec2 pos) {
|
||||
return mplat_spawn_dir(em, pos, vec2(0.0f, 1.0f));
|
||||
}
|
||||
|
||||
static Entity *spawn_powerup_health(EntityManager *em, Vec2 pos) {
|
||||
return powerup_spawn_health(em, pos);
|
||||
}
|
||||
|
||||
static Entity *spawn_powerup_jetpack(EntityManager *em, Vec2 pos) {
|
||||
return powerup_spawn_jetpack(em, pos);
|
||||
}
|
||||
|
||||
static Entity *spawn_powerup_drone(EntityManager *em, Vec2 pos) {
|
||||
return powerup_spawn_drone(em, pos);
|
||||
}
|
||||
|
||||
/* ── Registry population ─────────────────────────── */
|
||||
|
||||
static void reg_add(const char *name, const char *display,
|
||||
EntitySpawnFn fn, SDL_Color color, int w, int h) {
|
||||
EntityRegistry *r = &g_entity_registry;
|
||||
if (r->count >= MAX_REGISTRY_ENTRIES) return;
|
||||
r->entries[r->count++] = (EntityRegEntry){
|
||||
.name = name,
|
||||
.display = display,
|
||||
.spawn_fn = fn,
|
||||
.color = color,
|
||||
.width = w,
|
||||
.height = h,
|
||||
};
|
||||
}
|
||||
|
||||
void entity_registry_init(EntityManager *em) {
|
||||
g_entity_registry.count = 0;
|
||||
|
||||
/* Register all entity behaviors with the entity manager */
|
||||
player_register(em);
|
||||
player_set_entity_manager(em);
|
||||
grunt_register(em);
|
||||
flyer_register(em);
|
||||
projectile_register(em);
|
||||
turret_register(em);
|
||||
mplat_register(em);
|
||||
flame_vent_register(em);
|
||||
force_field_register(em);
|
||||
powerup_register(em);
|
||||
drone_register(em);
|
||||
|
||||
/* ════════════════════════════════════════════
|
||||
* REGISTRY TABLE
|
||||
*
|
||||
* To add a new entity type to the game:
|
||||
* 1. Create its .h/.c files
|
||||
* 2. Register its behaviors above
|
||||
* 3. Add one reg_add() line below
|
||||
*
|
||||
* The editor will pick it up automatically.
|
||||
* ════════════════════════════════════════════ */
|
||||
|
||||
/* .lvl name display name spawn fn color (editor) w h */
|
||||
reg_add("grunt", "Grunt", grunt_spawn, (SDL_Color){200, 60, 60, 255}, GRUNT_WIDTH, GRUNT_HEIGHT);
|
||||
reg_add("flyer", "Flyer", flyer_spawn, (SDL_Color){140, 80, 200, 255}, FLYER_WIDTH, FLYER_HEIGHT);
|
||||
reg_add("turret", "Turret", turret_spawn, (SDL_Color){160, 160, 160, 255}, TURRET_WIDTH, TURRET_HEIGHT);
|
||||
reg_add("platform", "Platform (H)", mplat_spawn, (SDL_Color){80, 180, 80, 255}, MPLAT_WIDTH, MPLAT_HEIGHT);
|
||||
reg_add("platform_v", "Platform (V)", spawn_platform_v, (SDL_Color){80, 160, 100, 255}, MPLAT_WIDTH, MPLAT_HEIGHT);
|
||||
reg_add("flame_vent", "Flame Vent", flame_vent_spawn, (SDL_Color){255, 120, 40, 255}, FLAME_WIDTH, FLAME_HEIGHT);
|
||||
reg_add("force_field", "Force Field", force_field_spawn, (SDL_Color){60, 140, 255, 255}, FFIELD_WIDTH, FFIELD_HEIGHT);
|
||||
reg_add("powerup_hp", "Health Pickup", spawn_powerup_health, (SDL_Color){255, 80, 80, 255}, 12, 12);
|
||||
reg_add("powerup_jet", "Jetpack Refill", spawn_powerup_jetpack,(SDL_Color){255, 200, 50, 255}, 12, 12);
|
||||
reg_add("powerup_drone", "Drone Pickup", spawn_powerup_drone, (SDL_Color){80, 200, 255, 255}, 12, 12);
|
||||
|
||||
printf("Entity registry: %d types registered\n", g_entity_registry.count);
|
||||
}
|
||||
|
||||
const EntityRegEntry *entity_registry_find(const char *name) {
|
||||
for (int i = 0; i < g_entity_registry.count; i++) {
|
||||
if (strcmp(g_entity_registry.entries[i].name, name) == 0) {
|
||||
return &g_entity_registry.entries[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Entity *entity_registry_spawn(EntityManager *em, const char *name, Vec2 pos) {
|
||||
const EntityRegEntry *entry = entity_registry_find(name);
|
||||
if (!entry) {
|
||||
fprintf(stderr, "entity_registry_spawn: unknown type '%s'\n", name);
|
||||
return NULL;
|
||||
}
|
||||
return entry->spawn_fn(em, pos);
|
||||
}
|
||||
Reference in New Issue
Block a user