track player kills & deaths

This commit is contained in:
jacob 2026-02-11 18:01:48 -06:00
parent f328cf712f
commit 6602fcdccf
12 changed files with 276 additions and 81 deletions

View File

@ -673,6 +673,20 @@ String BB_ReadString(Arena *arena, BB_Reader *bbr)
return result;
}
String BB_ReadStringRaw(BB_Reader *bbr)
{
BB_ReadDebugMagic(bbr, BB_DebugMagicKind_String, 0);
String result = Zi;
u64 len = BB_ReadUV(bbr);
u8 *src = BB_ReadBytesRaw(bbr, len);
if (src != 0)
{
result.len = len;
result.text = src;
}
return result;
}
// Will fill dst with zeroes if bitbuff overflows
void BB_ReadBytes(BB_Reader *bbr, String out)
{

View File

@ -164,6 +164,7 @@ Uid BB_ReadUid(BB_Reader *bbr);
//- Raw data
String BB_ReadString(Arena *arena, BB_Reader *bbr);
String BB_ReadStringRaw(BB_Reader *bbr);
void BB_ReadBytes(BB_Reader *bbr, String dst);
u8 *BB_ReadBytesRaw(BB_Reader *bbr, u64 num_bytes);
void BB_ReadSeekBytes(BB_Reader *bbr, u64 num_bytes);

View File

@ -120,7 +120,12 @@ String P_NameFromTileKind(P_TileKind kind)
P_TilesXList(X)
#undef X
};
return names[kind];
String result = Zi;
if (kind >= 0 && kind < countof(names))
{
result = names[kind];
}
return result;
}
////////////////////////////////////////////////////////////
@ -133,7 +138,12 @@ String P_NameFromPrefabKind(P_PrefabKind kind)
P_PrefabsXList(X)
#undef X
};
return names[kind];
String result = Zi;
if (kind >= 0 && kind < countof(names))
{
result = names[kind];
}
return result;
}
////////////////////////////////////////////////////////////
@ -1653,6 +1663,7 @@ P_Msg *P_PushMsg(P_MsgKind kind, String data)
P_Msg *msg = &msg_node->msg;
msg->kind = kind;
msg->data = PushString(P_tl.out_msgs_arena, data);
msg->xf = XformIdentity;
DllQueuePush(P_tl.out_msgs.first, P_tl.out_msgs.last, msg_node);
++P_tl.out_msgs.count;
return msg;
@ -1981,6 +1992,7 @@ void P_StepFrame(P_Frame *frame)
guy->is_guy = 1;
guy->has_weapon = 1;
guy->key = player->guy;
guy->player = player->key;
//- Choose guy spawn point
{
@ -2957,6 +2969,19 @@ void P_StepFrame(P_Frame *frame)
// TODO: Remove this
if (!P_IsEntNil(victim))
{
P_Ent *damager = bullet;
if (!damager->is_player)
{
damager = P_EntFromKey(frame, damager->bullet_firer);
}
if (!damager->is_player)
{
damager = P_EntFromKey(frame, damager->player);
}
if (damager->is_player)
{
victim->last_damaging_player = damager->key;
}
victim->health -= 0.25;
}
@ -2986,6 +3011,16 @@ void P_StepFrame(P_Frame *frame)
P_Ent *old_guy = P_EntFromKey(prev_frame, guy->key);
if (old_guy->health > 0)
{
P_Ent *player = P_EntFromKey(frame, guy->player);
P_Ent *killer = P_EntFromKey(frame, guy->last_damaging_player);
if (player->is_player)
{
player->deaths += 1;
}
if (killer->is_player && !P_MatchEntKey(player->key, killer->key))
{
killer->kills += 1;
}
guy->exists = 0;
}
}

View File

@ -152,9 +152,17 @@ Struct(P_Ent)
P_EntKey guy;
f32 ping;
f32 kills;
f32 deaths;
u8 string_len;
u8 string_text[P_MaxPlayerNameLen + 8];
//- Guy
P_EntKey player;
P_EntKey last_damaging_player;
//- Spawn
b32 is_guy_spawn;
@ -386,12 +394,15 @@ Enum(P_MsgKind)
// Client -> Server
P_MsgKind_SaveWorld,
P_MsgKind_ResetWorld,
P_MsgKind_Teleport,
P_MsgKind_TileEdit,
P_MsgKind_Prefab,
P_MsgKind_Delete,
// Server -> Client
P_MsgKind_Tiles,
P_MsgKind_COUNT
};
Struct(P_Msg)

