diff --git a/js/consts.mjs b/js/consts.mjs index 15d11dc..081277e 100644 --- a/js/consts.mjs +++ b/js/consts.mjs @@ -7,3 +7,5 @@ export const SECTOR_SIZE = 512; // Star count per sector. export const STAR_DENSITY = (SECTOR_SIZE ** 2) / 10000; +// G, G-boy, The big G, Mr. G, g's big brother, G-dog +export const GRAVITATIONAL_CONSTANT = 0.01; diff --git a/js/data.mjs b/js/data.mjs new file mode 100644 index 0000000..85039cc --- /dev/null +++ b/js/data.mjs @@ -0,0 +1,32 @@ +export const modules = { + capsule: { + small: { + name: 'Small Capsule', + tooltip: 'A small, simple capsule. Provides just enough ' + + 'rotational power for a small rocket.', + type: 'capsule', + mass: 2, + rotation: 0.1 + } + }, + fuel: { + small: { + name: 'Small Fuel Tank', + tooltip: 'A small flimsy tank with enough fuel for a short trip.', + type: 'fuel', + mass: 1, + capacity: 3 + } + }, + thruster: { + light: { + name: 'Light Main Thruster', + tooltip: 'Powerful enough to lift a small ship, but not much ' + + 'more. Not very efficient.', + type: 'thruster', + mass: 2, + thrust: 10, + isp: 200 + } + } +} diff --git a/js/game/events.mjs b/js/game/events.mjs index 0ac2b52..bdccf45 100644 --- a/js/game/events.mjs +++ b/js/game/events.mjs @@ -1,5 +1,5 @@ import * as game from './index.mjs'; export function startGame() { - console.log('started'); + game.changeView('game'); } diff --git a/js/game/index.mjs b/js/game/index.mjs index df96860..f62296c 100644 --- a/js/game/index.mjs +++ b/js/game/index.mjs @@ -2,15 +2,15 @@ import * as graphics from '../graphics/index.mjs'; import * as gui from '../gui/index.mjs'; import * as assets from '../assets.mjs'; import * as input from '../input.mjs'; +import * as world from '../world/index.mjs'; +import * as events from './events.mjs'; -export let game; +export let state; export async function init() { - game = { - state: { - room: 'menu', - paused: false - } + state = { + view: 'menu', + paused: false }; graphics.init(); @@ -18,6 +18,8 @@ export async function init() { gui.init(); input.init(); + //events.startGame(); + // Recursive `requestAnimationFrame` can cause problems with Parcel. while(true) { await tick(); @@ -25,7 +27,17 @@ export async function init() { } } +export function changeView(view) { + state.view = view; + gui.changeView(view); + + if (view == 'game') { + world.init(); + } +} + async function tick() { + if (state.view == 'game') world.tick(); gui.tick(); graphics.render(); input.tick(); diff --git a/js/graphics/index.mjs b/js/graphics/index.mjs index 73200de..633b84b 100644 --- a/js/graphics/index.mjs +++ b/js/graphics/index.mjs @@ -3,6 +3,7 @@ import {getContainedSectors} from '../world/index.mjs'; import * as background from './background.mjs'; import * as gui from './gui.mjs'; import * as draw from './draw.mjs'; +import * as ship from './ship.mjs'; export let canvas, context, tempCanvas, tempContext; export let view; @@ -36,6 +37,7 @@ export function render() { // TODO: Translate canvas. background.render(); + ship.render(); context.restore(); diff --git a/js/graphics/rocket.mjs b/js/graphics/rocket.mjs deleted file mode 100644 index 474ccf4..0000000 --- a/js/graphics/rocket.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import {canvas, context} from './index.mjs'; -import * as assets from '../assets.mjs'; - -export function render() { - -} diff --git a/js/graphics/ship.mjs b/js/graphics/ship.mjs new file mode 100644 index 0000000..0f9cbae --- /dev/null +++ b/js/graphics/ship.mjs @@ -0,0 +1,12 @@ +import {canvas, context} from './index.mjs'; +import * as assets from '../assets.mjs'; +import * as world from '../world/index.mjs'; + +export function render() { + world.ships.forEach(renderShip); +} + +function renderShip(ship) { + context.fillStyle = 'red'; + context.fillRect(ship.x, ship.y, 10, 10); +} diff --git a/js/gui/index.mjs b/js/gui/index.mjs index a2e8735..723f56c 100644 --- a/js/gui/index.mjs +++ b/js/gui/index.mjs @@ -20,6 +20,10 @@ export function changeView(view) { if (view == 'title') { root.append(modules.title()); } + + if (view == 'game') { + root.append(modules.game()); + } } export function measureText(msg, font) { diff --git a/js/gui/modules.mjs b/js/gui/modules.mjs index 64078da..1797c15 100644 --- a/js/gui/modules.mjs +++ b/js/gui/modules.mjs @@ -33,3 +33,9 @@ export function title() { return shadow; } + +export function game() { + let shadow = root(); + + return shadow; +} diff --git a/js/world/body.mjs b/js/world/body.mjs new file mode 100644 index 0000000..7679640 --- /dev/null +++ b/js/world/body.mjs @@ -0,0 +1,26 @@ +import {GRAVITATIONAL_CONSTANT as G} from '../consts.mjs'; + +export default class Body { + constructor(x, y, mass) { + this.x = x; + this.y = y; + this.r = 0; + this.xvel = 0; + this.yvel = 0; + this.rvel = 0; + this.mass = mass; + } + + tickGravity(bodies) { + bodies.forEach(b => { + let force = b.mass / this.mass / (distanceTo(b) ** 2) * G; + let angle = Math.atan2(b.y - this.y, b.x - this.x); + this.xvel += Math.cos(angle) * force; + this.yvel += Math.sin(angle) * force; + }); + } + + distanceTo(body) { + return Math.sqrt(((body.x - this.x) ** 2) + ((body.y - this.y) ** 2)); + } +} diff --git a/js/world/celestial.mjs b/js/world/celestial.mjs new file mode 100644 index 0000000..57b95ea --- /dev/null +++ b/js/world/celestial.mjs @@ -0,0 +1,10 @@ +import Body from './body.mjs'; + +export default class Celestial extends Body { + constructor(x, y, radius, { + density = 1, + mass = (radius ** 2) * density + }) { + super(x, y, mass); + } +} diff --git a/js/world/index.mjs b/js/world/index.mjs index d19aabe..15f8f1f 100644 --- a/js/world/index.mjs +++ b/js/world/index.mjs @@ -1,3 +1,26 @@ import * as sector from './sector.mjs'; +import * as spawn from './spawn.mjs'; export {getSectorFromWorld, getContainedSectors} from './sector.mjs'; + +export const entities = new Set(); +export const celestials = new Set(); +export const ships = new Set(); + +export let playerShip = null; + +export function setPlayerShip(ship) { + playerShip = ship; +} + +export function init() { + entities.clear(); + celestials.clear(); + spawn.player(); +} + +export function tick() { + celestials.forEach(c => c.tick()); + entities.forEach(e => e.tick()); + ships.forEach(s => s.tick()); +} diff --git a/js/world/module.mjs b/js/world/module.mjs new file mode 100644 index 0000000..17f1456 --- /dev/null +++ b/js/world/module.mjs @@ -0,0 +1,15 @@ +export default class Module { + constructor(x, y, { + name = 'Unnamed Module', + type = 'block', + mass = 1, + ...properties + }) { + this.x = x; + this.y = y; + this.name = name; + this.type = type; + this.mass = mass; + + } +} diff --git a/js/world/ship.mjs b/js/world/ship.mjs new file mode 100644 index 0000000..1f79b4e --- /dev/null +++ b/js/world/ship.mjs @@ -0,0 +1,35 @@ +import Module from './module.mjs'; +import Body from './body.mjs'; + +export default class Ship extends Body { + constructor(x, y) { + super(x, y, 0); + + this.com = [0, 0]; + this.modules = new Set(); + } + + tick() { + + } + + addModule(x, y, properties, options) { + let module = new Module(x, y, {...properties, ...options}); + this.modules.add(module); + this.refreshShape(); + } + + deleteModule(module) { + this.modules.delete(module); + this.refreshShape(); + } + + refreshShape() { + let points = []; + this.modules.forEach(m => points.push([m.x, m.y, m.mass])); + this.mass = points.reduce((a, [,,b]) => a + b, 0); + this.com = points.reduce(([ax, ay], b) => + [ax + b.x * b.mass, ay + b.y * b.mass], [0, 0]) + .map(x => x / this.mass); + } +} diff --git a/js/world/spawn.mjs b/js/world/spawn.mjs new file mode 100644 index 0000000..c3a8e34 --- /dev/null +++ b/js/world/spawn.mjs @@ -0,0 +1,13 @@ +import Ship from './ship.mjs'; +import Module from './module.mjs'; +import {modules} from '../data.mjs'; +import * as world from './index.mjs'; + +export function player() { + let ship = new Ship(0, 0); + ship.addModule(0, 0, modules.capsule.small); + ship.addModule(0, 1, modules.fuel.small, { filled: true }); + ship.addModule(0, 2, modules.thruster.light); + world.ships.add(ship); + world.setPlayerShip(ship); +}