From c73130e3ffd350681d5ef422d7ba8dad5e892afb Mon Sep 17 00:00:00 2001 From: Asraelite Date: Fri, 31 Mar 2023 11:43:56 +0200 Subject: [PATCH] General improvements I forgot what I actually changed. It may not even be playable, I just want to get this up there. --- .gitignore | 1 + dist/img/background_small.png | Bin 0 -> 35190 bytes dist/img/modules/small_navigation.svg | 140 ++++++++ dist/improcket.min.js | 500 +++++++++++++++----------- dist/styles.css | 4 +- js/assets.mjs | 5 +- js/consts.mjs | 4 +- js/data.mjs | 29 +- js/game/control.mjs | 13 +- js/game/edit.mjs | 16 +- js/game/events.mjs | 2 +- js/game/index.mjs | 11 +- js/graphics/background.mjs | 7 +- js/graphics/gui.mjs | 4 +- js/graphics/index.mjs | 11 +- js/graphics/world.mjs | 28 +- js/gui/modules.mjs | 14 + js/gui/text.mjs | 2 +- js/input.mjs | 2 +- js/world/body.mjs | 16 +- js/world/celestial.mjs | 13 +- js/world/index.mjs | 27 +- js/world/ship.mjs | 5 + js/world/tracer.mjs | 2 +- rollup | 2 +- 25 files changed, 584 insertions(+), 274 deletions(-) create mode 100644 dist/img/background_small.png create mode 100644 dist/img/modules/small_navigation.svg diff --git a/.gitignore b/.gitignore index 0f6f9cf..b6183af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .cache/ *.map +dist/improcket.min.js diff --git a/dist/img/background_small.png b/dist/img/background_small.png new file mode 100644 index 0000000000000000000000000000000000000000..1a5fe90c51101dbb1faac8b40e116c226c0da3a7 GIT binary patch literal 35190 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 literal 0 HcmV?d00001 diff --git a/dist/img/modules/small_navigation.svg b/dist/img/modules/small_navigation.svg new file mode 100644 index 0000000..1c20bb2 --- /dev/null +++ b/dist/img/modules/small_navigation.svg @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/dist/improcket.min.js b/dist/improcket.min.js index e0460cd..608e08f 100644 --- a/dist/improcket.min.js +++ b/dist/improcket.min.js @@ -11,8 +11,9 @@ const modules = { mass: 2, value: 5, connectivity: [false, false, true, false], - capacity: 2, - rotation: 1 + capacity: 3, + rotation: 1, + computation: 100, }, large: { name: 'Large Capsule', @@ -24,7 +25,8 @@ const modules = { value: 10, connectivity: [false, false, true, false], capacity: 5, - rotation: 4 + rotation: 4, + computation: 130, }, advanced: { name: 'Advanced Capsule', @@ -36,7 +38,8 @@ const modules = { value: 30, connectivity: [false, false, true, false], capacity: 4, - rotation: 5 + rotation: 5, + computation: 150, } }, fuel: { @@ -111,7 +114,7 @@ const modules = { 'heavy', type: 'connector', id: 'xheavy', - mass: 5, + mass: 2, value: 3, connectivity: [true, true, true, true] }, @@ -127,7 +130,7 @@ const modules = { }, gyroscope: { small: { - name: 'Small gyroscope', + name: 'Small Gyroscope', tooltip: 'Provides a small amount of rotational power to the ship.', type: 'gyroscope', id: 'small', @@ -137,7 +140,7 @@ const modules = { rotation: 2 }, large: { - name: 'Large gyroscope', + name: 'Large Gyroscope', tooltip: 'Provides a lot of rotational force for large ships.', type: 'gyroscope', id: 'large', @@ -147,6 +150,18 @@ const modules = { rotation: 4 } }, + navigation: { + small: { + name: 'Navigational Computer', + tooltip: 'Increases the length of your predicted orbital path.', + type: 'navigation', + id: 'small', + mass: 1, + value: 10, + connectivity: [true, false, true, false], + computation: 150, + }, + }, cargo: { small: { name: 'Cargo bay', @@ -167,7 +182,7 @@ const images = { logoSvg: 'logo2.svg' }, background: { - back: 'background.png', + back: 'background_small.png', middle: 'stars_back.png', front: 'stars_front.png' }, @@ -207,6 +222,9 @@ const images = { small: 'modules/small_gyroscope.svg', large: 'modules/large_gyroscope.svg' }, + navigation: { + small: 'modules/small_navigation.svg', + }, fuelcan: 'modules/fuelcan.svg' }, celestials: { @@ -233,7 +251,7 @@ const audio = { pause: 'swoosh1.mp3' }; -async function init() { +async function init$9() { let parse = (obj, convert) => Object.entries(obj).forEach(([k, v]) => { typeof v == 'object' ? parse(v, convert) : obj[k] = convert(v); }); @@ -318,7 +336,7 @@ class Module { // For fixing floating point rounding errors. const EPSILON = 1e-8; // Don't change these. -const TAU = Math.PI * 2; +const TAU$1 = Math.PI * 2; // Unit length of sector. May affect spawning a bit. const SECTOR_SIZE = 512; // G, G-boy, The big G, Mr. G, g's big brother, G-dog @@ -346,7 +364,9 @@ const ENTITY_ROTATION_RATE = 0.01; const PLANET_SPAWN_RATE = 100; const ENTITY_SPAWN_RATE = 8; const MIN_CELESTIAL_SPACING = 15; -const FUEL_CAN_AMOUNT = 4; +const FUEL_CAN_AMOUNT = 10000; + +const PLANET_IMAGE_SIZE = 250; class Body { constructor(x, y, mass) { @@ -377,7 +397,7 @@ class Body { } normalizeAngle(a = this.r) { - return ((a % TAU) + TAU) % TAU; + return ((a % TAU$1) + TAU$1) % TAU$1; } getCelestialCollision(celestials) { @@ -420,13 +440,15 @@ class Body { } tickGravity(bodies, speed = 1) { - bodies.forEach(b => { - let force = b.mass / (this.distanceTo(b) ** 2) * GRAVITATIONAL_CONSTANT; - let [[ax, ay], [bx, by]] = [this.com, b.com]; + for (let body of bodies) { + const distanceSquared = this.distanceToSquared(body); + if (distanceSquared > (1000 ** 2)) continue; + let force = body.mass / distanceSquared * GRAVITATIONAL_CONSTANT; + let [[ax, ay], [bx, by]] = [this.com, body.com]; let angle = Math.atan2(by - ay, bx - ax); this.xvel += Math.cos(angle) * force * speed; this.yvel += Math.sin(angle) * force * speed; - }); + } } distanceTo(body) { @@ -435,6 +457,12 @@ class Body { ((by - ay) ** 2)), 1); } + distanceToSquared(body) { + let [[ax, ay], [bx, by]] = [this.com, body.com]; + return Math.max(((bx - ax) ** 2) + + ((by - ay) ** 2), 1); + } + angleTo(ax, ay, bx, by) { return Math.atan2(by - ay, bx - ax); } @@ -606,7 +634,7 @@ let usedSpace = 0; let onupdate = () => {}; -function init$1() { +function init$8() { items.clear(); update(); } @@ -625,7 +653,7 @@ function getTiles() { function addItem(type, id) { let mapId = toId(type, id); - if (!items.has(mapId)) items.set(mapId, new Tile(type, id)); + if (!items.has(mapId)) items.set(mapId, new Tile$1(type, id)); let tile = items.get(mapId); tile.increase(); update(); @@ -664,7 +692,7 @@ function toId(type, id) { return `${type}.${id}`; } -class Tile { +class Tile$1 { constructor(type, id, q = 0) { this.type = type; this.id = id; @@ -748,7 +776,7 @@ let notLife = 0; let landedPlanets = new Set(); -function init$2() { +function init$7() { score = 0; shipLanded = false; } @@ -762,13 +790,13 @@ function playMusic() { volume('music', 0.4); } -function notify(message$$1, time = 80) { +function notify(message, time = 80) { if (notification === null) return; - notification.text = message$$1; + notification.text = message; notLife = time; } -function tick() { +function tick$6() { if (notification === null) return; if ((notLife-- <= 0 || state.gameOver) && !state.paused) notification.text = ''; @@ -779,15 +807,15 @@ function setNotificationElement(el) { } function startGame() { - init$2(); + init$7(); state.gameOver = false; - changeView$1('game'); + changeView('game'); perspective.reset(); perspective.focusPlayer(); } function toMenu() { - changeView$1('menu'); + changeView('menu'); } function togglePause() { @@ -808,7 +836,7 @@ function landShip(planet) { } function howToPlay() { - changeView$1('instructions'); + changeView('instructions'); } function newPlanet(planet) { @@ -858,27 +886,27 @@ function toggleEdit() { } state.editing = true; state.inventory = true; - init$4(); + init$5(); } -function toggleTrace() { - let trace$$1 = toggleTrace$1(); - notify('Path prediction: ' + (trace$$1 ? 'on' : 'off')); +function toggleTrace$1() { + let trace = toggleTrace(); + notify('Path prediction: ' + (trace ? 'on' : 'off')); } -function toggleMarkers() { - let markers$$1 = toggleMarkers$1(); - notify('Item markers: ' + (markers$$1 ? 'on' : 'off')); +function toggleMarkers$1() { + let markers = toggleMarkers(); + notify('Item markers: ' + (markers ? 'on' : 'off')); } -function cycleRotationMode() { - let message$$1 = { +function cycleRotationMode$1() { + let message = { parent: 'planet', local: 'ship', universe: 'universe' - }[cycleRotationMode$1()]; + }[cycleRotationMode()]; - notify('Rotation view: ' + message$$1); + notify('Rotation view: ' + message); } function endEditing() { @@ -906,7 +934,7 @@ function collectItem(type, id, name) { notify('Collected fuel: +10'); return true; } else { - if (usedSpace > capacity) { + if (usedSpace >= capacity) { notify('No space left in inventory', 60); return false; } @@ -951,7 +979,7 @@ class Tracer extends Body { } tick() { - this.run(100); + this.run(this.ship.computation); } tickPath(speed) { @@ -976,6 +1004,7 @@ class Ship extends Body { this.rotationPower = 0; this.cargoCapacity = 0; this.thrust = 0; + this.computation = 0; this.crashed = false; this.timeWithoutFuel = 0; } @@ -1067,6 +1096,7 @@ class Ship extends Body { this.rotationPower = 0; this.cargoCapacity = 0; this.thrust = 0; + this.computation = 0; this.modules.forEach(m => { if (m.type === 'fuel') { @@ -1074,12 +1104,15 @@ class Ship extends Body { } else if (m.type === 'capsule') { this.rotationPower += m.data.rotation; this.cargoCapacity += m.data.capacity; + this.computation += m.data.computation; } else if (m.type === 'thruster') { this.thrust += m.data.thrust; } else if (m.type === 'gyroscope') { this.rotationPower += m.data.rotation; } else if (m.type === 'cargo') { this.cargoCapacity += m.data.capacity; + } else if (m.type === 'navigation') { + this.computation += m.data.computation; } }); } @@ -1198,8 +1231,15 @@ class Celestial extends Body { this.radius = radius; this.type = type; - let imageArr = Object.values(images.celestials[this.type]); - this.image = imageArr[Math.random() * imageArr.length | 0]; + const imageArr = Object.values(images.celestials[this.type]); + const svgImage = imageArr[Math.random() * imageArr.length | 0]; + tempCanvas.width = PLANET_IMAGE_SIZE; + tempCanvas.height = PLANET_IMAGE_SIZE; + tempContext.clearRect(0, 0, PLANET_IMAGE_SIZE, PLANET_IMAGE_SIZE); + tempContext.drawImage(svgImage, 0, 0, PLANET_IMAGE_SIZE, PLANET_IMAGE_SIZE); + this.image = new Image(); + this.image.src = tempCanvas.toDataURL(); + // this.image = tempContext.getImageData(0, 0, PLANET_IMAGE_SIZE, PLANET_IMAGE_SIZE); } get com() { @@ -1287,7 +1327,7 @@ let spawnedSectors = new Map(); const visibleRadius = (400 / MIN_ZOOM) + SECTOR_SIZE; -function tick$1() { +function tick$5() { let [px, py] = playerShip.com; for (let x = px - visibleRadius; x < px + visibleRadius; x += SECTOR_SIZE) @@ -1350,7 +1390,7 @@ function randomPlanet(x, y, { if (cel !== null && dis < Math.max(radius, cel.radius) * mcs) return; - let planet = celestial(x, y, radius, { + let planet = celestial$1(x, y, radius, { density: density, type: type }); @@ -1425,7 +1465,7 @@ function startPlanet() { return planet; } -function celestial(x, y, radius, params) { +function celestial$1(x, y, radius, params) { let celestial = new Celestial(x - radius, y - radius, radius, params); celestials.add(celestial); return celestial; @@ -1439,15 +1479,17 @@ const tracers = new Set(); let playerShip = null; +let speed = 1; + function setPlayerShip(ship) { playerShip = ship; } -function init$3() { +function init$6() { clear(); player(); - let p = startPlanet(); - tick$1(); + startPlanet(); + tick$5(); } function clear() { @@ -1463,13 +1505,24 @@ function remove(object) { celestials.delete(object); } -function tick$2() { - particles.forEach(p => p.tick()); - celestials.forEach(c => c.tick()); - entities.forEach(e => e.tick()); - ships.forEach(s => s.tick()); +function increaseSpeed() { + if (speed < 5) speed += 1; +} + +function decreaseSpeed() { + if (speed > 1) speed -= 1; +} + +function tick$4() { + for (let i = 0; i < speed; i++) { + particles.forEach(p => p.tick()); + celestials.forEach(c => c.tick()); + entities.forEach(e => e.tick()); + ships.forEach(s => s.tick()); + } + + tick$5(); if (trace) tracers.forEach(t => t.tick()); - tick$1(); } let tiles = new Map(); @@ -1479,22 +1532,26 @@ let position = [0, 0]; let message = ''; let info = ''; -function init$4() { +function init$5() { let ship = playerShip; - let modules$$1 = ship.modules; + let modules = ship.modules; tiles.clear(); - modules$$1.forEach(m => { + modules.forEach(m => { let pos = [m.x, m.y]; - tiles.set(posId(...pos), new Tile$1(...pos, m)); + tiles.set(posId(...pos), new Tile(...pos, m)); }); message = ''; adjustSize(); + adjustGraphics(); +} + +function adjustGraphics() { let neededZoom = canvas.width / (Math.max(width, height) + 10); - changePerspective('planet', 0, -5); + changePerspective('planet', 0, -3); setZoom(neededZoom); } @@ -1526,6 +1583,9 @@ function end() { let [dx, dy] = [nx - ox, ny - oy]; ship.x -= dx; ship.y -= dy; + const [rdx, rdy] = ship.rotateVector(dx, dy); + ship.x -= rdx; + ship.y -= rdy; } return result; @@ -1537,6 +1597,7 @@ function getAttributes() { let rotation = 0; let mass = 0; let thrust = 0; + let computation = 0; tiles.forEach(t => { if (t.type === null) return; @@ -1545,12 +1606,15 @@ function getAttributes() { } else if (t.type === 'capsule') { rotation += t.module.rotation; cargo += t.module.capacity; + computation += t.module.computation; } else if (t.type === 'thruster') { thrust += t.module.thrust; } else if (t.type === 'gyroscope') { rotation += t.module.rotation; } else if (t.type === 'cargo') { cargo += t.module.capacity; + } else if (t.type === 'nafivation') { + computation += t.module.computation; } mass += t.module.mass; }); @@ -1560,7 +1624,8 @@ function getAttributes() { '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; + 'Cargo capacity: ' + cargo + '\n' + + 'Navigational computation: ' + computation; } function validate() { @@ -1627,12 +1692,11 @@ function clickTile(x, y) { let current = getTile(x, y).source; if (current.type !== null) { return; - } else { } let pos = positionAdjust(x, y); let id = posId(...pos); - tiles.set(id, new Tile$1(...pos, currentItem)); + tiles.set(id, new Tile(...pos, currentItem)); removeItem(...currentItem.ident); adjustSize(); validate(); @@ -1643,7 +1707,7 @@ function rightClickTile(x, y) { if (current.type === null) return; let { x: tx, y: ty } = current; let id = posId(tx, ty); - tiles.set(id, new Tile$1(tx, ty, null)); + tiles.set(id, new Tile(tx, ty, null)); addItem(current.type, current.id); adjustSize(); validate(); @@ -1657,7 +1721,7 @@ function getTile(x, y) { function getRawTile(x, y) { let id = posId(x, y); if (!tiles.has(id)) - tiles.set(id, new Tile$1(x, y, null)); + tiles.set(id, new Tile(x, y, null)); return tiles.get(id); // TODO: Get linked tiles. } @@ -1678,7 +1742,7 @@ function getBoundaries() { return [sx, ex, sy, ey]; } -class Tile$1 { +class Tile { constructor(x, y, module) { if (module === null) { this.module = null; @@ -1726,7 +1790,7 @@ function tick$3() { mouse.scroll = 0; } -function init$5() { +function init$4() { window.addEventListener('keydown', event => { keyCode.pressed[event.code] = !keyCode.held[event.code]; keyCode.held[event.code] = true; @@ -1757,7 +1821,7 @@ function init$5() { window.addEventListener('wheel', event => { mouse.scroll = event.deltaY; - event.preventDefault(); + // event.preventDefault(); }); window.addEventListener('contextmenu', event => { @@ -1990,7 +2054,7 @@ class GuiInventory extends GuiElement { for (let y = 0; y < this.tileHeight; y++) for (let x = 0; x < this.tileWidth && tiles.length; x++) { - let i = y * this.tileWidth + (x % this.tileWidth) + offset; + y * this.tileWidth + (x % this.tileWidth) + offset; tile = tiles.shift(); let ex = x * tileSize + spacing / 2 + ox; @@ -2133,7 +2197,7 @@ class GuiText extends GuiElement { this.color = color; this.text = text; this.spacing = size * 1.2; - this.font = size + 'px Consolas'; + this.font = size + 'px Courier New'; this.align = align; this.valign = valign; } @@ -2163,14 +2227,14 @@ class GuiText extends GuiElement { } } -function root() { +function root$1() { return new GuiFrame(0, 0, canvas.width, canvas.height, { draw: false }); } function title() { - let shadow = root(); + let shadow = root$1(); let logo = new GuiImage(images.title.logo); shadow.append(logo); logo.scaleImage({ w: shadow.w * 0.7 }); @@ -2222,7 +2286,7 @@ running out of fuel. `; function instructions() { - let shadow = root(); + let shadow = root$1(); let frame = new GuiFrame(); shadow.append(frame); @@ -2246,7 +2310,7 @@ function instructions() { } function game() { - let shadow = root(); + let shadow = root$1(); let editButton = new GuiButton('Edit rocket', toggleEdit, 0, 0, 200); shadow.append(editButton); @@ -2279,21 +2343,35 @@ function game() { ship.maxFuel.toFixed(1); }; - let score$$1 = new GuiText('', 0, 0, 0, 0, { + let speed$1 = new GuiText('', 0, 0, 0, 0, { + size: 14, + align: 'right', + valign: 'bottom', + + }); + shadow.append(speed$1); + speed$1.posRelative({x: 1, y: 1}); + speed$1.y -= 30; + speed$1.x -= 10; + speed$1.tick = () => { + speed$1.text = 'Speed: ' + speed.toFixed(1) + 'x'; + }; + + let score$1 = new GuiText('', 0, 0, 0, 0, { size: 14, align: 'left', valign: 'bottom' }); - shadow.append(score$$1); - score$$1.posRelative({x: 0, y: 1}); - score$$1.y -= 10; - score$$1.x += 10; - score$$1.tick = () => { - score$$1.text = 'Score: ' + score; + shadow.append(score$1); + score$1.posRelative({x: 0, y: 1}); + score$1.y -= 10; + score$1.x += 10; + score$1.tick = () => { + score$1.text = 'Score: ' + score; }; - let editShadow = root(); + let editShadow = root$1(); shadow.append(editShadow); editShadow.posRelative({x: 0.45, y: 0, w: 0.55, h: 0.6}); editShadow.x -= 10; @@ -2326,7 +2404,7 @@ function game() { }; - let invShadow = root(); + let invShadow = root$1(); shadow.append(invShadow); invShadow.posRelative({x: 0, w: 0.4, h: 0.6}); invShadow.x += 10; @@ -2375,32 +2453,32 @@ function game() { setNotificationElement(notification); - let gameOver$$1 = root(); - shadow.append(gameOver$$1); - gameOver$$1.posRelative({x: 0.2, y: 0.2, w: 0.6, h: 0.6}); + let gameOver = root$1(); + shadow.append(gameOver); + gameOver.posRelative({x: 0.2, y: 0.2, w: 0.6, h: 0.6}); let gameOverMain = new GuiText('Game over', 0, 0, 0, 0, { size: 48, align: 'center', valign: 'top' }); - gameOver$$1.append(gameOverMain); + gameOver.append(gameOverMain); gameOverMain.posRelative({x: 0.5}); gameOverMain.y += 10; - gameOver$$1.tick = () => { - gameOver$$1.options.drawChildren = state.gameOver; + gameOver.tick = () => { + gameOver.options.drawChildren = state.gameOver; }; - let gameOverReason$$1 = new GuiText('', 0, 0, 0, 0, { + let gameOverReason$1 = new GuiText('', 0, 0, 0, 0, { size: 14, align: 'center', valign: 'top' }); - gameOver$$1.append(gameOverReason$$1); - gameOverReason$$1.posRelative({x: 0.5}); - gameOverReason$$1.y += 100; - gameOverReason$$1.tick = () => { - gameOverReason$$1.text = gameOverReason; + gameOver.append(gameOverReason$1); + gameOverReason$1.posRelative({x: 0.5}); + gameOverReason$1.y += 100; + gameOverReason$1.tick = () => { + gameOverReason$1.text = gameOverReason; }; let gameOverScore = new GuiText('', 0, 0, 0, 0, { @@ -2408,7 +2486,7 @@ function game() { align: 'center', valign: 'top' }); - gameOver$$1.append(gameOverScore); + gameOver.append(gameOverScore); gameOverScore.posRelative({x: 0.5}); gameOverScore.y += 200; gameOverScore.tick = () => { @@ -2416,7 +2494,7 @@ function game() { }; let gameOverExit = new GuiButton('Main menu', toMenu, 0, 0, 200); - gameOver$$1.append(gameOverExit); + gameOver.append(gameOverExit); gameOverExit.posRelative({ x: 0.5, xc: 0.5, y: 1 }); gameOverExit.y -= 10; @@ -2424,36 +2502,36 @@ function game() { } const elements = new Set(); -let root$1; +let root; -function init$6() { +function init$3() { elements.clear(); - root$1 = root(); - changeView('menu'); + root = root$1(); + changeView$1('menu'); } -function tick$4() { - root$1.tickElement(); +function tick$2() { + root.tickElement(); } -function changeView(view) { - root$1.clear(); +function changeView$1(view) { + root.clear(); if (view === 'menu') { - root$1.append(title()); + root.append(title()); } if (view === 'game') { - root$1.append(game()); + root.append(game()); } if (view === 'instructions') { - root$1.append(instructions()); + root.append(instructions()); } } -function render() { - renderElement(root$1); +function render$3() { + renderElement(root); } function renderElement(element) { @@ -2461,7 +2539,7 @@ function renderElement(element) { if (element.type === 'frame') renderFrame(element); if (element.type === 'image') renderImage(element); if (element.type === 'button') renderButton(element); - if (element.type === 'edit') renderEdit(element); + if (element.type === 'edit') ; if (element.type === 'itemButton') renderItemButton(element); if (element.type === 'inventory') renderInventory(element); if (element.type === 'text') renderText(element); @@ -2530,7 +2608,7 @@ function renderButton(element) { context.textAlign = 'center'; context.textBaseline = 'middle'; context.fillStyle = '#fff'; - context.font = '12pt Consolas'; + context.font = '12pt Courier New'; context.fillText(element.text, ...element.center); context.globalAlpha = 1; @@ -2567,16 +2645,12 @@ function renderItemButton(element) { context.textAlign = 'right'; context.textBaseline = 'bottom'; context.fillStyle = '#fff'; - context.font = 'bold 10pt Consolas'; + context.font = 'bold 10pt Courier New'; let [ex, ey] = element.end; context.fillText('x' + element.quantity, ex - 2, ey - 2); } } -function renderEdit(element) { - -} - function renderInventory(element) { context.globalAlpha = 0.1; context.fillStyle = '#541'; @@ -2584,12 +2658,12 @@ function renderInventory(element) { context.globalAlpha = 1; } -function render$1() { - particles.forEach(renderParticle); - celestials.forEach(renderCelestial); - if (trace) tracers.forEach(renderTracer); - ships.forEach(renderShip); - entities.forEach(renderEntity); +function render$2() { + for (particle of particles) renderParticle(particle); + for (celestial of celestials) if (isVisible(celestial)) renderCelestial(celestial); + if (trace) for (tracer of tracers) renderTracer(tracer); + for (ship of ships) renderShip(ship); + for (entity of entities) if (isVisible(entity)) renderEntity(entity); /* if (typeof window.q === 'undefined') window.q = []; @@ -2600,6 +2674,16 @@ function render$1() { */ } +function isVisible(body) { + const [bx, by] = body.com; + const [px, py] = [perspective.x, perspective.y]; + perspective.bounds; + const [centerX, centerY] = [px, py]; + const margin = 1000; + return bx > centerX - margin && bx < centerX + margin && + by > centerY - margin && by < centerY + margin; +} + function renderParticle(particle) { context.fillStyle = particle.color; context.fillRect(...particle.com, particle.size, particle.size); @@ -2633,18 +2717,16 @@ function renderShip(ship) { let [cx, cy] = ship.localCom; context.translate(-cx, -cy); ship.modules.forEach(m => { - let [mx, my] = [m.x, m.y]; - if (state.editing) { - - } + [m.x, m.y]; + if (state.editing) ; context.drawImage(m.currentImage, m.x, m.y, 1, 1); }); context.restore(); } -const celestialImages = { +({ green: Object.values(images.celestials.green) -}; +}); function renderCelestial(cel) { context.drawImage(cel.image, cel.x, cel.y, @@ -2671,7 +2753,7 @@ function renderTracer(tracer) { let patterns = null; -function init$7() { +function init$2() { patterns = { back: context.createPattern(images.background.back, 'repeat'), middle: context.createPattern(images.background.middle, 'repeat'), @@ -2679,55 +2761,44 @@ function init$7() { }; } -function render$2(angle) { - if (patterns === null) init$7(); - - renderLayer(patterns.back, 0.3, 1, angle); - renderLayer(patterns.middle, 0.5, 0.3, angle); - //renderLayer(patterns.front, 0.7, 0.3, angle); +function render$1(angle) { + if (patterns === null) init$2(); + // renderLayer(patterns.back, 0.3, 1, angle); + // renderLayer(patterns.middle, 0.5, 0.3, angle); + // renderLayer(patterns.front, 0.7, 0.3, angle); } -function renderLayer(pattern, speed = 1, scale = 1, angle = 0) { - context.save(); - let outset = (Math.abs(Math.cos(angle)) + Math.abs(Math.sin(angle))); - outset = ((outset - 1) * canvas.width) / scale; - let [px, py] = [perspective.x * speed, perspective.y * speed]; - context.translate(-px, -py); - context.scale(scale, scale); - context.fillStyle = pattern; - context.fillRect(px / scale - outset / 2, py / scale - outset / 2, - canvas.width / scale + outset, canvas.height / scale + outset); - context.restore(); -} - -const TAU$1 = TAU; +const TAU = TAU$1; let canvas, context, tempCanvas, tempContext; let perspective; let trace = true; let markers = true; -function init$8() { +function init$1() { canvas = document.querySelector('#main'); context = canvas.getContext('2d'); tempCanvas = document.querySelector('#temp'); tempContext = tempCanvas.getContext('2d'); - canvas.width = 600; - canvas.height = 600; + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + + canvas.style.width = canvas.width + 'px'; + canvas.style.height = canvas.height + 'px'; perspective = new Perspective(); context.fillStyle = '#000'; context.fillRect(0, 0, canvas.width, canvas.height); - context.font = '36px Consolas'; + context.font = '36px Courier New'; context.textAlign = 'center'; context.textBaseline = 'middle'; context.fillStyle = '#fff'; context.fillText('Loading...', canvas.width / 2, canvas.height / 2); } -function render$3() { +function render() { context.clearRect(0, 0, canvas.width, canvas.height); context.fillStyle = '#000'; context.fillRect(0, 0, canvas.width, canvas.height); @@ -2739,12 +2810,12 @@ function render$3() { context.save(); perspective.tick(); perspective.transformRotate(); - render$2(perspective.rotation); + render$1(perspective.rotation); perspective.transformCanvas(); - render$1(); + render$2(); context.restore(); - render(); + render$3(); } function changePerspective(rotationMode, shiftX = 0, shiftY = 0) { @@ -2753,7 +2824,7 @@ function changePerspective(rotationMode, shiftX = 0, shiftY = 0) { perspective.transition = 1; } -function cycleRotationMode$1() { +function cycleRotationMode() { if (perspective.rotationMode === 'parent') { perspective.changeRotationMode('local'); } else if (perspective.rotationMode === 'local') { @@ -2765,12 +2836,12 @@ function cycleRotationMode$1() { return perspective.rotationMode; } -function toggleTrace$1() { +function toggleTrace() { trace = !trace; return trace; } -function toggleMarkers$1() { +function toggleMarkers() { markers = !markers; return markers; } @@ -2903,7 +2974,7 @@ class Perspective { normalize() { this.targetZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, this.targetZoom)); - this.targetRotation %= TAU$1; + this.targetRotation %= TAU; } transformRotate() { @@ -2928,11 +2999,11 @@ class Perspective { } normalizeAngle(a = this.r) { - return ((a % TAU$1) + TAU$1) % TAU$1; + return ((a % TAU) + TAU) % TAU; } } -const mapping$1 = { +const mapping = { thrust: 'KeyW', left: 'KeyA', right: 'KeyD', @@ -2945,12 +3016,14 @@ const mapping$1 = { toggleMusic: 'KeyM', togglePause: 'KeyP', zoomIn: 'KeyZ', - zoomOut: 'KeyX' + zoomOut: 'KeyX', + increaseSpeed: 'Period', + decreaseSpeed: 'Comma', }; let held, pressed; -function tick$5() { +function tick$1() { held = keyCode.held; pressed = keyCode.pressed; @@ -2967,32 +3040,40 @@ function tick$5() { changeZoom(delta); } - if (held[mapping$1.zoomIn]) { + if (held[mapping.zoomIn]) { changeZoom(-10); } - if (held[mapping$1.zoomOut]) { + if (held[mapping.zoomOut]) { changeZoom(10); } - if (pressed[mapping$1.togglePause] && !state.gameOver) { + if (pressed[mapping.togglePause] && !state.gameOver) { togglePause(); } + + if (pressed[mapping.increaseSpeed]) { + increaseSpeed(); + } + + if (pressed[mapping.decreaseSpeed]) { + decreaseSpeed(); + } } if (state.gameOver) { stop('engine'); } - if (pressed[mapping$1.toggleMusic]) { + if (pressed[mapping.toggleMusic]) { toggle('music'); } } function tickPlaying() { - let power = held[mapping$1.reduce] ? 0.3 : 1; + let power = held[mapping.reduce] ? 0.3 : 1; - if (held[mapping$1.thrust] && playerShip.fuel !== 0) { + if (held[mapping.thrust] && playerShip.fuel !== 0) { playerShip.applyThrust({ forward: power }); let vol = Math.min(0.7, perspective.zoom / 10); volume('engine', vol); @@ -3000,7 +3081,7 @@ function tickPlaying() { stop('engine'); } - if (pressed[mapping$1.thrust]) { + if (pressed[mapping.thrust]) { if (playerShip.fuel !== 0) { start('engine'); } else { @@ -3008,40 +3089,40 @@ function tickPlaying() { } } - if (held[mapping$1.left]) { + if (held[mapping.left]) { playerShip.applyThrust({ turnLeft: power }); } - if (held[mapping$1.right]) { + if (held[mapping.right]) { playerShip.applyThrust({ turnRight: power }); } - if (pressed[mapping$1.inventory]) { + if (pressed[mapping.inventory]) { state.inventory = !state.inventory; } - if (pressed[mapping$1.cycleRotation]) { - cycleRotationMode(); + if (pressed[mapping.cycleRotation]) { + cycleRotationMode$1(); } - if (pressed[mapping$1.toggleTrace]) { - toggleTrace(); + if (pressed[mapping.toggleTrace]) { + toggleTrace$1(); } - if (pressed[mapping$1.toggleMarkers]) { - toggleMarkers(); + if (pressed[mapping.toggleMarkers]) { + toggleMarkers$1(); } } function tickEditing() { - if (pressed[mapping$1.exitEdit]) { + if (pressed[mapping.exitEdit]) { endEditing(); } } let state; -async function init$9() { +async function init() { state = { view: 'menu', playing: false, @@ -3051,68 +3132,59 @@ async function init$9() { gameOver: false }; - init$8(); - await init(); - init$6(); - init$5(); + init$1(); + await init$9(); + init$3(); + init$4(); playMusic(); //events.startGame(); - loop(tick$6); + loop(tick); } -function changeView$1(view) { +function changeView(view) { state.view = view; - changeView(view); + changeView$1(view); if (view === 'game') { state.playing = true; state.editing = false; state.paused = false; - init$3(); - init$4(); + init$6(); + init$5(); perspective.reset(); - init$1(); + init$8(); } else if (view === 'instructions') { state.playing = false; - changeView('instructions'); + changeView$1('instructions'); } else if (view === 'menu') { - changeView('menu'); + changeView$1('menu'); clear(); } } function loop(fn, fps = 60) { - let then = Date.now(); - let interval = 1000 / fps; (function loop(time) { + fn(); + requestAnimationFrame(loop); - - // again, Date.now() if it's available - let now = Date.now(); - let delta = now - then; - - if (delta > interval) { - then = now - (delta % interval); - fn(); - } - })(0); + })(); } -function tick$6() { - tick(); +function tick() { + tick$6(); if (state.view == 'game' && !state.paused) { - tick$2(); + tick$4(); } - tick$5(); + tick$1(); - tick$4(); - render$3(); + tick$2(); + render(); tick$3(); } -window.addEventListener('load', init$9); +window.addEventListener('load', init); //# sourceMappingURL=improcket.min.js.map diff --git a/dist/styles.css b/dist/styles.css index 6ff196f..3fb065e 100644 --- a/dist/styles.css +++ b/dist/styles.css @@ -13,8 +13,8 @@ body { } #main { - width: 600px; - height: 600px; + width: 1300px; + height: 900px; margin: 50px auto; border: 2px solid #111; background-color: #fff; diff --git a/js/assets.mjs b/js/assets.mjs index 64c416f..aff05ae 100644 --- a/js/assets.mjs +++ b/js/assets.mjs @@ -4,7 +4,7 @@ export const images = { logoSvg: 'logo2.svg' }, background: { - back: 'background.png', + back: 'background_small.png', middle: 'stars_back.png', front: 'stars_front.png' }, @@ -44,6 +44,9 @@ export const images = { small: 'modules/small_gyroscope.svg', large: 'modules/large_gyroscope.svg' }, + navigation: { + small: 'modules/small_navigation.svg', + }, fuelcan: 'modules/fuelcan.svg' }, celestials: { diff --git a/js/consts.mjs b/js/consts.mjs index 5240da8..4024c6f 100644 --- a/js/consts.mjs +++ b/js/consts.mjs @@ -37,4 +37,6 @@ export const ENTITY_ROTATION_RATE = 0.01; export const PLANET_SPAWN_RATE = 100; export const ENTITY_SPAWN_RATE = 8; export const MIN_CELESTIAL_SPACING = 15; -export const FUEL_CAN_AMOUNT = 4; +export const FUEL_CAN_AMOUNT = 10000; + +export const PLANET_IMAGE_SIZE = 250; diff --git a/js/data.mjs b/js/data.mjs index 151e8cb..176818f 100644 --- a/js/data.mjs +++ b/js/data.mjs @@ -11,8 +11,9 @@ export const modules = { mass: 2, value: 5, connectivity: [false, false, true, false], - capacity: 2, - rotation: 1 + capacity: 3, + rotation: 1, + computation: 100, }, large: { name: 'Large Capsule', @@ -24,7 +25,8 @@ export const modules = { value: 10, connectivity: [false, false, true, false], capacity: 5, - rotation: 4 + rotation: 4, + computation: 130, }, advanced: { name: 'Advanced Capsule', @@ -36,7 +38,8 @@ export const modules = { value: 30, connectivity: [false, false, true, false], capacity: 4, - rotation: 5 + rotation: 5, + computation: 150, } }, fuel: { @@ -111,7 +114,7 @@ export const modules = { 'heavy', type: 'connector', id: 'xheavy', - mass: 5, + mass: 2, value: 3, connectivity: [true, true, true, true] }, @@ -127,7 +130,7 @@ export const modules = { }, gyroscope: { small: { - name: 'Small gyroscope', + name: 'Small Gyroscope', tooltip: 'Provides a small amount of rotational power to the ship.', type: 'gyroscope', id: 'small', @@ -137,7 +140,7 @@ export const modules = { rotation: 2 }, large: { - name: 'Large gyroscope', + name: 'Large Gyroscope', tooltip: 'Provides a lot of rotational force for large ships.', type: 'gyroscope', id: 'large', @@ -147,6 +150,18 @@ export const modules = { rotation: 4 } }, + navigation: { + small: { + name: 'Navigational Computer', + tooltip: 'Increases the length of your predicted orbital path.', + type: 'navigation', + id: 'small', + mass: 1, + value: 10, + connectivity: [true, false, true, false], + computation: 150, + }, + }, cargo: { small: { name: 'Cargo bay', diff --git a/js/game/control.mjs b/js/game/control.mjs index 950e2d8..279b588 100644 --- a/js/game/control.mjs +++ b/js/game/control.mjs @@ -3,6 +3,7 @@ import * as events from './events.mjs'; import * as graphics from '../graphics/index.mjs'; import * as inventory from './inventory.mjs'; import * as audio from './audio.mjs'; +import * as world from '../world/index.mjs'; import {playerShip} from '../world/index.mjs'; import {state} from './index.mjs'; @@ -19,7 +20,9 @@ export const mapping = { toggleMusic: 'KeyM', togglePause: 'KeyP', zoomIn: 'KeyZ', - zoomOut: 'KeyX' + zoomOut: 'KeyX', + increaseSpeed: 'Period', + decreaseSpeed: 'Comma', }; let held, pressed; @@ -52,6 +55,14 @@ export function tick() { if (pressed[mapping.togglePause] && !state.gameOver) { events.togglePause(); } + + if (pressed[mapping.increaseSpeed]) { + world.increaseSpeed(); + } + + if (pressed[mapping.decreaseSpeed]) { + world.decreaseSpeed(); + } } if (state.gameOver) { diff --git a/js/game/edit.mjs b/js/game/edit.mjs index 4270154..d7564e0 100644 --- a/js/game/edit.mjs +++ b/js/game/edit.mjs @@ -29,8 +29,12 @@ export function init() { message = ''; adjustSize(); + adjustGraphics(); +} + +function adjustGraphics() { let neededZoom = graphics.canvas.width / (Math.max(width, height) + 10); - graphics.changePerspective('planet', 0, -5); + graphics.changePerspective('planet', 0, -3); graphics.setZoom(neededZoom); } @@ -62,6 +66,9 @@ export function end() { let [dx, dy] = [nx - ox, ny - oy]; ship.x -= dx; ship.y -= dy; + const [rdx, rdy] = ship.rotateVector(dx, dy); + ship.x -= rdx; + ship.y -= rdy; } return result; @@ -73,6 +80,7 @@ function getAttributes() { let rotation = 0; let mass = 0; let thrust = 0; + let computation = 0; tiles.forEach(t => { if (t.type === null) return; @@ -81,12 +89,15 @@ function getAttributes() { } else if (t.type === 'capsule') { rotation += t.module.rotation; cargo += t.module.capacity; + computation += t.module.computation; } else if (t.type === 'thruster') { thrust += t.module.thrust; } else if (t.type === 'gyroscope') { rotation += t.module.rotation; } else if (t.type === 'cargo') { cargo += t.module.capacity; + } else if (t.type === 'nafivation') { + computation += t.module.computation; } mass += t.module.mass; }); @@ -96,7 +107,8 @@ function getAttributes() { '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; + 'Cargo capacity: ' + cargo + '\n' + + 'Navigational computation: ' + computation; } export function validate() { diff --git a/js/game/events.mjs b/js/game/events.mjs index 21b8035..2875e2f 100644 --- a/js/game/events.mjs +++ b/js/game/events.mjs @@ -187,7 +187,7 @@ export function collectItem(type, id, name) { notify('Collected fuel: +10'); return true; } else { - if (inventory.usedSpace > inventory.capacity) { + if (inventory.usedSpace >= inventory.capacity) { notify('No space left in inventory', 60); return false; } diff --git a/js/game/index.mjs b/js/game/index.mjs index 65ff12e..0ea70cf 100644 --- a/js/game/index.mjs +++ b/js/game/index.mjs @@ -57,16 +57,9 @@ function loop(fn, fps = 60) { let interval = 1000 / fps; (function loop(time) { + fn(); + requestAnimationFrame(loop); - - // again, Date.now() if it's available - let now = Date.now(); - let delta = now - then; - - if (delta > interval) { - then = now - (delta % interval); - fn(); - } })(0); }; diff --git a/js/graphics/background.mjs b/js/graphics/background.mjs index 8e4f9e7..38dc3b9 100644 --- a/js/graphics/background.mjs +++ b/js/graphics/background.mjs @@ -13,10 +13,9 @@ function init() { export function render(angle) { if (patterns === null) init(); - - renderLayer(patterns.back, 0.3, 1, angle); - renderLayer(patterns.middle, 0.5, 0.3, angle); - //renderLayer(patterns.front, 0.7, 0.3, angle); + // renderLayer(patterns.back, 0.3, 1, angle); + // renderLayer(patterns.middle, 0.5, 0.3, angle); + // renderLayer(patterns.front, 0.7, 0.3, angle); } function renderLayer(pattern, speed = 1, scale = 1, angle = 0) { diff --git a/js/graphics/gui.mjs b/js/graphics/gui.mjs index 44c83fc..67a6b13 100644 --- a/js/graphics/gui.mjs +++ b/js/graphics/gui.mjs @@ -80,7 +80,7 @@ function renderButton(element) { context.textAlign = 'center'; context.textBaseline = 'middle'; context.fillStyle = '#fff'; - context.font = '12pt Consolas'; + context.font = '12pt Courier New'; context.fillText(element.text, ...element.center); context.globalAlpha = 1; @@ -117,7 +117,7 @@ function renderItemButton(element) { context.textAlign = 'right'; context.textBaseline = 'bottom'; context.fillStyle = '#fff'; - context.font = 'bold 10pt Consolas'; + context.font = 'bold 10pt Courier New'; let [ex, ey] = element.end; context.fillText('x' + element.quantity, ex - 2, ey - 2); } diff --git a/js/graphics/index.mjs b/js/graphics/index.mjs index 1cc6d5f..f94f44a 100644 --- a/js/graphics/index.mjs +++ b/js/graphics/index.mjs @@ -19,14 +19,17 @@ export function init() { tempCanvas = document.querySelector('#temp'); tempContext = tempCanvas.getContext('2d'); - canvas.width = 600; - canvas.height = 600; + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + + canvas.style.width = canvas.width + 'px'; + canvas.style.height = canvas.height + 'px'; perspective = new Perspective(); context.fillStyle = '#000'; context.fillRect(0, 0, canvas.width, canvas.height); - context.font = '36px Consolas'; + context.font = '36px Courier New'; context.textAlign = 'center'; context.textBaseline = 'middle'; context.fillStyle = '#fff'; @@ -54,7 +57,7 @@ export function render() { } export function getVisibleSectors() { - return world.getContainedSectors(...perspective.bounds); + // return world.getContainedSectors(...perspective.bounds); } export function changePerspective(rotationMode, shiftX = 0, shiftY = 0) { diff --git a/js/graphics/world.mjs b/js/graphics/world.mjs index 941c44e..532be54 100644 --- a/js/graphics/world.mjs +++ b/js/graphics/world.mjs @@ -1,15 +1,15 @@ -import {canvas, context} from './index.mjs'; +import { canvas, context } from './index.mjs'; import * as graphics from './index.mjs'; -import {images as assets} from '../assets.mjs'; +import { images as assets } from '../assets.mjs'; import * as world from '../world/index.mjs'; -import {state} from '../game/index.mjs'; +import { state } from '../game/index.mjs'; export function render() { - world.particles.forEach(renderParticle); - world.celestials.forEach(renderCelestial); - if (graphics.trace) world.tracers.forEach(renderTracer); - world.ships.forEach(renderShip); - world.entities.forEach(renderEntity); + for (particle of world.particles) renderParticle(particle); + for (celestial of world.celestials) if (isVisible(celestial)) renderCelestial(celestial); + if (graphics.trace) for (tracer of world.tracers) renderTracer(tracer); + for (ship of world.ships) renderShip(ship); + for (entity of world.entities) if (isVisible(entity)) renderEntity(entity); /* if (typeof window.q === 'undefined') window.q = []; @@ -20,6 +20,16 @@ export function render() { */ } +function isVisible(body) { + const [bx, by] = body.com; + const [px, py] = [graphics.perspective.x, graphics.perspective.y]; + const [, , w, h] = graphics.perspective.bounds; + const [centerX, centerY] = [px, py]; + const margin = 1000; + return bx > centerX - margin && bx < centerX + margin && + by > centerY - margin && by < centerY + margin; +} + function renderParticle(particle) { context.fillStyle = particle.color; context.fillRect(...particle.com, particle.size, particle.size); @@ -64,7 +74,7 @@ function renderShip(ship) { const celestialImages = { green: Object.values(assets.celestials.green) -} +}; function renderCelestial(cel) { context.drawImage(cel.image, cel.x, cel.y, diff --git a/js/gui/modules.mjs b/js/gui/modules.mjs index b77bb3d..67ba050 100644 --- a/js/gui/modules.mjs +++ b/js/gui/modules.mjs @@ -132,6 +132,20 @@ export function game() { ship.maxFuel.toFixed(1); }; + let speed = new GuiText('', 0, 0, 0, 0, { + size: 14, + align: 'right', + valign: 'bottom', + + }); + shadow.append(speed); + speed.posRelative({x: 1, y: 1}); + speed.y -= 30; + speed.x -= 10; + speed.tick = () => { + speed.text = 'Speed: ' + world.speed.toFixed(1) + 'x'; + }; + let score = new GuiText('', 0, 0, 0, 0, { size: 14, align: 'left', diff --git a/js/gui/text.mjs b/js/gui/text.mjs index bdc6eb1..2cadfe6 100644 --- a/js/gui/text.mjs +++ b/js/gui/text.mjs @@ -16,7 +16,7 @@ export default class GuiText extends GuiElement { this.color = color; this.text = text; this.spacing = size * 1.2; - this.font = size + 'px Consolas'; + this.font = size + 'px Courier New'; this.align = align; this.valign = valign; } diff --git a/js/input.mjs b/js/input.mjs index f72694e..3ca77a3 100644 --- a/js/input.mjs +++ b/js/input.mjs @@ -45,7 +45,7 @@ export function init() { window.addEventListener('wheel', event => { mouse.scroll = event.deltaY; - event.preventDefault(); + // event.preventDefault(); }); window.addEventListener('contextmenu', event => { diff --git a/js/world/body.mjs b/js/world/body.mjs index d45f3ef..86d126c 100644 --- a/js/world/body.mjs +++ b/js/world/body.mjs @@ -72,13 +72,15 @@ export default class Body { } tickGravity(bodies, speed = 1) { - bodies.forEach(b => { - let force = b.mass / (this.distanceTo(b) ** 2) * G; - let [[ax, ay], [bx, by]] = [this.com, b.com]; + for (let body of bodies) { + const distanceSquared = this.distanceToSquared(body); + if (distanceSquared > (1000 ** 2)) continue; + let force = body.mass / distanceSquared * G; + let [[ax, ay], [bx, by]] = [this.com, body.com]; let angle = Math.atan2(by - ay, bx - ax); this.xvel += Math.cos(angle) * force * speed; this.yvel += Math.sin(angle) * force * speed; - }); + } } distanceTo(body) { @@ -87,6 +89,12 @@ export default class Body { ((by - ay) ** 2)), 1); } + distanceToSquared(body) { + let [[ax, ay], [bx, by]] = [this.com, body.com]; + return Math.max(((bx - ax) ** 2) + + ((by - ay) ** 2), 1); + } + angleTo(ax, ay, bx, by) { return Math.atan2(by - ay, bx - ax); } diff --git a/js/world/celestial.mjs b/js/world/celestial.mjs index beafa4e..0bfe0c7 100644 --- a/js/world/celestial.mjs +++ b/js/world/celestial.mjs @@ -1,5 +1,7 @@ +import {tempCanvas, tempContext} from '../graphics/index.mjs'; import {images as assets} from '../assets.mjs'; import Body from './body.mjs'; +import { PLANET_IMAGE_SIZE } from '../consts.mjs'; export default class Celestial extends Body { constructor(x, y, radius, { @@ -11,8 +13,15 @@ export default class Celestial extends Body { this.radius = radius; this.type = type; - let imageArr = Object.values(assets.celestials[this.type]); - this.image = imageArr[Math.random() * imageArr.length | 0]; + const imageArr = Object.values(assets.celestials[this.type]); + const svgImage = imageArr[Math.random() * imageArr.length | 0]; + tempCanvas.width = PLANET_IMAGE_SIZE; + tempCanvas.height = PLANET_IMAGE_SIZE; + tempContext.clearRect(0, 0, PLANET_IMAGE_SIZE, PLANET_IMAGE_SIZE); + tempContext.drawImage(svgImage, 0, 0, PLANET_IMAGE_SIZE, PLANET_IMAGE_SIZE); + this.image = new Image(); + this.image.src = tempCanvas.toDataURL(); + // this.image = tempContext.getImageData(0, 0, PLANET_IMAGE_SIZE, PLANET_IMAGE_SIZE); } get com() { diff --git a/js/world/index.mjs b/js/world/index.mjs index 902498d..3c4504c 100644 --- a/js/world/index.mjs +++ b/js/world/index.mjs @@ -9,6 +9,8 @@ export const tracers = new Set(); export let playerShip = null; +export let speed = 1; + export function setPlayerShip(ship) { playerShip = ship; } @@ -33,11 +35,22 @@ export function remove(object) { celestials.delete(object); } -export function tick() { - particles.forEach(p => p.tick()); - celestials.forEach(c => c.tick()); - entities.forEach(e => e.tick()); - ships.forEach(s => s.tick()); - if (graphics.trace) tracers.forEach(t => t.tick()); - spawn.tick(); +export function increaseSpeed() { + if (speed < 5) speed += 1; +} + +export function decreaseSpeed() { + if (speed > 1) speed -= 1; +} + +export function tick() { + for (let i = 0; i < speed; i++) { + particles.forEach(p => p.tick()); + celestials.forEach(c => c.tick()); + entities.forEach(e => e.tick()); + ships.forEach(s => s.tick()); + } + + spawn.tick(); + if (graphics.trace) tracers.forEach(t => t.tick()); } diff --git a/js/world/ship.mjs b/js/world/ship.mjs index e10b7e9..b975b2a 100644 --- a/js/world/ship.mjs +++ b/js/world/ship.mjs @@ -22,6 +22,7 @@ export default class Ship extends Body { this.rotationPower = 0; this.cargoCapacity = 0; this.thrust = 0; + this.computation = 0; this.crashed = false; this.timeWithoutFuel = 0; } @@ -113,6 +114,7 @@ export default class Ship extends Body { this.rotationPower = 0; this.cargoCapacity = 0; this.thrust = 0; + this.computation = 0; this.modules.forEach(m => { if (m.type === 'fuel') { @@ -120,12 +122,15 @@ export default class Ship extends Body { } else if (m.type === 'capsule') { this.rotationPower += m.data.rotation; this.cargoCapacity += m.data.capacity; + this.computation += m.data.computation; } else if (m.type === 'thruster') { this.thrust += m.data.thrust; } else if (m.type === 'gyroscope') { this.rotationPower += m.data.rotation; } else if (m.type === 'cargo') { this.cargoCapacity += m.data.capacity; + } else if (m.type === 'navigation') { + this.computation += m.data.computation; } }); } diff --git a/js/world/tracer.mjs b/js/world/tracer.mjs index d55c64f..704cb53 100644 --- a/js/world/tracer.mjs +++ b/js/world/tracer.mjs @@ -40,7 +40,7 @@ export default class Tracer extends Body { } tick() { - this.run(100); + this.run(this.ship.computation); } tickPath(speed) { diff --git a/rollup b/rollup index 992f786..a39094c 100755 --- a/rollup +++ b/rollup @@ -1,2 +1,2 @@ #!/bin/bash -rollup js/index.mjs --o dist/improcket.min.js -m --silent --f es --watch +npx rollup js/index.mjs --o dist/improcket.min.js -m --f es --watch