Bring the flappy-bird example up to date

This commit is contained in:
Tony Garnock-Jones 2021-12-11 16:59:38 +01:00
parent a3484d4a07
commit a8e542a394
2 changed files with 118 additions and 77 deletions

View File

@ -12,6 +12,6 @@
</div> </div>
</div> </div>
<script src="index.js"></script> <script src="index.js"></script>
<script>Syndicate.bootModule(Main.__SYNDICATE__bootProc);</script> <script>Main.main();</script>
</body> </body>
</html> </html>

View File

@ -1,9 +1,9 @@
/// SPDX-License-Identifier: GPL-3.0-or-later /// SPDX-License-Identifier: GPL-3.0-or-later
/// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com> /// SPDX-FileCopyrightText: Copyright © 2016-2021 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
import { $QuitDataspace, Double, Facet, Inbound, Outbound, floatValue } from '@syndicate-lang/core'; import { Record, Dataspace, Double, floatValue, Ref, stringify } from '@syndicate-lang/core';
activate import { WindowEvent, template, Anchor } from '@syndicate-lang/html'; import { boot as bootHtml, WindowEvent, template, Anchor } from '@syndicate-lang/html';
activate import { PeriodicTick } from '@syndicate-lang/timer'; import { boot as bootTimer, PeriodicTick } from '@syndicate-lang/timer';
assertion type Position(x, y); assertion type Position(x, y);
assertion type GameOver(); assertion type GameOver();
@ -20,127 +20,168 @@ const PILLAR_GAP = 158;
const FIELD_HEIGHT = 561; const FIELD_HEIGHT = 561;
const PILLAR_HEAD_HEIGHT = 40; const PILLAR_HEAD_HEIGHT = 40;
boot { export function main() {
spawn named 'game-factory' { Dataspace.boot(mainDs => {
on start spawnGame(thisFacet); bootHtml(mainDs);
during GameOver() => { bootTimer(mainDs);
on stop spawnGame(thisFacet); spawn named 'game-factory' {
on message WindowEvent('+keypress', $_e) => send message Reset(); spawnGame(mainDs);
on message WindowEvent('+click', $_e) => send message Reset(); at mainDs {
during GameOver() => {
on stop spawnGame(mainDs);
on message WindowEvent('+keypress', $_e) => send message Reset();
on message WindowEvent('+click', $_e) => send message Reset();
}
}
} }
} });
} }
function spawnGame<T>(thisFacet: Facet<T>) { function spawnGame(mainDs: Ref) {
spawn dataspace named 'GameInstance' { spawn named 'GameInstance' {
spawn named 'game-instance-control' { const gameDs = create new Dataspace();
during GameOver() => assert Outbound(GameOver());
on message Inbound(Reset()) => send message $QuitDataspace; at gameDs {
during GameOver() => at mainDs {
assert GameOver();
}
}
at mainDs {
stop on message Reset();
} }
spawn named 'score' { spawn linked named 'score' {
let ui = new Anchor(); let ui = new Anchor();
field score: number = 0; field score: number = 0;
assert Score(this.score); at gameDs {
assert Score(score.value);
on start react { on message IncreaseScore() => score.value++;
assert Outbound(ui.html('#board-area',
template`<h1 class="score">${this.score}</h1>`));
stop on asserted GameOver() => react {
assert Outbound(ui.html('#board-area',
template`<h1 class="score">${this.score}<br/>GAME OVER</h1>`));
}
} }
on message IncreaseScore() => this.score++; react {
at mainDs {
assert ui.html('#board-area', template`<h1 class="score">${score.value}</h1>`);
}
at gameDs {
stop on asserted GameOver() => react {
at mainDs {
assert ui.html(
'#board-area',
template`<h1 class="score">${score.value}<br/>GAME OVER</h1>`);
}
}
}
}
} }
spawn named 'flappy' { spawn linked named 'flappy' {
let ui = new Anchor(); let ui = new Anchor();
field xpos: number = 0; field xpos: number = 0;
field ypos: number = 312; field ypos: number = 312;
field yvel: number = 0; field yvel: number = 0;
assert Position(Double(this.xpos), Double(this.ypos)); at gameDs {
assert Position(Double(xpos.value), Double(ypos.value));
assert Outbound( on (ypos.value > BOARD_HEIGHT - FLAPPY_HEIGHT) {
ui.html('#board-area', ypos.value = BOARD_HEIGHT - FLAPPY_HEIGHT;
template`<div class="flappy" react {
style="${`transform: rotate(${2 * this.yvel}deg); assert GameOver();
top: ${this.ypos}px`}"></div>`)); }
on (this.ypos > BOARD_HEIGHT - FLAPPY_HEIGHT) {
this.ypos = BOARD_HEIGHT - FLAPPY_HEIGHT;
react {
assert GameOver();
} }
} }
on start react { at mainDs {
stop on asserted GameOver(); assert ui.html(
'#board-area',
template`<div class="flappy"
style="${`transform: rotate(${2 * yvel.value}deg);
top: ${ypos.value}px`}"></div>`);
}
on message Inbound(WindowEvent('+keypress', $_e)) => this.yvel = -10; react {
on message Inbound(WindowEvent('+click', $_e)) => this.yvel = -10; at gameDs {
stop on asserted GameOver();
}
const ms_per_tick = 1000.0 / 60; at mainDs {
on message Inbound(PeriodicTick(Double(ms_per_tick))) => { on message WindowEvent('+keypress', $_e) => yvel.value = -10;
this.xpos += 0.15 * ms_per_tick; on message WindowEvent('+click', $_e) => yvel.value = -10;
this.ypos = (this.ypos + this.yvel);
this.yvel += ms_per_tick * 0.05; const ms_per_tick = 1000.0 / 60;
const ms_per_tick_d = Double(ms_per_tick);
on message PeriodicTick(ms_per_tick_d) => {
xpos.value += 0.15 * ms_per_tick;
ypos.value = (ypos.value + yvel.value);
yvel.value += ms_per_tick * 0.05;
}
} }
} }
} }
spawn named 'border-scroll' { spawn linked named 'border-scroll' {
let ui = new Anchor(); let ui = new Anchor();
field pos: number = 0; field pos: number = 0;
on asserted Position($xpos, _) => this.pos = floatValue(xpos) % 23; at gameDs {
assert Outbound(ui.html( on asserted Position($xpos, _) => pos.value = floatValue(xpos) % 23;
'#board-area', }
template`<div class="scrolling-border" style="${`background-position-x: ${-this.pos}px`}"></div>`, at mainDs {
0)); assert ui.html(
'#board-area',
template`<div class="scrolling-border" style="${`background-position-x: ${-pos.value}px`}"></div>`,
0);
}
} }
spawn named 'pipe-factory' { spawn linked named 'pipe-factory' {
field nextPipe: number = 0; field nextPipe: number = 0;
on asserted Score(this.nextPipe) => spawnPipe(thisFacet, this.nextPipe++); at gameDs {
on asserted Score(nextPipe.value) => spawnPipe(nextPipe.value++);
}
} }
function spawnPipe<T>(thisFacet: Facet<T>, i: number) { function spawnPipe(i: number) {
spawn named ['pipe', i] { const xlocation = (i + 1) * 324;
let ui = new Anchor();
const xlocation = (i + 1) * 324; spawn linked named ['pipe', i] {
let ui = new Anchor();
field xpos: number = xlocation;
const upperHeight = const upperHeight =
Math.random() * (FIELD_HEIGHT - PILLAR_GAP - PILLAR_HEAD_HEIGHT * 6) Math.random() * (FIELD_HEIGHT - PILLAR_GAP - PILLAR_HEAD_HEIGHT * 6)
+ PILLAR_HEAD_HEIGHT * 3; + PILLAR_HEAD_HEIGHT * 3;
const lowerHeight = FIELD_HEIGHT - upperHeight - PILLAR_GAP; const lowerHeight = FIELD_HEIGHT - upperHeight - PILLAR_GAP;
stop on (this.xpos < -(PILLAR_WIDTH + FLAPPY_XPOS)); stop on (xpos.value < -(PILLAR_WIDTH + FLAPPY_XPOS));
on start react { stop on (this.xpos <= 0) send message IncreaseScore(); }; at gameDs {
react {
stop on (xpos.value <= 0) send message IncreaseScore();
}
field xpos: number = xlocation; on asserted Position($flappyXpos, _) =>
on asserted Position($xpos, _) => this.xpos = xlocation - floatValue(xpos); xpos.value = xlocation - floatValue(flappyXpos);
on asserted Position($xpos, $ypos) => { on asserted Position($xpos, $ypos) => {
if (touchingPillar(floatValue(xpos), floatValue(ypos))) { if (touchingPillar(floatValue(xpos), floatValue(ypos))) {
react { react {
assert GameOver(); assert GameOver();
}
} }
} }
} }
assert Outbound(ui.html( at mainDs {
'#board-area', assert ui.html(
template`<div class="pillars"> '#board-area',
<div class="pillar pillar-upper" template`<div class="pillars">
style="${`left: ${this.xpos + FLAPPY_XPOS}px; height: ${upperHeight}px;`}"></div> <div class="pillar pillar-upper"
<div class="pillar pillar-lower" style="${`left: ${xpos.value + FLAPPY_XPOS}px; height: ${upperHeight}px;`}"></div>
style="${`left: ${this.xpos + FLAPPY_XPOS}px; height: ${lowerHeight}px;`}"></div> <div class="pillar pillar-lower"
</div>`)); style="${`left: ${xpos.value + FLAPPY_XPOS}px; height: ${lowerHeight}px;`}"></div>
</div>`);
}
function touchingPillar(xpos: number, ypos: number): boolean { function touchingPillar(xpos: number, ypos: number): boolean {
const inHorizontalRange = const inHorizontalRange =