Преглед на файлове

add controls and a sandbox mode

main
Martin Dørum преди 2 години
родител
ревизия
f8d1e8d249
променени са 8 файла, в които са добавени 254 реда и са изтрити 166 реда
  1. 60
    162
      src/CircuitSim.svelte
  2. 5
    2
      src/MainMenu.svelte
  3. 2
    0
      src/Router.svelte
  4. 15
    0
      src/Sandbox.svelte
  5. 9
    1
      src/Scene.svelte
  6. 0
    1
      src/SceneSelector.svelte
  7. 159
    0
      src/circuit-components.js
  8. 4
    0
      src/levels/01-intro.js

+ 60
- 162
src/CircuitSim.svelte Целия файл

<canvas bind:this={canvas}></canvas>
<main>
<canvas bind:this={canvas}></canvas>
<div class="controls">
{#each components as comp}
<button on:click={sim.addNodeAtCursor(new comp.ctor())}>{comp.name}</button>
{/each}
</div>
</main>


<style> <style>
canvas {
main, canvas {
width: 100vw; width: 100vw;
height: 100vh; height: 100vh;
position: absolute; position: absolute;
top: 0px; top: 0px;
left: 0px; left: 0px;
} }
</style>

<script>
import {onMount} from 'svelte';

class Link {
constructor(from, index) {
this.from = from;
this.index = index;
this.current = false;
this.next = false;

this.connections = [];
}

connect(node, index) {
node.inputs[index].links.push(this);
this.connections.push({node, index});
}

disconnect(node, index) {
for (let i = this.connections.length - 1; i >= 0; --i) {
let conn = this.connections[i];
if (conn.node == node && conn.index == index) {
this.connections.splice(i, 1);
}
}
}

destroy() {
for (let conn of this.connections) {
for (let i = conn.node.inputs[conn.index].links.length - 1; i >= 0; --i) {
if (conn.node.inputs[conn.index].links[i] == this) {
conn.node.inputs[conn.index].links.splice(i, 1);
}
}
}
}

commit() {
this.current = this.next;
}
}

class Input {
constructor(x, y) {
this.name = "IN";
this.x = x;
this.y = y;
this.inputs = [];
this.outputs = [{name: "Out", link: new Link(this, 0)}];

this.width = 3;
this.height = 1;

this.lit = false;
}

activate() {
this.lit = !this.lit;
}

tick() {
if (this.lit) {
this.outputs[0].link.next = true;
} else {
this.outputs[0].link.next = false;
}
}

commit() {
this.outputs[0].link.commit();
}
}

class NotGate {
constructor(x, y) {
this.name = "NOT";
this.x = x;
this.y = y;
this.inputs = [{name: "In", links: []}];
this.outputs = [{name: "Out", link: new Link(this, 0)}];

this.width = 4;
this.height = 1;

this.lit = false;
}

tick() {
this.outputs[0].link.next = true;
for (let link of this.inputs[0].links) {
if (link.current) {
this.outputs[0].link.next = false;
break;
}
}
}


commit() {
this.outputs[0].link.commit();
this.lit = this.outputs[0].link.current;
}
.controls {
position: absolute;
bottom: 0px;
left: 0px;
width: 100%;
height: 40px;
} }


class Diode {
constructor(x, y) {
this.name = "DIODE";
this.x = x;
this.y = y;
this.inputs = [{name: "In", links: []}];
this.outputs = [{name: "Out", link: new Link(this, 0)}];

this.width = 4;
this.height = 1;

this.lit = false;
}

tick() {
this.outputs[0].link.next = false;
for (let link of this.inputs[0].links) {
if (link.current) {
this.outputs[0].link.next = true;
break;
}
}
}

commit() {
this.outputs[0].link.commit();
this.lit = this.outputs[0].link.current;
}
.controls > * {
box-sizing: border-box;
height: 100%;
} }
</style>


class Lamp {
constructor(x, y) {
this.name = "LAMP";
this.x = x;
this.y = y;
this.inputs = [{name: "In", links: []}];
this.outputs = [];

this.width = 4;
this.height = 1;

this.lit = false;
this.nextLit = false;
}

tick() {
this.nextLit = false;
for (let link of this.inputs[0].links) {
if (link.current) {
this.nextLit = true;
break;
}
}
}
<script>
import {onMount, onDestroy} from 'svelte';


commit() {
this.lit = this.nextLit;
}
}
export let components;


class LogicSim { class LogicSim {
constructor(can) { constructor(can) {
this.currentTouch = null; this.currentTouch = null;
this.x = 0; this.x = 0;
this.y = 0; this.y = 0;
this.cursorX = 0;
this.cursorY = 0;


this.tooltip = null; this.tooltip = null;
this.selectedNodes = []; this.selectedNodes = [];
this.mouseMoveStart = null; this.mouseMoveStart = null;
this.currentLink = null; this.currentLink = null;
this.selection = null; this.selection = null;
this.cursorAttachedNode;


this.requestFrame(); this.requestFrame();




onMouseMove(offsetX, offsetY, movementX, movementY, buttons) { onMouseMove(offsetX, offsetY, movementX, movementY, buttons) {
let [x, y] = this.coordsFromScreenPos(offsetX, offsetY); let [x, y] = this.coordsFromScreenPos(offsetX, offsetY);
this.cursorX = x;
this.cursorY = y;

let dx = 0, dy = 0; let dx = 0, dy = 0;
if (this.mouseMoveStart) { if (this.mouseMoveStart) {
dx = x - this.mouseMoveStart.x; dx = x - this.mouseMoveStart.x;
p.y = y; p.y = y;
this.requestFrame(); this.requestFrame();
} }

if (this.cursorAttachedNode != null) {
let node = this.cursorAttachedNode;
let newX = Math.round(x - node.width / 2);
let newY = Math.round(y - node.height / 2);
if (newX != node.x || newY != node.y) {
node.x = newX;
node.y = newY;
this.requestFrame();
}
}
} }


onClick(offsetX, offsetY) { onClick(offsetX, offsetY) {
} }


let [x, y] = this.coordsFromScreenPos(offsetX, offsetY); let [x, y] = this.coordsFromScreenPos(offsetX, offsetY);

if (this.cursorAttachedNode != null) {
let node = this.cursorAttachedNode;
node.x = Math.round(x - node.width / 2);
node.y = Math.round(y - node.height / 2);
this.cursorAttachedNode = null;
return;
}

let node = this.getNodeAt(x, y); let node = this.getNodeAt(x, y);
let io = null; let io = null;
if (node != null) { if (node != null) {
} }


addNodeAtCursor(node) { addNodeAtCursor(node) {
node.x = Math.round(this.x);
node.y = Math.round(this.y);
node.x = Math.round(this.cursorX - node.width / 2);
node.y = Math.round(this.cursorY - node.height / 2);
this.nodes.push(node); this.nodes.push(node);
this.cursorAttachedNode = node;
this.requestFrame(); this.requestFrame();
} }




let canvas; let canvas;
let sim; let sim;
let interval = null;


onMount(() => { onMount(() => {
sim = new LogicSim(canvas); sim = new LogicSim(canvas);
interval = setInterval(sim.update.bind(sim), 100);
});

onDestroy(() => {
if (interval != null) {
clearInterval(interval);
interval = null;
}
}); });
</script> </script>

+ 5
- 2
src/MainMenu.svelte Целия файл

<main> <main>
<h1>Game</h1> <h1>Game</h1>


<a on:click={play} href="#/play">Start</a>
<ul>
<li><a on:click={play} href="#/play">Start</a></li>
<li><a href="#/sandbox">Sandbox Mode</a></li>
</ul>
</main> </main>


<style> <style>
} }


a { a {
color: #aaa;
color: #ccc;
} }
</style> </style>



+ 2
- 0
src/Router.svelte Целия файл

<script> <script>
import MainMenu from "./MainMenu.svelte"; import MainMenu from "./MainMenu.svelte";
import SceneSelector from "./SceneSelector.svelte"; import SceneSelector from "./SceneSelector.svelte";
import Sandbox from "./Sandbox.svelte";


let routes = { let routes = {
"/": MainMenu, "/": MainMenu,
"/play": SceneSelector, "/play": SceneSelector,
"/sandbox": Sandbox,
}; };


let element = null; let element = null;

+ 15
- 0
src/Sandbox.svelte Целия файл

<main>
<CircuitSim components={components} />
</main>

<script>
import CircuitSim from './CircuitSim.svelte';
import * as comps from './circuit-components.js';

let components = [
{name: "Input", ctor: comps.Input},
{name: "NotGate", ctor: comps.NotGate},
{name: "Diode", ctor: comps.Diode},
{name: "Lamp", ctor: comps.Lamp},
];
</script>

+ 9
- 1
src/Scene.svelte Целия файл

<button on:click={begin}>Skip</button> <button on:click={begin}>Skip</button>
</div> </div>
{:else} {:else}
<CircuitSim />
<CircuitSim components={components} />
{/if} {/if}
</main> </main>


export let level; export let level;


import CircuitSim from './CircuitSim.svelte'; import CircuitSim from './CircuitSim.svelte';
import * as availableComponents from './circuit-components.js';

let components = level.components.map(name => {
if (availableComponents[name] == null) {
throw new Error(name, "is not a valid component name");
}
return {name, ctor: availableComponents[name]};
});


let page = 0; let page = 0;
let showIntro = true; let showIntro = true;

+ 0
- 1
src/SceneSelector.svelte Целия файл



<script> <script>
import Scene from './Scene.svelte'; import Scene from './Scene.svelte';

import level01 from './levels/01-intro.js'; import level01 from './levels/01-intro.js';


let level = level01; let level = level01;

+ 159
- 0
src/circuit-components.js Целия файл

export class Link {
constructor(from, index) {
this.from = from;
this.index = index;
this.current = false;
this.next = false;

this.connections = [];
}

connect(node, index) {
node.inputs[index].links.push(this);
this.connections.push({node, index});
}

disconnect(node, index) {
for (let i = this.connections.length - 1; i >= 0; --i) {
let conn = this.connections[i];
if (conn.node == node && conn.index == index) {
this.connections.splice(i, 1);
}
}
}

destroy() {
for (let conn of this.connections) {
for (let i = conn.node.inputs[conn.index].links.length - 1; i >= 0; --i) {
if (conn.node.inputs[conn.index].links[i] == this) {
conn.node.inputs[conn.index].links.splice(i, 1);
}
}
}
}

commit() {
this.current = this.next;
}
}

export class Input {
constructor(x, y) {
this.name = "IN";
this.x = x;
this.y = y;
this.inputs = [];
this.outputs = [{name: "Out", link: new Link(this, 0)}];

this.width = 3;
this.height = 1;

this.lit = false;
}

activate() {
this.lit = !this.lit;
}

tick() {
if (this.lit) {
this.outputs[0].link.next = true;
} else {
this.outputs[0].link.next = false;
}
}

commit() {
this.outputs[0].link.commit();
}
}

export class NotGate {
constructor(x, y) {
this.name = "NOT";
this.x = x;
this.y = y;
this.inputs = [{name: "In", links: []}];
this.outputs = [{name: "Out", link: new Link(this, 0)}];

this.width = 4;
this.height = 1;

this.lit = false;
}

tick() {
this.outputs[0].link.next = true;
for (let link of this.inputs[0].links) {
if (link.current) {
this.outputs[0].link.next = false;
break;
}
}
}

commit() {
this.outputs[0].link.commit();
this.lit = this.outputs[0].link.current;
}
}

export class Diode {
constructor(x, y) {
this.name = "DIODE";
this.x = x;
this.y = y;
this.inputs = [{name: "In", links: []}];
this.outputs = [{name: "Out", link: new Link(this, 0)}];

this.width = 4;
this.height = 1;

this.lit = false;
}

tick() {
this.outputs[0].link.next = false;
for (let link of this.inputs[0].links) {
if (link.current) {
this.outputs[0].link.next = true;
break;
}
}
}

commit() {
this.outputs[0].link.commit();
this.lit = this.outputs[0].link.current;
}
}

export class Lamp {
constructor(x, y) {
this.name = "LAMP";
this.x = x;
this.y = y;
this.inputs = [{name: "In", links: []}];
this.outputs = [];

this.width = 4;
this.height = 1;

this.lit = false;
this.nextLit = false;
}

tick() {
this.nextLit = false;
for (let link of this.inputs[0].links) {
if (link.current) {
this.nextLit = true;
break;
}
}
}

commit() {
this.lit = this.nextLit;
}
}

+ 4
- 0
src/levels/01-intro.js Целия файл

outputs: [ outputs: [
"LED", "LED",
], ],
components: [
"Input",
"NotGate",
],


tests: [ tests: [
[false], [false], [false], [false],

Loading…
Отказ
Запис