working spawn points

This commit is contained in:
jacob 2026-02-05 23:51:04 -06:00
parent e0356f73e8
commit e88c91b1ab
9 changed files with 246 additions and 209 deletions

View File

@ -1,6 +1,47 @@
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Value conversion utilities //~ Value conversion utilities
String CR_SanitizeString(Arena *arena, String str)
{
String result = Zi;
result.text = ArenaNext(arena, u8);
for (u64 idx = 0; idx < str.len; ++idx)
{
u8 c = str.text[idx];
String sanitized = Zi;
switch (c)
{
default:
{
sanitized.text = &c;
sanitized.len = 1;
} break;
case '\r':
{
// Ignore
} break;
case '\n':
{
sanitized = Lit("\\n");
} break;
case '\\':
{
sanitized = Lit("\\");
} break;
case '"':
{
sanitized = Lit("\\\"");
} break;
}
result.len += PushString(arena, sanitized).len;
}
return result;
}
i64 CR_IntFromString(String str) i64 CR_IntFromString(String str)
{ {
i64 result = 0; i64 result = 0;
@ -330,7 +371,7 @@ CR_Item *CR_ItemFromString(Arena *arena, String str)
} }
else if (c == '\"') else if (c == '\"')
{ {
if (pos == text_start) if (!text_is_string && pos == text_start)
{ {
text_is_string = 1; text_is_string = 1;
text_start = pos + 1; text_start = pos + 1;

View File

@ -17,6 +17,7 @@ Struct(CR_Item)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Value conversion utilities //~ Value conversion utilities
String CR_SanitizeString(Arena *arena, String str);
i64 CR_IntFromString(String str); i64 CR_IntFromString(String str);
f64 CR_FloatFromString(String str); f64 CR_FloatFromString(String str);
b32 CR_BoolFromString(String str); b32 CR_BoolFromString(String str);

View File

@ -210,166 +210,26 @@ P_Anim P_AnimFromEnt(P_Ent *ent, i64 time_ns)
{ {
P_Anim result = Zi; P_Anim result = Zi;
i64 animation_rate_ns = NsFromSeconds(0.100);
// TODO: Determine animation dynamically // TODO: Determine animation dynamically
i64 animation_rate_ns = NsFromSeconds(0.100);
{ {
i64 walk_duration_ns = time_ns; i64 walk_duration_ns = time_ns;
result.frame_seq = walk_duration_ns / animation_rate_ns; result.frame_seq = walk_duration_ns / animation_rate_ns;
result.span = SPR_SpanKeyFromName(Lit("walk")); result.span = SPR_SpanKeyFromName(Lit("walk"));
} }
result.sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("sprite/bla3.ase"))); // TODO: Use prefab lookup
result.wep_sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("sprite/raah.ase"))); if (ent->is_guy)
{
result.sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("sprite/bla3.ase")));
result.wep_sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("sprite/raah.ase")));
}
else if (ent->is_guy_spawn)
{
result.sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("prefab/GuySpawn.ase")));
}
return result; return result;
// P_Anim result = Zi;
// // result.slice_to_world_af = AffineIdentity;
// // result.slice_to_world_af = ent->af;
// i64 animation_rate_ns = NsFromSeconds(0.100);
// SPR_SpanKey span_key = Zi;
// {
// // TODO: Determine span dynamically
// span_key = SPR_SpanKeyFromName(Lit("walk"));
// }
// SPR_SheetKey body_sheet_key = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("sprite/bla3.ase")));
// SPR_SheetKey weapon_sheet_key = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("sprite/raahase")));
// //- Body
// {
// // TODO: Real walk animation
// i64 walk_duration_ns = time_ns;
// i64 frame_seq = walk_duration_ns / animation_rate_ns;
// SPR_Frame sframe = SPR_FrameFromSheet(body_sheet_key, span_key, frame_seq);
// // FIXME: Use origin ray
// {
// Vec2 world_dims = DivVec2(sframe.dims, P_CellsPerMeter);
// result.body_to_world_af = AffineIdentity;
// result.body_to_world_af.og = ent->af.og;
// // result.body_to_world_af.og = SubVec2(result.body_to_world_af.og, MulVec2(world_dims, 0.25));
// result.body_to_world_af = AffineWithWorldRotation(result.body_to_world_af, AngleFromVec2(ent->control.look));
// result.body_to_world_af = TranslateAffine(result.body_to_world_af, MulVec2(world_dims, -0.5));
// result.body_to_world_af = ScaleAffine(result.body_to_world_af, world_dims);
// }
// {
// result.
// }
// result.body_slice = sframe.slice;
// {
// // result.
// }
// // SPR_Ray origin_ray = SPR_RayFromFrame(body_sheet_key, origin_layer_key, frame_idx);
// // SPR_Ray ap_ray = SPR_RayFromFrame(body_sheet_key, ap_layer_key, frame_idx);
// // {
// // // Vec2 world_dims = VEC2(0.5, 0.5);
// // Vec2 px_dims = slice.dims;
// // Vec2 world_dims = DivVec2(px_dims, P_CellsPerMeter);
// // result.slice_to_world_af = AffineIdentity;
// // result.slice_to_world_af.og = ent->af.og;
// // // result.slice_to_world_af.og = SubVec2(result.slice_to_world_af.og, MulVec2(world_dims, 0.25));
// // result.slice_to_world_af = AffineWithWorldRotation(result.slice_to_world_af, AngleFromVec2(ent->control.look));
// // result.slice_to_world_af = TranslateAffine(result.slice_to_world_af, MulVec2(world_dims, -0.5));
// // result.slice_to_world_af = ScaleAffine(result.slice_to_world_af, world_dims);
// // }
// // result.slice = SPR_SliceFromSheet(sheet,
// }
// //- Weapon
// {
// }
// return result;
// P_Anim result = Zi;
// // result.slice_to_world_af = AffineIdentity;
// // result.slice_to_world_af = ent->af;
// i64 animation_rate_ns = NsFromSeconds(0.100);
// SPR_LayerKey origin_layer_key = SPR_LayerKeyFromName(Lit(".origin"));
// SPR_LayerKey ap_layer_key = SPR_LayerKeyFromName(Lit(".ap"));
// SPR_SpanKey span_key = Zi;
// {
// // TODO: Determine span dynamically
// span_key = SPR_SpanKeyFromName(Lit("walk"));
// }
// SPR_SheetKey body_sheet_key = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("sprite/bla3.ase")));
// SPR_SheetKey weapon_sheet_key = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("sprite/raahase")));
// // Body
// {
// i64 frame_idx = 0;
// {
// // TODO: Real walk animation
// SPR_Span span = SPR_SpanFromKey(body_sheet_key, span_key);
// i64 walk_duration_ns = time_ns;
// i64 frame_seq = walk_duration_ns / animation_rate_ns;
// i64 frame_idx = span.start + (frame_seq % (span.end - span.start));
// }
// SPR_Slice slice = SPR_SliceFromFrame(body_sheet_key, SPR_NilLayerKey, frame_idx);
// SPR_Ray origin_ray = SPR_RayFromFrame(body_sheet_key, origin_layer_key, frame_idx);
// SPR_Ray ap_ray = SPR_RayFromFrame(body_sheet_key, ap_layer_key, frame_idx);
// {
// // Vec2 world_dims = VEC2(0.5, 0.5);
// Vec2 px_dims = slice.dims;
// Vec2 world_dims = DivVec2(px_dims, P_CellsPerMeter);
// result.slice_to_world_af = AffineIdentity;
// result.slice_to_world_af.og = ent->af.og;
// // result.slice_to_world_af.og = SubVec2(result.slice_to_world_af.og, MulVec2(world_dims, 0.25));
// result.slice_to_world_af = AffineWithWorldRotation(result.slice_to_world_af, AngleFromVec2(ent->control.look));
// result.slice_to_world_af = TranslateAffine(result.slice_to_world_af, MulVec2(world_dims, -0.5));
// result.slice_to_world_af = ScaleAffine(result.slice_to_world_af, world_dims);
// }
// // result.slice = SPR_SliceFromSheet(sheet,
// }
// // Weapon
// {
// }
// return result;
} }
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@ -68,8 +68,7 @@ Struct(P_DebugDrawNode)
//~ Ent types //~ Ent types
// TODO: Move boolean fields into bitwise property flags // TODO: Move boolean fields into bitwise property flags
// TODO: Pack efficiently
// TODO: Pack efficiently, deduplicate redundant fields
#define P_MinPlayerNameLen 1 #define P_MinPlayerNameLen 1
#define P_MaxPlayerNameLen 24 #define P_MaxPlayerNameLen 24
@ -140,6 +139,10 @@ Struct(P_Ent)
u8 string_len; u8 string_len;
u8 string_text[P_MaxPlayerNameLen + 8]; u8 string_text[P_MaxPlayerNameLen + 8];
//- Spawn
b32 is_guy_spawn;
//- Solver //- Solver
Vec2 solved_v; Vec2 solved_v;

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

