health kit

This commit is contained in:
jacob 2026-03-17 18:23:50 -05:00
parent 08b8f10616
commit e0cfc853cf
8 changed files with 155 additions and 7 deletions

View File

@ -237,6 +237,14 @@ P_Shape P_LocalShapeFromEnt(P_Ent *ent)
// .points[3] = VEC2(test_rect.p0.x, test_rect.p1.y),
// );
}
else if (ent->is_pickup)
{
result = P_ShapeFromDesc(
.mass = 10,
.count = 1,
.radius = 0.10,
);
}
return result;
}
@ -293,10 +301,22 @@ P_Anim P_AnimFromEnt(P_Frame *frame, P_Ent *ent)
{
result.sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("prefab/GuySpawn.ase")));
}
else if (ent->is_health_spawn)
{
result.sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("prefab/HealthSpawn.ase")));
}
else if (ent->is_bomb)
{
result.sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("misc/bomb.ase")));
}
else if (ent->is_health)
{
result.sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("misc/health.ase")));
}
else if (ent->is_health)
{
result.sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("misc/health.ase")));
}
if (wep->is_uzi)
{
@ -1285,7 +1305,7 @@ P_Space P_SpaceFromEnts(Arena *arena, P_Frame *frame)
//- Insert entity shapes
for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{
b32 should_insert = ent->is_guy;
b32 should_insert = ent->is_guy || ent->is_pickup;
if (should_insert)
{
P_Shape shape = P_WorldShapeFromEnt(ent);
@ -2227,6 +2247,53 @@ void P_StepFrame(P_Frame *frame)
}
}
//////////////////////////////
//- Spawn pickups
if (!is_predicting)
{
P_EntList queued_ents = Zi;
for (P_Ent *spawner = P_FirstEnt(frame); !P_IsEntNil(spawner); spawner = P_NextEnt(spawner))
{
b32 is_spawner = spawner->is_health_spawn;
if (is_spawner)
{
if (!P_IsEntKeyNil(spawner->pickup))
{
P_Ent *pickup = P_EntFromKey(frame, spawner->pickup);
if (P_IsEntNil(pickup))
{
spawner->last_spawn_reset_ns = frame->time_ns;
spawner->pickup = P_NilEntKey;
}
}
}
if (is_spawner && P_IsEntKeyNil(spawner->pickup))
{
i64 spawn_interval_ns = 0;
if (spawner->is_health_spawn)
{
spawn_interval_ns = NsFromSeconds(1);
}
b32 should_spawn = spawner->last_spawn_reset_ns == 0 || spawner->last_spawn_reset_ns + spawn_interval_ns <= frame->time_ns;
if (should_spawn)
{
spawner->last_spawn_reset_ns = frame->time_ns;
P_Ent *pickup = P_PushTempEnt(scratch.arena, &queued_ents);
{
pickup->key = P_RandEntKey();
pickup->xf = spawner->xf;
pickup->is_pickup = 1;
pickup->source = spawner->key;
pickup->is_health = spawner->is_health_spawn;
}
spawner->pickup = pickup->key;
}
}
}
P_SpawnEntsFromList(frame, queued_ents);
}
//////////////////////////////
//- Spawn guys
@ -3293,7 +3360,12 @@ void P_StepFrame(P_Frame *frame)
{
P_SpaceEntry *entry = &entry_node->entry;
P_EntKey potential_victim_key = (P_EntKey) { .v = entry->shape_id };
if (!P_MatchEntKey(potential_victim_key, firer->key) || P_IsEntKeyNil(firer->key))
P_Ent *potential_victim = P_EntFromKey(frame, potential_victim_key);
b32 can_hit = (
P_IsEntNil(potential_victim) ||
(potential_victim->is_guy && (!P_MatchEntKey(potential_victim->key, firer->key) || P_IsEntKeyNil(firer->key)))
);
if (can_hit)
{
P_Shape potential_victim_shape = entry->shape;
P_RaycastResult entrance_raycast = P_RaycastShape(potential_victim_shape, path->start, path_dir);

View File

@ -144,10 +144,16 @@ Struct(P_Ent)
b32 is_bomb;
//- Player / guy / weapon / bullet
//- Health
b32 is_health;
//- Player / guy / weapon / bullet / pickup
P_EntKey source;
b32 is_pickup;
//- Player / Guy
P_Control control;
@ -185,9 +191,13 @@ Struct(P_Ent)
i64 last_fire_ns;
//- Spawn
//- Spawner
b32 is_guy_spawn;
b32 is_health_spawn;
i64 last_spawn_reset_ns;
P_EntKey pickup;
//- Solver

BIN
src/pp/pp_res/misc/health.ase (Stored with Git LFS) Normal file

Binary file not shown.

BIN
src/pp/pp_res/prefab/HealthSpawn.ase (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -49,6 +49,7 @@ Enum(P_TileKind)
X(None, P_PrefabFlag_HideFromEditor) \
X(Bot, P_PrefabFlag_None) \
X(GuySpawn, P_PrefabFlag_None) \
X(HealthSpawn, P_PrefabFlag_None) \
/* --------------------------------------------------- */
//- Prefab flags

View File

@ -628,6 +628,19 @@ void S_TickForever(WaveLaneCtx *lane)
spawn->xf = msg->xf;
spawn->continuity_gen += 1;
} break;
case P_PrefabKind_HealthSpawn:
{
P_Ent *spawn = P_EntFromKey(world_frame, msg->key);
if (!spawn->is_health_spawn)
{
spawn = P_PushTempEnt(frame_arena, &ents);
spawn->key = msg->key;
spawn->is_health_spawn = 1;
}
spawn->xf = msg->xf;
spawn->continuity_gen += 1;
} break;
}
P_SpawnEntsFromList(world_frame, ents);
} break;

View File

@ -59,10 +59,24 @@ String P_PackWorld(Arena *arena, P_World *src_world)
{
result.len += PushString(arena, Lit(" guy_spawn\n")).len;
}
if (ent->is_health_spawn)
{
result.len += PushString(arena, Lit(" health_spawn\n")).len;
}
if (ent->is_health)
{
result.len += PushString(arena, Lit(" health\n")).len;
}
if (ent->is_pickup)
{
result.len += PushString(arena, Lit(" pickup\n")).len;
}
}
result.len += StringF(arena, " }\n").len;
result.len += StringF(arena, " created_at_ns: \"%F\"\n", FmtSint(ent->created_at_ns)).len;
result.len += StringF(arena, " created_at_tick: \"%F\"\n", FmtSint(ent->created_at_tick)).len;
result.len += StringF(arena, " last_spawn_reset_ns: \"%F\"\n", FmtSint(ent->last_spawn_reset_ns)).len;
result.len += StringF(arena, " pickup: \"0x%F\"\n", FmtHex(ent->pickup.v)).len;
result.len += StringF(arena, " pos: \"%F\"\n", FmtFloat2(ent->xf.t)).len;
result.len += StringF(arena, " angle: \"%F\"\n", FmtFloat(AngleFromVec2(ent->xf.r))).len;
result.len += StringF(arena, " look: \"%F\"\n", FmtFloat2(ent->control.look)).len;
@ -176,6 +190,18 @@ P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed)
{
ent->is_guy_spawn = 1;
}
if (MatchString(prop->value, Lit("health_spawn")))
{
ent->is_health_spawn = 1;
}
if (MatchString(prop->value, Lit("health")))
{
ent->is_health = 1;
}
if (MatchString(prop->value, Lit("pickup")))
{
ent->is_pickup = 1;
}
}
}
if (MatchString(attr->name, Lit("created_at_ns")))
@ -186,6 +212,14 @@ P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed)
{
ent->created_at_tick = CR_IntFromString(attr->value);
}
if (MatchString(attr->name, Lit("last_spawn_reset_ns")))
{
ent->last_spawn_reset_ns = CR_IntFromString(attr->value);
}
if (MatchString(attr->name, Lit("pickup")))
{
ent->pickup.v = CR_IntFromString(attr->value);
}
if (MatchString(attr->name, Lit("pos")))
{
ent->xf.t = CR_Vec2FromString(attr->value);

View File

@ -1785,7 +1785,7 @@ void V_TickForever(WaveLaneCtx *lane)
frame->local_world->seed = predict_world->seed;
P_ClearFrames(frame->local_world, I64Min, I64Max);
if (TweakBool("Interpolation enabled", 1))
if (TweakBool("Interpolation enabled", 0))
{
// Locate sim keyframes
P_Frame *left_sim_frame = &P_NilFrame;
@ -2030,7 +2030,7 @@ void V_TickForever(WaveLaneCtx *lane)
frame->shade_cursor = MulAffineVec2(frame->af.screen_to_shade, frame->screen_cursor);
frame->world_cursor = MulAffineVec2(frame->af.screen_to_world, frame->screen_cursor);
b32 show_editor_ui = TweakBool("Show editor UI", 0);
b32 show_editor_ui = TweakBool("Show editor UI", 1);
frame->world_selection_start = frame->world_cursor;
if (frame->is_editing)
@ -2150,7 +2150,7 @@ void V_TickForever(WaveLaneCtx *lane)
for (P_Ent *ent = P_FirstEnt(local_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{
// TODO: Use entity's prefab flags to determine editor draw status
if (ent->is_guy_spawn)
if (ent->is_guy_spawn || ent->is_health_spawn)
{
DrawNode *draw_node = PushStruct(frame->arena, DrawNode);
SllQueuePush(first_draw_node, last_draw_node, draw_node);
@ -2172,6 +2172,18 @@ void V_TickForever(WaveLaneCtx *lane)
}
}
//- Push pickups
for (P_Ent *ent = P_FirstEnt(local_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{
if (ent->is_pickup)
{
DrawNode *draw_node = PushStruct(frame->arena, DrawNode);
SllQueuePush(first_draw_node, last_draw_node, draw_node);
++draw_nodes_count;
draw_node->ent = ent;
}
}
//- Push guys
for (P_Ent *ent = P_FirstEnt(local_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{