From bc32417ba15297de68dfe4037a05a694ab87f79a Mon Sep 17 00:00:00 2001 From: jacob Date: Fri, 6 Feb 2026 00:56:01 -0600 Subject: [PATCH] randomnize spawn scores. ignore last player spawn point. --- src/base/base_math.c | 14 ++++ src/base/base_math.h | 4 ++ src/base/base_rand.c | 7 +- src/base/base_rand.h | 4 +- src/base/base_util.h | 11 +++- src/pp/pp.c | 104 ++++++++++++++++++++++++++---- src/pp/pp.h | 2 + src/pp/pp_res/prefab/GuySpawn.ase | 4 +- src/pp/pp_sim/pp_sim_core.c | 84 ------------------------ 9 files changed, 129 insertions(+), 105 deletions(-) diff --git a/src/base/base_math.c b/src/base/base_math.c index cdfef495..1adc9e80 100644 --- a/src/base/base_math.c +++ b/src/base/base_math.c @@ -40,6 +40,8 @@ f64 SaturateF64(f64 v) { return v < 0 ? 0 : v > 1 ? 1 : v; } //////////////////////////////////////////////////////////// //~ Float ops +//- Sign + i32 SignF32(f32 v) { return (v >= 0) - (v < 0); @@ -50,6 +52,18 @@ i32 SignF64(f64 v) return (v >= 0) - (v < 0); } +//- Norm + +f32 NormU32(u32 v) +{ + return (f32)(v & (((u32)1 << 24) - 1)) / (f32)((u32)1 << 24); +} + +f64 NormU64(u64 v) +{ + return (f64)(v & (((u64)1 << 53) - 1)) / (f64)((u64)1 << 53); +} + //////////////////////////////////////////////////////////// //~ Exponential ops diff --git a/src/base/base_math.h b/src/base/base_math.h index 764accb0..edada1f0 100644 --- a/src/base/base_math.h +++ b/src/base/base_math.h @@ -261,6 +261,10 @@ f64 SaturateF64(f64 v); i32 SignF32(f32 v); i32 SignF64(f64 v); +//- Norm +f32 NormU32(u32 v); +f64 NormU64(u64 v); + //////////////////////////////////////////////////////////// //~ Exponential ops diff --git a/src/base/base_rand.c b/src/base/base_rand.c index f9d9b461..d44bebbf 100644 --- a/src/base/base_rand.c +++ b/src/base/base_rand.c @@ -9,7 +9,12 @@ u64 RandU64FromState(RandState *state) return seed ^ MixU64(++state->counter); } +f32 RandF32FromState(RandState *state, f32 range_start, f32 range_end) +{ + return range_start + (range_end - range_start) * NormU32(RandU64FromState(state)); +} + f64 RandF64FromState(RandState *state, f64 range_start, f64 range_end) { - return range_start + (range_end - range_start) * ((f64)(RandU64FromState(state) % RandMaxF64) / (f64)RandMaxF64); + return range_start + (range_end - range_start) * NormU64(RandU64FromState(state)); } diff --git a/src/base/base_rand.h b/src/base/base_rand.h index 18dd5958..26bede4d 100644 --- a/src/base/base_rand.h +++ b/src/base/base_rand.h @@ -7,11 +7,9 @@ Struct(RandState) u64 counter; }; -// TODO: Use a value that gives good precision when dividing into range 0 -> 1 -#define RandMaxF64 U64Max - //////////////////////////////////////////////////////////// //~ Rand ops u64 RandU64FromState(RandState *state); +f32 RandF32FromState(RandState *state, f32 range_start, f32 range_end); f64 RandF64FromState(RandState *state, f64 range_start, f64 range_end); diff --git a/src/base/base_util.h b/src/base/base_util.h index 5c6d5769..2eccf51c 100644 --- a/src/base/base_util.h +++ b/src/base/base_util.h @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////// //~ Mergesort types -// Compare functions should +// Comparison functions should // return a positive value if a should go before b // return a negative value if a should go after b // return 0 otherwise @@ -104,6 +104,15 @@ Inline void Mergesort(void *items, u64 item_count, u64 item_size, MergesortCompa } } +//////////////////////////////////////////////////////////// +//~ Shuffle utils + +#define ShuffleStructs(ptr, count, seed) ShuffleStructs_((ptr), sizeof(*(ptr)), (count), (seed)) +Inline void ShuffleStructs_(void *items, u64 item_count, u64 item_size, u64 seed) +{ + +} + //////////////////////////////////////////////////////////// //~ Dict utils diff --git a/src/pp/pp.c b/src/pp/pp.c index 008ea5a7..4b746229 100644 --- a/src/pp/pp.c +++ b/src/pp/pp.c @@ -1513,23 +1513,99 @@ void P_StepFrame(P_Frame *frame) P_Ent *local_guy = P_EntFromKey(frame, local_player->guy); ////////////////////////////// - //- Spawn entities + //- Spawn guys - // { - // ////////////////////////////// - // //- Push bullets + if (!is_predicting) + { + P_EntList new_guys = Zi; + for (P_Ent *player = P_FirstEnt(frame); !P_IsEntNil(player); player = P_NextEnt(player)) + { + if (player->is_player) + { + if (P_IsKeyNil(player->guy)) + { + player->guy = P_RandKey(); + } + P_Ent *guy = P_EntFromKey(frame, player->guy); + if (P_IsEntNil(guy)) + { + guy = P_PushTempEnt(scratch.arena, &new_guys); + guy->is_guy = 1; + guy->has_weapon = 1; + guy->key = player->guy; - // for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) - // { - // if (ent->control.fire_held) - // { - // if (ent->has_weapon) - // { + //- Choose guy spawn point + { + P_Ent *highest_scoring_spawn = &P_NilEnt; + { + Struct(SpawnNode) + { + SpawnNode *next; + P_Ent *ent; + f32 score; + }; - // } - // } - // } - // } + i64 spawns_count = 0; + SpawnNode *first_spawn = 0; + SpawnNode *last_spawn = 0; + + // Push spawns + for (P_Ent *spawn_ent = P_FirstEnt(frame); !P_IsEntNil(spawn_ent); spawn_ent = P_NextEnt(spawn_ent)) + { + if (spawn_ent->is_guy_spawn) + { + SpawnNode *spawn = PushStruct(scratch.arena, SpawnNode); + SllQueuePush(first_spawn, last_spawn, spawn); + spawn->ent = spawn_ent; + spawn->score = P_WorldPitch * 1000; + ++spawns_count; + } + } + + // Score spawns + for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) + { + b32 should_score = 0; + if (ent->is_guy) + { + should_score = 1; + } + if (should_score) + { + for (SpawnNode *spawn = first_spawn; spawn; spawn = spawn->next) + { + // TODO: Something better than linear distance for scoring + f32 score = Vec2Len(SubVec2(ent->xf.t, spawn->ent->xf.t)); + spawn->score = MinF32(spawn->score, score); + } + } + } + + // Find highest scoring spawn + i64 highest_score = -Inf; + for (SpawnNode *spawn = first_spawn; spawn; spawn = spawn->next) + { + b32 ignore_spawn = spawns_count > 1 && P_MatchKey(spawn->ent->key, player->spawn); + if (!ignore_spawn) + { + f32 rand_score_spread = 5; + f32 virtual_score = spawn->score + rand_score_spread * NormU32(RandU64FromState(&world->rand)); + if (virtual_score > highest_score) + { + highest_score = virtual_score; + highest_scoring_spawn = spawn->ent; + } + } + } + } + guy->xf = highest_scoring_spawn->xf; + player->spawn = highest_scoring_spawn->key; + } + } + } + } + P_SpawnEntsFromList(frame, new_guys); + } ////////////////////////////// //- Update guy controls from player controls diff --git a/src/pp/pp.h b/src/pp/pp.h index dcbd6c35..40cbcbc3 100644 --- a/src/pp/pp.h +++ b/src/pp/pp.h @@ -132,6 +132,8 @@ Struct(P_Ent) //- Player + P_Key spawn; + b32 is_player; P_Key guy; diff --git a/src/pp/pp_res/prefab/GuySpawn.ase b/src/pp/pp_res/prefab/GuySpawn.ase index ecd9b2aa..29e381fc 100644 --- a/src/pp/pp_res/prefab/GuySpawn.ase +++ b/src/pp/pp_res/prefab/GuySpawn.ase @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc0ee0ff942f817e4e412a038f261982be795a11cc0850abae06429179576f6e -size 559 +oid sha256:3f25a96ebcf5c455ea9b26347ad69e409d69923bd0194e7fbb6f83e09e80ae00 +size 519 diff --git a/src/pp/pp_sim/pp_sim_core.c b/src/pp/pp_sim/pp_sim_core.c index c5c3ecc2..d5e4f51b 100644 --- a/src/pp/pp_sim/pp_sim_core.c +++ b/src/pp/pp_sim/pp_sim_core.c @@ -295,90 +295,6 @@ void S_TickForever(WaveLaneCtx *lane) P_SpawnEntsFromList(world_frame, new_players); } - ////////////////////////////// - //- Spawn guys - - { - P_EntList new_guys = Zi; - for (P_Ent *player = P_FirstEnt(world_frame); !P_IsEntNil(player); player = P_NextEnt(player)) - { - if (player->is_player) - { - if (P_IsKeyNil(player->guy)) - { - player->guy = P_RandKey(); - } - P_Ent *guy = P_EntFromKey(world_frame, player->guy); - if (P_IsEntNil(guy)) - { - guy = P_PushTempEnt(frame_arena, &new_guys); - guy->is_guy = 1; - guy->has_weapon = 1; - guy->key = player->guy; - - //- Choose guy spawn point - { - P_Ent *highest_scoring_spawn = &P_NilEnt; - { - Struct(SpawnNode) - { - SpawnNode *next; - f32 score; - P_Ent *ent; - }; - SpawnNode *first_spawn_node = 0; - SpawnNode *last_spawn_node = 0; - - // Push spawns - for (P_Ent *spawn = P_FirstEnt(world_frame); !P_IsEntNil(spawn); spawn = P_NextEnt(spawn)) - { - if (spawn->is_guy_spawn) - { - SpawnNode *spawn_node = PushStruct(frame_arena, SpawnNode); - SllQueuePush(first_spawn_node, last_spawn_node, spawn_node); - spawn_node->ent = spawn; - spawn_node->score = Inf; - } - } - - // Score spawns - for (P_Ent *ent = P_FirstEnt(world_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) - { - b32 should_score = 0; - if (ent->is_guy) - { - should_score = 1; - } - if (should_score) - { - for (SpawnNode *spawn_node = first_spawn_node; spawn_node; spawn_node = spawn_node->next) - { - f32 score = Vec2Len(SubVec2(ent->xf.t, spawn_node->ent->xf.t)); - spawn_node->score = MinF32(spawn_node->score, score); - } - } - } - - // Find highest scoring spawn_node - i64 highest_score = -Inf; - for (SpawnNode *spawn_node = first_spawn_node; spawn_node; spawn_node = spawn_node->next) - { - if (spawn_node->score > highest_score) - { - highest_score = spawn_node->score; - highest_scoring_spawn = spawn_node->ent; - } - } - } - - guy->xf = highest_scoring_spawn->xf; - } - } - } - } - P_SpawnEntsFromList(world_frame, new_guys); - } - ////////////////////////////// //- Read snapshots