diff --git a/src/pp/pp.c b/src/pp/pp.c index ff8e71c0..5de5aa4d 100644 --- a/src/pp/pp.c +++ b/src/pp/pp.c @@ -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); diff --git a/src/pp/pp.h b/src/pp/pp.h index 274738a7..914dcca2 100644 --- a/src/pp/pp.h +++ b/src/pp/pp.h @@ -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 diff --git a/src/pp/pp_res/misc/health.ase b/src/pp/pp_res/misc/health.ase new file mode 100644 index 00000000..01d5f2c7 --- /dev/null +++ b/src/pp/pp_res/misc/health.ase @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13565bf2aaa8aeece142cc0202e5d8e2c83b1a790cc279f3427a46bc339e8d61 +size 526 diff --git a/src/pp/pp_res/prefab/HealthSpawn.ase b/src/pp/pp_res/prefab/HealthSpawn.ase new file mode 100644 index 00000000..36b84ef0 --- /dev/null +++ b/src/pp/pp_res/prefab/HealthSpawn.ase @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29b8def74fc474d8c882ade99645dc82c0ef231dc7ff0e4d45d2683b08fe5ee3 +size 510 diff --git a/src/pp/pp_shared.cgh b/src/pp/pp_shared.cgh index 48b43851..67a0418a 100644 --- a/src/pp/pp_shared.cgh +++ b/src/pp/pp_shared.cgh @@ -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 diff --git a/src/pp/pp_sim/pp_sim_core.c b/src/pp/pp_sim/pp_sim_core.c index eff9947c..ebc25f47 100644 --- a/src/pp/pp_sim/pp_sim_core.c +++ b/src/pp/pp_sim/pp_sim_core.c @@ -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; diff --git a/src/pp/pp_transcode.c b/src/pp/pp_transcode.c index 2f02cad5..d7456d8b 100644 --- a/src/pp/pp_transcode.c +++ b/src/pp/pp_transcode.c @@ -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); diff --git a/src/pp/pp_vis/pp_vis_core.c b/src/pp/pp_vis/pp_vis_core.c index 9ffb7e21..014090e4 100644 --- a/src/pp/pp_vis/pp_vis_core.c +++ b/src/pp/pp_vis/pp_vis_core.c @@ -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)) {