From 826986cdbfc03d071536983e2ad17493f3b897e1 Mon Sep 17 00:00:00 2001 From: asraelite Date: Mon, 5 Mar 2018 22:29:35 +0000 Subject: [PATCH] Improve edit screen --- js/game/edit.mjs | 77 +++++++++++++++++++++++++++++++++++-------- js/game/events.mjs | 8 +++-- js/game/inventory.mjs | 2 -- js/graphics/gui.mjs | 32 +++++++++++++----- js/gui/button.mjs | 2 +- js/gui/edit.mjs | 8 +++-- js/gui/element.mjs | 6 ++-- js/gui/modules.mjs | 47 ++++++++++++++++++++++---- js/gui/text.mjs | 21 ++++++++++++ js/world/entity.mjs | 64 +++++++++++++++++++++++++++++++++++ js/world/ship.mjs | 6 +++- js/world/spawn.mjs | 2 +- 12 files changed, 235 insertions(+), 40 deletions(-) create mode 100644 js/gui/text.mjs create mode 100644 js/world/entity.mjs diff --git a/js/game/edit.mjs b/js/game/edit.mjs index 848bc03..67eb4da 100644 --- a/js/game/edit.mjs +++ b/js/game/edit.mjs @@ -12,68 +12,107 @@ export let width = 0; export let height = 0; export let position = [0, 0]; export let bounds = [0, 0, 0, 0]; +export let message = ''; export function init() { let ship = world.playerShip; let modules = ship.modules; - let margin = consts.EDIT_MARGIN; + + tiles.clear(); modules.forEach(m => { let pos = [m.x, m.y]; tiles.set(posId(...pos), new Tile(...pos, m)); }); + message = ''; + adjustSize(); + + let neededZoom = graphics.canvas.width / (Math.max(width, height) + 10); + graphics.changePerspective('planet', 0, -5); + graphics.setZoom(neededZoom); +} + +function adjustSize() { + let margin = consts.EDIT_MARGIN; let [sx, ex, sy, ey] = getBoundaries(); [width, height] = [ex - sx + margin * 2 + 1, ey - sy + margin * 2 + 1]; position = [sx - margin, sy - margin]; - let neededZoom = graphics.canvas.width / (Math.max(width, height) + 10); - - graphics.changePerspective('planet', 0, -5); - graphics.setZoom(neededZoom); } export function end() { let result = validate(); - return { + result = { valid: result === false, reason: result + }; + + if (result.valid) { + let ship = world.playerShip; + let [ox, oy] = ship.com; + ship.clearModules(); + tiles.forEach(t => { + if (t.type === null) return; + ship.addModule(t.x, t.y, modules[t.type][t.id]); + }); + let [nx, ny] = ship.com; + let [dx, dy] = [nx - ox, ny - oy]; + ship.x -= dx; + ship.y -= dy; } + + return result; } function validate() { let capsulesFound = 0; let thrustersFound = 0; + let fuelFound = 0; let unvisited = new Set(); tiles.forEach(t => { if (t.type !== null) unvisited.add(t) }); + let reason = ''; + if (unvisited.size == 0) { - return 'no capsule'; + reason = 'no capsule'; } let visit = (tile) => { unvisited.delete(tile); if (tile.type == 'capsule') capsulesFound++; if (tile.type == 'thruster') thrustersFound++; + if (tile.type == 'fuel') fuelFound++; tile.neighbours.forEach(n => { if (unvisited.has(n)) visit(n); }); }; - visit(unvisited.values().next().value); + if (unvisited.size > 0) + visit(unvisited.values().next().value); if (unvisited.size > 0) { - return 'not connected' + reason = 'not connected' } else if (capsulesFound === 0) { - return 'no capsule' + reason = 'no capsule' } else if (thrustersFound === 0) { - return 'no thruster' + reason = 'no thruster' + } else if (fuelFound === 0) { + reason = 'no fuel tank' } else { - return false; + reason = false; } + + if (reason === false) { + message = ''; + } else { + message = reason; + } + + return reason; } function positionAdjust(x, y) { @@ -95,6 +134,8 @@ export function clickTile(x, y) { let id = posId(...pos); tiles.set(id, new Tile(...pos, inventory.currentItem)); inventory.removeItem(...inventory.currentItem.ident); + adjustSize(); + validate(); } export function rightClickTile(x, y) { @@ -104,6 +145,8 @@ export function rightClickTile(x, y) { let id = posId(tx, ty); tiles.set(id, new Tile(tx, ty, null)); inventory.addItem(current.type, current.id); + adjustSize(); + validate(); } export function getTile(x, y) { @@ -124,7 +167,15 @@ function posId(x, y) { } function getBoundaries() { - return [0, 0, 0, 3]; + let sx = sy = ex = ey = null + tiles.forEach(t => { + if (t.type === null) return; + if (sx === null || t.x < sx) sx = t.x; + if (ex === null || t.x > ex) ex = t.x; + if (sy === null || t.y < sy) sy = t.y; + if (ey === null || t.y > ey) ey = t.y; + }); + return [sx, ex, sy, ey]; } class Tile { diff --git a/js/game/events.mjs b/js/game/events.mjs index 1c7df6d..627ab9b 100644 --- a/js/game/events.mjs +++ b/js/game/events.mjs @@ -21,7 +21,11 @@ export function launchShip() { game.state.landed = false; } -export function editShip() { +export function toggleEdit() { + if (game.state.editing) { + endEditing(); + return; + } game.state.editing = true; game.state.inventory = true; edit.init(); @@ -34,8 +38,6 @@ export function endEditing() { graphics.changePerspective('universe'); game.state.editing = false; game.state.inventory = false; - } else { - console.log(reason); } } diff --git a/js/game/inventory.mjs b/js/game/inventory.mjs index 54dd286..82f8636 100644 --- a/js/game/inventory.mjs +++ b/js/game/inventory.mjs @@ -6,8 +6,6 @@ export let currentItem = null; export function init() { items.clear(); - addItem('capsule', 'small'); - addItem('thruster', 'light'); } export function getTiles() { diff --git a/js/graphics/gui.mjs b/js/graphics/gui.mjs index 93c8f11..c8885c1 100644 --- a/js/graphics/gui.mjs +++ b/js/graphics/gui.mjs @@ -9,12 +9,13 @@ export function render() { function renderElement(element) { //console.log(element.options); if (element.options.draw) { - 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 == 'itemButton') renderItemButton(element); - if (element.type == 'inventory') renderInventory(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 === 'itemButton') renderItemButton(element); + if (element.type === 'inventory') renderInventory(element); + if (element.type === 'text') renderText(element); } if (element.options.drawChildren) @@ -30,11 +31,25 @@ function renderImage(element) { context.drawImage(element.image, ...element.shape); } +function renderText(element) { + context.font = element.font; + context.textAlign = element.align; + context.textBaseline = element.valign; + context.fillStyle = element.color; + context.fillText(element.text, element.x, element.y); +} + function renderButton(element) { - if (element.mouseHeld) { + if (element.mouseHeld && !element.options.disabled) { context.fillStyle = '#706244'; + } else if (element.mouseOver && !element.options.disabled) { + context.fillStyle = '#ad9869'; } else { - context.fillStyle = element.mouseOver ? '#ad9869' : '#917f58'; + context.fillStyle = '#917f58'; + } + + if (element.options.disabled) { + context.globalAlpha = 0.5; } context.fillRect(...element.shape); @@ -46,6 +61,7 @@ function renderButton(element) { context.fillStyle = '#fff'; context.font = '12pt Consolas'; context.fillText(element.text, ...element.center); + context.globalAlpha = 1; } function renderItemButton(element) { diff --git a/js/gui/button.mjs b/js/gui/button.mjs index 7b3d35f..288004a 100644 --- a/js/gui/button.mjs +++ b/js/gui/button.mjs @@ -10,7 +10,7 @@ export default class GuiButton extends GuiElement { } click() { - if (this.options.draw) + if (this.options.draw && !this.options.disabled) this.onclick(); } } diff --git a/js/gui/edit.mjs b/js/gui/edit.mjs index b5ab060..f37fb9d 100644 --- a/js/gui/edit.mjs +++ b/js/gui/edit.mjs @@ -38,8 +38,8 @@ export default class GuiEdit extends GuiElement { for (let x = 0; x < this.tileWidth; x++) for (let y = 0; y < this.tileHeight; y++) { let tile = edit.getTile(x, y); - let ex = x * tileSize + spacing / 2 + ox + this.x; - let ey = y * tileSize + spacing / 2 + oy + this.y; + let ex = x * tileSize + spacing / 2 + ox; + let ey = y * tileSize + spacing / 2 + oy; let [ew, eh] = [tileSize - spacing, tileSize - spacing]; let onclick = (button) => { @@ -54,9 +54,11 @@ export default class GuiEdit extends GuiElement { tick() { if (state.editing && !this.active) this.updateTiles(); this.active = state.editing; - this.options.draw = this.options.drawChildren = this.active; + this.parent.options.drawChildren = this.active; if (!this.active) return; + this.textElements.info.text = edit.message; + [this.tileWidth, this.tileHeight] = [edit.width, edit.height]; } diff --git a/js/gui/element.mjs b/js/gui/element.mjs index 503479a..71a793a 100644 --- a/js/gui/element.mjs +++ b/js/gui/element.mjs @@ -29,6 +29,8 @@ export default class GuiElement extends Rect { append(element) { this.children.add(element); element.parent = this; + element.x += this.x; + element.y += this.y; } clear() { @@ -39,10 +41,10 @@ export default class GuiElement extends Rect { posRelative({x = null, xc = 0, y = null, yc = 0, w = null, h = null}) { if (x !== null) { - this.x = (this.parent.w * x) - (this.w * xc); + this.x = (this.parent.w * x) - (this.w * xc) + this.parent.x; } if (y !== null) - this.y = (this.parent.h * y) - (this.h * yc); + this.y = (this.parent.h * y) - (this.h * yc) + this.parent.y; if (w !== null) this.w = this.parent.w * w; if (h !== null) diff --git a/js/gui/modules.mjs b/js/gui/modules.mjs index 387aaf4..2328055 100644 --- a/js/gui/modules.mjs +++ b/js/gui/modules.mjs @@ -1,10 +1,12 @@ import * as gui from './index.mjs'; +import {message as editMessage} from '../game/edit.mjs'; import {images as assets} from '../assets.mjs'; import {canvas} from '../graphics/index.mjs'; import GuiFrame from './frame.mjs'; import GuiImage from './image.mjs'; import GuiButton from './button.mjs'; import GuiEdit from './edit.mjs'; +import GuiText from './text.mjs'; import GuiInventory from './inventory.mjs'; import * as events from '../game/events.mjs'; import {state} from '../game/index.mjs'; @@ -40,19 +42,52 @@ export function title() { export function game() { let shadow = root(); - let editButton = new GuiButton('Edit rocket', events.editShip, 0, 0, 200); + let editButton = new GuiButton('Edit rocket', events.toggleEdit, 0, 0, 200); shadow.append(editButton); editButton.posRelative({ x: 0.5, xc: 0.5, y: 1 }); editButton.y -= 45; editButton.tick = () => { - editButton.options.draw = state.landed && !state.editing; + editButton.options.draw = state.landed; + editButton.options.disabled = state.editing && editMessage !== ''; + if (state.editing) { + editButton.text = 'Finish'; + } else { + editButton.text = 'Edit rocket'; + } } + + let editShadow = root(); + shadow.append(editShadow); + editShadow.posRelative({x: 0.45, y: 0, w: 0.55, h: 0.6}); + editShadow.x -= 10; + editShadow.y += 10; + let edit = new GuiEdit(0, 0, 0, 0); - shadow.append(edit); - edit.posRelative({x: 0.45, y: 0, w: 0.55, h: 0.6}); - edit.x -= 10; - edit.y += 10; + editShadow.append(edit); + edit.posRelative({w: 1, h: 1}); + + let editInfoText = new GuiText('', 0, 0, 0, 0, { + size: 10, + align: 'center' + }); + editShadow.append(editInfoText); + editInfoText.posRelative({x: 0.5, y: 1}); + editInfoText.y += 5; + + let editWarnText = new GuiText('', 0, 0, 0, 0, { + size: 10, + align: 'center' + }); + editShadow.append(editWarnText); + editWarnText.posRelative({x: 0.5, y: 1}); + editWarnText.y += 20; + + edit.textElements = { + info: editInfoText, + warn: editWarnText + }; + let inventory = new GuiInventory(0, 0, 0, 0); shadow.append(inventory); diff --git a/js/gui/text.mjs b/js/gui/text.mjs new file mode 100644 index 0000000..52005af --- /dev/null +++ b/js/gui/text.mjs @@ -0,0 +1,21 @@ +import * as gui from './index.mjs'; +import GuiElement from './element.mjs'; + +export default class GuiText extends GuiElement { + constructor(text = '', x, y, w, h, { + size = 10, + align = 'left', + valign = 'top', + color = '#fff' + } = {}) { + w = w; + h = h; + super(x, y, w, h); + this.type = 'text'; + this.color = color; + this.text = text; + this.font = size + 'pt Consolas'; + this.align = align; + this.valign = valign; + } +} diff --git a/js/world/entity.mjs b/js/world/entity.mjs new file mode 100644 index 0000000..9b9443f --- /dev/null +++ b/js/world/entity.mjs @@ -0,0 +1,64 @@ +import Body from './body.mjs'; +import {modules} from '../data.mjs'; +import {celestials, particles} from './index.mjs'; + +export function createThrustExhaust(thruster) { + let ship = thruster.ship; + let [fx, fy] = ship.relativeVector(0, 0.2); + let [dx, dy] = ship.relativeVector((Math.random() - 0.5) * 0.5, 0.5); + let [cx, cy] = thruster.com; + particles.add(new Particle(cx + dx, cy + dy, { + xvel: ship.xvel + fx, + yvel: ship.yvel + fy, + color: '#f4c542', + lifetime: 5, + size: 0.07 + })); +} + +class Particle extends Body { + constructor(x, y, { + xvel = 0, + yvel = 0, + spray = 0.1, + fizzle = 0, + maxFizzle = fizzle * 2, + color = '#fff', + gravity = false, + lifetime = 50, + size = 0.1, + friction = 0.99 + }) { + super(x, y, 0.1); + + this.size = size; + this.xvel = xvel + (Math.random() - 0.5) * spray; + this.yvel = yvel + (Math.random() - 0.5) * spray; + this.friction = friction; + this.fizzle = fizzle; + this.maxFizzle = maxFizzle; + this.color = color; + this.gravity = gravity; + this.life = lifetime; + } + + get com() { + return [this.x - this.size / 2, this.y - this.size / 2]; + } + + tick() { + if (!this.life--) { + particles.delete(this); + return; + } + + this.tickMotion(); + if (this.gravity) this.tickGravity(celestials); + + this.xvel *= this.friction; + this.yvel *= this.friction; + this.x += (Math.random() - 0.5) * this.fizzle; + this.y += (Math.random() - 0.5) * this.fizzle; + if (this.fizzle < this.mazFizzle) this.fizzle *= 1.05; + } +} diff --git a/js/world/ship.mjs b/js/world/ship.mjs index dd5ebc2..e929ad1 100644 --- a/js/world/ship.mjs +++ b/js/world/ship.mjs @@ -56,6 +56,10 @@ export default class Ship extends Body { this.landed ? events.landShip() : events.launchShip(); } + clearModules() { + this.modules.clear(); + } + addModule(x, y, properties, options) { let module = new Module(x, y, this, {...properties, ...options}); this.modules.add(module); @@ -128,7 +132,7 @@ export default class Ship extends Body { this.modules.forEach(m => { if (m.type !== 'thruster') return; - m.power = forward; + m.power += forward; }); } diff --git a/js/world/spawn.mjs b/js/world/spawn.mjs index 8c0bd82..4203e45 100644 --- a/js/world/spawn.mjs +++ b/js/world/spawn.mjs @@ -7,7 +7,7 @@ import * as world from './index.mjs'; export function player() { let ship = new Ship(0, -45); ship.addModule(0, 0, modules.capsule.small); - ship.addModule(0, 1, modules.fuel.small, { filled: true }); + ship.addModule(0, 1, modules.fuel.small); ship.addModule(0, 2, modules.thruster.light); world.ships.add(ship); world.setPlayerShip(ship);