diff --git a/dist/audio/music1.mp3 b/dist/audio/music1.mp3 new file mode 100644 index 0000000..4ae6c23 Binary files /dev/null and b/dist/audio/music1.mp3 differ diff --git a/dist/audio/music2.mp3 b/dist/audio/music2.mp3 new file mode 100644 index 0000000..b1e99f1 Binary files /dev/null and b/dist/audio/music2.mp3 differ diff --git a/js/assets.mjs b/js/assets.mjs index 7b93028..ccabc99 100644 --- a/js/assets.mjs +++ b/js/assets.mjs @@ -39,7 +39,8 @@ export const audio = { fuelPickup: 'blip2.mp3', endEdit: 'release1.mp3', newPlanet: 'up2.mp3', - engine: 'rocket2.ogg' + engine: 'rocket2.ogg', + music: 'music2.mp3' }; export async function init() { diff --git a/js/game/audio.mjs b/js/game/audio.mjs index 473bf45..d103671 100644 --- a/js/game/audio.mjs +++ b/js/game/audio.mjs @@ -16,8 +16,20 @@ export function start(name) { } export function stop(name) { - if (!playing.has(name)) return; + if (!playing.has(name)) return false; let howl = playing.get(name); - if (howl.playing()) + if (howl.playing()) { howl.stop(); + return true; + } + return false; +} + +export function toggle(name) { + if (!stop(name)) start(name); +} + +export function volume(name, level) { + if (!playing.has(name)) return; + playing.get(name).volume(level); } diff --git a/js/game/control.mjs b/js/game/control.mjs index 6033390..53411e6 100644 --- a/js/game/control.mjs +++ b/js/game/control.mjs @@ -14,7 +14,8 @@ export const mapping = { inventory: 'KeyE', cycleRotation: 'KeyC', toggleTrace: 'KeyT', - toggleMarkers: 'KeyR' + toggleMarkers: 'KeyR', + toggleMusic: 'KeyM' }; let held, pressed; @@ -34,11 +35,17 @@ export function tick() { graphics.changeZoom(-input.mouse.scroll); } } + + if (pressed[mapping.toggleMusic]) { + audio.toggle('music'); + } } function tickPlaying() { if (held[mapping.thrust]) { playerShip.applyThrust({ forward: 1 }); + let vol = Math.min(0.7, graphics.perspective.zoom / 10); + audio.volume('engine', vol); } else { audio.stop('engine'); } diff --git a/js/game/edit.mjs b/js/game/edit.mjs index 454540b..66e497f 100644 --- a/js/game/edit.mjs +++ b/js/game/edit.mjs @@ -91,8 +91,9 @@ function getAttributes() { info = 'Mass: ' + mass + '\n' + 'Fuel capacity: ' + fuel + '\n' + - 'Thrust/mass ratio: ' + (thrust / mass).toFixed(1) + '\n' + - 'Rotation speed: ' + (rotation / mass * 100).toFixed(1) + '\n' + + 'Thrust/mass ratio: ' + (thrust / Math.max(mass, 1)).toFixed(1) + '\n' + + 'Rotation speed: ' + (rotation / Math.max(mass, 1) * 100).toFixed(1) + + '\n' + 'Cargo capacity: ' + cargo; } diff --git a/js/game/events.mjs b/js/game/events.mjs index e4e7bb7..45457b5 100644 --- a/js/game/events.mjs +++ b/js/game/events.mjs @@ -15,6 +15,15 @@ let notLife = 0; let landedPlanets = new Set(); +export function playMusic() { + audio.start('music'); + audio.volume('music', 0.8); +} + +export function stopMusic() { + audio.stop('music'); +} + function notify(message, time = 80) { if (notification === null) return; notification.text = message; @@ -44,6 +53,10 @@ export function landShip(planet) { game.state.landed = true; } +export function howToPlay() { + game.state.controls = true; +} + function newPlanet(planet) { let value = (planet.radius * 2 + 50) | 0; landedPlanets.add(planet); @@ -70,7 +83,6 @@ export function toggleEdit() { export function toggleTrace() { let trace = graphics.toggleTrace(); notify('Path prediction: ' + (trace ? 'on' : 'off')); - audio.start('engine'); } export function toggleMarkers() { diff --git a/js/game/index.mjs b/js/game/index.mjs index 966493b..db1cf98 100644 --- a/js/game/index.mjs +++ b/js/game/index.mjs @@ -24,7 +24,8 @@ export async function init() { gui.init(); input.init(); - //events.startGame(); + //events.playMusic(); + events.startGame(); //tick(); return; diff --git a/js/graphics/index.mjs b/js/graphics/index.mjs index 9b0042c..9db6106 100644 --- a/js/graphics/index.mjs +++ b/js/graphics/index.mjs @@ -108,6 +108,7 @@ class Perspective { this.oldZoom = 0; this.transition = 0; this.zoomTransition = 0; + this.zoomTransitionSpeed = 0.9; this.reset(); } @@ -122,10 +123,11 @@ class Perspective { [this.shiftX, this.shiftY] = [x, y]; } - changeZoom(zoom) { + changeZoom(zoom, speed = 0.9) { this.oldZoom = this.currentZoom; this.targetZoom = zoom; this.zoomTransition = 1; + this.zoomTransitionSpeed = speed; } get currentShift() { @@ -182,7 +184,7 @@ class Perspective { this.zoom = this.currentZoom; this.transition *= 0.9; - this.zoomTransition *= 0.9; + this.zoomTransition *= this.zoomTransitionSpeed; } reset() { @@ -201,7 +203,8 @@ class Perspective { zoomDelta(delta) { let factor = 1 + (consts.ZOOM_SPEED * Math.abs(delta)); - this.targetZoom *= delta > 0 ? factor : 1 / factor; + let target = this.targetZoom * (delta > 0 ? factor : 1 / factor); + this.changeZoom(target, 0.7); this.normalize(); } diff --git a/js/graphics/world.mjs b/js/graphics/world.mjs index 3b20687..78b5758 100644 --- a/js/graphics/world.mjs +++ b/js/graphics/world.mjs @@ -28,8 +28,9 @@ function renderParticle(particle) { function renderEntity(entity) { context.save(); context.translate(...entity.com); - if (graphics.perspective.zoom < 2 && graphics.markers) { - context.globalAlpha = 0.7 / graphics.perspective.zoom; + let alpha = Math.max(1 - ((graphics.perspective.zoom - 1) / 2), 0) ** 2; + if (alpha > 0 && graphics.markers) { + context.globalAlpha = alpha; context.beginPath(); context.arc(0, 0, 4, 0, 2 * Math.PI); context.lineWidth = 1; diff --git a/js/gui/modules.mjs b/js/gui/modules.mjs index 8f40ba9..37ecc3b 100644 --- a/js/gui/modules.mjs +++ b/js/gui/modules.mjs @@ -29,13 +29,20 @@ export function title() { let startFunction = events.startGame; let start = new GuiButton('Start game', events.startGame, 0, 0, 200); shadow.append(start); - start.posRelative({ x: 0.5, xc: 0.5, y: 0.7 }); + start.posRelative({ x: 0.5, xc: 0.5, y: 1 }); + start.y -= 160; let secondFunction = () => {}; let second = new GuiButton('Don\'t start game', secondFunction, 0, 0, 200); shadow.append(second); - second.posRelative({ x: 0.5, xc: 0.5, y: 0.7 }); - second.y += 60; + second.posRelative({ x: 0.5, xc: 0.5, y: 1 }); + second.y -= 110; + + let thirdFunction = events.howToPlay; + let third = new GuiButton('How to play', thirdFunction, 0, 0, 200); + shadow.append(third); + third.posRelative({ x: 0.5, xc: 0.5, y: 1 }); + third.y -= 60; return shadow; } diff --git a/js/world/index.mjs b/js/world/index.mjs index 7e7aa4e..bb95857 100644 --- a/js/world/index.mjs +++ b/js/world/index.mjs @@ -25,6 +25,11 @@ export function init() { spawn.tick(); } +export function remove(object) { + entities.delete(object); + celestials.delete(object); +} + export function tick() { particles.forEach(p => p.tick()); celestials.forEach(c => c.tick()); diff --git a/js/world/ship.mjs b/js/world/ship.mjs index 4ad831a..1aa5a30 100644 --- a/js/world/ship.mjs +++ b/js/world/ship.mjs @@ -103,6 +103,7 @@ export default class Ship extends Body { this.maxFuel = 0; this.rotationPower = 0; this.cargoCapacity = 0; + this.thrust = 0; this.modules.forEach(m => { if (m.type === 'fuel') { diff --git a/js/world/spawn.mjs b/js/world/spawn.mjs index 9af7dd0..f1c244d 100644 --- a/js/world/spawn.mjs +++ b/js/world/spawn.mjs @@ -8,7 +8,7 @@ import * as world from './index.mjs'; import * as consts from '../consts.mjs'; import {SECTOR_SIZE as SS} from '../consts.mjs'; -let spawnedSectors = new Set(); +let spawnedSectors = new Map(); const visibleRadius = (400 / consts.MIN_ZOOM) + SS; @@ -21,6 +21,16 @@ export function tick() { let id = `${sx}.${sy}`; if (!spawnedSectors.has(id)) spawnSector(sx, sy); } + + spawnedSectors.forEach((objects, key) => { + let [sx, sy] = key.split('.'); + let [wx, wy] = [sx * SS, sy * SS]; + let dis = (wx - px) ** 2 + (wy - py) ** 2; + if (dis > (SS * 4) ** 2) { + spawnedSectors.delete(key); + objects.forEach(world.remove); + } + }); } function nearest(x, y, set) { @@ -40,17 +50,18 @@ function nearest(x, y, set) { function spawnSector(x, y) { let area = SS ** 2; + let spawned = new Set(); for (let i = 0; i < area / 1000; i++) { let [px, py] = [(x + Math.random()) * SS, (y + Math.random()) * SS]; if (Math.random() < consts.PLANET_SPAWN_RATE / 1000) { - randomPlanet(px, py); + spawned.add(randomPlanet(px, py)); } else if (Math.random() < consts.ENTITY_SPAWN_RATE / 1000){ - randomEntity(px, py); + spawned.add(randomEntity(px, py)); } } - spawnedSectors.add(`${x}.${y}`); + spawnedSectors.set(`${x}.${y}`, spawned); } function randomPlanet(x, y, {