You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
67 lines
4.1 KiB
JavaScript
67 lines
4.1 KiB
JavaScript
// features/bounce.js; springy blocks that launch a player who lands on them.
|
|
// Trampoline builds energy while you keep jumping (higher the longer you pump / hold jump) and decays it when you don't.
|
|
import { world, system, safeGetBlock, InputButton, ButtonState } from "../core/mc.js";
|
|
|
|
const BOUNCE_BLOCKS = new Set(["rzb_dcs:dv_mid", "rzb_dcs:dv_end"]); // diving board planks (front/stairs is a stepup, not springy)
|
|
const TRAMP_BLOCKS = new Set(["rzb_dcs:tr_fl", "rzb_dcs:tr_fr", "rzb_dcs:tr_bl", "rzb_dcs:tr_br"]); // trampoline mat cells (full-cube: stood ON TOP)
|
|
const FALL_MIN = 0.12; // ignore near-zero impact (walking) so standing never bounces
|
|
const SURFACE_MAX = 0.72; // act only when feet are AT the plank surface, not the band above (avoids mid-air re-bounces)
|
|
const REST = 0.96, MAX = 1.5; // diving board: strength = min(impact*REST, MAX)
|
|
|
|
// trampoline energy model (vertical launch speed, persists across bounces)
|
|
const TRAMP_MAX = 1.7; // cap on launch
|
|
const PUMP = 0.3; // added per bounce while jumping; also the floor of an active pump
|
|
const DAMP = 0.82; // passive bounces shrink by this each time, so you settle when you stop
|
|
const SEED = 0.85; // fresh landing starts from this fraction of your fall speed...
|
|
const SEED_MIN = 0.62; // ...but never less than this, so the first bounce always pops
|
|
const SETTLE = 0.15; // below this a decaying bounce stops, so you can stand
|
|
const JUMP_GRACE = 8; // treat jump as "held" if pressed within this many ticks of contact
|
|
|
|
const _last = new Map(); // player.id -> tick of last bounce (debounce)
|
|
const _lastVy = new Map(); // player.id -> previous tick's vy (pre-collision impact; contact-tick vy is clamped to ~0)
|
|
const _energy = new Map(); // player.id -> current trampoline energy (absent = not in a bounce cycle)
|
|
const _jumpTick = new Map();// player.id -> last tick the jump button was down
|
|
|
|
const jumpDown = (p) => { try { return p.inputInfo.getButtonState(InputButton.Jump) === ButtonState.Pressed; } catch { return false; } };
|
|
|
|
system.runInterval(() => {
|
|
const t = system.currentTick;
|
|
for (const p of world.getAllPlayers()) {
|
|
if (jumpDown(p)) _jumpTick.set(p.id, t);
|
|
let v; try { v = p.getVelocity(); } catch { continue; }
|
|
const vy = v ? v.y : 0, prevVy = _lastVy.get(p.id) ?? 0;
|
|
_lastVy.set(p.id, vy);
|
|
const l = p.location, by = Math.floor(l.y), x = Math.floor(l.x), z = Math.floor(l.z);
|
|
const feet = safeGetBlock(p.dimension, { x, y: by, z });
|
|
let tramp = false, ok = false;
|
|
if (feet && BOUNCE_BLOCKS.has(feet.typeId)) { ok = (l.y - by) <= SURFACE_MAX; } // plank: feet AT surface
|
|
else { const below = safeGetBlock(p.dimension, { x, y: by - 1, z }); if (below && TRAMP_BLOCKS.has(below.typeId)) { tramp = true; ok = true; } }
|
|
|
|
let grounded = false; try { grounded = p.isOnGround; } catch {}
|
|
if (!ok) { if (grounded) _energy.delete(p.id); continue; } // landed off the trampoline -> reset its energy
|
|
|
|
if (grounded) { try { p.addEffect("jump_boost", 8, { amplifier: tramp ? 1 : 0, showParticles: false }); } catch {} }
|
|
|
|
const impact = Math.max(-vy, -prevVy); // more-negative of this/last tick (contact vy is clamped by collision)
|
|
if (impact < FALL_MIN) continue;
|
|
if (t - (_last.get(p.id) ?? -99) < 4) continue; // one bounce per landing
|
|
_last.set(p.id, t);
|
|
|
|
let strength;
|
|
if (tramp) {
|
|
const held = t - (_jumpTick.get(p.id) ?? -99) <= JUMP_GRACE;
|
|
let e = _energy.get(p.id);
|
|
if (e === undefined) e = Math.min(Math.max(impact * SEED, SEED_MIN), TRAMP_MAX); // fresh landing: seed from the fall, floored
|
|
else if (held) e = Math.min(Math.max(e, 0) + PUMP, TRAMP_MAX); // pumping/holding jump: build up
|
|
else e = e * DAMP; // passive: shrink and settle
|
|
if (held) e = Math.max(e, PUMP); // a jumped bounce always pops
|
|
if (e < SETTLE) { _energy.delete(p.id); continue; } // fully settled: let them stand
|
|
_energy.set(p.id, e);
|
|
strength = e;
|
|
} else {
|
|
strength = Math.min(impact * REST, MAX);
|
|
}
|
|
try { p.applyKnockback({ x: 0, z: 0 }, strength); } catch { try { p.applyKnockback(0, 0, 0, strength); } catch {} } // 2.0.0 vs older signature
|
|
}
|
|
}, 1);
|