Binary file not shown.

View File

@ -37,7 +37,7 @@ Enum(P_TileKind)
X(None, P_PrefabFlag_HideFromEditor) \ X(None, P_PrefabFlag_HideFromEditor) \
X(Guy, P_PrefabFlag_None) \ X(Guy, P_PrefabFlag_None) \
X(Dummy, P_PrefabFlag_None) \ X(Dummy, P_PrefabFlag_None) \
X(SpawnPoint, P_PrefabFlag_None) \ X(GuySpawn, P_PrefabFlag_None) \
/* --------------------------------------------------- */ /* --------------------------------------------------- */
//- Prefab flags //- Prefab flags

View File

@ -232,6 +232,10 @@ void S_TickForever(WaveLaneCtx *lane)
DllQueuePushNP(bin->first, bin->last, client, next_in_bin, prev_in_bin); DllQueuePushNP(bin->first, bin->last, client, next_in_bin, prev_in_bin);
} }
// FIXME: Only accept desired player key from host client
client->player = msg->key;
// Acknowledge player connection // Acknowledge player connection
{ {
@ -292,7 +296,7 @@ void S_TickForever(WaveLaneCtx *lane)
} }
////////////////////////////// //////////////////////////////
//- Create guy entities //- Spawn guys
{ {
P_EntList new_guys = Zi; P_EntList new_guys = Zi;
@ -308,10 +312,68 @@ void S_TickForever(WaveLaneCtx *lane)
if (P_IsEntNil(guy)) if (P_IsEntNil(guy))
{ {
guy = P_PushTempEnt(frame_arena, &new_guys); guy = P_PushTempEnt(frame_arena, &new_guys);
guy->is_guy = 1;
guy->has_weapon = 1;
guy->key = player->guy; 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;
}
} }
guy->is_guy = 1;
guy->has_weapon = 1;
} }
} }
P_SpawnEntsFromList(world_frame, new_guys); P_SpawnEntsFromList(world_frame, new_guys);
@ -592,33 +654,42 @@ void S_TickForever(WaveLaneCtx *lane)
guy->xf = msg->xf; guy->xf = msg->xf;
guy->is_guy = 1; guy->is_guy = 1;
guy->has_weapon = 1; guy->has_weapon = 1;
guy->exists = 1;
} break; } break;
case P_PrefabKind_Dummy: case P_PrefabKind_Dummy:
{ {
P_Ent *dummy = P_EntFromKey(world_frame, msg->key); P_Ent *dummy = P_EntFromKey(world_frame, msg->key);
if (P_IsEntNil(dummy)) if (!dummy->is_dummy)
{ {
dummy = P_PushTempEnt(frame_arena, &ents); dummy = P_PushTempEnt(frame_arena, &ents);
dummy->key = msg->key; dummy->key = msg->key;
dummy->is_player = 1; dummy->is_player = 1;
dummy->is_dummy = 1; dummy->is_dummy = 1;
dummy->exists = 1;
P_SetEntString(dummy, name); P_SetEntString(dummy, name);
} }
P_Ent *guy = P_EntFromKey(world_frame, dummy->guy); P_Ent *guy = P_EntFromKey(world_frame, dummy->guy);
if (P_IsEntNil(guy)) if (!guy->is_guy)
{ {
guy = P_PushTempEnt(frame_arena, &ents); guy = P_PushTempEnt(frame_arena, &ents);
guy->key = P_RandKey(); guy->key = P_RandKey();
guy->is_guy = 1; guy->is_guy = 1;
guy->has_weapon = 1; guy->has_weapon = 1;
guy->exists = 1;
dummy->guy = guy->key; dummy->guy = guy->key;
} }
guy->xf = msg->xf; guy->xf = msg->xf;
} break; } break;
case P_PrefabKind_GuySpawn:
{
P_Ent *spawn = P_EntFromKey(world_frame, msg->key);
if (!spawn->is_guy_spawn)
{
spawn = P_PushTempEnt(frame_arena, &ents);
spawn->key = msg->key;
spawn->is_guy_spawn = 1;
}
spawn->xf = msg->xf;
} break;
} }
P_SpawnEntsFromList(world_frame, ents); P_SpawnEntsFromList(world_frame, ents);
} break; } break;

