Lot of bugfixes

* Adding clipper to generate the terrain's negative powder-contour offset.
* Fixing RDB issues (fracture, active trigger, performances)
* Fixing blood emission
* Fixing the freezing formula on liquids
* Fixing Steve spawning due to a missed test.
* Reaction table properly build
This commit is contained in:
Gaël committed 2026-10-21 20:46:13 +02:00
1 parent a34f42d53e
commit 00a9c1c26c
48 files changed
+5799 -435

No files matched your search

+28 -5
View File
@@ -58,7 +58,9 @@ bool shooter_safe(int flags, int age) { const int f = (flags >> 16) & 255; retur
int shot_flags(int typeFlags, int dz) { return ((dz >> 10) & 1) != 0 ? (typeFlags & ~(255 << 16)) | TF_FRIENDLY | (6 << 16) : typeFlags; }
bool victim_at(vec2 pos, int shooter, int herd, int flags, int age, out int hit) {
hit = -1;
for (int k = 0; k < ctrl.inp.w; ++k) {
const int aheadBody = body_slot(gAhead), hereBody = body_slot(pos);
for (int candidate = 0; candidate < hcollision.x; ++candidate) {
const int k = htargets[candidate];
if (ents[k].ex19.w == -float(gl_GlobalInvocationID.x + 1u)) continue; // its own damage proxy
const int kind = ents[k].st.x;
if (kind == 0 && (ents[k].st.w & 64) != 0 && ents[k].ex19.w > 0.5) { // a worm's segment (WormComponent hitbox_radius around every part): the hit goes to the worm
@@ -73,7 +75,7 @@ bool victim_at(vec2 pos, int shooter, int herd, int flags, int age, out int hit)
if ((ents[k].st.w & (8 | 4096)) != 0) continue; // (4096: its sprites, damage model and hitbox are switched off - Olematon away from an Evil Eye)
if (k == shooter - 1 && shooter_safe(flags, age)) continue;
if ((flags & TF_FRIENDLY) == 0 && !(shooter > 0 && (ents[shooter - 1].ex8.x & 4) != 0) && herd != 0 && ents[k].ex8.y == herd && kind != 4) continue; // (a berserk shooter has no herd: its projectiles hurt everybody)
if (kind == 4 && ents[k].ex7.w > 0.5) { if (body_slot(gAhead) == k || body_slot(pos) == k) { hit = k; return true; } continue; } // no HitboxComponent: its pixels (00c073b0)
if (kind == 4 && ents[k].ex7.w > 0.5) { if (aheadBody == k || hereBody == k) { hit = k; return true; } continue; } // no HitboxComponent: its pixels (00c073b0)
const float r = k == 0 ? 1.0 : 2.0;
const vec2 d = pos - ents[k].a.xy;
if (d.x >= ents[k].box.x - r && d.x <= ents[k].box.y + r && d.y >= ents[k].box.z - r && d.y <= ents[k].box.w + r) { hit = k; return true; }
@@ -83,7 +85,9 @@ bool victim_at(vec2 pos, int shooter, int herd, int flags, int age, out int hit)
bool creature_at(vec2 pos, float r, out int hit) {
hit = -1;
for (int k = 1; k < ctrl.inp.w; ++k) {
for (int candidate = 0; candidate < hcollision.x; ++candidate) {
const int k = htargets[candidate];
if (k == 0) continue;
int kind = ents[k].st.x;
if (!(kind == 1 || kind == 2 || (kind == 4 && ents[k].hp.x < 1.0e8)) || (ents[k].st.w & 8) != 0) continue;
vec2 d = pos - ents[k].a.xy;
@@ -131,8 +135,6 @@ void explode(vec2 pos, float radius, float damage, int type, int team, int shoot
int cellMat = int(ft[tb + 2].w);
float cellProb = uf(ft[tb + 3].x);
if (radius <= 0.0) return;
for (int k = 1; k < ctrl.inp.w; ++k) // corpse parts in range bleed
if (ents[k].st.x == 4 && ents[k].ex7.y > 0.0 && distance(ents[k].a.xy, pos) <= radius) body_damage(k, pos, 1.0);
// 00657960 (docs/EXPLOSION_KNOCKBACK_AND_VINES.md): every entity whose position is within explosion_radius, except the config's dont_damage_this (the shooter only when the projectile has
// explosion_dont_damage_shooter), takes the config's damage in full (only with damage_mortals; the perks scaled it at the Shot) and gets a knockback: scalar = radius * knockback_force *
// 10 * (0.5 t + 0.5), t = 1 - distance / radius clamped; its direction is the radial physics force (lerp of physics_explosion_power.min / .max by t: zero = no kick); it is compared with
@@ -160,6 +162,8 @@ void explode(vec2 pos, float radius, float damage, int type, int team, int shoot
{ // 00657960: the packet's impulse = the direction x ((power.max - power.min) x (1 - d / R) + power.min) x physics_multiplier_ragdoll_force x 3600 (the blood of the hit
// and the ragdoll take it); RAGDOLL_FX of the explosion packet: the constructor's NORMAL
const float a = ((pw.y - pw.x) * clamp(1.0 - dist / radius, 0.0, 1.0) + pw.x) * (float((fti(tb + 11).y >> 8) & 4095) / 256.0) * 3600.0;
hit_liquid(k, dk, c, away * a, 1.0, 1.0, 1, hash3(critSeed, uint(k), 711u));
if (kind == 4) body_damage(k, c, dk);
if (k != 0) { // (not the player: its aux.x is the maximum hp - a blast there made the respawn hp 765)
atomicExchange(ents[k].aux.x, int(away.x * a * 1000.0)); atomicExchange(ents[k].aux.y, int(away.y * a * 1000.0));
atomicAnd(ents[k].ex3.z, ~0xF000); atomicOr(ents[k].ex3.z, 1 << 12);
@@ -328,6 +332,7 @@ void verlet_step(uint id, FProj p, int tb, int vi, int age, int fired, int team,
if (rel.x < ents[e].box.x - r || rel.x > ents[e].box.y + r || rel.y < ents[e].box.z - r || rel.y > ents[e].box.w + r) continue;
if (kind == 3) { if (float(pc.frame) < aux.w) continue; aux.w = float(pc.frame + 20); } // mPlayerCooldownEnd
if (kind != 3 && herd != 0 && ents[e].ex8.y == herd) continue; // (the shooter's herd is spared)
hit_liquid(e, dmg, pts[k], v * 60.0 * ftf(vb + 3).x, 1.0, 1.0, 1, hash3(id, uint(k), uint(pc.frame)));
atomicAdd(ents[e].x.x, int(dmg * 1000.0));
note_attacker(e, shooter);
ents[e].ex19.yz = vec2(float(p.d.x + 1), float(pc.frame & 0xFFFFF));
@@ -375,6 +380,7 @@ void verlet_step(uint id, FProj p, int tb, int vi, int age, int fired, int team,
if (age > 1 && (tflags & 8) == 0 && ((tflags & 4) == 0 || age % max(fti(tb + 11).z & 0xFFFF, 1) == 0)
&& victim_at(tip, shooter, herd, shot_flags(tw, p.d.z), age, hk)) {
const float hitDmg = hit_damage(hk, p.b.x, tb, p.f) * boost_of(tw, hk) * fadeK;
projectile_hit_liquid(hk, p.b.x, tb, p.f, boost_of(tw, hk) * fadeK, tip, tipV * 60.0 * ftf(tb + 9).z, hash3(id, uint(pc.frame), 251u));
if (hitDmg > 0.0) atomicAdd(ents[hk].x.x, int(hitDmg * 1000.0));
note_attacker(hk, shooter);
ents[hk].ex19.yz = vec2(float(p.d.x + 1), float(pc.frame & 0xFFFFF));
@@ -396,6 +402,22 @@ void verlet_step(uint id, FProj p, int tb, int vi, int age, int fired, int team,
void main() {
uint id = gl_GlobalInvocationID.x;
if (pc.pass_ == 2) {
// Query once per simulation tick, shared by every flight substep.
// Dead creatures and props are omitted. Worm segments are validated
// against their root by victim_at, preserving its existing hit rules.
if (id == 0u) {
int count = 0;
for (int k = 0; k < min(ctrl.inp.w, 4096); ++k) {
const int kind = ents[k].st.x;
if (kind != 0 && (ents[k].st.w & 8) != 0) continue;
if (kind == 1 || kind == 2 || kind == 3 || (kind == 4 && ents[k].hp.x < 1.0e8) ||
(kind == 0 && (ents[k].st.w & 64) != 0 && ents[k].ex19.w > 0.5)) htargets[count++] = k;
}
hcollision.x = count;
}
return;
}
if (id >= uint(FP_MAX)) return;
FProj p = fprojs[id];
if (p.d.w == 0) return;
@@ -574,6 +596,7 @@ void main() {
const float cm = crit_mult((p.d.z >> 12) & 4095, t1.w, shooter, hk, seed);
const float hitDmg = hit_damage(hk, p.b.x * speedScale, tb, p.f) * boost_of(t1.w, hk) * cm;
if (cm > 1.0) note_crit(pos, hk);
projectile_hit_liquid(hk, p.b.x * speedScale, tb, p.f, boost_of(t1.w, hk) * cm, pos, vel * 60.0 * ftf(tb + 9).z, hash3(id, uint(pc.frame), uint(age)));
note_block(hk, p.b.x, hitDmg);
atomicAdd(ents[hk].x.x, int(hitDmg * 1000.0));
if (ents[hk].st.x == 4) body_damage(hk, pos, hitDmg); // a prop with a liquid inventory leaks