randomnize spawn scores. ignore last player spawn point.

This commit is contained in:
jacob 2026-02-06 00:56:01 -06:00
parent e88c91b1ab
commit bc32417ba1
9 changed files with 129 additions and 105 deletions

View File

@ -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

View File

@ -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

View File

@ -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));
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -132,6 +132,8 @@ Struct(P_Ent)
//- Player
P_Key spawn;
b32 is_player;
P_Key guy;

BIN
src/pp/pp_res/prefab/GuySpawn.ase (Stored with Git LFS)

Binary file not shown.

View File

@ -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