387 lines
12 KiB
TOML
387 lines
12 KiB
TOML
[world]
|
|
name = "Kiln Demo"
|
|
start = "start"
|
|
|
|
# Named Rhai scripts shared across all boards in this world.
|
|
# Objects reference a script by name via `script_name`.
|
|
[scripts]
|
|
greeter = """
|
|
fn init(me) {
|
|
log(`hello from object — player at ${Player.x}, ${Player.y}`);
|
|
set_tile("☻"); // change my glyph — proves the write path
|
|
say("Hello there,\\ntraveller!");
|
|
log(`Player health: ${Player.health}`);
|
|
}
|
|
|
|
fn enter(me, dir) {
|
|
if Player.x == me.x && Player.y == me.y && !me.waiting {
|
|
say("Hey! Get offa me!");
|
|
me.delay(5.0);
|
|
}
|
|
}
|
|
"""
|
|
|
|
mover = """
|
|
fn init(me) {
|
|
log(`q: ${me.queue.length}`);
|
|
if Board.registry.get("dancer_x") != () {
|
|
let new_x = Board.registry.get("dancer_x");
|
|
let new_y = Board.registry.get("dancer_y");
|
|
if me.x != new_x || me.y != new_y {
|
|
teleport(me.id, new_x, new_y);
|
|
}
|
|
} else {
|
|
Board.registry.set("dancer_x", me.x);
|
|
Board.registry.set("dancer_y", me.y);
|
|
log(`Set reg to ${me.x} ${me.y}`);
|
|
}
|
|
}
|
|
// move() costs 250 ms, so the object steps ~4 cells/sec no matter how often
|
|
// tick() runs. The engine only calls tick() once this object's queue has drained,
|
|
// so a whole dance can be queued up front without piling up.
|
|
fn tick(me, dt) {
|
|
move(East);
|
|
move(South);
|
|
move(North);
|
|
move(East);
|
|
say("Hey!", 0.5);
|
|
me.delay(0.5);
|
|
move(West);
|
|
move(South);
|
|
move(North);
|
|
move(West);
|
|
say("Ho!", 0.5);
|
|
me.delay(0.5);
|
|
}
|
|
// Fires when a solid (the player, an object, or a pushed crate) presses into this
|
|
// object; dir is the direction the bump came from.
|
|
fn bump(me, dir) {
|
|
log(`mover bumped from ${dir}`);
|
|
say("Ow!");
|
|
}
|
|
"""
|
|
|
|
muffin = """
|
|
fn bump(me, _dir) {
|
|
scroll([
|
|
"You find a small muffin on the ground, your favorite kind.",
|
|
"It smells of cinnamon and warm mornings.",
|
|
["eat", "Pick up the muffin and eat it."],
|
|
["ignore", "This is obviously a trap — ignore it."]
|
|
]);
|
|
}
|
|
fn eat(me) {
|
|
say("Yeah it was poisoned.");
|
|
alter_health(-2);
|
|
}
|
|
fn ignore() {
|
|
log("You walk away from the muffin. Wise.");
|
|
say("Suspicious...");
|
|
}
|
|
"""
|
|
|
|
noticeboard = """
|
|
fn bump(me, _dir) {
|
|
scroll([
|
|
" TOWN NOTICE BOARD",
|
|
" ",
|
|
"ROAD CLOSURE — The eastern road through Miller's Crossing remains closed",
|
|
"following flood damage to the main bridge. Repairs are underway but travellers",
|
|
"should expect delays of at least three weeks. The north fork via Ashwell is",
|
|
"passable, though it adds half a day to the journey.",
|
|
" ",
|
|
"LOST ANIMAL — One grey mule, answers to the name Pepper. Last seen grazing",
|
|
"near the old mill on the morning of the 14th. The animal has a notched left",
|
|
"ear and wears a blue rope halter. Any information to the innkeeper; a reward",
|
|
"of two silver pieces is offered for her safe return.",
|
|
" ",
|
|
"POSITION AVAILABLE — Captain Aldric of the town garrison seeks an experienced",
|
|
"wilderness guide for an expedition into the Darkwood. Duration: ten to fourteen",
|
|
"days. Pay is good and provisions will be supplied. Candidates must present",
|
|
"themselves at the barracks before the evening bell. Some risk is involved and",
|
|
"the captain asks that only those in sound health and good standing apply.",
|
|
" ",
|
|
"HARVEST FESTIVAL — The annual Harvest Festival will be held on the last",
|
|
"Saturday of the month. The market square will be closed to carts from dawn.",
|
|
"Stall permits must be obtained from the clerk's office no later than Thursday.",
|
|
"There will be music, a pie competition, and the traditional lantern parade at",
|
|
"dusk. All residents are warmly encouraged to attend.",
|
|
" ",
|
|
" Posted by order of the Town Council.",
|
|
" Unauthorized notices will be removed.",
|
|
]);
|
|
}
|
|
"""
|
|
|
|
bookshelf = """
|
|
fn bump(me, _dir) {
|
|
scroll([
|
|
" THE BOOKSHELF",
|
|
" ",
|
|
"A well-worn shelf packed with old volumes. You scan the spines:",
|
|
" ",
|
|
" 'Practical Herbalism and Country Remedies'",
|
|
" 'A History of the Seven Kingdoms, Vol. II'",
|
|
" 'Monsters of the Northern Reach'",
|
|
" 'The Innkeeper's Handbook'",
|
|
" 'Common Stars and Their Names'",
|
|
" 'Letters to No One in Particular' (handwritten)",
|
|
" ",
|
|
"One has a pressed flower tucked in as a bookmark.",
|
|
]);
|
|
}
|
|
"""
|
|
|
|
fireplace = """
|
|
fn bump(me, _dir) {
|
|
say("The fire crackles warmly.\\nYou feel at ease.");
|
|
}
|
|
"""
|
|
|
|
chest = """
|
|
fn bump(me, _dir) {
|
|
scroll([
|
|
" THE CHEST",
|
|
" ",
|
|
"A sturdy oak chest with iron fittings. The latch is unlocked.",
|
|
" ",
|
|
"Inside you find:",
|
|
"",
|
|
"- A wool blanket, slightly moth-eaten",
|
|
"- Three copper coins",
|
|
"- A half-empty bottle of something medicinal",
|
|
"- A child's wooden toy horse",
|
|
"- A folded letter, sealed but never sent",
|
|
]);
|
|
}
|
|
"""
|
|
|
|
yammerer = """
|
|
fn tick(me, dt) {
|
|
say("blahblahblah", 2.0);
|
|
delay(2.0);
|
|
}
|
|
"""
|
|
|
|
shifter = """
|
|
fn tick(me, dt) {
|
|
let x = me.x;
|
|
let y = me.y;
|
|
|
|
if !me.waiting {
|
|
shift([[x-1, y], [x, y-1], [x+1, y], [x, y+1]]);
|
|
delay(0.25);
|
|
}
|
|
}
|
|
"""
|
|
|
|
# A sensor script: no glyph, off the grid — it just watches for the player stepping
|
|
# onto its cell and logs. Sensors are placed via [[boards.NAME.sensors]].
|
|
tripwire = """
|
|
fn enter(me, dir) {
|
|
if Player.x == me.x && Player.y == me.y {
|
|
log("tripwire: the player crossed here");
|
|
}
|
|
}
|
|
"""
|
|
|
|
[boards.start]
|
|
name = "Starting Room"
|
|
width = 60
|
|
height = 25
|
|
# A single procedural grass floor across the whole board (the old mixed
|
|
# grass/dirt/stone/water floor is not representable with one floor attribute).
|
|
floor = { biome = "grass" }
|
|
|
|
# The grid holds every solid: terrain, crates, the player and scripted objects. A
|
|
# space is a transparent empty cell, so the floor shows through. Portals and sensors
|
|
# are *not* here — they sit alongside the grid (see the arrays further down), because
|
|
# the player has to be able to share their cell.
|
|
grid = """
|
|
############################################################
|
|
# #
|
|
# o #
|
|
# oS #
|
|
# #
|
|
# #
|
|
# )######( B #
|
|
# # #
|
|
# # v #
|
|
# # #
|
|
# | #
|
|
# #
|
|
# V oo - @ #
|
|
# /# #
|
|
# t #
|
|
# #
|
|
# >| #
|
|
# #
|
|
# - #
|
|
# ^ #
|
|
# M #
|
|
# #
|
|
# #
|
|
# #
|
|
############################################################
|
|
"""
|
|
[boards.start.palette]
|
|
"#" = { type = "builtin", kind = "wall", tile = "#", fg = "#808080", bg = "#606060" }
|
|
"o" = { type = "builtin", kind = "crate", tile = 254, fg = "#aaaaaa", bg = "#000000" }
|
|
"-" = { type = "builtin", kind = "hcrate", tile = 29, fg = "#aaaaaa", bg = "#000000" }
|
|
"|" = { type = "builtin", kind = "vcrate", tile = 18, fg = "#aaaaaa", bg = "#000000" }
|
|
">" = { type = "builtin", kind = "pusher_east" }
|
|
"^" = { type = "builtin", kind = "pusher_north" }
|
|
"/" = { type = "builtin", kind = "spinner_cw" }
|
|
"t" = { type = "builtin", kind = "gem" }
|
|
"v" = { type = "builtin", kind = "transporter_south" }
|
|
")" = { type = "builtin", kind = "transporter_east" }
|
|
"(" = { type = "builtin", kind = "transporter_west" }
|
|
"@" = { type = "player" }
|
|
|
|
# The dancer: walks a fixed loop, and teleports back to where it started if the
|
|
# board Registry remembers a previous position.
|
|
[boards.start.palette.V]
|
|
type = "object"
|
|
script = "mover"
|
|
enter = "block"
|
|
tile = 1
|
|
fg = "#33aa33"
|
|
bg = "#000000"
|
|
|
|
[boards.start.palette.M]
|
|
type = "object"
|
|
script = "muffin"
|
|
name = "muffin"
|
|
enter = "block"
|
|
tile = 15
|
|
fg = "#ffdd88"
|
|
bg = "#000000"
|
|
|
|
[boards.start.palette.B]
|
|
type = "object"
|
|
script = "noticeboard"
|
|
name = "noticeboard"
|
|
enter = "block"
|
|
tile = 240
|
|
fg = "#cc9944"
|
|
bg = "#000000"
|
|
|
|
[boards.start.palette.S]
|
|
type = "object"
|
|
script = "shifter"
|
|
name = "shifter"
|
|
enter = "block"
|
|
tile = 240
|
|
fg = "#cc9944"
|
|
bg = "#000000"
|
|
|
|
[[boards.start.portals]]
|
|
x = 2
|
|
y = 5
|
|
name = "to_house"
|
|
target_board = "house"
|
|
target_name = "from_start"
|
|
|
|
# The greeter is a sensor, not a grid object: it is visible but does not block, so
|
|
# stepping onto it fires its `enter` hook. (A non-solid grid object no longer
|
|
# exists — every object in the grid is solid.)
|
|
[[boards.start.sensors]]
|
|
x = 36
|
|
y = 12
|
|
script = "greeter"
|
|
draw_layer = "Below" # capitalized: DrawLayer's variants are not renamed
|
|
glyph = { tile = "#", fg = "#aa3333", bg = "#000000" }
|
|
|
|
# An invisible tripwire, deliberately placed *under* the hcrate at (40,12): you have
|
|
# to shove the crate west before you can stand here and set it off.
|
|
[[boards.start.sensors]]
|
|
x = 40
|
|
y = 12
|
|
script = "tripwire"
|
|
|
|
[boards.house]
|
|
name = "The House"
|
|
width = 60
|
|
height = 25
|
|
dark = true
|
|
|
|
# A single fixed floor glyph across the whole board. There are no light sources
|
|
# here, so the board is lit only by the player's own torch (SIGHT_RADIUS, 10).
|
|
floor = { glyph = { tile = 176, fg = "#888888", bg = "#444444" } }
|
|
|
|
# The grid: terrain, the player and objects. The return portal is off-grid, below.
|
|
grid = """
|
|
############################################################
|
|
# #
|
|
# K ###F### #
|
|
# # # #
|
|
# ####### #
|
|
# #
|
|
# #
|
|
# ######### T #
|
|
# # # T #
|
|
# ######### #
|
|
# T T #
|
|
# #
|
|
# @ T #
|
|
# #
|
|
# T #
|
|
# ######C###### #
|
|
# T # # #
|
|
# ############# #
|
|
# #
|
|
# #
|
|
# #
|
|
# #
|
|
# #
|
|
# #
|
|
############################################################
|
|
"""
|
|
[boards.house.palette]
|
|
"#" = { type = "builtin", kind = "wall", tile = 178, fg = "#888888", bg = "#555555" }
|
|
"@" = { type = "player" }
|
|
|
|
[boards.house.palette.K]
|
|
type = "object"
|
|
script = "bookshelf"
|
|
name = "bookshelf"
|
|
enter = "block"
|
|
tile = 240
|
|
fg = "#aa7733"
|
|
bg = "#3d1c00"
|
|
|
|
[boards.house.palette.F]
|
|
type = "object"
|
|
script = "fireplace"
|
|
name = "fireplace"
|
|
enter = "block"
|
|
tile = 15
|
|
fg = "#ff8800"
|
|
bg = "#220000"
|
|
|
|
[boards.house.palette.C]
|
|
type = "object"
|
|
script = "chest"
|
|
name = "chest"
|
|
enter = "block"
|
|
tile = 240
|
|
fg = "#ccaa44"
|
|
bg = "#222200"
|
|
|
|
# Seven of these, so it stays unnamed — object names must be board-unique.
|
|
[boards.house.palette.T]
|
|
type = "object"
|
|
script = "yammerer"
|
|
enter = "block"
|
|
tile = 1
|
|
fg = "#99ff44"
|
|
bg = "#000000"
|
|
|
|
[[boards.house.portals]]
|
|
x = 56
|
|
y = 5
|
|
name = "from_start"
|
|
target_board = "start"
|
|
target_name = "to_house"
|
|
|