View File

@ -26,10 +26,6 @@ String P_PackWorld(Arena *arena, P_World *src_world)
{ {
should_pack = 0; should_pack = 0;
} }
if (ent->is_player)
{
should_pack = 0;
}
if (should_pack) if (should_pack)
{ {
result.len += StringF(arena, " 0x%F:\n", FmtHex(ent->key.v)).len; result.len += StringF(arena, " 0x%F:\n", FmtHex(ent->key.v)).len;
@ -58,13 +54,20 @@ String P_PackWorld(Arena *arena, P_World *src_world)
{ {
result.len += PushString(arena, Lit(" bullet\n")).len; result.len += PushString(arena, Lit(" bullet\n")).len;
} }
if (ent->is_guy_spawn)
{
result.len += PushString(arena, Lit(" guy_spawn\n")).len;
}
} }
result.len += StringF(arena, " }\n").len; result.len += StringF(arena, " }\n").len;
result.len += StringF(arena, " pos: \"%F\"\n", FmtFloat2(ent->xf.t)).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, " angle: \"%F\"\n", FmtFloat(AngleFromVec2(ent->xf.r))).len;
result.len += StringF(arena, " look: \"%F\"\n", FmtFloat2(ent->control.look)).len;
result.len += StringF(arena, " exists: \"%F\"\n", FmtFloat(ent->exists)).len; result.len += StringF(arena, " exists: \"%F\"\n", FmtFloat(ent->exists)).len;
result.len += StringF(arena, " look: \"%F\"\n", FmtFloat2(ent->control.look)).len; 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, " health: \"%F\"\n", FmtFloat(ent->health)).len;
result.len += StringF(arena, " guy: \"0x%F\"\n", FmtHex(ent->guy.v)).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; result.len += PushString(arena, Lit(" }\n")).len;
} }
@ -159,6 +162,10 @@ P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed)
{ {
ent->is_bullet = 1; ent->is_bullet = 1;
} }
if (MatchString(prop->value, Lit("guy_spawn")))
{
ent->is_guy_spawn = 1;
}
} }
} }
if (MatchString(attr->name, Lit("pos"))) if (MatchString(attr->name, Lit("pos")))
@ -169,21 +176,29 @@ P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed)
{ {
ent->xf.r = Vec2FromAngle(CR_FloatFromString(attr->value)); ent->xf.r = Vec2FromAngle(CR_FloatFromString(attr->value));
} }
if (MatchString(attr->name, Lit("look")))
{
ent->control.look = CR_Vec2FromString(attr->value);
}
if (MatchString(attr->name, Lit("exists"))) if (MatchString(attr->name, Lit("exists")))
{ {
ent->exists = CR_FloatFromString(attr->value); ent->exists = CR_FloatFromString(attr->value);
} }
if (MatchString(attr->name, Lit("health")))
{
ent->health = CR_FloatFromString(attr->value);
}
if (MatchString(attr->name, Lit("look"))) if (MatchString(attr->name, Lit("look")))
{ {
ent->control.look = CR_Vec2FromString(attr->value); ent->control.look = CR_Vec2FromString(attr->value);
} }
if (MatchString(attr->name, Lit("health"))) if (MatchString(attr->name, Lit("health")))
{ {
ent->health = CR_FloatFromString(attr->value);; ent->health = CR_FloatFromString(attr->value);
}
if (MatchString(attr->name, Lit("guy")))
{
ent->guy.v = CR_IntFromString(attr->value);
}
if (MatchString(attr->name, Lit("text")))
{
P_SetEntString(ent, attr->value);
} }
} }
} }

