distinct bullet hit entities
This commit is contained in:
parent
a76cfc5dfd
commit
18ad1fb69e
@ -145,65 +145,76 @@ f64 CR_FloatFromString(String str)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find decimal place
|
String whole_str = STRING(str.len - whole_start_idx, &str.text[whole_start_idx]);
|
||||||
u64 frac_start_idx = whole_start_idx;
|
if (MatchString(whole_str, Lit("inf")))
|
||||||
for (; ok && frac_start_idx < str.len; ++frac_start_idx)
|
|
||||||
{
|
{
|
||||||
u8 c = str.text[frac_start_idx];
|
result = Inf * sign;
|
||||||
if (c == '.')
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else if (MatchString(whole_str, Lit("NaN")))
|
||||||
// Parse whole part
|
|
||||||
u64 whole_part = 0;
|
|
||||||
for (u64 char_idx = whole_start_idx; ok && char_idx < frac_start_idx; ++char_idx)
|
|
||||||
{
|
{
|
||||||
u8 c = str.text[char_idx];
|
result = Nan;
|
||||||
if (c >= '0' && c <= '9')
|
|
||||||
{
|
|
||||||
u8 digit = c - '0';
|
|
||||||
whole_part += digit * PowU64(10, frac_start_idx - (char_idx + 1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ok = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse frac part
|
|
||||||
u64 frac_part = 0;
|
|
||||||
for (u64 char_idx = frac_start_idx + 1; ok && char_idx < str.len; ++char_idx)
|
|
||||||
{
|
|
||||||
u8 c = str.text[char_idx];
|
|
||||||
if (c >= '0' && c <= '9')
|
|
||||||
{
|
|
||||||
u8 digit = c - '0';
|
|
||||||
frac_part += digit * PowU64(10, str.len - (char_idx + 1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ok = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ok)
|
|
||||||
{
|
|
||||||
if (frac_part != 0)
|
|
||||||
{
|
|
||||||
result = ((f64)whole_part + ((f64)frac_part / PowU64(10, str.len - (frac_start_idx + 1)))) * sign;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = (f64)whole_part * sign;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = 0;
|
// Find decimal place
|
||||||
}
|
u64 frac_start_idx = whole_start_idx;
|
||||||
|
for (; ok && frac_start_idx < str.len; ++frac_start_idx)
|
||||||
|
{
|
||||||
|
u8 c = str.text[frac_start_idx];
|
||||||
|
if (c == '.')
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse whole part
|
||||||
|
u64 whole_part = 0;
|
||||||
|
for (u64 char_idx = whole_start_idx; ok && char_idx < frac_start_idx; ++char_idx)
|
||||||
|
{
|
||||||
|
u8 c = str.text[char_idx];
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
{
|
||||||
|
u8 digit = c - '0';
|
||||||
|
whole_part += digit * PowU64(10, frac_start_idx - (char_idx + 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ok = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse frac part
|
||||||
|
u64 frac_part = 0;
|
||||||
|
for (u64 char_idx = frac_start_idx + 1; ok && char_idx < str.len; ++char_idx)
|
||||||
|
{
|
||||||
|
u8 c = str.text[char_idx];
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
{
|
||||||
|
u8 digit = c - '0';
|
||||||
|
frac_part += digit * PowU64(10, str.len - (char_idx + 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ok = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
if (frac_part != 0)
|
||||||
|
{
|
||||||
|
result = ((f64)whole_part + ((f64)frac_part / PowU64(10, str.len - (frac_start_idx + 1)))) * sign;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = (f64)whole_part * sign;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
506
src/pp/pp.c
506
src/pp/pp.c
@ -3184,137 +3184,140 @@ void P_StepFrame(P_Frame *frame)
|
|||||||
|
|
||||||
// TODO: Separate 'hits' from bullets, so that bullets can have multiple hits
|
// TODO: Separate 'hits' from bullets, so that bullets can have multiple hits
|
||||||
|
|
||||||
for (P_Ent *bullet = P_FirstEnt(frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet))
|
|
||||||
{
|
{
|
||||||
if (bullet->is_bullet)
|
P_EntList hits_to_spawn = Zi;
|
||||||
|
for (P_Ent *bullet = P_FirstEnt(frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet))
|
||||||
{
|
{
|
||||||
P_Ent *weapon = P_EntFromKey(frame, bullet->source);
|
if (bullet->is_bullet)
|
||||||
P_Ent *firer = P_EntFromKey(frame, weapon->source);
|
|
||||||
P_Ent *damager = P_EntFromKey(frame, bullet->damage_attribution);
|
|
||||||
|
|
||||||
b32 is_first_bullet_tick = bullet->created_at_tick == frame->tick;
|
|
||||||
|
|
||||||
//////////////////////////////
|
|
||||||
//- Bullet properties
|
|
||||||
|
|
||||||
f32 spread = Tau * 0.05;
|
|
||||||
// f32 spread = Tau * 0.01;
|
|
||||||
// f32 spread = 0;
|
|
||||||
|
|
||||||
b32 should_ricochet = 0;
|
|
||||||
|
|
||||||
f32 initial_speed = 1;
|
|
||||||
f32 speed_falloff = 0;
|
|
||||||
if (weapon->is_uzi)
|
|
||||||
{
|
{
|
||||||
initial_speed = TweakFloat("Bullet speed", 100, 1, 100);
|
P_Ent *weapon = P_EntFromKey(frame, bullet->source);
|
||||||
}
|
P_Ent *firer = P_EntFromKey(frame, weapon->source);
|
||||||
else if (weapon->is_launcher)
|
P_Ent *damager = P_EntFromKey(frame, bullet->damage_attribution);
|
||||||
{
|
|
||||||
should_ricochet = 1;
|
|
||||||
initial_speed = 50;
|
|
||||||
// initial_speed = 100;
|
|
||||||
speed_falloff = 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////
|
b32 is_first_bullet_tick = bullet->created_at_tick == frame->tick;
|
||||||
//- Initialize
|
|
||||||
|
|
||||||
// if (bullet->created_at_tick != frame->tick)
|
//////////////////////////////
|
||||||
// {
|
//- Bullet properties
|
||||||
// Vec2 start = bullet->bullet_start;
|
|
||||||
// Vec2 end = bullet->bullet_end;
|
|
||||||
// Vec2 vel = SubVec2(end, start);
|
|
||||||
// bullet->bullet_start = end;
|
|
||||||
// bullet->bullet_end = AddVec2(end, vel);
|
|
||||||
// bullet->xf.t = bullet->bullet_start;
|
|
||||||
// bullet->xf.r = NormVec2(vel);
|
|
||||||
// }
|
|
||||||
|
|
||||||
Struct(BulletPath)
|
f32 spread = Tau * 0.05;
|
||||||
{
|
// f32 spread = Tau * 0.01;
|
||||||
BulletPath *next;
|
// f32 spread = 0;
|
||||||
Vec2 start;
|
|
||||||
Vec2 end;
|
|
||||||
};
|
|
||||||
BulletPath *first_bullet_path = 0;
|
|
||||||
BulletPath *last_bullet_path = 0;
|
|
||||||
|
|
||||||
if (is_first_bullet_tick)
|
b32 should_ricochet = 0;
|
||||||
{
|
|
||||||
Vec2 fire_pos = Zi;
|
f32 initial_speed = 1;
|
||||||
Vec2 fire_dir = Zi;
|
f32 speed_falloff = 0;
|
||||||
Vec2 fire_base0 = Zi;
|
if (weapon->is_uzi)
|
||||||
Vec2 fire_base1 = Zi;
|
|
||||||
{
|
{
|
||||||
Vec2 look = firer->control.look;
|
initial_speed = TweakFloat("Bullet speed", 100, 1, 100);
|
||||||
P_Anim anim = P_AnimFromEnt(frame, firer);
|
}
|
||||||
SPR_Sprite body = SPR_SpriteFromSheet(anim.sheet, anim.span, anim.frame_seq);
|
else if (weapon->is_launcher)
|
||||||
SPR_Sprite wep = SPR_SpriteFromSheet(anim.wep_sheet, anim.span, anim.frame_seq);
|
{
|
||||||
|
should_ricochet = 1;
|
||||||
|
initial_speed = 50;
|
||||||
|
// initial_speed = 100;
|
||||||
|
speed_falloff = 5;
|
||||||
|
}
|
||||||
|
|
||||||
//- Compute sprite transforms
|
//////////////////////////////
|
||||||
Affine ent_to_world_af = MulAffineXform(AffineIdentity, firer->xf);
|
//- Initialize
|
||||||
Affine body_pix_to_world_af = AffineIdentity;
|
|
||||||
Affine wep_pix_to_world_af = AffineIdentity;
|
// if (bullet->created_at_tick != frame->tick)
|
||||||
|
// {
|
||||||
|
// Vec2 start = bullet->bullet_start;
|
||||||
|
// Vec2 end = bullet->bullet_end;
|
||||||
|
// Vec2 vel = SubVec2(end, start);
|
||||||
|
// bullet->bullet_start = end;
|
||||||
|
// bullet->bullet_end = AddVec2(end, vel);
|
||||||
|
// bullet->xf.t = bullet->bullet_start;
|
||||||
|
// bullet->xf.r = NormVec2(vel);
|
||||||
|
// }
|
||||||
|
|
||||||
|
Struct(BulletPath)
|
||||||
|
{
|
||||||
|
BulletPath *next;
|
||||||
|
Vec2 start;
|
||||||
|
Vec2 end;
|
||||||
|
};
|
||||||
|
BulletPath *first_bullet_path = 0;
|
||||||
|
BulletPath *last_bullet_path = 0;
|
||||||
|
|
||||||
|
if (is_first_bullet_tick)
|
||||||
|
{
|
||||||
|
Vec2 fire_pos = Zi;
|
||||||
|
Vec2 fire_dir = Zi;
|
||||||
|
Vec2 fire_base0 = Zi;
|
||||||
|
Vec2 fire_base1 = Zi;
|
||||||
{
|
{
|
||||||
Vec2 pix_scale = VEC2(1.0 / P_CellsPerMeter, 1.0 / P_CellsPerMeter);
|
Vec2 look = firer->control.look;
|
||||||
|
P_Anim anim = P_AnimFromEnt(frame, firer);
|
||||||
|
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);
|
||||||
|
|
||||||
//- Compute body transform
|
//- Compute sprite transforms
|
||||||
Affine body_pix_to_ent_af = AffineIdentity;
|
Affine ent_to_world_af = MulAffineXform(AffineIdentity, firer->xf);
|
||||||
|
Affine body_pix_to_world_af = AffineIdentity;
|
||||||
|
Affine wep_pix_to_world_af = AffineIdentity;
|
||||||
{
|
{
|
||||||
body_pix_to_ent_af = ScaleAffine(body_pix_to_ent_af, pix_scale);
|
Vec2 pix_scale = VEC2(1.0 / P_CellsPerMeter, 1.0 / P_CellsPerMeter);
|
||||||
|
|
||||||
SPR_Ray anchor_ray = body.rays[SPR_RayKind_Anchor];
|
//- Compute body transform
|
||||||
body_pix_to_ent_af = RotateAffine(body_pix_to_ent_af, InvertRot(anchor_ray.dir));
|
Affine body_pix_to_ent_af = AffineIdentity;
|
||||||
body_pix_to_ent_af = TranslateAffine(body_pix_to_ent_af, NegVec2(anchor_ray.pos));
|
{
|
||||||
|
body_pix_to_ent_af = ScaleAffine(body_pix_to_ent_af, pix_scale);
|
||||||
|
|
||||||
|
SPR_Ray anchor_ray = body.rays[SPR_RayKind_Anchor];
|
||||||
|
body_pix_to_ent_af = RotateAffine(body_pix_to_ent_af, InvertRot(anchor_ray.dir));
|
||||||
|
body_pix_to_ent_af = TranslateAffine(body_pix_to_ent_af, NegVec2(anchor_ray.pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
//- Compute weapon transform
|
||||||
|
Affine wep_pix_to_ent_af = AffineIdentity;
|
||||||
|
{
|
||||||
|
wep_pix_to_ent_af = ScaleAffine(wep_pix_to_ent_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_ent_af = RotateAffine(wep_pix_to_ent_af, InvertRot(body_anchor_ray.dir));
|
||||||
|
wep_pix_to_ent_af = TranslateAffine(wep_pix_to_ent_af, SubVec2(body_ap_ray.pos, body_anchor_ray.pos));
|
||||||
|
wep_pix_to_ent_af = RotateAffine(wep_pix_to_ent_af, NegVec2(body_ap_ray.dir));
|
||||||
|
|
||||||
|
SPR_Ray anchor_ray = wep.rays[SPR_RayKind_Anchor];
|
||||||
|
wep_pix_to_ent_af = RotateAffine(wep_pix_to_ent_af, anchor_ray.dir);
|
||||||
|
wep_pix_to_ent_af = TranslateAffine(wep_pix_to_ent_af, NegVec2(anchor_ray.pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
body_pix_to_world_af = MulAffine(ent_to_world_af, body_pix_to_ent_af);
|
||||||
|
wep_pix_to_world_af = MulAffine(ent_to_world_af, wep_pix_to_ent_af);
|
||||||
}
|
}
|
||||||
|
|
||||||
//- Compute weapon transform
|
SPR_Ray muzzle_ray = wep.rays[SPR_RayKind_Ap];
|
||||||
Affine wep_pix_to_ent_af = AffineIdentity;
|
fire_pos = MulAffineVec2(wep_pix_to_world_af, muzzle_ray.pos);
|
||||||
{
|
fire_dir = NormVec2(MulAffineBasisVec2(wep_pix_to_world_af, muzzle_ray.dir));
|
||||||
wep_pix_to_ent_af = ScaleAffine(wep_pix_to_ent_af, pix_scale);
|
|
||||||
|
|
||||||
SPR_Ray body_anchor_ray = body.rays[SPR_RayKind_Anchor];
|
fire_base0 = MulVec2(PerpVec2(NegVec2(firer->xf.r)), WedgeVec2(firer->xf.r, SubVec2(firer->xf.t, fire_pos)));
|
||||||
SPR_Ray body_ap_ray = body.rays[SPR_RayKind_Ap];
|
fire_base0 = AddVec2(fire_base0, firer->xf.t);
|
||||||
wep_pix_to_ent_af = RotateAffine(wep_pix_to_ent_af, InvertRot(body_anchor_ray.dir));
|
|
||||||
wep_pix_to_ent_af = TranslateAffine(wep_pix_to_ent_af, SubVec2(body_ap_ray.pos, body_anchor_ray.pos));
|
|
||||||
wep_pix_to_ent_af = RotateAffine(wep_pix_to_ent_af, NegVec2(body_ap_ray.dir));
|
|
||||||
|
|
||||||
SPR_Ray anchor_ray = wep.rays[SPR_RayKind_Anchor];
|
Vec2 chamber_pos = MulAffineVec2(body_pix_to_world_af, body.rays[SPR_RayKind_Ap].pos);
|
||||||
wep_pix_to_ent_af = RotateAffine(wep_pix_to_ent_af, anchor_ray.dir);
|
|
||||||
wep_pix_to_ent_af = TranslateAffine(wep_pix_to_ent_af, NegVec2(anchor_ray.pos));
|
|
||||||
}
|
|
||||||
|
|
||||||
body_pix_to_world_af = MulAffine(ent_to_world_af, body_pix_to_ent_af);
|
fire_base1 = MulVec2(PerpVec2(fire_dir), WedgeVec2(fire_dir, SubVec2(fire_pos, chamber_pos)));
|
||||||
wep_pix_to_world_af = MulAffine(ent_to_world_af, wep_pix_to_ent_af);
|
fire_base1 = AddVec2(fire_base1, chamber_pos);
|
||||||
|
|
||||||
|
P_DebugDrawLine(fire_base0, fire_base1, Color_Yellow);
|
||||||
|
P_DebugDrawLine(fire_base1, fire_pos, Color_Green);
|
||||||
|
P_DebugDrawPoint(fire_base0, Color_Yellow);
|
||||||
|
P_DebugDrawPoint(fire_base1, Color_Green);
|
||||||
|
P_DebugDrawPoint(fire_pos, Color_Red);
|
||||||
|
|
||||||
|
Vec2 bullet_dir = RotateVec2Angle(fire_dir, spread * (Norm24(MixU64s(bullet->key.v, P_BulletSpreadBasis)) - 0.5));
|
||||||
|
|
||||||
|
bullet->xf.t = fire_pos;
|
||||||
|
bullet->xf.r = NormVec2(bullet_dir);
|
||||||
|
|
||||||
|
bullet->v = MulVec2(NormVec2(bullet_dir), initial_speed);
|
||||||
|
bullet->v = AddVec2(bullet->v, firer->v);
|
||||||
}
|
}
|
||||||
|
|
||||||
SPR_Ray muzzle_ray = wep.rays[SPR_RayKind_Ap];
|
|
||||||
fire_pos = MulAffineVec2(wep_pix_to_world_af, muzzle_ray.pos);
|
|
||||||
fire_dir = NormVec2(MulAffineBasisVec2(wep_pix_to_world_af, muzzle_ray.dir));
|
|
||||||
|
|
||||||
fire_base0 = MulVec2(PerpVec2(NegVec2(firer->xf.r)), WedgeVec2(firer->xf.r, SubVec2(firer->xf.t, fire_pos)));
|
|
||||||
fire_base0 = AddVec2(fire_base0, firer->xf.t);
|
|
||||||
|
|
||||||
Vec2 chamber_pos = MulAffineVec2(body_pix_to_world_af, body.rays[SPR_RayKind_Ap].pos);
|
|
||||||
|
|
||||||
fire_base1 = MulVec2(PerpVec2(fire_dir), WedgeVec2(fire_dir, SubVec2(fire_pos, chamber_pos)));
|
|
||||||
fire_base1 = AddVec2(fire_base1, chamber_pos);
|
|
||||||
|
|
||||||
P_DebugDrawLine(fire_base0, fire_base1, Color_Yellow);
|
|
||||||
P_DebugDrawLine(fire_base1, fire_pos, Color_Green);
|
|
||||||
P_DebugDrawPoint(fire_base0, Color_Yellow);
|
|
||||||
P_DebugDrawPoint(fire_base1, Color_Green);
|
|
||||||
P_DebugDrawPoint(fire_pos, Color_Red);
|
|
||||||
|
|
||||||
Vec2 bullet_dir = RotateVec2Angle(fire_dir, spread * (Norm24(MixU64s(bullet->key.v, P_BulletSpreadBasis)) - 0.5));
|
|
||||||
|
|
||||||
bullet->xf.t = fire_pos;
|
|
||||||
bullet->xf.r = NormVec2(bullet_dir);
|
|
||||||
|
|
||||||
bullet->v = MulVec2(NormVec2(bullet_dir), initial_speed);
|
|
||||||
bullet->v = AddVec2(bullet->v, firer->v);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -3322,168 +3325,171 @@ void P_StepFrame(P_Frame *frame)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// On bullet's first tick, we want to ensure that the firer/weapon
|
||||||
// On bullet's first tick, we want to ensure that the firer/weapon
|
// wasn't obstructed (e.g. to prevent shooting through walls), so we
|
||||||
// wasn't obstructed (e.g. to prevent shooting through walls), so we
|
// insert a path from the bullet's base to its starting position before
|
||||||
// insert a path from the bullet's base to its starting position before
|
// its actual firing path
|
||||||
// its actual firing path
|
|
||||||
{
|
|
||||||
// Firer origin -> weapon chamber path
|
|
||||||
BulletPath *path = PushStruct(scratch.arena, BulletPath);
|
|
||||||
SllQueuePush(first_bullet_path, last_bullet_path, path);
|
|
||||||
path->start = fire_base0;
|
|
||||||
path->end = fire_base1;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
// Weapon chamber -> bullet start path
|
|
||||||
BulletPath *path = PushStruct(scratch.arena, BulletPath);
|
|
||||||
SllQueuePush(first_bullet_path, last_bullet_path, path);
|
|
||||||
path->start = fire_base1;
|
|
||||||
path->end = fire_pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Vec2 dir = MulVec2(bullet->v, sim_dt);
|
|
||||||
Vec2 p0 = bullet->xf.t;
|
|
||||||
Vec2 p1 = p0;
|
|
||||||
|
|
||||||
// Cur pos -> next pos path
|
|
||||||
if (!is_first_bullet_tick)
|
|
||||||
{
|
|
||||||
p1 = AddVec2(p0, dir);
|
|
||||||
BulletPath *path = PushStruct(scratch.arena, BulletPath);
|
|
||||||
SllQueuePush(first_bullet_path, last_bullet_path, path);
|
|
||||||
path->start = p0;
|
|
||||||
path->end = p1;
|
|
||||||
}
|
|
||||||
|
|
||||||
P_EntKey victim_key = Zi;
|
|
||||||
P_RaycastResult victim_raycast = Zi;
|
|
||||||
b32 hit = 0;
|
|
||||||
{
|
|
||||||
for (BulletPath *path = first_bullet_path; path && !hit; path = path->next)
|
|
||||||
{
|
|
||||||
Vec2 path_dir = SubVec2(path->end, path->start);
|
|
||||||
P_Space *cast_spaces[] = {
|
|
||||||
&world->walls_space,
|
|
||||||
&post_solve_ents_space,
|
|
||||||
};
|
|
||||||
P_SpaceEntryList cast_entries = Zi;
|
|
||||||
P_UniqueSpaceEntriesFromRay(
|
|
||||||
scratch.arena,
|
|
||||||
&cast_entries,
|
|
||||||
countof(cast_spaces),
|
|
||||||
cast_spaces,
|
|
||||||
path->start,
|
|
||||||
path->end
|
|
||||||
);
|
|
||||||
f32 closest_len_sq = Inf;
|
|
||||||
for (P_SpaceEntryNode *entry_node = cast_entries.first; entry_node; entry_node = entry_node->next)
|
|
||||||
{
|
{
|
||||||
P_SpaceEntry *entry = &entry_node->entry;
|
// Firer origin -> weapon chamber path
|
||||||
P_EntKey potential_victim_key = (P_EntKey) { .v = entry->shape_id };
|
BulletPath *path = PushStruct(scratch.arena, BulletPath);
|
||||||
P_Ent *potential_victim = P_EntFromKey(frame, potential_victim_key);
|
SllQueuePush(first_bullet_path, last_bullet_path, path);
|
||||||
b32 can_hit = (
|
path->start = fire_base0;
|
||||||
P_IsEntNil(potential_victim) ||
|
path->end = fire_base1;
|
||||||
(potential_victim->is_guy && (!P_MatchEntKey(potential_victim->key, firer->key) || P_IsEntKeyNil(firer->key)))
|
}
|
||||||
|
{
|
||||||
|
// Weapon chamber -> bullet start path
|
||||||
|
BulletPath *path = PushStruct(scratch.arena, BulletPath);
|
||||||
|
SllQueuePush(first_bullet_path, last_bullet_path, path);
|
||||||
|
path->start = fire_base1;
|
||||||
|
path->end = fire_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec2 dir = MulVec2(bullet->v, sim_dt);
|
||||||
|
Vec2 p0 = bullet->xf.t;
|
||||||
|
Vec2 p1 = p0;
|
||||||
|
|
||||||
|
// Cur pos -> next pos path
|
||||||
|
if (!is_first_bullet_tick)
|
||||||
|
{
|
||||||
|
p1 = AddVec2(p0, dir);
|
||||||
|
BulletPath *path = PushStruct(scratch.arena, BulletPath);
|
||||||
|
SllQueuePush(first_bullet_path, last_bullet_path, path);
|
||||||
|
path->start = p0;
|
||||||
|
path->end = p1;
|
||||||
|
}
|
||||||
|
|
||||||
|
P_EntKey victim_key = Zi;
|
||||||
|
P_RaycastResult victim_raycast = Zi;
|
||||||
|
b32 has_hit = 0;
|
||||||
|
{
|
||||||
|
for (BulletPath *path = first_bullet_path; path && !has_hit; path = path->next)
|
||||||
|
{
|
||||||
|
Vec2 path_dir = SubVec2(path->end, path->start);
|
||||||
|
P_Space *cast_spaces[] = {
|
||||||
|
&world->walls_space,
|
||||||
|
&post_solve_ents_space,
|
||||||
|
};
|
||||||
|
P_SpaceEntryList cast_entries = Zi;
|
||||||
|
P_UniqueSpaceEntriesFromRay(
|
||||||
|
scratch.arena,
|
||||||
|
&cast_entries,
|
||||||
|
countof(cast_spaces),
|
||||||
|
cast_spaces,
|
||||||
|
path->start,
|
||||||
|
path->end
|
||||||
);
|
);
|
||||||
if (can_hit)
|
f32 closest_len_sq = Inf;
|
||||||
|
for (P_SpaceEntryNode *entry_node = cast_entries.first; entry_node; entry_node = entry_node->next)
|
||||||
{
|
{
|
||||||
P_Shape potential_victim_shape = entry->shape;
|
P_SpaceEntry *entry = &entry_node->entry;
|
||||||
P_RaycastResult entrance_raycast = P_RaycastShape(potential_victim_shape, path->start, path_dir);
|
P_EntKey potential_victim_key = (P_EntKey) { .v = entry->shape_id };
|
||||||
Vec2 entrance = entrance_raycast.p;
|
P_Ent *potential_victim = P_EntFromKey(frame, potential_victim_key);
|
||||||
if (entrance_raycast.is_intersecting)
|
b32 can_hit = (
|
||||||
|
P_IsEntNil(potential_victim) ||
|
||||||
|
(potential_victim->is_guy && (!P_MatchEntKey(potential_victim->key, firer->key) || P_IsEntKeyNil(firer->key)))
|
||||||
|
);
|
||||||
|
if (can_hit)
|
||||||
{
|
{
|
||||||
P_RaycastResult exit_raycast = P_RaycastShape(potential_victim_shape, path->start, NegVec2(path_dir));
|
P_Shape potential_victim_shape = entry->shape;
|
||||||
Vec2 exit = exit_raycast.p;
|
P_RaycastResult entrance_raycast = P_RaycastShape(potential_victim_shape, path->start, path_dir);
|
||||||
f32 da = DotVec2(path_dir, SubVec2(entrance, path->start));
|
Vec2 entrance = entrance_raycast.p;
|
||||||
f32 db = DotVec2(path_dir, SubVec2(exit, path->start));
|
if (entrance_raycast.is_intersecting)
|
||||||
if (db > 0 && (da <= Vec2LenSq(path_dir) || da <= 0))
|
|
||||||
{
|
{
|
||||||
f32 len_sq = Vec2LenSq(SubVec2(entrance_raycast.p, path->start));
|
P_RaycastResult exit_raycast = P_RaycastShape(potential_victim_shape, path->start, NegVec2(path_dir));
|
||||||
if (len_sq < closest_len_sq)
|
Vec2 exit = exit_raycast.p;
|
||||||
|
f32 da = DotVec2(path_dir, SubVec2(entrance, path->start));
|
||||||
|
f32 db = DotVec2(path_dir, SubVec2(exit, path->start));
|
||||||
|
if (db > 0 && (da <= Vec2LenSq(path_dir) || da <= 0))
|
||||||
{
|
{
|
||||||
closest_len_sq = len_sq;
|
f32 len_sq = Vec2LenSq(SubVec2(entrance_raycast.p, path->start));
|
||||||
victim_key = potential_victim_key;
|
if (len_sq < closest_len_sq)
|
||||||
victim_raycast = entrance_raycast;
|
{
|
||||||
hit = 1;
|
closest_len_sq = len_sq;
|
||||||
|
victim_key = potential_victim_key;
|
||||||
|
victim_raycast = entrance_raycast;
|
||||||
|
has_hit = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
P_Ent *victim = P_EntFromKey(frame, victim_key);
|
||||||
P_Ent *victim = P_EntFromKey(frame, victim_key);
|
|
||||||
|
|
||||||
bullet->has_hit = 0;
|
// Create hit
|
||||||
|
Vec2 final_pos = p1;
|
||||||
Vec2 final_pos = p1;
|
if (has_hit)
|
||||||
if (hit)
|
|
||||||
{
|
|
||||||
Vec2 normal = victim_raycast.normal;
|
|
||||||
|
|
||||||
bullet->has_hit = 1;
|
|
||||||
bullet->hit_entry = victim_raycast.p;
|
|
||||||
bullet->hit_entry_normal = normal;
|
|
||||||
|
|
||||||
// FIXME: Use relative velocity at collision point
|
|
||||||
bullet->hit_entry_velocity = bullet->v;
|
|
||||||
|
|
||||||
// bullet->bullet_end = bullet->hit_entry;
|
|
||||||
if (victim->is_guy)
|
|
||||||
{
|
{
|
||||||
bullet->hit_material = P_MaterialKind_Flesh;
|
Vec2 normal = victim_raycast.normal;
|
||||||
}
|
bullet->bullet_hits_count += 1;
|
||||||
else
|
|
||||||
{
|
P_Ent *hit = P_PushTempEnt(scratch.arena, &hits_to_spawn);
|
||||||
bullet->hit_material = P_MaterialKind_Wall;
|
{
|
||||||
|
hit->key = P_EntKeyFromU64(MixU64s(bullet->key.v, P_BulletHitBasis + bullet->bullet_hits_count));
|
||||||
|
hit->is_hit = 1;
|
||||||
|
hit->hit_entry = victim_raycast.p;
|
||||||
|
hit->hit_entry_normal = normal;
|
||||||
|
hit->lifetime_seconds = P_ObservationDurationSeconds;
|
||||||
|
// FIXME: Use relative velocity at collision point
|
||||||
|
hit->hit_entry_velocity = bullet->v;
|
||||||
|
if (victim->is_guy)
|
||||||
|
{
|
||||||
|
hit->hit_material = P_MaterialKind_Flesh;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hit->hit_material = P_MaterialKind_Wall;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reflect velocity along normal
|
||||||
|
if (should_ricochet)
|
||||||
|
{
|
||||||
|
bullet->v = SubVec2(bullet->v, MulVec2(normal, 2 * DotVec2(bullet->v, normal)));
|
||||||
|
bullet->v = MulVec2(bullet->v, 0.5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bullet->exists = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 collision_offset = 0.01; // Tiny offset along normal to prevent collision with the victim during ricochets
|
||||||
|
final_pos = AddVec2(hit->hit_entry, MulVec2(hit->hit_entry_normal, collision_offset));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reflect velocity along normal
|
bullet->xf.t = final_pos;
|
||||||
if (should_ricochet)
|
if (Vec2LenSq(dir) > (0.001 * 0.001))
|
||||||
{
|
{
|
||||||
bullet->v = SubVec2(bullet->v, MulVec2(normal, 2 * DotVec2(bullet->v, normal)));
|
bullet->xf.r = NormVec2(dir);
|
||||||
bullet->v = MulVec2(bullet->v, 0.5);
|
|
||||||
}
|
}
|
||||||
else
|
bullet->v = MulVec2(bullet->v, 1.0 - SaturateF32(speed_falloff * sim_dt));
|
||||||
|
|
||||||
|
// TODO: Remove this
|
||||||
|
if (!is_client && !P_IsEntNil(victim))
|
||||||
|
{
|
||||||
|
if (damager->is_player)
|
||||||
|
{
|
||||||
|
victim->damage_attribution = damager->key;
|
||||||
|
}
|
||||||
|
victim->health -= 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prune out of bounds bullet
|
||||||
|
Rng2 bounds = Zi;
|
||||||
|
bounds.p0 = VEC2(-P_WorldPitch / 2, -P_WorldPitch / 2);
|
||||||
|
bounds.p1 = VEC2(P_WorldPitch / 2, P_WorldPitch / 2);
|
||||||
|
if (
|
||||||
|
bullet->xf.t.x < bounds.p0.x || bullet->xf.t.y < bounds.p0.y ||
|
||||||
|
bullet->xf.t.x > bounds.p1.x || bullet->xf.t.y > bounds.p1.y
|
||||||
|
)
|
||||||
{
|
{
|
||||||
bullet->exists = 0;
|
bullet->exists = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
f32 collision_offset = 0.01; // Tiny offset along normal to prevent collision with the victim during ricochets
|
|
||||||
final_pos = AddVec2(bullet->hit_entry, MulVec2(bullet->hit_entry_normal, collision_offset));
|
|
||||||
}
|
|
||||||
|
|
||||||
bullet->xf.t = final_pos;
|
|
||||||
if (Vec2LenSq(dir) > (0.001 * 0.001))
|
|
||||||
{
|
|
||||||
bullet->xf.r = NormVec2(dir);
|
|
||||||
}
|
|
||||||
bullet->v = MulVec2(bullet->v, 1.0 - SaturateF32(speed_falloff * sim_dt));
|
|
||||||
|
|
||||||
// TODO: Remove this
|
|
||||||
if (!is_client && !P_IsEntNil(victim))
|
|
||||||
{
|
|
||||||
if (damager->is_player)
|
|
||||||
{
|
|
||||||
victim->damage_attribution = damager->key;
|
|
||||||
}
|
|
||||||
victim->health -= 0.25;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prune out of bounds bullet
|
|
||||||
Rng2 bounds = Zi;
|
|
||||||
bounds.p0 = VEC2(-P_WorldPitch / 2, -P_WorldPitch / 2);
|
|
||||||
bounds.p1 = VEC2(P_WorldPitch / 2, P_WorldPitch / 2);
|
|
||||||
if (
|
|
||||||
bullet->xf.t.x < bounds.p0.x || bullet->xf.t.y < bounds.p0.y ||
|
|
||||||
bullet->xf.t.x > bounds.p1.x || bullet->xf.t.y > bounds.p1.y
|
|
||||||
)
|
|
||||||
{
|
|
||||||
bullet->exists = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
P_SpawnEntsFromList(frame, hits_to_spawn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
15
src/pp/pp.h
15
src/pp/pp.h
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#define P_WallShapeIDBasis 0x40d501b4cf6d4f0cull
|
#define P_WallShapeIDBasis 0x40d501b4cf6d4f0cull
|
||||||
#define P_BulletSpreadBasis 0xc3b72fe38ca5a1d6ull
|
#define P_BulletSpreadBasis 0xc3b72fe38ca5a1d6ull
|
||||||
|
#define P_BulletHitBasis 0xbc70fc783c1c507full
|
||||||
|
|
||||||
Struct(P_EntKey)
|
Struct(P_EntKey)
|
||||||
{
|
{
|
||||||
@ -84,7 +85,7 @@ Struct(P_DebugDrawNode)
|
|||||||
#define P_RollTimeNs NsFromSeconds(0.5)
|
#define P_RollTimeNs NsFromSeconds(0.5)
|
||||||
#define P_RollTurnTimeNs (NsFromSeconds(0.1))
|
#define P_RollTurnTimeNs (NsFromSeconds(0.1))
|
||||||
#define P_RollTimeoutNs NsFromSeconds(0.5)
|
#define P_RollTimeoutNs NsFromSeconds(0.5)
|
||||||
#define P_ObservationLifetimeNs NsFromSeconds(2)
|
#define P_ObservationDurationSeconds 1
|
||||||
|
|
||||||
Struct(P_Control)
|
Struct(P_Control)
|
||||||
{
|
{
|
||||||
@ -121,8 +122,9 @@ Struct(P_Ent)
|
|||||||
|
|
||||||
//- Client data
|
//- Client data
|
||||||
|
|
||||||
i64 observation_time_ns;
|
i64 initial_observation_time_ns;
|
||||||
b32 has_observed;
|
i64 last_observation_time_ns;
|
||||||
|
b32 is_first_observation;
|
||||||
|
|
||||||
//- Build data
|
//- Build data
|
||||||
|
|
||||||
@ -137,11 +139,14 @@ Struct(P_Ent)
|
|||||||
|
|
||||||
Xform xf;
|
Xform xf;
|
||||||
|
|
||||||
//- Bullet / hit
|
//- Bullet
|
||||||
|
|
||||||
b32 is_bullet;
|
b32 is_bullet;
|
||||||
|
u32 bullet_hits_count;
|
||||||
|
|
||||||
b32 has_hit;
|
//- Hit
|
||||||
|
|
||||||
|
b32 is_hit;
|
||||||
Vec2 hit_entry;
|
Vec2 hit_entry;
|
||||||
Vec2 hit_entry_normal;
|
Vec2 hit_entry_normal;
|
||||||
Vec2 hit_entry_velocity;
|
Vec2 hit_entry_velocity;
|
||||||
|
|||||||
@ -23,7 +23,7 @@ String P_PackWorld(Arena *arena, P_World *src_world)
|
|||||||
{
|
{
|
||||||
// TODO: Pack ignored ents
|
// TODO: Pack ignored ents
|
||||||
b32 ignore = 0;
|
b32 ignore = 0;
|
||||||
if (ent->is_weapon || ent->is_bullet || ent->is_bomb)
|
if (ent->is_weapon || ent->is_bullet || ent->is_bomb || ent->is_hit)
|
||||||
{
|
{
|
||||||
ignore = 1;
|
ignore = 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1895,14 +1895,16 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
//- Observe entities
|
//- Observe entities
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (P_Ent *ent = P_FirstEnt(local_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
|
for (P_Ent *ent = P_FirstEnt(local_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
|
||||||
{
|
{
|
||||||
P_EntKey key = ent->key;
|
P_EntKey key = ent->key;
|
||||||
i64 expiration_ns = P_ObservationLifetimeNs;
|
i64 expiration_ns = NsFromSeconds(P_ObservationDurationSeconds);
|
||||||
i64 observation_time_ns = 0;
|
V_Observation *obs = 0;
|
||||||
{
|
{
|
||||||
V_ObservationBin *bin = &V.observation_bins[key.v % countof(V.observation_bins)];
|
V_ObservationBin *bin = &V.observation_bins[key.v % countof(V.observation_bins)];
|
||||||
V_Observation *obs = bin->first;
|
obs = bin->first;
|
||||||
for (; obs; obs = obs->next)
|
for (; obs; obs = obs->next)
|
||||||
{
|
{
|
||||||
if (obs->key == key.v)
|
if (obs->key == key.v)
|
||||||
@ -1910,19 +1912,20 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obs)
|
if (obs)
|
||||||
{
|
{
|
||||||
if (obs->time_ns + expiration_ns > frame->time_ns)
|
if (frame->time_ns > obs->last_observation_time_ns + expiration_ns)
|
||||||
{
|
{
|
||||||
// Re-observe matching expired observation
|
// Observation expired, re-init
|
||||||
DllQueueRemove(V.first_observation, V.last_observation, obs);
|
obs->initial_observation_time_ns = frame->time_ns;
|
||||||
DllQueuePush(V.first_observation, V.last_observation, obs);
|
|
||||||
obs->time_ns = frame->time_ns;
|
|
||||||
}
|
}
|
||||||
|
DllQueueRemove(V.first_observation, V.last_observation, obs);
|
||||||
|
DllQueuePush(V.first_observation, V.last_observation, obs);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (V.first_observation && V.first_observation->time_ns + expiration_ns <= frame->time_ns)
|
if (V.first_observation && frame->time_ns > V.first_observation->last_observation_time_ns + expiration_ns)
|
||||||
{
|
{
|
||||||
// Remove expired observation for reuse
|
// Remove expired observation for reuse
|
||||||
obs = V.first_observation;
|
obs = V.first_observation;
|
||||||
@ -1935,14 +1938,14 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
obs = PushStruct(perm, V_Observation);
|
obs = PushStruct(perm, V_Observation);
|
||||||
}
|
}
|
||||||
obs->key = key.v;
|
obs->key = key.v;
|
||||||
obs->time_ns = frame->time_ns;
|
obs->initial_observation_time_ns = frame->time_ns;
|
||||||
DllQueuePush(V.first_observation, V.last_observation, obs);
|
DllQueuePush(V.first_observation, V.last_observation, obs);
|
||||||
DllQueuePushNP(bin->first, bin->last, obs, next_in_bin, prev_in_bin);
|
DllQueuePushNP(bin->first, bin->last, obs, next_in_bin, prev_in_bin);
|
||||||
}
|
}
|
||||||
observation_time_ns = obs->time_ns;
|
obs->last_observation_time_ns = frame->time_ns;
|
||||||
}
|
}
|
||||||
ent->observation_time_ns = observation_time_ns;
|
ent->last_observation_time_ns = frame->time_ns;
|
||||||
ent->has_observed = observation_time_ns < frame->time_ns;
|
ent->is_first_observation = obs->initial_observation_time_ns == frame->time_ns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1950,6 +1953,61 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// for (P_Ent *ent = P_FirstEnt(local_frame); !P_IsEntNil(ent); ent = P_NextEnt(ent))
|
||||||
|
// {
|
||||||
|
// P_EntKey key = ent->key;
|
||||||
|
// i64 expiration_ns = P_ObservationLifetimeNs;
|
||||||
|
// i64 observation_time_ns = 0;
|
||||||
|
// {
|
||||||
|
// V_ObservationBin *bin = &V.observation_bins[key.v % countof(V.observation_bins)];
|
||||||
|
// V_Observation *obs = bin->first;
|
||||||
|
// for (; obs; obs = obs->next)
|
||||||
|
// {
|
||||||
|
// if (obs->key == key.v)
|
||||||
|
// {
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (obs)
|
||||||
|
// {
|
||||||
|
// if (obs->time_ns + expiration_ns > frame->time_ns)
|
||||||
|
// {
|
||||||
|
// // Re-observe matching expired observation
|
||||||
|
// DllQueueRemove(V.first_observation, V.last_observation, obs);
|
||||||
|
// DllQueuePush(V.first_observation, V.last_observation, obs);
|
||||||
|
// obs->time_ns = frame->time_ns;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// if (V.first_observation && V.first_observation->time_ns + expiration_ns <= frame->time_ns)
|
||||||
|
// {
|
||||||
|
// // Remove expired observation for reuse
|
||||||
|
// obs = V.first_observation;
|
||||||
|
// V_ObservationBin *old_bin = &V.observation_bins[obs->key % countof(V.observation_bins)];
|
||||||
|
// DllQueueRemove(V.first_observation, V.last_observation, obs);
|
||||||
|
// DllQueueRemoveNP(old_bin->first, old_bin->last, obs, next_in_bin, prev_in_bin);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// obs = PushStruct(perm, V_Observation);
|
||||||
|
// }
|
||||||
|
// obs->key = key.v;
|
||||||
|
// obs->time_ns = frame->time_ns;
|
||||||
|
// DllQueuePush(V.first_observation, V.last_observation, obs);
|
||||||
|
// DllQueuePushNP(bin->first, bin->last, obs, next_in_bin, prev_in_bin);
|
||||||
|
// }
|
||||||
|
// observation_time_ns = obs->time_ns;
|
||||||
|
// }
|
||||||
|
// ent->observation_time_ns = observation_time_ns;
|
||||||
|
// ent->has_observed = observation_time_ns < frame->time_ns;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -2483,80 +2541,6 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// //////////////////////////////
|
|
||||||
// //- Push test bullet particles
|
|
||||||
|
|
||||||
// // TODO: Not like this
|
|
||||||
|
|
||||||
// for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet))
|
|
||||||
// {
|
|
||||||
// if (bullet->is_bullet)
|
|
||||||
// {
|
|
||||||
// // FIXME: Use 'last visible' pos
|
|
||||||
// P_Ent *old_bullet = P_EntFromKey(prev_local_frame, bullet->key);
|
|
||||||
// Vec2 start = old_bullet->xf.t;
|
|
||||||
// Vec2 end = bullet->xf.t;
|
|
||||||
// if (P_IsEntNil(old_bullet))
|
|
||||||
// {
|
|
||||||
// start = end;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// b32 skip = 0;
|
|
||||||
// if (bullet->has_hit)
|
|
||||||
// {
|
|
||||||
// Vec2 hit_pos = bullet->hit_entry;
|
|
||||||
// if (DotVec2(SubVec2(hit_pos, start), SubVec2(end, start)) < 0)
|
|
||||||
// {
|
|
||||||
// skip = 1;
|
|
||||||
// }
|
|
||||||
// // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, start), Color_Red);
|
|
||||||
// // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, end), Color_Purple);
|
|
||||||
// end = hit_pos;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (!skip)
|
|
||||||
// {
|
|
||||||
// {
|
|
||||||
// f32 trail_len = Vec2Len(SubVec2(end, start));
|
|
||||||
// f32 particles_count = trail_len * frame->dt * Kibi(8); // Particles per meter per second
|
|
||||||
// particles_count = MaxF32(particles_count, 1);
|
|
||||||
|
|
||||||
// f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start)));
|
|
||||||
// // f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start)));
|
|
||||||
|
|
||||||
// V_Emitter emitter = Zi;
|
|
||||||
// {
|
|
||||||
// emitter.kind = V_ParticleKind_BulletTrail;
|
|
||||||
// emitter.count = particles_count;
|
|
||||||
|
|
||||||
// f32 angle_spread = Tau / 4.0;
|
|
||||||
|
|
||||||
// emitter.angle.min = angle - angle_spread / 2;
|
|
||||||
// emitter.angle.max = angle + angle_spread / 2;
|
|
||||||
|
|
||||||
// emitter.pos.p0 = start;
|
|
||||||
// emitter.pos.p1 = end;
|
|
||||||
|
|
||||||
// // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
|
|
||||||
|
|
||||||
// // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
|
|
||||||
|
|
||||||
// emitter.speed.min = -1;
|
|
||||||
// emitter.speed.max = 1;
|
|
||||||
// }
|
|
||||||
// V_PushParticles(emitter);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
//- Push test impact particles
|
//- Push test impact particles
|
||||||
|
|
||||||
@ -2566,24 +2550,15 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
|
|
||||||
// if (0)
|
// if (0)
|
||||||
{
|
{
|
||||||
for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet))
|
for (P_Ent *hit = P_FirstEnt(local_frame); !P_IsEntNil(hit); hit = P_NextEnt(hit))
|
||||||
{
|
{
|
||||||
if (bullet->is_bullet && bullet->has_hit)
|
if (hit->is_hit && hit->is_first_observation)
|
||||||
{
|
{
|
||||||
// FIXME: Use actual velocity
|
P_MaterialKind material = hit->hit_material;
|
||||||
P_Ent *old_bullet = P_EntFromKey(prev_local_frame, bullet->key);
|
|
||||||
Vec2 start = old_bullet->xf.t;
|
|
||||||
Vec2 end = bullet->xf.t;
|
|
||||||
if (P_IsEntNil(old_bullet))
|
|
||||||
{
|
|
||||||
start = end;
|
|
||||||
}
|
|
||||||
|
|
||||||
P_MaterialKind material = bullet->hit_material;
|
Vec2 hit_entry = hit->hit_entry;
|
||||||
|
Vec2 hit_entry_normal = hit->hit_entry_normal;
|
||||||
Vec2 hit_entry = bullet->hit_entry;
|
Vec2 hit_entry_velocity = hit->hit_entry_velocity;
|
||||||
Vec2 hit_entry_normal = bullet->hit_entry_normal;
|
|
||||||
Vec2 hit_entry_velocity = bullet->hit_entry_velocity;
|
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
//- Wall particles
|
//- Wall particles
|
||||||
@ -2699,6 +2674,232 @@ void V_TickForever(WaveLaneCtx *lane)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// //////////////////////////////
|
||||||
|
// //- Push test bullet particles
|
||||||
|
|
||||||
|
// // TODO: Not like this
|
||||||
|
|
||||||
|
// for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet))
|
||||||
|
// {
|
||||||
|
// if (bullet->is_bullet)
|
||||||
|
// {
|
||||||
|
// // FIXME: Use 'last visible' pos
|
||||||
|
// P_Ent *old_bullet = P_EntFromKey(prev_local_frame, bullet->key);
|
||||||
|
// Vec2 start = old_bullet->xf.t;
|
||||||
|
// Vec2 end = bullet->xf.t;
|
||||||
|
// if (P_IsEntNil(old_bullet))
|
||||||
|
// {
|
||||||
|
// start = end;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// b32 skip = 0;
|
||||||
|
// if (bullet->has_hit)
|
||||||
|
// {
|
||||||
|
// Vec2 hit_pos = bullet->hit_entry;
|
||||||
|
// if (DotVec2(SubVec2(hit_pos, start), SubVec2(end, start)) < 0)
|
||||||
|
// {
|
||||||
|
// skip = 1;
|
||||||
|
// }
|
||||||
|
// // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, start), Color_Red);
|
||||||
|
// // V_DrawPoint(MulAffineVec2(frame->af.world_to_screen, end), Color_Purple);
|
||||||
|
// end = hit_pos;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!skip)
|
||||||
|
// {
|
||||||
|
// {
|
||||||
|
// f32 trail_len = Vec2Len(SubVec2(end, start));
|
||||||
|
// f32 particles_count = trail_len * frame->dt * Kibi(8); // Particles per meter per second
|
||||||
|
// particles_count = MaxF32(particles_count, 1);
|
||||||
|
|
||||||
|
// f32 angle = AngleFromVec2(PerpVec2(SubVec2(end, start)));
|
||||||
|
// // f32 angle = AngleFromVec2(NegVec2(SubVec2(end, start)));
|
||||||
|
|
||||||
|
// V_Emitter emitter = Zi;
|
||||||
|
// {
|
||||||
|
// emitter.kind = V_ParticleKind_BulletTrail;
|
||||||
|
// emitter.count = particles_count;
|
||||||
|
|
||||||
|
// f32 angle_spread = Tau / 4.0;
|
||||||
|
|
||||||
|
// emitter.angle.min = angle - angle_spread / 2;
|
||||||
|
// emitter.angle.max = angle + angle_spread / 2;
|
||||||
|
|
||||||
|
// emitter.pos.p0 = start;
|
||||||
|
// emitter.pos.p1 = end;
|
||||||
|
|
||||||
|
// // emitter.color_lin = LinearFromSrgb(VEC4(0, 1, 0, 1));
|
||||||
|
|
||||||
|
// // emitter.color_lin = LinearFromSrgb(VEC4(0.8, 0.6, 0.2, 1));
|
||||||
|
|
||||||
|
// emitter.speed.min = -1;
|
||||||
|
// emitter.speed.max = 1;
|
||||||
|
// }
|
||||||
|
// V_PushParticles(emitter);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// //////////////////////////////
|
||||||
|
// //- Push test impact particles
|
||||||
|
|
||||||
|
// // TODO: Not like this
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// // if (0)
|
||||||
|
// {
|
||||||
|
// for (P_Ent *bullet = P_FirstEnt(local_frame); !P_IsEntNil(bullet); bullet = P_NextEnt(bullet))
|
||||||
|
// {
|
||||||
|
// if (bullet->is_bullet && bullet->has_hit)
|
||||||
|
// {
|
||||||
|
// // FIXME: Use actual velocity
|
||||||
|
// P_Ent *old_bullet = P_EntFromKey(prev_local_frame, bullet->key);
|
||||||
|
// Vec2 start = old_bullet->xf.t;
|
||||||
|
// Vec2 end = bullet->xf.t;
|
||||||
|
// if (P_IsEntNil(old_bullet))
|
||||||
|
// {
|
||||||
|
// start = end;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// P_MaterialKind material = bullet->hit_material;
|
||||||
|
|
||||||
|
// Vec2 hit_entry = bullet->hit_entry;
|
||||||
|
// Vec2 hit_entry_normal = bullet->hit_entry_normal;
|
||||||
|
// Vec2 hit_entry_velocity = bullet->hit_entry_velocity;
|
||||||
|
|
||||||
|
// //////////////////////////////
|
||||||
|
// //- Wall particles
|
||||||
|
|
||||||
|
// if (material != P_MaterialKind_Flesh)
|
||||||
|
// {
|
||||||
|
// //- Wall debris
|
||||||
|
// {
|
||||||
|
// V_Emitter emitter = Zi;
|
||||||
|
// {
|
||||||
|
// // emitter.flags |= V_ParticleFlag_PruneWhenStill;
|
||||||
|
// // emitter.flags |= V_ParticleFlag_StainOnPrune;
|
||||||
|
|
||||||
|
|
||||||
|
// emitter.kind = V_ParticleKind_Debris;
|
||||||
|
// emitter.count = 4;
|
||||||
|
|
||||||
|
// emitter.pos.p0 = emitter.pos.p1 = hit_entry;
|
||||||
|
|
||||||
|
// emitter.speed.min = 0;
|
||||||
|
// emitter.speed.max = 20;
|
||||||
|
|
||||||
|
// // emitter.velocity_falloff = 5;
|
||||||
|
// // emitter.velocity_falloff_spread = emitter.velocity_falloff_spread * 1.5;
|
||||||
|
|
||||||
|
// Vec2 dir = hit_entry_normal;
|
||||||
|
|
||||||
|
// f32 angle = AngleFromVec2(dir);
|
||||||
|
// f32 angle_spread = Tau * 0.5;
|
||||||
|
|
||||||
|
// emitter.angle.min = angle - angle_spread / 2;
|
||||||
|
// emitter.angle.max = angle + angle_spread / 2;
|
||||||
|
|
||||||
|
// // emitter.lifetime = 0.25;
|
||||||
|
// // emitter.lifetime = 0.05;
|
||||||
|
// // emitter.lifetime = 0.04;
|
||||||
|
// // emitter.lifetime_spread = emitter.lifetime * 2;
|
||||||
|
// }
|
||||||
|
// V_PushParticles(emitter);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //- Wall dust
|
||||||
|
// // {
|
||||||
|
// // V_Emitter emitter = Zi;
|
||||||
|
// // {
|
||||||
|
// // emitter.kind = V_ParticleKind_Smoke;
|
||||||
|
// // emitter.count = 128;
|
||||||
|
|
||||||
|
// // emitter.pos.p0 = emitter.pos.p1 = hit_entry;
|
||||||
|
|
||||||
|
// // // emitter.color_lin = LinearFromSrgb(VEC4(0.5, 0.5, 0.5, 0.75));
|
||||||
|
|
||||||
|
// // emitter.speed.min = 10;
|
||||||
|
// // emitter.speed.max = 20;
|
||||||
|
|
||||||
|
// // // emitter.velocity_falloff = 12;
|
||||||
|
// // // emitter.velocity_falloff_spread = emitter.velocity_falloff_spread * 1.5;
|
||||||
|
|
||||||
|
// // Vec2 dir = hit_entry_normal;
|
||||||
|
// // f32 angle = AngleFromVec2(dir);
|
||||||
|
// // f32 angle_spread = Tau * 0.1;
|
||||||
|
|
||||||
|
// // emitter.angle.min = angle - angle_spread / 2;
|
||||||
|
// // emitter.angle.max = angle + angle_spread / 2;
|
||||||
|
|
||||||
|
// // }
|
||||||
|
// // V_PushParticles(emitter);
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //////////////////////////////
|
||||||
|
// //- Blood particles
|
||||||
|
|
||||||
|
// if (material == P_MaterialKind_Flesh)
|
||||||
|
// {
|
||||||
|
// {
|
||||||
|
// V_Emitter emitter = Zi;
|
||||||
|
|
||||||
|
// emitter.kind = V_ParticleKind_BloodTrail;
|
||||||
|
// // emitter.kind = V_ParticleKind_BloodDebris;
|
||||||
|
|
||||||
|
// Vec2 dir = NormVec2(NegVec2(hit_entry_velocity));
|
||||||
|
|
||||||
|
// f32 angle = AngleFromVec2(dir);
|
||||||
|
// // f32 angle = 0;
|
||||||
|
// // f32 angle_spread = Tau * 0.25;
|
||||||
|
// f32 angle_spread = TweakFloat("Emitter angle spread", 0.1, 0, 1) * Tau;
|
||||||
|
// // f32 angle_spread = Tau;
|
||||||
|
// // f32 angle_spread = 0;
|
||||||
|
|
||||||
|
// // f32 speed = 5;
|
||||||
|
// // f32 speed = 25;
|
||||||
|
// f32 speed = 50;
|
||||||
|
// // f32 speed = 100;
|
||||||
|
// f32 speed_spread = speed * 2;
|
||||||
|
|
||||||
|
// emitter.pos.p0 = emitter.pos.p1 = hit_entry;
|
||||||
|
// emitter.speed.min = speed - speed_spread * 0.5;
|
||||||
|
// emitter.speed.max = speed + speed_spread * 0.5;
|
||||||
|
// emitter.angle.min = angle - angle_spread * 0.5;
|
||||||
|
// emitter.angle.max = angle + angle_spread * 0.5;
|
||||||
|
// emitter.count = TweakFloat("Emitter count", 1, 0, 100) * (Kibi(1) * frame->dt);
|
||||||
|
// V_PushParticles(emitter);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // V_DrawPoint(victim_raycast.p, Color_Green);
|
||||||
|
// // V_DrawLine(victim_raycast.p, AddVec2(victim_raycast.p, MulVec2(victim_raycast.normal, 0.5)), Color_White);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////////
|
//////////////////////////////
|
||||||
//- Push test emitter
|
//- Push test emitter
|
||||||
|
|
||||||
|
|||||||
@ -239,7 +239,8 @@ Struct(V_Observation)
|
|||||||
V_Observation *next_in_bin;
|
V_Observation *next_in_bin;
|
||||||
V_Observation *prev_in_bin;
|
V_Observation *prev_in_bin;
|
||||||
u64 key;
|
u64 key;
|
||||||
i64 time_ns;
|
i64 initial_observation_time_ns;
|
||||||
|
i64 last_observation_time_ns;
|
||||||
};
|
};
|
||||||
|
|
||||||
Struct(V_ObservationBin)
|
Struct(V_ObservationBin)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user