View File

@ -19,5 +19,10 @@ P_PrefabFlag P_FlagsFromPrefabKind(P_PrefabKind kind)
P_PrefabsXList(X)
#undef X
};
return flags[kind];
P_PrefabFlag result = P_PrefabFlag_None;
if (kind >= 0 && kind < countof(flags))
{
result = flags[kind];
}
return result;
}

View File

@ -44,7 +44,6 @@ Enum(P_TileKind)
#define P_PrefabsXList(X) \
X(None, P_PrefabFlag_HideFromEditor) \
X(Guy, P_PrefabFlag_None) \
X(Bot, P_PrefabFlag_None) \
X(GuySpawn, P_PrefabFlag_None) \
/* --------------------------------------------------- */

View File

@ -557,6 +557,15 @@ void S_TickForever(WaveLaneCtx *lane)
}
}
} break;
//- Teleport
case P_MsgKind_Teleport:
{
P_Ent *ent = P_EntFromKey(world_frame, msg->key);
if (!P_IsEntNil(ent))
{
ent->xf = msg->xf;
}
} break;
//- Prefab
case P_MsgKind_Prefab:
{
@ -565,15 +574,6 @@ void S_TickForever(WaveLaneCtx *lane)
P_PrefabKind prefab = msg->prefab;
switch (prefab)
{
case P_PrefabKind_Guy:
{
P_Ent *guy = P_PushTempEnt(frame_arena, &ents);
guy->key = msg->key;
guy->xf = msg->xf;
guy->is_guy = 1;
guy->has_weapon = 1;
} break;
case P_PrefabKind_Bot:
{
P_Ent *bot = P_EntFromKey(world_frame, msg->key);
@ -592,6 +592,7 @@ void S_TickForever(WaveLaneCtx *lane)
guy->key = P_RandEntKey();
guy->is_guy = 1;
guy->has_weapon = 1;
guy->player = bot->key;
bot->guy = guy->key;
}
guy->xf = msg->xf;

View File

@ -70,6 +70,9 @@ String P_PackWorld(Arena *arena, P_World *src_world)
result.len += StringF(arena, " look: \"%F\"\n", FmtFloat2(ent->control.look)).len;
result.len += StringF(arena, " health: \"%F\"\n", FmtFloat(ent->health)).len;
result.len += StringF(arena, " guy: \"0x%F\"\n", FmtHex(ent->guy.v)).len;
result.len += StringF(arena, " player: \"0x%F\"\n", FmtHex(ent->player.v)).len;
result.len += StringF(arena, " kills: \"%F\"\n", FmtFloat(ent->kills)).len;
result.len += StringF(arena, " deaths: \"%F\"\n", FmtFloat(ent->deaths)).len;
result.len += StringF(arena, " text: \"%F\"\n", FmtString(CR_SanitizeString(scratch.arena, STRING(ent->string_len, ent->string_text)))).len;
}
result.len += PushString(arena, Lit(" }\n")).len;
@ -211,6 +214,18 @@ P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed)
{
ent->guy.v = CR_IntFromString(attr->value);
}
if (MatchString(attr->name, Lit("player")))
{
ent->player.v = CR_IntFromString(attr->value);
}
if (MatchString(attr->name, Lit("kills")))
{
ent->kills = CR_FloatFromString(attr->value);
}
if (MatchString(attr->name, Lit("deaths")))
{
ent->deaths = CR_FloatFromString(attr->value);
}
if (MatchString(attr->name, Lit("text")))
{
P_SetEntString(ent, attr->value);
@ -275,37 +290,61 @@ P_MsgList P_UnpackMessages(Arena *arena, String packed, NET_Key sender)
b32 done = 0;
while (!done)
{
b32 is_raw = BB_ReadBit(&bbr);
if (is_raw)
//- Read
P_Msg msg = Zi;
{
P_MsgNode *dst_msg_node = PushStruct(arena, P_MsgNode);
DllQueuePush(result.first, result.last, dst_msg_node);
result.count += 1;
dst_msg_node->msg.kind = P_MsgKind_Raw;
dst_msg_node->msg.src = sender;
dst_msg_node->msg.dst = NET_NilKey;
dst_msg_node->msg.data = PushString(arena, packed);
done = 1;
}
else
{
u8 *msg_src = BB_ReadBytesRaw(&bbr, sizeof(P_Msg));
if (msg_src != 0)
b32 is_raw = BB_ReadBit(&bbr);
if (is_raw)
{
// Read msg header
P_MsgNode *dst_msg_node = PushStruct(arena, P_MsgNode);
CopyStruct(&dst_msg_node->msg, msg_src);
DllQueuePush(result.first, result.last, dst_msg_node);
result.count += 1;
// Read msg data string
String data_str = BB_ReadString(arena, &bbr);
dst_msg_node->msg.src = sender;
dst_msg_node->msg.dst = NET_NilKey;
dst_msg_node->msg.data = data_str;
msg.kind = P_MsgKind_Raw;
msg.data = packed;
done = 1;
}
else
{
done = 1;
u8 *raw_src_msg = BB_ReadBytesRaw(&bbr, sizeof(P_Msg));
if (raw_src_msg != 0)
{
CopyStruct(&msg, raw_src_msg);
String data_str = BB_ReadStringRaw(&bbr);
msg.data = data_str;
}
else
{
done = 1;
}
}
}
//- Validate
b32 skip = 0;
{
if (msg.kind < P_MsgKind_None || msg.kind > P_MsgKind_COUNT)
{
skip = 1;
}
if (msg.prefab < P_PrefabKind_None || msg.prefab > P_PrefabKind_COUNT)
{
skip = 1;
}
}
if (!skip)
{
//- Sanitize
{
msg.src = sender;
msg.dst = NET_NilKey;
msg.xf = NormXform(msg.xf);
msg.data = PushString(arena, msg.data);
}
//- Collect
{
P_MsgNode *msg_node = PushStruct(arena, P_MsgNode);
DllQueuePush(result.first, result.last, msg_node);
result.count += 1;
msg_node->msg = msg;
}
}
}

View File

@ -4101,34 +4101,57 @@ void V_TickForever(WaveLaneCtx *lane)
if (frame->held_buttons[Button_Tab])
{
UI_Size name_sz = UI_GROW(0.75, 0);
UI_Size ping_sz = UI_GROW(0.25, 0);
// UI_Size name_sz = UI_GROW(0.75, 0);
// UI_Size kills_sz = UI_GROW(0.25, 0);
// UI_Size deaths_sz = UI_GROW(0.25, 0);
// UI_Size ping_sz = UI_GROW(0.25, 0);
// UI_Size spacing_sz = UI_FNT(1, 0);
UI_Size name_sz = UI_FNT(10, 0);
UI_Size kills_sz = UI_FNT(10, 0);
UI_Size deaths_sz = UI_FNT(10, 0);
UI_Size ping_sz = UI_FNT(5, 0);
UI_Size spacing_sz = UI_FNT(1, 0);
Enum(BoardRowFlag)
{
BoardRowFlag_None = 0,
BoardRowFlag_Header = (1 << 0),
};
Struct(BoardRow)
{
BoardRow *next;
BoardRowFlag flags;
String name;
f32 ping;
f32 kills;
f32 deaths;
};
i64 players_count = 0;
i64 board_rows_count = 0;
BoardRow *first_board_row = 0;
BoardRow *last_board_row = 0;
for (P_Ent *player = P_FirstEnt(sim_world->last_frame); !P_IsEntNil(player); player = P_NextEnt(player))
{
BoardRow *row = PushStruct(frame->arena, BoardRow);
SllQueuePush(first_board_row, last_board_row, row);
board_rows_count += 1;
row->flags |= BoardRowFlag_Header;
}
for (P_Ent *player = P_FirstEnt(local_frame); !P_IsEntNil(player); player = P_NextEnt(player))
{
if (player->is_player)
{
players_count += 1;
BoardRow *row = PushStruct(frame->arena, BoardRow);
{
SllQueuePush(first_board_row, last_board_row, row);
board_rows_count += 1;
}
SllQueuePush(first_board_row, last_board_row, row);
board_rows_count += 1;
String name = P_StringFromEnt(player);
row->name = name;
row->ping = player->ping;
row->kills = player->kills;
row->deaths = player->deaths;
}
}
@ -4137,9 +4160,8 @@ void V_TickForever(WaveLaneCtx *lane)
Vec4 board_bg = VEC4(0, 0, 0, 1);
f32 opacity = 0.75;
UI_Size board_width = UI_FNT(50, 0);
UI_Size board_height = UI_FNT(20, 0);
UI_Size board_width = UI_SHRINK(0, 0);
UI_Size board_height = UI_SHRINK(0, 0);
Vec2 pos = VEC2(frame->screen_dims.x / 2, 50);
UI_PushCP(UI_NilKey);
{
@ -4152,41 +4174,113 @@ void V_TickForever(WaveLaneCtx *lane)
UI_SetNext(Flags, UI_BoxFlag_Floating);
UI_PushCP(UI_BuildColumnEx(board_key));
{
UI_BuildSpacer(spacing_sz, Axis_Y);
// UI_BuildSpacer(spacing_sz, Axis_Y);
for (BoardRow *board_row = first_board_row; board_row; board_row = board_row->next)
{
UI_SetNext(Width, UI_GROW(1, 1));
UI_SetNext(Height, UI_FNT(2.5, 0));
b32 is_header = AnyBit(board_row->flags, BoardRowFlag_Header);
UI_SetNext(Width, UI_SHRINK(0, 0));
if (is_header)
{
UI_SetNext(Height, UI_FNT(3, 0));
}
else
{
UI_SetNext(Height, UI_FNT(2, 0));
}
UI_SetNext(Tint, 0);
UI_SetNext(ChildAlignment, UI_Region_Left);
UI_PushCP(UI_BuildRow()); // Scoreboard row
{
UI_Push(ChildAlignment, UI_Region_Left);
if (is_header)
{
UI_Push(FontSize, UI_Top(FontSize) * theme.h2);
}
else
{
UI_Push(FontSize, UI_Top(FontSize) * theme.h3);
}
UI_BuildSpacer(spacing_sz, Axis_X);
UI_SetNext(Width, name_sz);
UI_PushCP(UI_BuildColumn()); // Player name column
{
UI_SetNext(FontSize, UI_Top(FontSize) * theme.h2);
UI_BuildLabelF("Player: \"%F\"", FmtString(board_row->name));
if (is_header)
{
UI_BuildLabelF("Name");
}
else
{
UI_BuildLabelF("%F", FmtString(board_row->name));
}
}
UI_PopCP(UI_TopCP());
UI_BuildDivider(UI_PIX(1, 1), theme.col.divider, Axis_X);
UI_BuildSpacer(spacing_sz, Axis_X);
UI_SetNext(Width, kills_sz);
UI_PushCP(UI_BuildColumn()); // Kills column
{
if (is_header)
{
UI_BuildLabelF("Kills");
}
else
{
UI_BuildLabelF("%F", FmtFloat(board_row->kills, .p = 2));
}
}
UI_PopCP(UI_TopCP());
UI_BuildDivider(UI_PIX(1, 1), theme.col.divider, Axis_X);
UI_BuildSpacer(spacing_sz, Axis_X);
UI_SetNext(Width, deaths_sz);
UI_PushCP(UI_BuildColumn()); // Deaths column
{
if (is_header)
{
UI_BuildLabelF("Deaths");
}
else
{
UI_BuildLabelF("%F", FmtFloat(board_row->deaths, .p = 2));
}
}
UI_PopCP(UI_TopCP());
UI_BuildDivider(UI_PIX(1, 1), theme.col.divider, Axis_X);
UI_BuildSpacer(spacing_sz, Axis_X);
UI_SetNext(Width, ping_sz);
UI_PushCP(UI_BuildColumn()); // Ping column
{
UI_SetNext(FontSize, UI_Top(FontSize) * theme.h2);
UI_BuildLabelF("Ping: %F ", FmtFloat(board_row->ping, .p = 2));
if (is_header)
{
UI_BuildLabelF("Ping");
}
else
{
UI_BuildLabelF("%F", FmtFloat(board_row->ping, .p = 2));
}
}
UI_PopCP(UI_TopCP());
}
UI_PopCP(UI_TopCP());
if (board_row->next)
if (is_header)
{
UI_BuildDivider(UI_PIX(1, 1), theme.col.divider, Axis_Y);
UI_BuildDivider(UI_PIX(3, 1), theme.col.divider, Axis_Y);
}
else
{
if (board_row->next)
{
UI_BuildDivider(UI_PIX(1, 1), theme.col.divider, Axis_Y);
}
}
}
UI_BuildSpacer(spacing_sz, Axis_Y);
// UI_BuildSpacer(spacing_sz, Axis_Y);
}
UI_PopCP(UI_TopCP());
}
@ -4453,26 +4547,16 @@ void V_TickForever(WaveLaneCtx *lane)
// } break;
case V_CmdKind_reset_world:
case V_CmdKind_spawn_tp_player:
{
// Reset world
Vec2 guy_pos = VEC2(5, 0);
if (kind == V_CmdKind_reset_world)
{
P_Msg *msg = P_PushMsg(P_MsgKind_ResetWorld, Zstr);
msg->affect_bots = 1;
}
else
{
guy_pos = frame->world_cursor;
}
// Spawn player guy
{
P_Msg *msg = P_PushMsg(P_MsgKind_Prefab, Zstr);
msg->prefab = P_PrefabKind_Guy;
msg->key = local_guy->key;
msg->xf.t = guy_pos;
}
P_Msg *msg = P_PushMsg(P_MsgKind_ResetWorld, Zstr);
msg->affect_bots = 1;
} break;
case V_CmdKind_tp_player:
{
P_Msg *msg = P_PushMsg(P_MsgKind_Teleport, Zstr);
msg->key = local_guy->key;
msg->xf.t = frame->world_cursor;
} break;
case V_CmdKind_spawn_tp_bot:

View File

@ -13,7 +13,7 @@
X(toggle_console, Toggle Developer Console, V_CmdDescFlag_None, V_HOTKEY( Button_GraveAccent ), ) \
X(toggle_fullscreen, Toggle Fullscreen Mode, V_CmdDescFlag_None, V_HOTKEY( Button_Enter, .alt = 1 ) ) \
X(toggle_window_topmost, Toggle Window Topmost, V_CmdDescFlag_None, V_HOTKEY( Button_F4 ), ) \
X(spawn_tp_player, Spawn/Teleport Player, V_CmdDescFlag_None, V_HOTKEY( Button_Q ), ) \
X(tp_player, Teleport Player, V_CmdDescFlag_None, V_HOTKEY( Button_Q ), ) \
X(spawn_tp_bot, Spawn/Teleport Bot, V_CmdDescFlag_None, V_HOTKEY( Button_T ), ) \
X(spawn_bot, Spawn Bot, V_CmdDescFlag_None, V_HOTKEY( Button_T, .ctrl = 1 ), ) \
X(delete, Delete entity at cursor, V_CmdDescFlag_None, V_HOTKEY( Button_M2 ), ) \

View File

@ -44,7 +44,12 @@ String SPR_NameFromRayKind(SPR_RayKind kind)
SPR_RayKindXList(X)
#undef X
};
return names[kind];
String result = Zi;
if (kind >= 0 && kind < countof(names))
{
result = names[kind];
}
return result;
}
SPR_LayerKind SPR_LayerKindFromName(String name)

View File

@ -1350,7 +1350,8 @@ void UI_EndFrame(UI_Frame *frame, i32 vsync)
f32 scaled_size = 0;
if (box->solved_scale.v[axis] == 1)
{
scaled_size = RoundF32(unscaled_size);
// scaled_size = RoundF32(unscaled_size);
scaled_size = unscaled_size;
}
else
{