View File

@ -630,6 +630,8 @@ void V_TickForever(WaveLaneCtx *lane)
BB_WriteF32(&bbw, prev_frame->edit_camera_pos.x); BB_WriteF32(&bbw, prev_frame->edit_camera_pos.x);
BB_WriteF32(&bbw, prev_frame->edit_camera_pos.y); BB_WriteF32(&bbw, prev_frame->edit_camera_pos.y);
BB_WriteF32(&bbw, prev_frame->edit_camera_zoom); BB_WriteF32(&bbw, prev_frame->edit_camera_zoom);
BB_WriteF32(&bbw, prev_frame->look.x);
BB_WriteF32(&bbw, prev_frame->look.y);
BB_WriteString(&bbw, prev_frame->window_restore); BB_WriteString(&bbw, prev_frame->window_restore);
} }
else else
@ -639,6 +641,8 @@ void V_TickForever(WaveLaneCtx *lane)
frame->edit_camera_pos.x = BB_ReadF32(&bbr); frame->edit_camera_pos.x = BB_ReadF32(&bbr);
frame->edit_camera_pos.y = BB_ReadF32(&bbr); frame->edit_camera_pos.y = BB_ReadF32(&bbr);
frame->edit_camera_zoom = BB_ReadF32(&bbr); frame->edit_camera_zoom = BB_ReadF32(&bbr);
frame->look.x = BB_ReadF32(&bbr);
frame->look.y = BB_ReadF32(&bbr);
frame->window_restore = BB_ReadString(frame->arena, &bbr); frame->window_restore = BB_ReadString(frame->arena, &bbr);
} }
@ -1101,7 +1105,8 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Try to connect to server //- Try to connect to server
frame->desired_sim_key = NET_KeyFromString(Lit("127.0.0.1"), Lit("22121")); NET_Key local_sim_key = NET_KeyFromString(Lit("127.0.0.1"), Lit("22121"));
frame->desired_sim_key = local_sim_key;
if (!NET_MatchKey(frame->sim_key, frame->desired_sim_key)) if (!NET_MatchKey(frame->sim_key, frame->desired_sim_key))
{ {
i64 retry_rate_ns = NsFromSeconds(0.100); i64 retry_rate_ns = NsFromSeconds(0.100);
@ -1122,6 +1127,7 @@ void V_TickForever(WaveLaneCtx *lane)
P_MsgNode tmp_msg_node = Zi; P_MsgNode tmp_msg_node = Zi;
tmp_msg_node.msg.kind = P_MsgKind_Connect; tmp_msg_node.msg.kind = P_MsgKind_Connect;
tmp_msg_node.msg.data = Lit("Bro"); tmp_msg_node.msg.data = Lit("Bro");
tmp_msg_node.msg.key = V.player_key;
P_MsgList tmp_msglist = Zi; P_MsgList tmp_msglist = Zi;
tmp_msglist.count = 1; tmp_msglist.count = 1;
@ -2070,52 +2076,90 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Draw guy sprites //- Draw entities
for (P_Ent *ent = P_FirstEnt(local_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{ {
if (ent->is_guy) Struct(DrawNode)
{ {
P_Anim anim = P_AnimFromEnt(ent, local_frame->time_ns); DrawNode *next;
SPR_Sprite body = SPR_SpriteFromSheet(anim.sheet, anim.span, anim.frame_seq); P_Ent *ent;
SPR_Sprite wep = SPR_SpriteFromSheet(anim.wep_sheet, anim.span, anim.frame_seq); };
//- Compute sprite transforms i64 draw_nodes_count = 0;
Affine body_pix_to_world_af = AffineIdentity; DrawNode *first_draw_node = 0;
Affine wep_pix_to_world_af = AffineIdentity; DrawNode *last_draw_node = 0;
//- Push editor ents
if (frame->is_editing)
{
for (P_Ent *ent = P_FirstEnt(local_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{ {
Vec2 pix_scale = VEC2(1.0 / P_CellsPerMeter, 1.0 / P_CellsPerMeter); // TODO: Use entity's prefab flags to determine editor draw status
Affine ent_to_world_af = MulAffineXform(AffineIdentity, ent->xf); if (ent->is_guy_spawn)
//- Compute body transform
{ {
body_pix_to_world_af = MulAffine(body_pix_to_world_af, ent_to_world_af); DrawNode *draw_node = PushStruct(frame->arena, DrawNode);
body_pix_to_world_af = ScaleAffine(body_pix_to_world_af, pix_scale); SllQueuePush(first_draw_node, last_draw_node, draw_node);
++draw_nodes_count;
SPR_Ray anchor_ray = body.rays[SPR_RayKind_Anchor]; draw_node->ent = ent;
body_pix_to_world_af = RotateAffine(body_pix_to_world_af, InvertRot(anchor_ray.dir));
body_pix_to_world_af = TranslateAffine(body_pix_to_world_af, NegVec2(anchor_ray.pos));
} }
}
}
//- Compute weapon transform //- Push guys
{ for (P_Ent *ent = P_FirstEnt(local_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
wep_pix_to_world_af = MulAffine(wep_pix_to_world_af, ent_to_world_af); {
wep_pix_to_world_af = ScaleAffine(wep_pix_to_world_af, pix_scale); if (ent->is_guy)
{
DrawNode *draw_node = PushStruct(frame->arena, DrawNode);
SllQueuePush(first_draw_node, last_draw_node, draw_node);
++draw_nodes_count;
draw_node->ent = ent;
}
}
SPR_Ray body_anchor_ray = body.rays[SPR_RayKind_Anchor]; //- Build quads
SPR_Ray body_ap_ray = body.rays[SPR_RayKind_Ap]; for (DrawNode *draw_node = first_draw_node; draw_node; draw_node = draw_node->next)
wep_pix_to_world_af = RotateAffine(wep_pix_to_world_af, InvertRot(body_anchor_ray.dir)); {
wep_pix_to_world_af = TranslateAffine(wep_pix_to_world_af, SubVec2(body_ap_ray.pos, body_anchor_ray.pos)); P_Ent *ent = draw_node->ent;
wep_pix_to_world_af = RotateAffine(wep_pix_to_world_af, InvertRot(body_ap_ray.dir)); P_Anim anim = P_AnimFromEnt(ent, local_frame->time_ns);
SPR_Ray anchor_ray = wep.rays[SPR_RayKind_Anchor]; Vec2 pix_scale = VEC2(1.0 / P_CellsPerMeter, 1.0 / P_CellsPerMeter);
wep_pix_to_world_af = RotateAffine(wep_pix_to_world_af, anchor_ray.dir); Affine ent_to_world_af = MulAffineXform(AffineIdentity, ent->xf);
wep_pix_to_world_af = TranslateAffine(wep_pix_to_world_af, NegVec2(anchor_ray.pos));
} //- Compute body sprite
SPR_Sprite body = Zi;
Affine body_pix_to_world_af = AffineIdentity;
{
body = SPR_SpriteFromSheet(anim.sheet, anim.span, anim.frame_seq);
body_pix_to_world_af = MulAffine(body_pix_to_world_af, ent_to_world_af);
body_pix_to_world_af = ScaleAffine(body_pix_to_world_af, pix_scale);
SPR_Ray anchor_ray = body.rays[SPR_RayKind_Anchor];
body_pix_to_world_af = RotateAffine(body_pix_to_world_af, InvertRot(anchor_ray.dir));
body_pix_to_world_af = TranslateAffine(body_pix_to_world_af, NegVec2(anchor_ray.pos));
}
//- Compute weapon sprite
SPR_Sprite wep = Zi;
Affine wep_pix_to_world_af = AffineIdentity;
if (ent->is_guy)
{
wep = SPR_SpriteFromSheet(anim.wep_sheet, anim.span, anim.frame_seq);
wep_pix_to_world_af = MulAffine(wep_pix_to_world_af, ent_to_world_af);
wep_pix_to_world_af = ScaleAffine(wep_pix_to_world_af, pix_scale);
SPR_Ray body_anchor_ray = body.rays[SPR_RayKind_Anchor];
SPR_Ray body_ap_ray = body.rays[SPR_RayKind_Ap];
wep_pix_to_world_af = RotateAffine(wep_pix_to_world_af, InvertRot(body_anchor_ray.dir));
wep_pix_to_world_af = TranslateAffine(wep_pix_to_world_af, SubVec2(body_ap_ray.pos, body_anchor_ray.pos));
wep_pix_to_world_af = RotateAffine(wep_pix_to_world_af, InvertRot(body_ap_ray.dir));
SPR_Ray anchor_ray = wep.rays[SPR_RayKind_Anchor];
wep_pix_to_world_af = RotateAffine(wep_pix_to_world_af, anchor_ray.dir);
wep_pix_to_world_af = TranslateAffine(wep_pix_to_world_af, NegVec2(anchor_ray.pos));
} }
//- Push weapon quad //- Push weapon quad
@ -2146,7 +2190,6 @@ void V_TickForever(WaveLaneCtx *lane)
////////////////////////////// //////////////////////////////
//- Push test bullet particles //- Push test bullet particles