Generic Doodad Script Selection

In the Doodad Properties window, instead of browsing to select a .js
file to install your script, a SelectBox of built-in generic scripts are
available. These scripts implement simple behaviors and adapt to the
full canvas size of the doodad.

Built-in scripts so far include:

* generic-anvil.js: behaves just like the Anvil.
* generic-fire.js: the entire canvas hitbox acts like fire pixels,
  "burning" mobile doodads and failing the level for the player.
* generic-solid.js: the entire canvas hitbox acts solid
This commit is contained in:
Noah 2021-09-02 22:33:28 -07:00
parent 0fa1bf8a76
commit 7ea86b4ffc
5 changed files with 291 additions and 30 deletions

View File

@ -0,0 +1,61 @@
// Generic "Anvil" Doodad Script
/*
A doodad that falls and is dangerous while it falls.
Can be attached to any doodad.
*/
var falling = false;
function main() {
// Make the hitbox be the full canvas size of this doodad.
// Adjust if you want a narrower hitbox.
var size = Self.Size()
Self.SetHitbox(0, 0, size.W, size.H)
// Note: doodad is not "solid" but hurts if it falls on you.
Self.SetMobile(true);
Self.SetGravity(true);
// Monitor our Y position to tell if we've been falling.
var lastPoint = Self.Position();
setInterval(function () {
var nowAt = Self.Position();
if (nowAt.Y > lastPoint.Y) {
falling = true;
} else {
falling = false;
}
lastPoint = nowAt;
}, 100);
Events.OnCollide(function (e) {
if (!e.Settled) {
return;
}
// Were we falling?
if (falling) {
if (e.InHitbox) {
if (e.Actor.IsPlayer()) {
// Fatal to the player.
Sound.Play("crumbly-break.wav");
FailLevel("Watch out for " + Self.Title + "!");
return;
}
else if (e.Actor.IsMobile()) {
// Destroy mobile doodads.
Sound.Play("crumbly-break.wav");
e.Actor.Destroy();
}
}
}
});
// When we receive power, we reset to our original position.
var origPoint = Self.Position();
Message.Subscribe("power", function (powered) {
Self.MoveTo(origPoint);
Self.SetVelocity(Vector(0, 0));
});
}

View File

@ -0,0 +1,36 @@
// Generic "Fire" Doodad Script
/*
The entire square shape of your doodad acts similar to "Fire"
pixels - killing the player character upon contact.
Can be attached to any doodad.
*/
function main() {
// Make the hitbox be the full canvas size of this doodad.
// Adjust if you want a narrower hitbox.
var size = Self.Size()
Self.SetHitbox(0, 0, size.W, size.H)
Events.OnCollide(function (e) {
if (!e.Settled || !e.InHitbox) {
return;
}
// Turn mobile actors black, like real fire does.
if (e.Actor.IsMobile()) {
e.Actor.Canvas.MaskColor = RGBA(1, 1, 1, 255)
}
// End the level if it's the player.
if (e.Actor.IsPlayer()) {
FailLevel("Watch out for " + Self.Title + "!");
}
})
Events.OnLeave(function (e) {
if (e.Actor.IsMobile()) {
e.Actor.MaskColor = RGBA(0, 0, 0, 0)
}
})
}

View File

@ -0,0 +1,19 @@
// Generic "Solid" Doodad Script
/*
The entire square shape of your doodad acts similar to "solid"
pixels - blocking collision from all sides.
Can be attached to any doodad.
*/
function main() {
// Make the hitbox be the full canvas size of this doodad.
// Adjust if you want a narrower hitbox.
var size = Self.Size()
Self.SetHitbox(0, 0, size.W, size.H)
// Solid to all collisions.
Events.OnCollide(function (e) {
return false;
})
}

View File

