Add Technical Doodads + UI Fixes

New category for the Doodad Dropper: "Technical"

Technical doodads have a dashed outline and label for now, and they
turn invisible on level start, and are for hidden technical effects on
your level.

The doodads include:

* Goal Region: acts like an invisible Exit Flag (128x128), the level is
  won when the player character touches this region.
* Fire Region: acts like a death barrier (128x128), kills the player
  when a generic "You have died!" message.
* Power Source: on level start, acts like a switch and emits a
  power(true) signal to all linked doodads. Link it to your Electric
  Door for it to be open by default in your level!
* Stall Player (250ms): The player is paused for a moment the first time
  it touches this region. Useful to work around timing issues, e.g.
  help prevent the player from winning a race against another character.

There are some UI improvements to the Doodad Dropper window:

* If the first page of doodads is short, extra spacers are added so the
  alignment and size shows correctly.
* Added a 'background pattern' to the window: any unoccupied icon space
  has an inset rectangle slot.
* "Last pages" which are short still render weirdly without reserving
  the correct height in the TabFrame.

Doodad scripting engine updates:

* Self.Hide() and Self.Show() available.
* Subscribe to "broadcast:ready" to know when the level is ready, so you
  can safely Publish messages without deadlocks!
This commit is contained in:
Noah 2021-10-02 20:52:16 -07:00
parent a1976f6ab7
commit ed925b9ace
11 changed files with 129 additions and 0 deletions

View File

@ -66,6 +66,10 @@ objects() {
cd crumbly-floor/ cd crumbly-floor/
make make
cd .. cd ..
cd regions/
make
cd ..
} }
onoff() { onoff() {

View File

@ -0,0 +1,25 @@
ALL: build
.PHONY: build
build:
# Goal Region
doodad convert -t "Goal Region" goal-128.png reg-goal.doodad
doodad install-script goal.js reg-goal.doodad
# Fire Region
doodad convert -t "Fire Region" fire-128.png reg-fire.doodad
doodad install-script fire.js reg-fire.doodad
# Stall Region
doodad convert -t "Stall Player (250ms)" stall-128.png reg-stall-250.doodad
doodad edit-doodad --tag "ms=250" reg-stall-250.doodad
doodad install-script stall.js reg-stall-250.doodad
# Power Source
doodad convert -t "Power Source" power-64.png power-source.doodad
doodad install-script power.js power-source.doodad
for i in *.doodad; do\
doodad edit-doodad --tag "category=technical" $${i};\
done
cp *.doodad ../../../assets/doodads/

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 B

View File

@ -0,0 +1,19 @@
// Goal Region.
function main() {
Self.Hide();
Events.OnCollide(function (e) {
if (!e.Settled) {
return;
}
// Only care if it's the player.
if (!e.Actor.IsPlayer()) {
return;
}
if (e.InHitbox) {
FailLevel("You have died!");
}
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 986 B

View File

@ -0,0 +1,19 @@
// Goal Region.
function main() {
Self.Hide();
Events.OnCollide(function (e) {
if (!e.Settled) {
return;
}
// Only care if it's the player.
if (!e.Actor.IsPlayer()) {
return;
}
if (e.InHitbox) {
EndLevel();
}
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

View File

@ -0,0 +1,22 @@
// Power source.
// Emits a power(true) signal once on level start.
// If it receives a power signal, it will repeat it after 5 seconds.
// Link two of these bad boys together and you got yourself a clock.
function main() {
Self.Hide();
// See if we are not linked to anything.
var links = Self.GetLinks();
if (links.length === 0) {
console.error(
"%s at %s is not linked to anything! This doodad emits a power(true) on level start to all linked doodads.",
Self.Title,
Self.Position()
);
}
Message.Subscribe("broadcast:ready", function () {
Message.Publish("switch:toggle", true);
Message.Publish("power", true);
});
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,40 @@
// Stall Player.
// Tags: ms (int)
// Grabs the player one time. Resets if it receives power.
function main() {
Self.Hide();
var active = true,
timeout = 250,
ms = Self.GetTag("ms");
if (ms.length > 0) {
timeout = parseInt(ms);
}
Events.OnCollide(function (e) {
if (!active || !e.Settled) {
return;
}
// Only care if it's the player.
if (!e.Actor.IsPlayer()) {
return;
}
if (e.InHitbox) {
// Grab hold of the player.
e.Actor.Freeze();
setTimeout(function () {
e.Actor.Unfreeze();
}, timeout);
active = false;
}
});
// Reset the trap if powered by a button.
Message.Subscribe("power", function (powered) {
active = true;
});
}