unique weapon properties

This commit is contained in:
jacob 2026-02-11 22:17:09 -06:00
parent 6602fcdccf
commit 4b207e81fe
14 changed files with 256 additions and 100 deletions

View File

@ -1157,6 +1157,20 @@ Vec2 NormRot(Vec2 r)
return r; return r;
} }
////////////////////////////////////////////////////////////
//~ Line
Vec2 IntersectLines(Vec2 a0, Vec2 b0, Vec2 a1, Vec2 b1)
{
Vec2 vab0 = SubVec2(b0, a0);
Vec2 vab1 = SubVec2(b1, a1);
Vec2 va0a1 = SubVec2(a1, a0);
f32 denom = WedgeVec2(vab0, vab1);
f32 t0 = WedgeVec2(va0a1, vab1) / denom;
Vec2 result = AddVec2(a0, MulVec2(vab0, t0));
return result;
}
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Spring //~ Spring

View File

@ -521,6 +521,11 @@ Vec2 MulXformVec2(Xform xf, Vec2 v);
Xform NormXform(Xform xf); Xform NormXform(Xform xf);
Vec2 NormRot(Vec2 r); Vec2 NormRot(Vec2 r);
////////////////////////////////////////////////////////////
//~ Line
Vec2 IntersectLines(Vec2 a0, Vec2 b0, Vec2 a1, Vec2 b1);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Spring //~ Spring

View File

