forked from tas/major_tom
Add jetpack fuel pickup and slow base recharge to 30s
Jetpack charges now take 30s to passively recharge (up from 3s), making fuel management a core gameplay loop. A new fuel canister powerup (POWERUP_FUEL) restores exactly one charge on pickup. The existing jetpack powerup remains as the rare full-refill + 15s boost. Fuel pickups replace most procedural jetpack spawns at higher spawn rates to compensate for the weaker per-pickup value. Fuel canisters also appear in corridors and arenas. Adds orange canister pixel art, editor icon, entity registry entry, and places fuel pickups throughout moon01.
This commit is contained in:
@@ -253,6 +253,8 @@ static const uint64_t s_icon_bitmaps[ICON_COUNT] = {
|
||||
0x006C7E7E3E1C0800ULL,
|
||||
/* ICON_BOLT: lightning bolt */
|
||||
0x0C1C18387060C000ULL,
|
||||
/* ICON_FUEL: fuel canister */
|
||||
0x003C4242423C1800ULL,
|
||||
/* ICON_DRONE: quad-rotor */
|
||||
0x00427E3C3C7E4200ULL,
|
||||
/* ICON_GUN: pistol shape */
|
||||
|
||||
@@ -28,6 +28,10 @@ static Entity *spawn_powerup_jetpack(EntityManager *em, Vec2 pos) {
|
||||
return powerup_spawn_jetpack(em, pos);
|
||||
}
|
||||
|
||||
static Entity *spawn_powerup_fuel(EntityManager *em, Vec2 pos) {
|
||||
return powerup_spawn_fuel(em, pos);
|
||||
}
|
||||
|
||||
static Entity *spawn_powerup_drone(EntityManager *em, Vec2 pos) {
|
||||
return powerup_spawn_drone(em, pos);
|
||||
}
|
||||
@@ -80,6 +84,7 @@ void entity_registry_populate(void) {
|
||||
reg_add("force_field", "Force Field", force_field_spawn, (SDL_Color){60, 140, 255, 255}, FFIELD_WIDTH, FFIELD_HEIGHT, ICON_FORCEFIELD);
|
||||
reg_add("powerup_hp", "Health Pickup", spawn_powerup_health, (SDL_Color){255, 80, 80, 255}, 12, 12, ICON_HEART);
|
||||
reg_add("powerup_jet", "Jetpack Refill", spawn_powerup_jetpack,(SDL_Color){255, 200, 50, 255}, 12, 12, ICON_BOLT);
|
||||
reg_add("powerup_fuel", "Jetpack Fuel", spawn_powerup_fuel, (SDL_Color){255, 150, 30, 255}, 12, 12, ICON_FUEL);
|
||||
reg_add("powerup_drone", "Drone Pickup", spawn_powerup_drone, (SDL_Color){80, 200, 255, 255}, 12, 12, ICON_DRONE);
|
||||
reg_add("powerup_gun", "Gun Pickup", spawn_powerup_gun, (SDL_Color){200, 200, 220, 255}, 12, 12, ICON_GUN);
|
||||
reg_add("asteroid", "Asteroid", asteroid_spawn, (SDL_Color){140, 110, 80, 255}, ASTEROID_WIDTH, ASTEROID_HEIGHT, ICON_ASTEROID);
|
||||
|
||||
@@ -36,10 +36,11 @@ typedef enum EditorIcon {
|
||||
ICON_FORCEFIELD = 6, /* electric field */
|
||||
ICON_HEART = 7, /* health pickup */
|
||||
ICON_BOLT = 8, /* jetpack / lightning */
|
||||
ICON_DRONE = 9, /* drone companion */
|
||||
ICON_GUN = 10, /* weapon pickup */
|
||||
ICON_ASTEROID = 11, /* rock */
|
||||
ICON_SPACECRAFT = 12, /* ship */
|
||||
ICON_FUEL = 9, /* fuel canister */
|
||||
ICON_DRONE = 10, /* drone companion */
|
||||
ICON_GUN = 11, /* weapon pickup */
|
||||
ICON_ASTEROID = 12, /* rock */
|
||||
ICON_SPACECRAFT = 13, /* ship */
|
||||
ICON_COUNT,
|
||||
ICON_NONE = -1 /* no icon (fallback) */
|
||||
} EditorIcon;
|
||||
|
||||
@@ -328,6 +328,24 @@ static void handle_collisions(EntityManager *em) {
|
||||
break;
|
||||
}
|
||||
|
||||
case POWERUP_FUEL: {
|
||||
PlayerData *ppd = (PlayerData *)player->data;
|
||||
if (ppd && ppd->dash_charges < ppd->dash_max_charges) {
|
||||
ppd->dash_charges++;
|
||||
/* Reset recharge timer for the next charge */
|
||||
if (ppd->dash_charges < ppd->dash_max_charges) {
|
||||
float rate = (ppd->jetpack_boost_timer > 0)
|
||||
? PLAYER_JETPACK_BOOST_RECHARGE
|
||||
: PLAYER_DASH_RECHARGE;
|
||||
ppd->dash_recharge_timer = rate;
|
||||
} else {
|
||||
ppd->dash_recharge_timer = 0.0f;
|
||||
}
|
||||
picked_up = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case POWERUP_DRONE:
|
||||
drone_spawn(em, vec2(
|
||||
player->body.pos.x + player->body.size.x * 0.5f,
|
||||
|
||||
@@ -273,11 +273,11 @@ static void gen_platforms(Tilemap *map, int x0, int w, int ground_row,
|
||||
}
|
||||
}
|
||||
|
||||
/* Jetpack refill on a high platform — reward for climbing */
|
||||
if (rng_float() < 0.3f) {
|
||||
/* Fuel pickup on a high platform — reward for climbing */
|
||||
if (rng_float() < 0.45f) {
|
||||
int top_py = ground_row - 3 - (num_plats - 1) * 3;
|
||||
if (top_py >= ceil_row) {
|
||||
add_entity(map, "powerup_jet", x0 + rng_range(2, w - 3), top_py - 1);
|
||||
add_entity(map, "powerup_fuel", x0 + rng_range(2, w - 3), top_py - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -340,6 +340,11 @@ static void gen_corridor(Tilemap *map, int x0, int w, int ground_row,
|
||||
if (difficulty > 0.3f && rng_float() < 0.35f) {
|
||||
add_entity(map, "powerup_hp", x0 + w - 3, ground_row - 1);
|
||||
}
|
||||
|
||||
/* Fuel pickup hidden in the corridor */
|
||||
if (rng_float() < 0.3f) {
|
||||
add_entity(map, "powerup_fuel", x0 + rng_range(2, w - 3), ceil_row + 2);
|
||||
}
|
||||
}
|
||||
|
||||
/* SEG_ARENA: wide open area, multiple enemies */
|
||||
@@ -405,6 +410,8 @@ static void gen_arena(Tilemap *map, int x0, int w, int ground_row,
|
||||
if (rng_float() < 0.5f) {
|
||||
if (difficulty > 0.6f && rng_float() < 0.25f) {
|
||||
add_entity(map, "powerup_drone", cp_x + 2, cp_y - 1);
|
||||
} else if (rng_float() < 0.35f) {
|
||||
add_entity(map, "powerup_fuel", cp_x + 2, cp_y - 1);
|
||||
} else {
|
||||
add_entity(map, "powerup_hp", cp_x + 2, cp_y - 1);
|
||||
}
|
||||
@@ -482,9 +489,9 @@ static void gen_shaft(Tilemap *map, int x0, int w, int ground_row,
|
||||
}
|
||||
}
|
||||
|
||||
/* Jetpack refill near the top — reward for climbing */
|
||||
if (rng_float() < 0.4f) {
|
||||
add_entity(map, "powerup_jet", x0 + w / 2, ceil_row + 3);
|
||||
/* Fuel pickup near the top — reward for climbing */
|
||||
if (rng_float() < 0.5f) {
|
||||
add_entity(map, "powerup_fuel", x0 + w / 2, ceil_row + 3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,10 +731,10 @@ static void gen_climb(Tilemap *map, int x0, int w,
|
||||
}
|
||||
}
|
||||
|
||||
/* Powerup midway through the climb */
|
||||
if (rng_float() < 0.4f) {
|
||||
/* Fuel pickup midway through the climb */
|
||||
if (rng_float() < 0.5f) {
|
||||
int mid_y = (top_ground + bot_ground) / 2;
|
||||
add_entity(map, "powerup_jet", (shaft_left + shaft_right) / 2, mid_y - 2);
|
||||
add_entity(map, "powerup_fuel", (shaft_left + shaft_right) / 2, mid_y - 2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1381,9 +1388,9 @@ static void gen_station_vent(Tilemap *map, int x0, int w, float difficulty) {
|
||||
add_entity(map, "grunt", x0 + rng_range(2, w - 3), STATION_FLOOR_ROW - 1);
|
||||
}
|
||||
|
||||
/* Jetpack refill reward (useful in low gravity) */
|
||||
if (rng_float() < 0.35f) {
|
||||
add_entity(map, "powerup_jet", x0 + rng_range(2, w - 3), vent_ceil + 1);
|
||||
/* Fuel pickup reward (useful in low gravity) */
|
||||
if (rng_float() < 0.4f) {
|
||||
add_entity(map, "powerup_fuel", x0 + rng_range(2, w - 3), vent_ceil + 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#define PLAYER_DASH_SPEED 350.0f /* dash velocity (px/s) */
|
||||
#define PLAYER_DASH_DURATION 0.15f /* seconds the dash lasts */
|
||||
#define PLAYER_DASH_MAX_CHARGES 3 /* max jetpack charges */
|
||||
#define PLAYER_DASH_RECHARGE 3.0f /* seconds to recharge one charge*/
|
||||
#define PLAYER_DASH_RECHARGE 30.0f /* seconds to recharge one charge*/
|
||||
|
||||
/* Jetpack boost (from powerup) */
|
||||
#define PLAYER_JETPACK_BOOST_DURATION 15.0f /* seconds the boost lasts */
|
||||
|
||||
@@ -41,6 +41,7 @@ static void powerup_update(Entity *self, float dt, const Tilemap *map) {
|
||||
switch (pd->kind) {
|
||||
case POWERUP_HEALTH: color = (SDL_Color){220, 50, 50, 200}; break;
|
||||
case POWERUP_JETPACK: color = (SDL_Color){255, 180, 50, 200}; break;
|
||||
case POWERUP_FUEL: color = (SDL_Color){255, 120, 30, 200}; break;
|
||||
case POWERUP_DRONE: color = (SDL_Color){50, 200, 255, 200}; break;
|
||||
case POWERUP_GUN: color = (SDL_Color){200, 200, 220, 200}; break;
|
||||
default: color = (SDL_Color){255, 255, 255, 200}; break;
|
||||
@@ -129,6 +130,7 @@ Entity *powerup_spawn(EntityManager *em, Vec2 pos, PowerupKind kind) {
|
||||
switch (kind) {
|
||||
case POWERUP_HEALTH: animation_set(&e->anim, &anim_powerup_health); break;
|
||||
case POWERUP_JETPACK: animation_set(&e->anim, &anim_powerup_jetpack); break;
|
||||
case POWERUP_FUEL: animation_set(&e->anim, &anim_powerup_fuel); break;
|
||||
case POWERUP_DRONE: animation_set(&e->anim, &anim_powerup_drone); break;
|
||||
case POWERUP_GUN: animation_set(&e->anim, &anim_powerup_gun); break;
|
||||
default: animation_set(&e->anim, &anim_powerup_health); break;
|
||||
@@ -145,6 +147,10 @@ Entity *powerup_spawn_jetpack(EntityManager *em, Vec2 pos) {
|
||||
return powerup_spawn(em, pos, POWERUP_JETPACK);
|
||||
}
|
||||
|
||||
Entity *powerup_spawn_fuel(EntityManager *em, Vec2 pos) {
|
||||
return powerup_spawn(em, pos, POWERUP_FUEL);
|
||||
}
|
||||
|
||||
Entity *powerup_spawn_drone(EntityManager *em, Vec2 pos) {
|
||||
return powerup_spawn(em, pos, POWERUP_DRONE);
|
||||
}
|
||||
|
||||
@@ -9,13 +9,15 @@
|
||||
* Small collectible items that grant the player
|
||||
* an instant benefit on contact:
|
||||
* - Health: restores 1 HP
|
||||
* - Jetpack: instantly refills all dash charges
|
||||
* - Jetpack: instantly refills all dash charges + boost
|
||||
* - Fuel: instantly fills one jetpack charge
|
||||
* - Drone: spawns an orbiting combat drone
|
||||
* ═══════════════════════════════════════════════════ */
|
||||
|
||||
typedef enum PowerupKind {
|
||||
POWERUP_HEALTH,
|
||||
POWERUP_JETPACK,
|
||||
POWERUP_FUEL,
|
||||
POWERUP_DRONE,
|
||||
POWERUP_GUN,
|
||||
POWERUP_KIND_COUNT
|
||||
@@ -36,6 +38,7 @@ Entity *powerup_spawn(EntityManager *em, Vec2 pos, PowerupKind kind);
|
||||
/* Convenience spawners for level/levelgen */
|
||||
Entity *powerup_spawn_health(EntityManager *em, Vec2 pos);
|
||||
Entity *powerup_spawn_jetpack(EntityManager *em, Vec2 pos);
|
||||
Entity *powerup_spawn_fuel(EntityManager *em, Vec2 pos);
|
||||
Entity *powerup_spawn_drone(EntityManager *em, Vec2 pos);
|
||||
Entity *powerup_spawn_gun(EntityManager *em, Vec2 pos);
|
||||
|
||||
|
||||
@@ -902,6 +902,48 @@ static const uint32_t powerup_gun2[16*16] = {
|
||||
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
|
||||
};
|
||||
|
||||
/* ── Fuel powerup sprite ───────────────────────────── */
|
||||
|
||||
/* Fuel canister — orange/amber napalm canister icon */
|
||||
static const uint32_t powerup_fuel1[16*16] = {
|
||||
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
|
||||
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
|
||||
T, T, T, T, T, T, GYD, GYD, GYD, T, T, T, T, T, T, T,
|
||||
T, T, T, T, T, GYD, GYL, GYL, GYL, GYD, T, T, T, T, T, T,
|
||||
T, T, T, T, T, ORD, ORG, ORG, ORG, ORD, T, T, T, T, T, T,
|
||||
T, T, T, T, ORD, ORG, YLW, YLW, ORG, ORG, ORD, T, T, T, T, T,
|
||||
T, T, T, T, ORD, ORG, YLW, YLW, ORG, ORG, ORD, T, T, T, T, T,
|
||||
T, T, T, T, ORD, ORG, ORG, ORG, ORG, ORG, ORD, T, T, T, T, T,
|
||||
T, T, T, T, ORD, ORG, YLD, YLD, YLD, ORG, ORD, T, T, T, T, T,
|
||||
T, T, T, T, ORD, ORG, ORG, ORG, ORG, ORG, ORD, T, T, T, T, T,
|
||||
T, T, T, T, ORD, ORG, YLW, YLW, ORG, ORG, ORD, T, T, T, T, T,
|
||||
T, T, T, T, T, ORD, ORG, ORG, ORG, ORD, T, T, T, T, T, T,
|
||||
T, T, T, T, T, T, ORD, ORD, ORD, T, T, T, T, T, T, T,
|
||||
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
|
||||
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
|
||||
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
|
||||
};
|
||||
|
||||
/* Fuel canister frame 2 — brighter glow */
|
||||
static const uint32_t powerup_fuel2[16*16] = {
|
||||
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
|
||||
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
|
||||
T, T, T, T, T, T, GYL, GYL, GYL, T, T, T, T, T, T, T,
|
||||
T, T, T, T, T, GYL, WHT, WHT, WHT, GYL, T, T, T, T, T, T,
|
||||
T, T, T, T, T, ORG, YLW, YLW, YLW, ORG, T, T, T, T, T, T,
|
||||
T, T, T, T, ORG, YLW, WHT, WHT, YLW, YLW, ORG, T, T, T, T, T,
|
||||
T, T, T, T, ORG, YLW, WHT, WHT, YLW, YLW, ORG, T, T, T, T, T,
|
||||
T, T, T, T, ORG, YLW, YLW, YLW, YLW, YLW, ORG, T, T, T, T, T,
|
||||
T, T, T, T, ORG, YLW, ORG, ORG, ORG, YLW, ORG, T, T, T, T, T,
|
||||
T, T, T, T, ORG, YLW, YLW, YLW, YLW, YLW, ORG, T, T, T, T, T,
|
||||
T, T, T, T, ORG, YLW, WHT, WHT, YLW, YLW, ORG, T, T, T, T, T,
|
||||
T, T, T, T, T, ORG, YLW, YLW, YLW, ORG, T, T, T, T, T, T,
|
||||
T, T, T, T, T, T, ORG, ORG, ORG, T, T, T, T, T, T, T,
|
||||
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
|
||||
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
|
||||
T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T,
|
||||
};
|
||||
|
||||
/* ── Asteroid sprite ────────────────────────────────── */
|
||||
|
||||
/* Asteroid-specific greys — cold, cratered moonrock */
|
||||
@@ -1024,6 +1066,8 @@ static const SpriteDef s_sprite_defs[] = {
|
||||
{6, 3, powerup_gun2},
|
||||
{6, 4, asteroid1},
|
||||
{6, 5, asteroid2},
|
||||
{6, 6, powerup_fuel1},
|
||||
{6, 7, powerup_fuel2},
|
||||
};
|
||||
|
||||
#define SHEET_COLS 8
|
||||
@@ -1210,6 +1254,12 @@ static AnimFrame s_powerup_drone_frames[] = {
|
||||
FRAME(6, 5, 0.3f),
|
||||
};
|
||||
|
||||
/* Fuel powerup */
|
||||
static AnimFrame s_powerup_fuel_frames[] = {
|
||||
FRAME(6, 6, 0.35f),
|
||||
FRAME(7, 6, 0.35f),
|
||||
};
|
||||
|
||||
/* Gun powerup */
|
||||
static AnimFrame s_powerup_gun_frames[] = {
|
||||
FRAME(2, 6, 0.4f),
|
||||
@@ -1256,6 +1306,7 @@ AnimDef anim_force_field_off;
|
||||
|
||||
AnimDef anim_powerup_health;
|
||||
AnimDef anim_powerup_jetpack;
|
||||
AnimDef anim_powerup_fuel;
|
||||
AnimDef anim_powerup_drone;
|
||||
AnimDef anim_powerup_gun;
|
||||
AnimDef anim_asteroid;
|
||||
@@ -1290,6 +1341,7 @@ void sprites_init_anims(void) {
|
||||
|
||||
anim_powerup_health = (AnimDef){s_powerup_health_frames, 2, true, NULL};
|
||||
anim_powerup_jetpack = (AnimDef){s_powerup_jetpack_frames, 2, true, NULL};
|
||||
anim_powerup_fuel = (AnimDef){s_powerup_fuel_frames, 2, true, NULL};
|
||||
anim_powerup_drone = (AnimDef){s_powerup_drone_frames, 2, true, NULL};
|
||||
anim_powerup_gun = (AnimDef){s_powerup_gun_frames, 2, true, NULL};
|
||||
anim_asteroid = (AnimDef){s_asteroid_frames, 2, true, NULL};
|
||||
|
||||
@@ -52,6 +52,7 @@ extern AnimDef anim_force_field_off;
|
||||
/* ── Powerup animations ────────────────────────── */
|
||||
extern AnimDef anim_powerup_health;
|
||||
extern AnimDef anim_powerup_jetpack;
|
||||
extern AnimDef anim_powerup_fuel;
|
||||
extern AnimDef anim_powerup_drone;
|
||||
extern AnimDef anim_powerup_gun;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user