@ -13,6 +13,10 @@ func (w *Canvas) MakeSelfAPI(actor *Actor) map[string]interface{} {
// functions // functions
"ID": actor.ID, "ID": actor.ID,
"Size": func() render.Rect {
var size = actor.Doodad().ChunkSize()
return render.NewRect(size, size)
},
"GetTag": actor.Doodad().Tag, "GetTag": actor.Doodad().Tag,
"Position": actor.Position, "Position": actor.Position,
"MoveTo": func(p render.Point) { "MoveTo": func(p render.Point) {

View File

@ -1,11 +1,13 @@
package windows package windows
import ( import (
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"git.kirsle.net/apps/doodle/assets"
"git.kirsle.net/apps/doodle/pkg/balance" "git.kirsle.net/apps/doodle/pkg/balance"
"git.kirsle.net/apps/doodle/pkg/doodads" "git.kirsle.net/apps/doodle/pkg/doodads"
"git.kirsle.net/apps/doodle/pkg/log" "git.kirsle.net/apps/doodle/pkg/log"
@ -16,6 +18,37 @@ import (
"git.kirsle.net/go/ui" "git.kirsle.net/go/ui"
) )
// Some generic built-in doodad scripts users can attach.
var GenericScripts = []struct {
Label string
Help string
Filename string
}{
{
Label: "Generic Solid",
Help: "The whole canvas of your doodad acts solid.\n" +
"The player and other mobile doodads can walk on\n" +
"top of it, and it blocks movement from the sides.",
Filename: "assets/scripts/generic-solid.js",
},
{
Label: "Generic Fire",
Help: "The whole canvas of your doodad acts like fire.\n" +
"Mobile doodads who touch it turn dark, and if\n" +
"the player touches it - game over! The failure\n" +
"message says: 'Watch out for (title)!'",
Filename: "assets/scripts/generic-fire.js",
},
{
Label: "Generic Anvil",
Help: "This doodad will behave like the Anvil: fall with\n" +
"gravity and be deadly to any mobile doodad that it\n" +
"lands on! The failure message says:\n" +
"'Watch out for (title)!'",
Filename: "assets/scripts/generic-anvil.js",
},
}
// DoodadProperties window. // DoodadProperties window.
type DoodadProperties struct { type DoodadProperties struct {
// Settings passed in by doodle // Settings passed in by doodle
@ -247,6 +280,32 @@ func (c DoodadProperties) makeMetaTab(tabFrame *ui.TabFrame, Width, Height int)
}) })
} }
// Attaching a Script Frame
{
label := ui.NewLabel(ui.Label{
Text: "Attach a Script",
Font: balance.LabelFont,
})
tab.Pack(label, ui.Pack{
Side: ui.N,
FillX: true,
})
frame := ui.NewFrame("Attach Script Frame")
tab.Pack(frame, ui.Pack{
Side: ui.N,
FillX: true,
})
// Browse Script label.
lblBrowse := ui.NewLabel(ui.Label{
Text: "Browse and attach a .js file:",
Font: balance.MenuFont,
})
frame.Pack(lblBrowse, ui.Pack{
Side: ui.W,
})
// Browse Script button. // Browse Script button.
btnBrowse := ui.NewButton("Browse Script", ui.NewLabel(ui.Label{ btnBrowse := ui.NewButton("Browse Script", ui.NewLabel(ui.Label{
Text: "Attach a script...", Text: "Attach a script...",
@ -276,10 +335,92 @@ func (c DoodadProperties) makeMetaTab(tabFrame *ui.TabFrame, Width, Height int)
return nil return nil
}) })
c.Supervisor.Add(btnBrowse) c.Supervisor.Add(btnBrowse)
tab.Pack(btnBrowse, ui.Pack{ frame.Pack(btnBrowse, ui.Pack{
Side: ui.N, Side: ui.E,
Padding: 4,
}) })
}
// Built-in Generic Scripts Frame
{
frame := ui.NewFrame("Generic Scripts Frame")
tab.Pack(frame, ui.Pack{
Side: ui.N,
FillX: true,
PadY: 4,
})
label := ui.NewLabel(ui.Label{
Text: "Or select from a generic script:",
Font: ui.MenuFont,
})
frame.Pack(label, ui.Pack{
Side: ui.W,
})
// SelectBox for the built-ins.
sb := ui.NewSelectBox("Select", ui.Label{
Font: ui.MenuFont,
})
tab.Pack(sb, ui.Pack{
Side: ui.N,
FillX: true,
})
for _, script := range GenericScripts {
sb.AddItem(script.Label, script.Filename, func() {})
}
sb.SetValue(GenericScripts[0].Filename)
sb.AlwaysChange = true
sb.Handle(ui.Change, func(ed ui.EventData) error {
if selection, ok := sb.GetValue(); ok {
if filename, ok := selection.Value.(string); ok {
// Get this script from the built-in assets.
data, err := assets.Asset(filename)
if err != nil {
shmem.Flash("Couldn't get script: %s", err)
return nil
}
// Find the data from the builtins.
var label, help string
for _, script := range GenericScripts {
if script.Filename == filename {
label = script.Label
help = script.Help
break
}
}
// Prompt the user + a description of this option.
var (
basename = filepath.Base(filename)
description = fmt.Sprintf(
"Do you want to install %s to your doodad?\n\n"+
"%s\n\n%s",
basename,
label,
help,
)
)
modal.Confirm(description).Then(func() {
c.EditDoodad.Script = string(data)
shmem.Flash("Attached %s to your doodad", filepath.Base(filename))
// Toggle the if/else frames.
ifScript.Show()
elseScript.Hide()
})
}
}
return nil
})
sb.Supervise(c.Supervisor)
c.Supervisor.Add(sb)
}
// Show/hide appropriate frames. // Show/hide appropriate frames.
if c.EditDoodad.Script == "" { if c.EditDoodad.Script == "" {