Make portals appear earlier with fade-in visuals and SFX
This commit is contained in:
parent
2f07d74c78
commit
94d371d901
55
js/game.js
55
js/game.js
@ -317,6 +317,7 @@ function playSfx(type) {
|
||||
ptero_spawn: { f0: 520, f1: 460, dur: 0.1, wave: 'sawtooth', vol: 0.05 },
|
||||
meteor_spawn: { f0: 300, f1: 200, dur: 0.14, wave: 'sawtooth', vol: 0.06 },
|
||||
meteor_land: { f0: 140, f1: 70, dur: 0.18, wave: 'square', vol: 0.08 },
|
||||
portal_fade: { f0: 240, f1: 480, dur: 0.28, wave: 'triangle', vol: 0.05 },
|
||||
hit: { f0: 260, f1: 140, dur: 0.1, wave: 'square', vol: 0.06 }
|
||||
};
|
||||
|
||||
@ -409,7 +410,10 @@ function createInitialState(config = gameConfig) {
|
||||
worldCleared: false,
|
||||
portal: {
|
||||
active: false,
|
||||
visible: false,
|
||||
entered: false,
|
||||
fadeAlpha: 0,
|
||||
fadeInSfxPlayed: false,
|
||||
// Keep portal close to the world edge so forward motion naturally carries
|
||||
// the player into it once progression is unlocked.
|
||||
x: worldWidth - 60,
|
||||
@ -721,7 +725,7 @@ function drawMeteors() {
|
||||
}
|
||||
|
||||
function drawPortal() {
|
||||
if (!state.portal.active) return;
|
||||
if (!state.portal.visible || state.portal.fadeAlpha <= 0.01) return;
|
||||
|
||||
const portal = state.portal;
|
||||
const sx = portal.x - state.cameraX;
|
||||
@ -732,7 +736,10 @@ function drawPortal() {
|
||||
const glowHeight = portal.height + 16;
|
||||
|
||||
ctx.save();
|
||||
ctx.globalAlpha = 0.2 + pulse * 0.2;
|
||||
ctx.globalAlpha = portal.fadeAlpha;
|
||||
|
||||
ctx.save();
|
||||
ctx.globalAlpha *= 0.2 + pulse * 0.2;
|
||||
ctx.fillStyle = '#84c7ff';
|
||||
ctx.fillRect(sx - 10, portal.y - 8, glowWidth, glowHeight);
|
||||
ctx.restore();
|
||||
@ -756,6 +763,7 @@ function drawPortal() {
|
||||
ctx.fillStyle = '#0f0f0f';
|
||||
ctx.font = 'bold 14px Arial';
|
||||
ctx.fillText('PORTAL', sx - 8, portal.y - 10);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function resetWorldForDifficulty(nextDifficulty) {
|
||||
@ -775,11 +783,14 @@ function resetWorldForDifficulty(nextDifficulty) {
|
||||
}
|
||||
|
||||
function updatePortal(dt) {
|
||||
state.portal.pulse += dt;
|
||||
const portal = state.portal;
|
||||
portal.pulse += dt;
|
||||
|
||||
const nextDifficulty = getNextDifficulty(gameConfig.difficulty);
|
||||
if (!nextDifficulty) {
|
||||
state.portal.active = false;
|
||||
portal.active = false;
|
||||
portal.visible = false;
|
||||
portal.fadeAlpha = Math.max(0, portal.fadeAlpha - dt * 3.2);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -787,26 +798,40 @@ function updatePortal(dt) {
|
||||
const worldEndX = worldWidth - p.width - 8;
|
||||
// Activate slightly before the hard clamp at world end so the player can
|
||||
// continue moving right and enter the portal without reversing direction.
|
||||
const portalActivationX = Math.max(0, state.portal.x - p.width - 24);
|
||||
const reachedProgressionPoint = p.x >= portalActivationX;
|
||||
const portalActivationX = Math.max(0, portal.x - p.width - 24);
|
||||
// Reveal portal much earlier, while keeping its fixed world position.
|
||||
const portalRevealX = Math.max(0, portal.x - canvas.width * 1.6);
|
||||
|
||||
if (reachedProgressionPoint) {
|
||||
state.portal.active = true;
|
||||
portal.visible = p.x >= portalRevealX;
|
||||
portal.active = p.x >= portalActivationX;
|
||||
|
||||
if (portal.visible) {
|
||||
portal.fadeAlpha = Math.min(1, portal.fadeAlpha + dt * 2.8);
|
||||
if (!portal.fadeInSfxPlayed) {
|
||||
playSfx('portal_fade');
|
||||
portal.fadeInSfxPlayed = true;
|
||||
}
|
||||
} else {
|
||||
portal.fadeAlpha = Math.max(0, portal.fadeAlpha - dt * 3.2);
|
||||
portal.fadeInSfxPlayed = false;
|
||||
}
|
||||
|
||||
if (portal.active) {
|
||||
state.worldCleared = p.x >= worldEndX;
|
||||
}
|
||||
|
||||
if (!state.portal.active || state.portal.entered) return;
|
||||
if (!portal.active || portal.entered || portal.fadeAlpha < 0.9) return;
|
||||
|
||||
const portalHitbox = {
|
||||
x: state.portal.x,
|
||||
y: state.portal.y,
|
||||
width: state.portal.width,
|
||||
height: state.portal.height
|
||||
x: portal.x,
|
||||
y: portal.y,
|
||||
width: portal.width,
|
||||
height: portal.height
|
||||
};
|
||||
|
||||
if (intersects(p, portalHitbox)) {
|
||||
state.portal.entered = true;
|
||||
addFloatingText(state.portal.x + state.portal.width / 2, state.portal.y - 14, `${nextDifficulty.toUpperCase()}!`, '#9fe6ff');
|
||||
portal.entered = true;
|
||||
addFloatingText(portal.x + portal.width / 2, portal.y - 14, `${nextDifficulty.toUpperCase()}!`, '#9fe6ff');
|
||||
resetWorldForDifficulty(nextDifficulty);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user