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:
1 parent
a34f42d53e
commit
00a9c1c26c
48 files changed
+5799
-435
No files matched your search
+22
-56
@@ -46,6 +46,7 @@ layout(std430, set = 0, binding = 23) buffer Ctrl {
|
||||
vec4 hand2; // host: held wand: pivot
|
||||
ivec4 burnN; // rigid_burn.comp: x the burnt pixels; y: the player's respawns, z: saved by SAVING_GRACE, w: an extra life used (the host reads and clears them)
|
||||
} ctrl;
|
||||
#include "hit_liquid.glsl"
|
||||
|
||||
const int ST_HOVER = 128; // an item that hovers (a wand in the world): no physics, a small bob (play_hover_animation)
|
||||
const int ST_SWIMMER = 256; // the immersion probe at ex.w (the integer buoyancy_check_offset_y) is tested
|
||||
@@ -54,20 +55,19 @@ const int ST_GROUNDED = 1, ST_CANJUMP = 2, ST_CANFLY = 4, ST_DEAD = 8, ST_LIQUID
|
||||
// rigid bodies are solid for creatures and the player: rigid_raster.comp marks the cells their masks cover (read here with a one-tick delay)
|
||||
layout(std430, set = 0, binding = 40) readonly buffer BodyGrid { uint bodyGrid[]; };
|
||||
layout(std430, set = 0, binding = 46) readonly buffer GlowTab { uint gtab[]; }; // per material: gfx_glow in the low 16 bits, the stain statuses it gives in bits 16-21 (WET, OILED, BLOODY, ALCOHOLIC, SLIMY, RADIOACTIVE)
|
||||
bool simplePhysics = false;
|
||||
bool body_solid(ivec2 w) {
|
||||
ivec2 p = w - ivec2(pc.view_x, pc.view_y);
|
||||
if (p.x < 0 || p.y < 0 || p.x >= pc.gw || p.y >= pc.gh) return false;
|
||||
const uint b = bodyGrid[p.y * pc.gw + p.x];
|
||||
return b != 0u && (b & 0x80000000u) == 0u; // (bit 31: a body that is no platform - the lanterns and their chains do not block, main.cpp isPlatform)
|
||||
}
|
||||
// 00bb6820: the leak emitter's offset is the local hit point, clamped to the HitboxComponent (a corpse part: +-12 here); without one (ex7.w, a prop) as it is, zero beyond 256 px
|
||||
vec2 leak_offset(Ent e, vec2 rel) { return e.ex7.w > 0.5 ? (dot(rel, rel) > 65536.0 ? vec2(0.0) : rel) : clamp(rel, vec2(-12.0), vec2(12.0)); }
|
||||
bool solid_cell_at(ivec2 w) {
|
||||
ivec2 p = w - ivec2(pc.view_x, pc.view_y);
|
||||
if (p.x < 0 || p.y < 0 || p.x >= pc.gw || p.y >= pc.gh) return false; // outside the window there is no ground (such entities are frozen)
|
||||
uint m = cell_mat(cells[p.y * pc.gw + p.x]);
|
||||
if (m != 0u && (mflags(m) & (F_STATIC | F_SAND)) != 0u) return true;
|
||||
return body_solid(w);
|
||||
return !simplePhysics && body_solid(w);
|
||||
}
|
||||
// The character's cell test 0063ef70: a platform cell counts only when one of its four neighbours (below, left, right, above) is one too - a lone pixel (a crumb of
|
||||
// rock, a single body pixel) does not stop the player. (Its other exception, the bodies held by telekinesis - 00c2dfc0 stamps their b2Body user data +0x48 with
|
||||
@@ -112,6 +112,9 @@ bool line_clear(vec2 a, vec2 b) { // no solid cell between two points (3-cell s
|
||||
return true;
|
||||
}
|
||||
bool box_hits(ivec2 o, vec4 box) {
|
||||
// SimplePhysicsSystem (00c17fc0 -> 0063f6b0) traces the entity origin
|
||||
// through grid cells. The character box/platform push-out is a different system.
|
||||
if (simplePhysics) return solid_cell_at(o);
|
||||
ivec2 lo = o + ivec2(int(box.x), int(box.z)), hi = o + ivec2(int(box.y), int(box.w)); // hi exclusive
|
||||
for (int y = lo.y; y < hi.y; ++y)
|
||||
for (int x = lo.x; x < hi.x; ++x)
|
||||
@@ -120,32 +123,6 @@ bool box_hits(ivec2 o, vec4 box) {
|
||||
}
|
||||
|
||||
// Blood: real particles of the creature's blood material that fly out along the hit and become cells where they land.
|
||||
void blood_burst(uint seed, vec2 pos, vec2 dirv, int count, float speed, int matp1, uint colour) {
|
||||
if (matp1 <= 0) return;
|
||||
for (int k = 0; k < count && k < 64; ++k) {
|
||||
float r1 = float(hash3(seed, uint(k), 1u) & 0xFFFFu) / 65536.0, r2 = float(hash3(seed, uint(k), 2u) & 0xFFFFu) / 65536.0;
|
||||
vec2 v = dirv * (0.4 + r1 * 1.2) * speed + vec2((r2 - 0.5) * 1.4 * speed, -0.3 - r1 * 0.9 * speed);
|
||||
emit_material_particle(pos + vec2(0.0, -3.0), v, 70.0 + r2 * 40.0, PART_GRAV, colour, matp1 - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Liquid of a living hit (00ab9a90): the count does not depend on the damage, N = trunc((u * (40 - 20) + 20) * blood_multiplier + 0.5) (the incoming multiplier and the world
|
||||
// multiplier are 1); the first droplet leaves along the hit, each next one is turned by a random angle (-0.6..0.6 rad) from the previous one, a speed of 0.5..1.25 times
|
||||
// the base, at the hit point +-1.5. The velocity is 20 x s x the packet impulse (px/s): a projectile's velocity x ragdoll_force_multiplier (00c091f0), an explosion's radial force (00657960).
|
||||
void hit_blood(uint seed, vec2 hit, vec2 dirv, float bloodMult, int matp1, uint colour, float speedMul) {
|
||||
if (matp1 <= 0 || bloodMult <= 0.0) return;
|
||||
const float u0 = float(hash3(seed, 0u, 11u) & 0x7FFFu) / 32767.0;
|
||||
int n = int((u0 * 20.0 + 20.0) * bloodMult + 0.5);
|
||||
vec2 v = dirv;
|
||||
for (int k = 0; k < n && k < 64; ++k) {
|
||||
float u1 = float(hash3(seed, uint(k), 12u) & 0x7FFFu) / 32767.0, u2 = float(hash3(seed, uint(k), 13u) & 0x7FFFu) / 32767.0;
|
||||
float u3 = float(hash3(seed, uint(k), 14u) & 0x7FFFu) / 32767.0, u4 = float(hash3(seed, uint(k), 15u) & 0x7FFFu) / 32767.0;
|
||||
float sp = 0.5 + 0.75 * u1, a = -0.6 + 1.2 * u2;
|
||||
v = vec2(v.x * cos(a) - v.y * sin(a), v.x * sin(a) + v.y * cos(a));
|
||||
emit_material_particle(vec2(hit.x - 1.5 + 3.0 * u4, hit.y - 1.5 + 3.0 * u3), v * (20.0 * sp / 60.0) * speedMul, 70.0 + u2 * 40.0, PART_GRAV, colour, matp1 - 1); // (20 s v px/s, v the packet impulse)
|
||||
}
|
||||
}
|
||||
|
||||
layout(std430, set = 0, binding = 27) buffer Edits { ivec4 ehead; vec4 ee[512]; }; // cell edits (see edits.comp): flames falling from a burning creature
|
||||
bool extinguishes_at(ivec2 w) { // water and the like put a burning creature out
|
||||
ivec2 p = w - ivec2(pc.view_x, pc.view_y);
|
||||
@@ -166,6 +143,7 @@ void main() {
|
||||
if (id >= uint(ctrl.inp.w)) return;
|
||||
Ent e = ents[id];
|
||||
int kind = e.st.x;
|
||||
simplePhysics = kind == 4 && (e.st.w & ST_RIGID) == 0;
|
||||
if (kind == 0 || (e.st.w & ST_DEAD) != 0) return;
|
||||
const bool is_prop = kind == 4;
|
||||
if (is_prop && e.ex7.y > 0.0) { // a corpse part with a liquid inventory: the particle emitter of the ragdoll piece (docs/BLOOD_EMITTER_LIFECYCLE.md)
|
||||
@@ -177,49 +155,35 @@ void main() {
|
||||
vec4 vr = e.ex;
|
||||
float nextf = e.ex2.x, mode = e.ex2.y;
|
||||
const uint fr = uint(ctrl.inp2.x);
|
||||
const int dmg = atomicExchange(ents[id].ex8.z, 0);
|
||||
if (dmg > 90) { // 00bb6820: strictly more than min_damage_to_leak (0.09)
|
||||
const vec2 rel = vec2(float(ents[id].ex8.x), float(ents[id].ex8.y)) * 0.001;
|
||||
const uint sd = hash3(id, fr, 53u);
|
||||
float u[8];
|
||||
for (int k = 0; k < 8; ++k) u[k] = float(hash3(sd, uint(k), 54u) & 0x7FFFu) / 32767.0;
|
||||
if (u[7] < 1.0) { // u < leak_on_damage_percent (1): fails only for the draw 32767 / 32767
|
||||
vec2 d = leak_offset(e, rel);
|
||||
const float dl = length(d);
|
||||
d = dl > 1e-4 ? d / dl : vec2(0.0);
|
||||
const float dx = d.x * (0.85 + 0.29999995 * u[0]), dy = d.y * (0.85 + 0.29999995 * u[1]);
|
||||
const float speed = 100.0 * (0.7 + (1.1 - 0.7) * u[2]);
|
||||
const float vx = speed * dx, vy = speed * dy;
|
||||
vr = vec4(vx * (0.6 + 0.39999998 * u[3]), vx * (1.0 + 0.39999998 * u[4]), vy * (0.6 + 0.39999998 * u[5]), vy * (1.0 + 0.39999998 * u[6]));
|
||||
mode = 2.0;
|
||||
}
|
||||
}
|
||||
atomicExchange(ents[id].ex8.z, 0); // Per-hit reconfiguration already happened at the source.
|
||||
if (mode > 0.5 && float(fr) >= nextf && e.ex7.z > 0.0) {
|
||||
const uint seed = hash3(id, fr, 51u);
|
||||
nextf = float(fr) + ((e.ex2.z > 0.5 && mode < 1.5) ? 3.0 + float(hash3(seed, 3u, 58u) % 3u) : 1.0 + float(seed & 1u)); // the next emission is scheduled before anything is spawned (3-5 frames when the cosmetic spray exists and no damage has rewritten the emitter)
|
||||
const int cnt = mode > 1.5 ? 1 : 1 + int(hash3(seed, 7u, 56u) % 3u);
|
||||
if (reserve >= float(cnt)) {
|
||||
if ((bloodPolicy[id].state.x & 2u) == 0u && reserve >= float(cnt)) {
|
||||
const float ca = cos(e.a.z), sa = sin(e.a.z);
|
||||
int made = 0;
|
||||
for (int k = 0; k < cnt; ++k) {
|
||||
const uint r = hash3(seed, uint(k), 52u);
|
||||
const float ux = float(r & 0x7FFFu) / 32767.0, uy = float((r >> 15) & 0x7FFFu) / 32767.0, px = float(hash3(r, 1u, 57u) & 0xFFFFu) / 65536.0, py = float(hash3(r, 2u, 57u) & 0xFFFFu) / 65536.0;
|
||||
const vec2 lo = leak_offset(e, vec2(float(ents[id].ex8.x), float(ents[id].ex8.y)) * 0.001);
|
||||
const vec2 lo = vec2(float(ents[id].ex8.x), float(ents[id].ex8.y)) * 0.001;
|
||||
const vec2 off = mode > 1.5 ? vec2(ca * lo.x - sa * lo.y, sa * lo.x + ca * lo.y) : vec2(0.0); // the damage event's offset (the hit point), turned with the body
|
||||
const vec2 at = e.a.xy + off + vec2(-1.0 + 2.0 * px, -1.0 + 2.0 * py); // rect (-1,-1) .. (1,1)
|
||||
const vec2 along = (e.ex7.w < 0.5 || (bloodPolicy[id].state.x & 32u) != 0u) ? mix(e.ex10.xy, e.a.xy, float(k + 1) / float(cnt)) : e.a.xy;
|
||||
const vec2 at = along + off + vec2(-0.5 + 2.0 * px, -0.5 + 2.0 * py); // rect (-1,-1) .. (1,1)
|
||||
if (solid_cell_at(ivec2(floor(at)))) break;
|
||||
vec2 v = vec2(mix(vr.x, vr.y, ux), mix(vr.z, vr.w, uy)) * (1.0 / 60.0); // px/s -> cells/frame
|
||||
vec2 v = vec2(mix(min(vr.x, vr.y), max(vr.x, vr.y), ux), mix(min(vr.z, vr.w), max(vr.z, vr.w), uy)) * (1.0 / 60.0); // px/s -> cells/frame
|
||||
v = vec2(ca * v.x - sa * v.y, sa * v.x + ca * v.y); // the entity's rotation
|
||||
if (emit_material_particle(at, v, 70.0 + float((r >> 24) & 0x3Fu), PART_GRAV, uint(e.ex3.y), int(e.ex7.z) - 1)) ++made; // only the particles that were created cost reserve (00bc75e0)
|
||||
}
|
||||
reserve -= float(made);
|
||||
ents[id].ex10.xy = e.a.xy;
|
||||
}
|
||||
}
|
||||
if (e.ex2.z > 0.5 && e.ex7.z > 0.0 && float(fr) >= e.ex6.z) { // blood_spray_create_some_cosmetic: a second emitter on the same reserve, 1-3 cosmetic droplets (no cells) every 1-2 frames
|
||||
const uint seed2 = hash3(id, fr, 59u);
|
||||
ents[id].ex6.z = float(fr) + 1.0 + float(seed2 & 1u);
|
||||
const int cnt2 = 1 + int(hash3(seed2, 7u, 56u) % 3u);
|
||||
if (reserve >= float(cnt2)) {
|
||||
const int cnt2 = 1 + int(hash3(seed2, 7u, 56u) % 2u);
|
||||
if ((bloodPolicy[id].state.x & 2u) == 0u && reserve >= float(cnt2)) {
|
||||
float u2[8];
|
||||
for (int k = 0; k < 8; ++k) u2[k] = float(hash3(seed2, uint(k), 60u) & 0x7FFFu) / 32767.0;
|
||||
const vec2 d = e.ex6.xy * vec2(0.85 + 0.29999995 * u2[0], 0.85 + 0.29999995 * u2[1]); // the same formula as the death emitter, other draws
|
||||
@@ -231,13 +195,15 @@ void main() {
|
||||
for (int k = 0; k < cnt2; ++k) {
|
||||
const uint r = hash3(seed2, uint(k), 61u);
|
||||
const float ux = float(r & 0x7FFFu) / 32767.0, uy = float((r >> 15) & 0x7FFFu) / 32767.0, px = float(hash3(r, 1u, 62u) & 0xFFFFu) / 65536.0, py = float(hash3(r, 2u, 62u) & 0xFFFFu) / 65536.0;
|
||||
const vec2 at = e.a.xy + vec2(-1.0 + 2.0 * px, -1.0 + 2.0 * py);
|
||||
const vec2 along = mix(e.ex10.zw, e.a.xy, float(k + 1) / float(cnt2));
|
||||
const vec2 at = along + vec2(-1.0 + 2.0 * px, -1.0 + 2.0 * py);
|
||||
if (solid_cell_at(ivec2(floor(at)))) break;
|
||||
vec2 v = vec2(mix(cr.x, cr.y, ux), mix(cr.z, cr.w, uy)) * (1.0 / 60.0);
|
||||
vec2 v = vec2(mix(min(cr.x, cr.y), max(cr.x, cr.y), ux), mix(min(cr.z, cr.w), max(cr.z, cr.w), uy)) * (1.0 / 60.0);
|
||||
v = vec2(ca * v.x - sa * v.y, sa * v.x + ca * v.y);
|
||||
if (emit_particle_hit(at, v, 25.0 + float((r >> 24) & 0x1Fu), 0.1, uint(e.ex3.y), int(e.ex7.z) - 1) != 0xFFFFFFFFu) ++made; // a droplet without a cell: it fades in the air and stains where it lands (it owns no cell: 00716ce0)
|
||||
}
|
||||
reserve -= float(made);
|
||||
ents[id].ex10.zw = e.a.xy;
|
||||
}
|
||||
}
|
||||
ents[id].ex = vr; ents[id].ex2.x = nextf; ents[id].ex2.y = mode; ents[id].ex7.y = max(reserve, 0.0);
|
||||
@@ -260,7 +226,7 @@ void main() {
|
||||
int energy = e.x.y;
|
||||
// pushed out of solid cells (sand piles landing on it, an explosion refilling, a wall appearing): find the nearest free spot above or beside
|
||||
const bool rigidBody = is_prop && (flags & ST_RIGID) != 0; // moved by the rigid-body engine: its own mask must not count as an obstacle
|
||||
if (kind != 0 && !rigidBody && box_hits(ip, e.box)) {
|
||||
if (kind != 0 && !rigidBody && !simplePhysics && box_hits(ip, e.box)) {
|
||||
bool freed = false;
|
||||
for (int k = 1; k <= 12 && !freed; ++k) {
|
||||
const ivec2 cand[5] = ivec2[5](ivec2(0, -k), ivec2(-k, -k), ivec2(k, -k), ivec2(-k, 0), ivec2(k, 0));
|
||||
@@ -403,11 +369,11 @@ void main() {
|
||||
hit_flash(id, dmg > 0, (types & 0x10000) != 0);
|
||||
if (dmg > 0) {
|
||||
hitv = vec2(float(atomicExchange(ents[id].aux.x, 0)), float(atomicExchange(ents[id].aux.y, 0))) * 0.001;
|
||||
hit_blood(hash3(id, uint(ctrl.inp2.x), 31u), pos + vec2(0.0, 0.5 * (e.box.z + e.box.w)), hitv, e.ex7.x, e.ex3.x, uint(e.ex3.y), 1.0); // (hitv: the packet impulse, px/s)
|
||||
}
|
||||
}
|
||||
// melee attack
|
||||
if (tAlive && !frozen && (ents[tgt].ex8.x & 8192) == 0 && e.ex.z > 0.5 && abs(dx) < e.ai.z && abs(dy) < e.ai.z + 4.0 && cooldown <= 0.0) {
|
||||
hit_liquid(tgt, e.ai.w * dealt_mult(e) * ents[tgt].ex9.w, ents[tgt].a.xy, vec2(0.0), 1.0, 1.0, 1, hash3(id, uint(pc.frame), 801u));
|
||||
atomicAdd(ents[tgt].x.x, int(e.ai.w * dealt_mult(e) * ents[tgt].ex9.w * 1000.0)); // the target's melee multiplier
|
||||
if (e.ai.w > 0.0 && e.ai.w * dealt_mult(e) * ents[tgt].ex9.w <= 0.0 && ents[tgt].st.x >= 1 && ents[tgt].st.x <= 3) ents[tgt].ex11.w = float(pc.frame); // (a blocked hit: BLOCK on the host)
|
||||
ents[id].rg2.w = 1 | ((ctrl.inp2.x & 0xFFFFFF) << 4);
|
||||
|
||||
Reference in new issue
Block a user