@ -244,29 +244,42 @@ P_Shape P_WorldShapeFromEnt(P_Ent *ent)
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Animation helpers //~ Animation helpers
P_Anim P_AnimFromEnt(P_Ent *ent, i64 time_ns) P_Anim P_AnimFromEnt(P_Frame *frame, P_Ent *ent)
{ {
P_Anim result = Zi; P_Anim result = Zi;
P_Ent *wep = P_EntFromKey(frame, ent->weapon);
// TODO: Determine animation dynamically // TODO: Determine animation dynamically
i64 animation_rate_ns = NsFromSeconds(0.100); i64 animation_rate_ns = NsFromSeconds(0.100);
{ {
i64 walk_duration_ns = time_ns; i64 walk_duration_ns = frame->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("test"));
result.span = SPR_SpanKeyFromName(Lit("walk")); result.span = SPR_SpanKeyFromName(Lit("walk"));
} }
// TODO: Use prefab lookup // TODO: Use prefab lookup
if (ent->is_guy) if (ent->is_guy)
{ {
result.sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("sprite/bla3.ase"))); result.sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("guy/guy.ase")));
result.wep_sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("sprite/raah.ase")));
} }
else if (ent->is_guy_spawn)
if (ent->is_guy_spawn)
{ {
result.sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("prefab/GuySpawn.ase"))); result.sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("prefab/GuySpawn.ase")));
} }
if (wep->is_uzi)
{
result.wep_sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("wep/uzi.ase")));
}
if (wep->is_launcher)
{
result.wep_sheet = SPR_SheetKeyFromResource(ResourceKeyFromStore(&P_Resources, Lit("wep/launcher.ase")));
}
return result; return result;
} }
@ -374,7 +387,7 @@ P_CollisionResult P_CollisionResultFromShapes(P_Shape shape0, P_Shape shape1)
P_CollisionResult result = Zi; P_CollisionResult result = Zi;
TempArena scratch = BeginScratchNoConflict(); TempArena scratch = BeginScratchNoConflict();
f32 tolerance = 0.00005f; // How close can non-overlapping shapes be before collision is considered f32 tolerance = 0.05f; // How close can non-overlapping shapes be before collision is considered
f32 min_unique_pt_dist_sq = (0.001f * 0.001f); // NOTE: Should always be less than tolerance, since colliding = 1 if origin is within this distance. f32 min_unique_pt_dist_sq = (0.001f * 0.001f); // NOTE: Should always be less than tolerance, since colliding = 1 if origin is within this distance.
u32 max_iterations = 64; // To prevent extremely large prototypes when origin is in exact center of rounded feature u32 max_iterations = 64; // To prevent extremely large prototypes when origin is in exact center of rounded feature
@ -1141,6 +1154,16 @@ P_Ent *P_EntFromKey(P_Frame *frame, P_EntKey key)
return result; return result;
} }
P_Ent *P_SourcePlayerFromEnt(P_Frame *frame, P_Ent *ent)
{
P_Ent *result = ent;
while (!P_IsEntNil(result) && !result->is_player)
{
result = P_EntFromKey(frame, result->source);
}
return result;
}
P_Constraint *P_ConstraintFromKey(P_Frame *frame, P_ConstraintKey key) P_Constraint *P_ConstraintFromKey(P_Frame *frame, P_ConstraintKey key)
{ {
P_Constraint *result = &P_NilConstraint; P_Constraint *result = &P_NilConstraint;
@ -1237,6 +1260,7 @@ P_Space P_SpaceFromEnts(Arena *arena, P_Frame *frame)
aabb = IntersectRng2(aabb, space_aabb); aabb = IntersectRng2(aabb, space_aabb);
if (!IsRng2Empty(aabb)) if (!IsRng2Empty(aabb))
{ {
++space.unique_entries_count;
for (i32 y = aabb.p0.y; y < aabb.p1.y; ++y) for (i32 y = aabb.p0.y; y < aabb.p1.y; ++y)
{ {
for (i32 x = aabb.p0.x; x < aabb.p1.x; ++x) for (i32 x = aabb.p0.x; x < aabb.p1.x; ++x)
@ -1247,6 +1271,7 @@ P_Space P_SpaceFromEnts(Arena *arena, P_Frame *frame)
SllStackPush(cell->first, entry_node); SllStackPush(cell->first, entry_node);
entry_node->entry.shape = shape; entry_node->entry.shape = shape;
entry_node->entry.shape_id = ent->key.v; entry_node->entry.shape_id = ent->key.v;
++space.entries_count;
} }
} }
} }
@ -1401,9 +1426,7 @@ P_Space P_SpaceFromWalls(Arena *arena, P_Frame *frame)
id = MixU64s(id, (((u64)wall->start.x) | ((u64)wall->start.y << 32))); id = MixU64s(id, (((u64)wall->start.x) | ((u64)wall->start.y << 32)));
id = MixU64s(id, (((u64)wall->end.x) | ((u64)wall->end.y << 32))); id = MixU64s(id, (((u64)wall->end.x) | ((u64)wall->end.y << 32)));
// FIXME: Remove this ++space.unique_entries_count;
// TrueRand(StringFromStruct(&id));
for (i32 y = aabb.p0.y; y < aabb.p1.y; ++y) for (i32 y = aabb.p0.y; y < aabb.p1.y; ++y)
{ {
for (i32 x = aabb.p0.x; x < aabb.p1.x; ++x) for (i32 x = aabb.p0.x; x < aabb.p1.x; ++x)
@ -1416,6 +1439,7 @@ P_Space P_SpaceFromWalls(Arena *arena, P_Frame *frame)
entry_node->entry.shape_id = id; entry_node->entry.shape_id = id;
entry_node->entry.dir.x = (wall->dir == WallDir_Right) + ((wall->dir == WallDir_Left) * -1); entry_node->entry.dir.x = (wall->dir == WallDir_Right) + ((wall->dir == WallDir_Left) * -1);
entry_node->entry.dir.y = (wall->dir == WallDir_Down) + ((wall->dir == WallDir_Up) * -1); entry_node->entry.dir.y = (wall->dir == WallDir_Down) + ((wall->dir == WallDir_Up) * -1);
++space.entries_count;
} }
} }
} }
@ -1436,6 +1460,7 @@ P_SpaceCell P_SpaceCellFromPos(P_Space *space, Vec2 pos)
return result; return result;
} }
// TODO: Parameterized bin-count
void P_UniqueSpaceEntriesFromRay(Arena *arena, P_SpaceEntryList *result, i32 spaces_count, P_Space **spaces, Vec2 ray_p0, Vec2 ray_p1) void P_UniqueSpaceEntriesFromRay(Arena *arena, P_SpaceEntryList *result, i32 spaces_count, P_Space **spaces, Vec2 ray_p0, Vec2 ray_p1)
{ {
TempArena scratch = BeginScratch(arena); TempArena scratch = BeginScratch(arena);
@ -1444,6 +1469,8 @@ void P_UniqueSpaceEntriesFromRay(Arena *arena, P_SpaceEntryList *result, i32 spa
u64 bins_count = 256; u64 bins_count = 256;
BinEntry **bins = PushStructs(scratch.arena, BinEntry *, bins_count); BinEntry **bins = PushStructs(scratch.arena, BinEntry *, bins_count);
P_DebugDrawLine(ray_p0, ray_p1, Color_Red);
// TODO: Clip to avoid unnecessary iterations outside of world bounds // TODO: Clip to avoid unnecessary iterations outside of world bounds
ray_p0 = AddVec2(ray_p0, VEC2(P_WorldPitch / 2.0, P_WorldPitch / 2.0)); ray_p0 = AddVec2(ray_p0, VEC2(P_WorldPitch / 2.0, P_WorldPitch / 2.0));
ray_p1 = AddVec2(ray_p1, VEC2(P_WorldPitch / 2.0, P_WorldPitch / 2.0)); ray_p1 = AddVec2(ray_p1, VEC2(P_WorldPitch / 2.0, P_WorldPitch / 2.0));
@ -1470,7 +1497,7 @@ void P_UniqueSpaceEntriesFromRay(Arena *arena, P_SpaceEntryList *result, i32 spa
{ {
Vec2 world_pos = Vec2FromVec(grid_pos); Vec2 world_pos = Vec2FromVec(grid_pos);
world_pos = SubVec2(world_pos, VEC2(P_WorldPitch / 2.0, P_WorldPitch / 2.0)); world_pos = SubVec2(world_pos, VEC2(P_WorldPitch / 2.0, P_WorldPitch / 2.0));
P_DebugDrawRect(RNG2(world_pos, AddVec2(world_pos, VEC2(1, 1))), Color_Cyan); P_DebugDrawRect(RNG2(world_pos, AddVec2(world_pos, VEC2(1, 1))), VEC4(0.85, 0.5, 0.75, 0.75));
} }
for (i32 space_idx = 0; space_idx < spaces_count; ++space_idx) for (i32 space_idx = 0; space_idx < spaces_count; ++space_idx)
{ {
@ -1616,6 +1643,63 @@ void P_DebugDrawFrame(P_Frame *frame)
{ {
if (P_tl.debug_draw_enabled) if (P_tl.debug_draw_enabled)
{ {
P_World *world = frame->world;
//////////////////////////////
//- Draw walls
{
TempArena scratch = BeginScratchNoConflict();
{
Struct(BinEntry) { BinEntry *next; u64 shape_id; };
u64 bins_count = NextPow2U64(world->walls_space.unique_entries_count * 4);
BinEntry **bins = PushStructs(scratch.arena, BinEntry *, bins_count);
i64 cells_count = world->walls_space.dims.x * world->walls_space.dims.y;
for (i64 cell_idx = 0; cell_idx < cells_count; ++cell_idx)
{
P_SpaceCell *cell = &world->walls_space.cells[cell_idx];
for (P_SpaceEntryNode *space_entry_node = cell->first; space_entry_node; space_entry_node = space_entry_node->next)
{
P_SpaceEntry *space_entry = &space_entry_node->entry;
BinEntry **bin = &bins[space_entry->shape_id % bins_count];
BinEntry *bin_entry = *bin;
for (; bin_entry; bin_entry = bin_entry->next)
{
if (bin_entry->shape_id == space_entry->shape_id)
{
break;
}
}
if (!bin_entry)
{
// Draw unique wall
{
bin_entry = PushStruct(scratch.arena, BinEntry);
bin_entry->shape_id = space_entry->shape_id;
SllStackPush(*bin, bin_entry);
}
{
Vec4 color = VEC4(0.5, 0.75, 0.5, 0.75);
P_DebugDrawShape(space_entry->shape, color);
}
}
}
}
}
EndScratch(scratch);
}
for (i32 cell_y = 0; cell_y < world->walls_space.dims.y; ++cell_y)
{
for (i32 cell_x = 0; cell_x < world->walls_space.dims.x; ++cell_x)
{
}
}
//////////////////////////////
//- Draw entities
for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{ {
P_Shape world_shape = P_WorldShapeFromEnt(ent); P_Shape world_shape = P_WorldShapeFromEnt(ent);
@ -1651,6 +1735,35 @@ void P_DebugDrawFrame(P_Frame *frame)
P_DebugDrawLine(p0, p1, color); P_DebugDrawLine(p0, p1, color);
} }
} }
//////////////////////////////
//- Draw constraints
for (P_Constraint *constraint = P_FirstConstraint(frame); !P_IsConstraintNil(constraint); constraint = P_NextConstraint(constraint))
{
P_Ent *ent0 = P_EntFromKey(frame, constraint->ent0);
P_Ent *ent1 = P_EntFromKey(frame, constraint->ent1);
Vec2 normal = constraint->normal;
Vec2 center0 = constraint->static_center0;
Vec2 center1 = constraint->static_center1;
if (!P_IsEntNil(ent0))
{
center0 = P_WorldShapeFromEnt(ent0).center_of_mass;
}
if (!P_IsEntNil(ent1))
{
center1 = P_WorldShapeFromEnt(ent1).center_of_mass;
}
for (i32 contact_idx = 0; contact_idx < constraint->points_count; ++contact_idx)
{
P_ContactPoint *contact = &constraint->points[contact_idx];
Vec2 p0 = AddVec2(center0, contact->vcp0);
Vec2 p1 = AddVec2(center1, contact->vcp1);
P_DebugDrawPoint(p0, Color_Cyan);
P_DebugDrawLine(p0, AddVec2(p0, normal), Color_White);
}
}
} }
} }
@ -1942,20 +2055,26 @@ void P_StepFrame(P_Frame *frame)
{ {
i64 ents_to_prune_count = 0; i64 ents_to_prune_count = 0;
P_Ent **ents_to_prune = PushStructsNoZero(scratch.arena, P_Ent *, frame->ents_count); P_EntKey *ents_to_prune = PushStructsNoZero(scratch.arena, P_EntKey, frame->ents_count);
for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent)) for (P_Ent *ent = P_FirstEnt(frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
{ {
if (ent->exists <= 0) if (ent->exists <= 0)
{ {
ents_to_prune[ents_to_prune_count] = ent; ents_to_prune[ents_to_prune_count++] = ent->key;
ents_to_prune_count += 1; if (!P_IsEntKeyNil(ent->weapon))
{
ents_to_prune[ents_to_prune_count++] = ent->weapon;
}
} }
} }
for (i64 prune_idx = 0; prune_idx < ents_to_prune_count; ++prune_idx) for (i64 prune_idx = 0; prune_idx < ents_to_prune_count; ++prune_idx)
{ {
// FIXME: Ensure sure prunes are received by player // FIXME: Ensure sure prunes are received by clients
P_Ent *ent = ents_to_prune[prune_idx]; P_EntKey key = ents_to_prune[prune_idx];
P_Ent *ent = P_EntFromKey(frame, key);
if (!P_IsEntNil(ent))
{
P_EntBin *bin = &frame->ent_bins[ent->key.v % frame->ent_bins_count]; P_EntBin *bin = &frame->ent_bins[ent->key.v % frame->ent_bins_count];
DllQueueRemoveNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin); DllQueueRemoveNP(bin->first, bin->last, ent, next_in_bin, prev_in_bin);
DllQueueRemoveNPZ(&P_NilEnt, frame->first_ent, frame->last_ent, ent, next, prev); DllQueueRemoveNPZ(&P_NilEnt, frame->first_ent, frame->last_ent, ent, next, prev);
@ -1963,6 +2082,7 @@ void P_StepFrame(P_Frame *frame)
SllStackPush(world->first_free_ent, ent); SllStackPush(world->first_free_ent, ent);
} }
} }
}
////////////////////////////// //////////////////////////////
//- Query ents //- Query ents
@ -1976,7 +2096,7 @@ void P_StepFrame(P_Frame *frame)
if (!is_predicting) if (!is_predicting)
{ {
P_EntList new_guys = Zi; P_EntList queued_ents = Zi;
for (P_Ent *player = P_FirstEnt(frame); !P_IsEntNil(player); player = P_NextEnt(player)) for (P_Ent *player = P_FirstEnt(frame); !P_IsEntNil(player); player = P_NextEnt(player))
{ {
if (player->is_player) if (player->is_player)
@ -1988,11 +2108,10 @@ void P_StepFrame(P_Frame *frame)
P_Ent *guy = P_EntFromKey(frame, player->guy); P_Ent *guy = P_EntFromKey(frame, player->guy);
if (P_IsEntNil(guy)) if (P_IsEntNil(guy))
{ {
guy = P_PushTempEnt(scratch.arena, &new_guys); guy = P_PushTempEnt(scratch.arena, &queued_ents);
guy->is_guy = 1; guy->is_guy = 1;
guy->has_weapon = 1;
guy->key = player->guy; guy->key = player->guy;
guy->player = player->key; guy->source = player->key;
//- Choose guy spawn point //- Choose guy spawn point
{ {
@ -2065,9 +2184,37 @@ void P_StepFrame(P_Frame *frame)
} }
} }
} }
P_SpawnEntsFromList(frame, new_guys); P_SpawnEntsFromList(frame, queued_ents);
} }
//////////////////////////////
//- Equip spawned guys
// TODO: Remove this (weapon testing)
{
P_EntList queued_ents = Zi;
for (P_Ent *guy = P_FirstEnt(frame); !P_IsEntNil(guy); guy = P_NextEnt(guy))
{
if (guy->is_guy && guy->created_at_tick == frame->tick)
{
P_Ent *weapon = P_EntFromKey(frame, guy->weapon);
if (!weapon->is_weapon)
{
weapon = P_PushTempEnt(scratch.arena, &queued_ents);
weapon->is_weapon = 1;
weapon->key = P_RandEntKey();
weapon->source = guy->key;
// weapon->is_uzi = 1;
weapon->is_launcher = 1;
guy->weapon = weapon->key;
}
}
}
P_SpawnEntsFromList(frame, queued_ents);
}
////////////////////////////// //////////////////////////////
//- Update guy controls from player controls //- Update guy controls from player controls
@ -2243,7 +2390,8 @@ void P_StepFrame(P_Frame *frame)
if (!skip_collision && !IsVec2Zero(space_entry->dir)) if (!skip_collision && !IsVec2Zero(space_entry->dir))
{ {
// Skip collision if normal violates one-way direction // Skip collision if normal violates one-way direction
f32 threshold = 0.5; // f32 threshold = 0.5;
f32 threshold = 0;
skip_collision = DotVec2(space_entry->dir, collision.collision_normal) >= threshold; skip_collision = DotVec2(space_entry->dir, collision.collision_normal) >= threshold;
} }
@ -2351,21 +2499,6 @@ void P_StepFrame(P_Frame *frame)
contact->vcp0 = vcp0; contact->vcp0 = vcp0;
contact->vcp1 = vcp1; contact->vcp1 = vcp1;
contact->starting_separation = collision_point.separation; contact->starting_separation = collision_point.separation;
// Debug draw
// {
// // P_Ent *ent0 = P_EntFromKey(frame, constraint->ent0);
// // P_Ent *ent1 = P_EntFromKey(frame, constraint->ent1);
// Vec2 normal = constraint->normal;
// Vec2 center0 = Zi;
// Vec2 center1 = Zi;
// if (!P_IsEntNil(ent0)) center0 = P_WorldShapeFromEnt(ent0).center_of_mass;
// if (!P_IsEntNil(ent1)) center1 = P_WorldShapeFromEnt(ent1).center_of_mass;
// Vec2 p0 = AddVec2(center0, vcp0);
// Vec2 p1 = AddVec2(center1, vcp1);
// P_DebugDrawPoint(p0, Color_Cyan);
// P_DebugDrawLine(p0, AddVec2(p0, normal), Color_White);
// }
} }
} }
} }
@ -2711,8 +2844,9 @@ void P_StepFrame(P_Frame *frame)
P_EntList bullets_to_spawn = Zi; P_EntList bullets_to_spawn = Zi;
for (P_Ent *firer = P_FirstEnt(frame); !P_IsEntNil(firer); firer = P_NextEnt(firer)) for (P_Ent *firer = P_FirstEnt(frame); !P_IsEntNil(firer); firer = P_NextEnt(firer))
{ {
if (firer->has_weapon && firer->control.fire_held) P_Ent *weapon = P_EntFromKey(frame, firer->weapon);
// if (firer->has_weapon && firer->control.fire_presses) if (weapon->is_weapon && firer->control.fire_held)
// if (weapon->is_weapon && firer->control.fire_presses)
{ {
// i64 fire_delta_ns = frame->time_ns - firer->last_fire_ns; // i64 fire_delta_ns = frame->time_ns - firer->last_fire_ns;
@ -2736,7 +2870,7 @@ void P_StepFrame(P_Frame *frame)
if (can_fire) if (can_fire)
{ {
Vec2 look = firer->control.look; Vec2 look = firer->control.look;
P_Anim anim = P_AnimFromEnt(firer, frame->time_ns); P_Anim anim = P_AnimFromEnt(frame, firer);
SPR_Sprite body = SPR_SpriteFromSheet(anim.sheet, anim.span, anim.frame_seq); SPR_Sprite body = SPR_SpriteFromSheet(anim.sheet, anim.span, anim.frame_seq);
SPR_Sprite wep = SPR_SpriteFromSheet(anim.wep_sheet, anim.span, anim.frame_seq); SPR_Sprite wep = SPR_SpriteFromSheet(anim.wep_sheet, anim.span, anim.frame_seq);
@ -2829,7 +2963,8 @@ void P_StepFrame(P_Frame *frame)
bullet->bullet_base1 = fire_base1; bullet->bullet_base1 = fire_base1;
bullet->bullet_start = fire_pos; bullet->bullet_start = fire_pos;
bullet->bullet_end = AddVec2(bullet->bullet_start, MulVec2(dir, speed)); bullet->bullet_end = AddVec2(bullet->bullet_start, MulVec2(dir, speed));
bullet->bullet_firer = firer->key; bullet->source = weapon->key;
bullet->damage_attribution = firer->source;
} }
} }
firer->last_fire_ns = frame->time_ns; firer->last_fire_ns = frame->time_ns;
@ -2848,6 +2983,10 @@ void P_StepFrame(P_Frame *frame)
{ {
if (bullet->is_bullet) if (bullet->is_bullet)
{ {
P_Ent *bullet_weapon = P_EntFromKey(frame, bullet->source);
P_Ent *bullet_guy = P_EntFromKey(frame, bullet_weapon->source);
P_Ent *bullet_damager = P_EntFromKey(frame, bullet->damage_attribution);
bullet->has_hit = 0; bullet->has_hit = 0;
Struct(BulletPath) Struct(BulletPath)
@ -2888,12 +3027,11 @@ void P_StepFrame(P_Frame *frame)
path->end = bullet->bullet_end; path->end = bullet->bullet_end;
} }
P_DebugDrawLine(bullet->bullet_start, bullet->bullet_end, Color_Red);
P_EntKey victim_key = Zi; P_EntKey victim_key = Zi;
P_RaycastResult victim_raycast = Zi; P_RaycastResult victim_raycast = Zi;
b32 hit = 0;
{ {
for (BulletPath *path = first_bullet_path; path; path = path->next) for (BulletPath *path = first_bullet_path; path && !hit; path = path->next)
{ {
Vec2 path_dir = SubVec2(path->end, path->start); Vec2 path_dir = SubVec2(path->end, path->start);
P_Space *cast_spaces[] = { P_Space *cast_spaces[] = {
@ -2914,7 +3052,7 @@ void P_StepFrame(P_Frame *frame)
{ {
P_SpaceEntry *entry = &entry_node->entry; P_SpaceEntry *entry = &entry_node->entry;
P_EntKey potential_victim_key = (P_EntKey) { .v = entry->shape_id }; P_EntKey potential_victim_key = (P_EntKey) { .v = entry->shape_id };
if (!P_MatchEntKey(potential_victim_key, bullet->bullet_firer)) if (!P_MatchEntKey(potential_victim_key, bullet_guy->key) || P_IsEntKeyNil(bullet_guy->key))
{ {
P_Shape potential_victim_shape = entry->shape; P_Shape potential_victim_shape = entry->shape;
P_RaycastResult entrance_raycast = P_RaycastShape(potential_victim_shape, path->start, path_dir); P_RaycastResult entrance_raycast = P_RaycastShape(potential_victim_shape, path->start, path_dir);
@ -2933,23 +3071,18 @@ void P_StepFrame(P_Frame *frame)
closest_len_sq = len_sq; closest_len_sq = len_sq;
victim_key = potential_victim_key; victim_key = potential_victim_key;
victim_raycast = entrance_raycast; victim_raycast = entrance_raycast;
hit = 1;
} }
} }
} }
} }
} }
if (!P_IsEntKeyNil(victim_key))
{
// No need to check subsequent paths if this path hit
break;
} }
} }
}
P_Ent *victim = P_EntFromKey(frame, victim_key); P_Ent *victim = P_EntFromKey(frame, victim_key);
// TODO: Truncate bullet trail // TODO: Truncate bullet trail
if (!P_IsEntKeyNil(victim_key)) if (hit)
{ {
bullet->has_hit = 1; bullet->has_hit = 1;
bullet->hit_entry = victim_raycast.p; bullet->hit_entry = victim_raycast.p;
@ -2969,18 +3102,9 @@ void P_StepFrame(P_Frame *frame)
// TODO: Remove this // TODO: Remove this
if (!P_IsEntNil(victim)) if (!P_IsEntNil(victim))
{ {
P_Ent *damager = bullet; if (bullet_damager->is_player)
if (!damager->is_player)
{ {
damager = P_EntFromKey(frame, damager->bullet_firer); victim->damage_attribution = bullet_damager->key;
}
if (!damager->is_player)
{
damager = P_EntFromKey(frame, damager->player);
}
if (damager->is_player)
{
victim->last_damaging_player = damager->key;
} }
victim->health -= 0.25; victim->health -= 0.25;
} }
@ -3011,8 +3135,8 @@ void P_StepFrame(P_Frame *frame)
P_Ent *old_guy = P_EntFromKey(prev_frame, guy->key); P_Ent *old_guy = P_EntFromKey(prev_frame, guy->key);
if (old_guy->health > 0) if (old_guy->health > 0)
{ {
P_Ent *player = P_EntFromKey(frame, guy->player); P_Ent *player = P_EntFromKey(frame, guy->source);
P_Ent *killer = P_EntFromKey(frame, guy->last_damaging_player); P_Ent *killer = P_EntFromKey(frame, guy->damage_attribution);
if (player->is_player) if (player->is_player)
{ {
player->deaths += 1; player->deaths += 1;

View File

@ -122,13 +122,8 @@ Struct(P_Ent)
Xform xf; Xform xf;
// TODO: Remove this (weapon testing)
i64 last_fire_ns;
b32 has_weapon;
//- Bullet / hit //- Bullet / hit
P_EntKey bullet_firer;
b32 is_bullet; b32 is_bullet;
Vec2 bullet_base0; Vec2 bullet_base0;
Vec2 bullet_base1; Vec2 bullet_base1;
@ -140,10 +135,18 @@ Struct(P_Ent)
Vec2 hit_entry_normal; Vec2 hit_entry_normal;
P_MaterialKind hit_material; P_MaterialKind hit_material;
//- Player / guy / weapon / bullet
P_EntKey source;
//- Player / Guy //- Player / Guy
P_Control control; P_Control control;
//- Guy / Bullet
P_EntKey damage_attribution;
//- Player //- Player
P_EntKey spawn; P_EntKey spawn;
@ -160,8 +163,15 @@ Struct(P_Ent)
//- Guy //- Guy
P_EntKey player; P_EntKey weapon;
P_EntKey last_damaging_player;
//- Weapon
b32 is_weapon;
b32 is_uzi;
b32 is_launcher;
i64 last_fire_ns;
//- Spawn //- Spawn
@ -293,6 +303,8 @@ Struct(P_SpaceCell)
Struct(P_Space) Struct(P_Space)
{ {
i64 entries_count;
i64 unique_entries_count;
Vec2I32 dims; Vec2I32 dims;
P_SpaceCell *cells; P_SpaceCell *cells;
}; };
@ -603,7 +615,7 @@ P_Shape P_WorldShapeFromEnt(P_Ent *ent);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Animation helpers //~ Animation helpers
P_Anim P_AnimFromEnt(P_Ent *ent, i64 time_ns); P_Anim P_AnimFromEnt(P_Frame *frame, P_Ent *ent);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
//~ Collision //~ Collision
@ -623,6 +635,7 @@ Vec2 P_EdgePointFromShape(P_Shape shape, Vec2 dir);
//~ Lookup helpers //~ Lookup helpers
P_Ent *P_EntFromKey(P_Frame *frame, P_EntKey key); P_Ent *P_EntFromKey(P_Frame *frame, P_EntKey key);
P_Ent *P_SourcePlayerFromEnt(P_Frame *frame, P_Ent *ent);
P_Constraint *P_ConstraintFromKey(P_Frame *frame, P_ConstraintKey key); P_Constraint *P_ConstraintFromKey(P_Frame *frame, P_ConstraintKey key);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

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

Binary file not shown.

BIN
src/pp/pp_res/sprite/bla.ase (Stored with Git LFS)

Binary file not shown.

BIN
src/pp/pp_res/sprite/bla2.ase (Stored with Git LFS)

Binary file not shown.

BIN
src/pp/pp_res/sprite/bla3.ase (Stored with Git LFS)

Binary file not shown.

BIN
src/pp/pp_res/wep/launcher.ase (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -591,8 +591,7 @@ void S_TickForever(WaveLaneCtx *lane)
guy = P_PushTempEnt(frame_arena, &ents); guy = P_PushTempEnt(frame_arena, &ents);
guy->key = P_RandEntKey(); guy->key = P_RandEntKey();
guy->is_guy = 1; guy->is_guy = 1;
guy->has_weapon = 1; guy->source = bot->key;
guy->player = bot->key;
bot->guy = guy->key; bot->guy = guy->key;
} }
guy->xf = msg->xf; guy->xf = msg->xf;

View File

@ -47,9 +47,9 @@ String P_PackWorld(Arena *arena, P_World *src_world)
{ {
result.len += PushString(arena, Lit(" bot\n")).len; result.len += PushString(arena, Lit(" bot\n")).len;
} }
if (ent->has_weapon) if (ent->is_weapon)
{ {
result.len += PushString(arena, Lit(" has_weapon\n")).len; result.len += PushString(arena, Lit(" is_weapon\n")).len;
} }
if (ent->is_bullet) if (ent->is_bullet)
{ {
@ -70,7 +70,7 @@ 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, " 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, " 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, " source: \"0x%F\"\n", FmtHex(ent->source.v)).len;
result.len += StringF(arena, " kills: \"%F\"\n", FmtFloat(ent->kills)).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, " 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 += StringF(arena, " text: \"%F\"\n", FmtString(CR_SanitizeString(scratch.arena, STRING(ent->string_len, ent->string_text)))).len;
@ -164,9 +164,9 @@ P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed)
{ {
ent->is_bot = 1; ent->is_bot = 1;
} }
if (MatchString(prop->value, Lit("has_weapon"))) if (MatchString(prop->value, Lit("weapon")))
{ {
ent->has_weapon = 1; ent->is_weapon = 1;
} }
if (MatchString(prop->value, Lit("bullet"))) if (MatchString(prop->value, Lit("bullet")))
{ {
@ -214,9 +214,9 @@ P_UnpackedWorld P_UnpackWorld(Arena *arena, String packed)
{ {
ent->guy.v = CR_IntFromString(attr->value); ent->guy.v = CR_IntFromString(attr->value);
} }
if (MatchString(attr->name, Lit("player"))) if (MatchString(attr->name, Lit("source")))
{ {
ent->player.v = CR_IntFromString(attr->value); ent->source.v = CR_IntFromString(attr->value);
} }
if (MatchString(attr->name, Lit("kills"))) if (MatchString(attr->name, Lit("kills")))
{ {

View File

@ -842,7 +842,7 @@ void V_TickForever(WaveLaneCtx *lane)
//- Compute movement & look //- Compute movement & look
f32 max_look_radius = 5; f32 max_look_radius = 5;
f32 min_look_radius = 0.01; f32 min_look_radius = 0.025;
frame->is_looking = !frame->is_editing && !frame->palette.is_showing && !frame->held_buttons[Button_Alt]; frame->is_looking = !frame->is_editing && !frame->palette.is_showing && !frame->held_buttons[Button_Alt];
frame->is_moving = !frame->is_editing; frame->is_moving = !frame->is_editing;
@ -964,6 +964,7 @@ void V_TickForever(WaveLaneCtx *lane)
target_camera_pos.x = ClampF32(target_camera_pos.x, -world_pitch / 2, world_pitch / 2); target_camera_pos.x = ClampF32(target_camera_pos.x, -world_pitch / 2, world_pitch / 2);
target_camera_pos.y = ClampF32(target_camera_pos.y, -world_pitch / 2, world_pitch / 2); target_camera_pos.y = ClampF32(target_camera_pos.y, -world_pitch / 2, world_pitch / 2);
target_camera_zoom = ClampF32(target_camera_zoom, min_zoom, max_zoom); target_camera_zoom = ClampF32(target_camera_zoom, min_zoom, max_zoom);
// Interpolate camera // Interpolate camera
{ {
if (prev_frame->tick == 0) if (prev_frame->tick == 0)
@ -1825,7 +1826,7 @@ void V_TickForever(WaveLaneCtx *lane)
{ {
P_Ent *ent = local_guy; P_Ent *ent = local_guy;
P_Anim anim = P_AnimFromEnt(ent, local_frame->time_ns); P_Anim anim = P_AnimFromEnt(local_frame, ent);
SPR_Sprite body = SPR_SpriteFromSheet(anim.sheet, anim.span, anim.frame_seq); SPR_Sprite body = SPR_SpriteFromSheet(anim.sheet, anim.span, anim.frame_seq);
SPR_Sprite wep = SPR_SpriteFromSheet(anim.wep_sheet, anim.span, anim.frame_seq); SPR_Sprite wep = SPR_SpriteFromSheet(anim.wep_sheet, anim.span, anim.frame_seq);
@ -1890,8 +1891,11 @@ void V_TickForever(WaveLaneCtx *lane)
Vec2 desired_chamber_pos = MulVec2(PerpVec2(desired_fire_dir), WedgeVec2(desired_fire_dir, SubVec2(desired_fire_pos, desired_hold_pos))); Vec2 desired_chamber_pos = MulVec2(PerpVec2(desired_fire_dir), WedgeVec2(desired_fire_dir, SubVec2(desired_fire_pos, desired_hold_pos)));
desired_chamber_pos = AddVec2(desired_chamber_pos, desired_hold_pos); desired_chamber_pos = AddVec2(desired_chamber_pos, desired_hold_pos);
Vec2 desired_crosshair_base = MulVec2(PerpVec2(NegVec2(desired_ent_to_world_af.bx)), WedgeVec2(desired_ent_to_world_af.bx, SubVec2(desired_ent_to_world_af.og, desired_chamber_pos))); Vec2 desired_crosshair_base = IntersectLines(
desired_crosshair_base = AddVec2(desired_crosshair_base, desired_ent_to_world_af.og); desired_ent_to_world_af.og, AddVec2(desired_ent_to_world_af.og, desired_ent_to_world_af.by),
desired_fire_pos, desired_chamber_pos
);
desired_crosshair_base = SubVec2(desired_crosshair_base, MulVec2(NormVec2(desired_fire_dir), min_look_radius));
frame->world_guy_origin = desired_ent_to_world_af.og; frame->world_guy_origin = desired_ent_to_world_af.og;
frame->world_crosshair_base = desired_crosshair_base; frame->world_crosshair_base = desired_crosshair_base;
@ -2072,7 +2076,7 @@ void V_TickForever(WaveLaneCtx *lane)
for (DrawNode *draw_node = first_draw_node; draw_node; draw_node = draw_node->next) for (DrawNode *draw_node = first_draw_node; draw_node; draw_node = draw_node->next)
{ {
P_Ent *ent = draw_node->ent; P_Ent *ent = draw_node->ent;
P_Anim anim = P_AnimFromEnt(ent, local_frame->time_ns); P_Anim anim = P_AnimFromEnt(local_frame, ent);
Vec2 pix_scale = VEC2(1.0 / P_CellsPerMeter, 1.0 / P_CellsPerMeter); Vec2 pix_scale = VEC2(1.0 / P_CellsPerMeter, 1.0 / P_CellsPerMeter);
Affine ent_to_world_af = MulAffineXform(AffineIdentity, ent->xf); Affine ent_to_world_af = MulAffineXform(AffineIdentity, ent->xf);