156 строки
5.0 KiB
TypeScript
156 строки
5.0 KiB
TypeScript
|
|
namespace menu {
|
|
export type Callback = (opts: Options) => void
|
|
export class Item {
|
|
public onRight: Callback
|
|
public onLeft: Callback
|
|
constructor(public name: string, public cb: Callback) { }
|
|
}
|
|
|
|
export function item(name: string, cb: Callback, onRight?: Callback, onLeft?: Callback) {
|
|
const item = new Item(name, cb)
|
|
item.onRight = onRight
|
|
item.onLeft = onLeft
|
|
return item
|
|
}
|
|
|
|
export interface Options {
|
|
title: string
|
|
footer?: string
|
|
elements?: Item[]
|
|
onB?: (opts: Options) => void
|
|
update?: (opts: Options) => void
|
|
highlightColor?: number
|
|
active?: boolean
|
|
}
|
|
|
|
export function exit(opts: Options) {
|
|
game.popScene()
|
|
opts.active = false
|
|
}
|
|
|
|
export function show(opts: Options) {
|
|
let cursor = 0;
|
|
let offset = 0;
|
|
let blinkOut = 0;
|
|
|
|
const move = (dx: number) => {
|
|
let nc = cursor + dx
|
|
if (nc < 0) nc = 0
|
|
else if (nc >= opts.elements.length)
|
|
nc = opts.elements.length - 1
|
|
if (nc - offset < 2) offset = nc - 2
|
|
if (nc - offset > 6) offset = nc - 6
|
|
if (offset < 0) offset = 0
|
|
cursor = nc
|
|
}
|
|
|
|
function showMenu() {
|
|
cursor = 0
|
|
offset = 0
|
|
|
|
controller.down.onEvent(ControllerButtonEvent.Pressed, () => move(1))
|
|
controller.down.onEvent(ControllerButtonEvent.Repeated, () => move(1))
|
|
|
|
controller.up.onEvent(ControllerButtonEvent.Pressed, () => move(-1))
|
|
controller.up.onEvent(ControllerButtonEvent.Repeated, () => move(-1))
|
|
|
|
controller.A.onEvent(ControllerButtonEvent.Pressed, () => {
|
|
blinkOut = control.millis() + 100
|
|
const e = opts.elements[cursor]
|
|
if (e && e.cb)
|
|
control.runInBackground(() => e.cb(opts))
|
|
})
|
|
controller.B.onEvent(ControllerButtonEvent.Pressed, () => {
|
|
if (opts.onB) {
|
|
blinkOut = control.millis() + 100
|
|
control.runInBackground(() => opts.onB(opts))
|
|
} else {
|
|
exit(opts)
|
|
}
|
|
})
|
|
|
|
controller.left.onEvent(ControllerButtonEvent.Pressed, () => {
|
|
const e = opts.elements[cursor]
|
|
if (e && e.onLeft) {
|
|
blinkOut = control.millis() + 100
|
|
control.runInBackground(() => e.onLeft(opts))
|
|
}
|
|
})
|
|
|
|
controller.right.onEvent(ControllerButtonEvent.Pressed, () => {
|
|
const e = opts.elements[cursor]
|
|
if (e && e.onRight) {
|
|
blinkOut = control.millis() + 100
|
|
control.runInBackground(() => e.onRight(opts))
|
|
}
|
|
})
|
|
|
|
game.onPaint(function () {
|
|
if (game.consoleOverlay.isVisible())
|
|
return
|
|
|
|
const x = 10
|
|
if (opts.elements.length == 0) {
|
|
screen.print("Nothing here", x, 60, 4, image.font5)
|
|
screen.print("press B to go back", x, 80, 4, image.font5)
|
|
return
|
|
}
|
|
// if elements changed, cursor could be off limits - move(0) will limit it
|
|
if (cursor >= opts.elements.length) move(0)
|
|
screen.fillRect(0, 0, 160, 12, 12)
|
|
screen.print(opts.title, x - 1, 2, 4, image.font8)
|
|
let hl = opts.highlightColor || 5
|
|
if (blinkOut) {
|
|
if (blinkOut < control.millis()) blinkOut = 0
|
|
else hl = 6
|
|
}
|
|
for (let i = 0; i < 9; ++i) {
|
|
const e = opts.elements[i + offset];
|
|
const ename = e ? e.name : ""
|
|
let y = 15 + i * 11
|
|
if (i + offset == cursor) {
|
|
screen.fillRect(0, y - 2, 160, 11, hl)
|
|
screen.print(ename, x, y, 15)
|
|
}
|
|
else
|
|
screen.print(ename, x, y, 1)
|
|
}
|
|
if (opts.footer)
|
|
screen.print(opts.footer, x, 120 - 6, 4, image.font5)
|
|
})
|
|
|
|
}
|
|
|
|
game.pushScene()
|
|
opts.active = true
|
|
if (opts.update) opts.update(opts)
|
|
showMenu()
|
|
|
|
let cnt = 0
|
|
while (opts.active) {
|
|
if (cnt++ > 20 && opts.update) {
|
|
opts.update(opts)
|
|
cnt = 0
|
|
}
|
|
pause(20)
|
|
}
|
|
}
|
|
|
|
export function wait(ms: number, msg: string) {
|
|
game.pushScene();
|
|
const dialog = new game.SplashDialog(screen.width, 35);
|
|
dialog.setText(msg);
|
|
dialog.cursor = img`.`
|
|
|
|
const s = sprites.create(dialog.image, -1);
|
|
|
|
game.onUpdate(() => {
|
|
dialog.update();
|
|
})
|
|
|
|
pause(ms)
|
|
game.popScene()
|
|
}
|
|
}
|