Add More Trapdoor Doodads
* Add the other trapdoor directions: Left, Right and Up. * UI: Show a color square in each Palette Swatch button in Edit Mode. * Instead of just the label like "solid", "fire", "decoration" it also shows a square box colored as the swatch color. The label and box are left-aligned in the button. * Minor Play Mode physics update: * The player jump is now limited: they may only continue to move upwards for 20 ticks, after which they must touch ground before jumping again. * Remove the "press Down to move down" button. Only gravity moves you down. * Fix a crash in the Editor Mode when you dragged doodads on top of each other. Source of bug was the loopActorCollision() function, which only should be useful to Play Mode, and it expected the scripting engine to be attached to the Canvas. In EditorMode there is no scripting engine.
|
@ -63,7 +63,13 @@ trapdoors() {
|
||||||
cd trapdoors/
|
cd trapdoors/
|
||||||
|
|
||||||
doodad convert -t "Trapdoor" down{1,2,3,4}.png trapdoor-down.doodad
|
doodad convert -t "Trapdoor" down{1,2,3,4}.png trapdoor-down.doodad
|
||||||
doodad install-script down.js trapdoor-down.doodad
|
doodad convert -t "Trapdoor Left" left{1,2,3,4}.png trapdoor-left.doodad
|
||||||
|
doodad convert -t "Trapdoor Right" right{1,2,3,4}.png trapdoor-right.doodad
|
||||||
|
doodad convert -t "Trapdoor Up" up{1,2,3,4}.png trapdoor-up.doodad
|
||||||
|
doodad install-script trapdoor.js trapdoor-down.doodad
|
||||||
|
doodad install-script trapdoor.js trapdoor-left.doodad
|
||||||
|
doodad install-script trapdoor.js trapdoor-right.doodad
|
||||||
|
doodad install-script trapdoor.js trapdoor-up.doodad
|
||||||
|
|
||||||
cp trapdoor-*.doodad ../../../assets/doodads/
|
cp trapdoor-*.doodad ../../../assets/doodads/
|
||||||
|
|
||||||
|
|
BIN
dev-assets/doodads/trapdoors/left1.png
Normal file
After Width: | Height: | Size: 912 B |
BIN
dev-assets/doodads/trapdoors/left2.png
Normal file
After Width: | Height: | Size: 1013 B |
BIN
dev-assets/doodads/trapdoors/left3.png
Normal file
After Width: | Height: | Size: 1009 B |
BIN
dev-assets/doodads/trapdoors/left4.png
Normal file
After Width: | Height: | Size: 765 B |
BIN
dev-assets/doodads/trapdoors/right1.png
Normal file
After Width: | Height: | Size: 933 B |
BIN
dev-assets/doodads/trapdoors/right2.png
Normal file
After Width: | Height: | Size: 1011 B |
BIN
dev-assets/doodads/trapdoors/right3.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
dev-assets/doodads/trapdoors/right4.png
Normal file
After Width: | Height: | Size: 789 B |
71
dev-assets/doodads/trapdoors/trapdoor.js
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
function main() {
|
||||||
|
// What direction is the trapdoor facing?
|
||||||
|
// - Titles are like "Trapdoor Left" or "Trapdoor Right"
|
||||||
|
// - The default (down) is called just "Trapdoor"
|
||||||
|
var direction = Self.Doodad.Title.split(" ")[1];
|
||||||
|
if (!direction) {
|
||||||
|
direction = "down";
|
||||||
|
}
|
||||||
|
direction = direction.toLowerCase();
|
||||||
|
|
||||||
|
console.log("Trapdoor(%s) initialized", direction);
|
||||||
|
|
||||||
|
var timer = 0;
|
||||||
|
|
||||||
|
// Set our hitbox based on our orientation.
|
||||||
|
var thickness = 6;
|
||||||
|
var doodadSize = 72;
|
||||||
|
if (direction === "left") {
|
||||||
|
Self.SetHitbox(48, 0, doodadSize, doodadSize);
|
||||||
|
} else if (direction === "right") {
|
||||||
|
Self.SetHitbox(0, 0, thickness+4, doodadSize);
|
||||||
|
} else if (direction === "up") {
|
||||||
|
Self.SetHitbox(0, doodadSize - thickness, doodadSize, doodadSize);
|
||||||
|
} else { // Down, default.
|
||||||
|
Self.SetHitbox(0, 0, 72, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
var animationSpeed = 100;
|
||||||
|
var opened = false;
|
||||||
|
|
||||||
|
// Register our animations.
|
||||||
|
var frames = [];
|
||||||
|
for (var i = 1; i <= 4; i++) {
|
||||||
|
frames.push(direction + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self.AddAnimation("open", animationSpeed, frames);
|
||||||
|
frames.reverse();
|
||||||
|
Self.AddAnimation("close", animationSpeed, frames);
|
||||||
|
|
||||||
|
Events.OnCollide( function(e) {
|
||||||
|
if (opened) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is the actor colliding our solid part?
|
||||||
|
if (e.InHitbox) {
|
||||||
|
// Are they touching our opening side?
|
||||||
|
if (direction === "left" && (e.Overlap.X+e.Overlap.W) < (doodadSize-thickness)) {
|
||||||
|
return false;
|
||||||
|
} else if (direction === "right" && e.Overlap.X > 0) {
|
||||||
|
return false;
|
||||||
|
} else if (direction === "up" && (e.Overlap.Y+e.Overlap.H) < doodadSize) {
|
||||||
|
return false;
|
||||||
|
} else if (direction === "down" && e.Overlap.Y > 0) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
opened = true;
|
||||||
|
Self.PlayAnimation("open", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Events.OnLeave(function() {
|
||||||
|
if (opened) {
|
||||||
|
Self.PlayAnimation("close", function() {
|
||||||
|
opened = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
BIN
dev-assets/doodads/trapdoors/up1.png
Normal file
After Width: | Height: | Size: 831 B |
BIN
dev-assets/doodads/trapdoors/up2.png
Normal file
After Width: | Height: | Size: 964 B |
BIN
dev-assets/doodads/trapdoors/up3.png
Normal file
After Width: | Height: | Size: 1.0 KiB |
BIN
dev-assets/doodads/trapdoors/up4.png
Normal file
After Width: | Height: | Size: 846 B |
|
@ -19,7 +19,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Width of the panel frame.
|
// Width of the panel frame.
|
||||||
var paletteWidth int32 = 150
|
var paletteWidth int32 = 160
|
||||||
|
|
||||||
// EditorUI manages the user interface for the Editor Scene.
|
// EditorUI manages the user interface for the Editor Scene.
|
||||||
type EditorUI struct {
|
type EditorUI struct {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package doodle
|
package doodle
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"git.kirsle.net/apps/doodle/lib/render"
|
"git.kirsle.net/apps/doodle/lib/render"
|
||||||
"git.kirsle.net/apps/doodle/lib/ui"
|
"git.kirsle.net/apps/doodle/lib/ui"
|
||||||
"git.kirsle.net/apps/doodle/pkg/balance"
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
||||||
|
@ -66,16 +68,42 @@ func (u *EditorUI) setupPaletteFrame(window *ui.Window) *ui.Frame {
|
||||||
// Draw the radio buttons for the palette.
|
// Draw the radio buttons for the palette.
|
||||||
if u.Canvas != nil && u.Canvas.Palette != nil {
|
if u.Canvas != nil && u.Canvas.Palette != nil {
|
||||||
for _, swatch := range u.Canvas.Palette.Swatches {
|
for _, swatch := range u.Canvas.Palette.Swatches {
|
||||||
|
swFrame := ui.NewFrame(fmt.Sprintf("Swatch(%s) Button Frame", swatch.Name))
|
||||||
|
|
||||||
|
colorFrame := ui.NewFrame(fmt.Sprintf("Swatch(%s) Color Box", swatch.Name))
|
||||||
|
colorFrame.Configure(ui.Config{
|
||||||
|
Width: 16,
|
||||||
|
Height: 16,
|
||||||
|
Background: swatch.Color,
|
||||||
|
BorderSize: 1,
|
||||||
|
BorderStyle: ui.BorderSunken,
|
||||||
|
})
|
||||||
|
swFrame.Pack(colorFrame, ui.Pack{
|
||||||
|
Anchor: ui.W,
|
||||||
|
})
|
||||||
|
|
||||||
label := ui.NewLabel(ui.Label{
|
label := ui.NewLabel(ui.Label{
|
||||||
Text: swatch.Name,
|
Text: swatch.Name,
|
||||||
Font: balance.StatusFont,
|
Font: balance.StatusFont,
|
||||||
})
|
})
|
||||||
label.Font.Color = swatch.Color.Darken(128)
|
label.Font.Color = swatch.Color.Darken(128)
|
||||||
|
swFrame.Pack(label, ui.Pack{
|
||||||
|
Anchor: ui.W,
|
||||||
|
})
|
||||||
|
|
||||||
btn := ui.NewRadioButton("palette", &u.selectedSwatch, swatch.Name, label)
|
btn := ui.NewRadioButton("palette", &u.selectedSwatch, swatch.Name, swFrame)
|
||||||
btn.Handle(ui.Click, onClick)
|
btn.Handle(ui.Click, onClick)
|
||||||
u.Supervisor.Add(btn)
|
u.Supervisor.Add(btn)
|
||||||
|
|
||||||
|
btn.Compute(u.d.Engine)
|
||||||
|
swFrame.Configure(ui.Config{
|
||||||
|
Height: label.Size().H,
|
||||||
|
|
||||||
|
// TODO: magic number, trying to left-align
|
||||||
|
// the label by making the frame as wide as possible.
|
||||||
|
Width: paletteWidth - 16,
|
||||||
|
})
|
||||||
|
|
||||||
frame.Pack(btn, ui.Pack{
|
frame.Pack(btn, ui.Pack{
|
||||||
Anchor: ui.N,
|
Anchor: ui.N,
|
||||||
Fill: true,
|
Fill: true,
|
||||||
|
|
|
@ -48,6 +48,7 @@ type PlayScene struct {
|
||||||
|
|
||||||
// Player character
|
// Player character
|
||||||
Player *uix.Actor
|
Player *uix.Actor
|
||||||
|
playerJumpCounter int // limit jump length
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name of the scene.
|
// Name of the scene.
|
||||||
|
@ -349,17 +350,22 @@ func (s *PlayScene) movePlayer(ev *events.State) {
|
||||||
|
|
||||||
var velocity render.Point
|
var velocity render.Point
|
||||||
|
|
||||||
if ev.Down.Now {
|
|
||||||
velocity.Y = playerSpeed
|
|
||||||
}
|
|
||||||
if ev.Left.Now {
|
if ev.Left.Now {
|
||||||
velocity.X = -playerSpeed
|
velocity.X = -playerSpeed
|
||||||
}
|
}
|
||||||
if ev.Right.Now {
|
if ev.Right.Now {
|
||||||
velocity.X = playerSpeed
|
velocity.X = playerSpeed
|
||||||
}
|
}
|
||||||
if ev.Up.Now {
|
if ev.Up.Now && (s.Player.Grounded() || s.playerJumpCounter >= 0) {
|
||||||
velocity.Y = -playerSpeed
|
velocity.Y = -playerSpeed
|
||||||
|
|
||||||
|
if s.Player.Grounded() {
|
||||||
|
s.playerJumpCounter = 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.Player.Grounded() {
|
||||||
|
s.playerJumpCounter--
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Apply gravity if not grounded.
|
// // Apply gravity if not grounded.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package uix
|
package uix
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -16,9 +17,11 @@ import (
|
||||||
// loopActorCollision is the Loop function that checks if pairs of
|
// loopActorCollision is the Loop function that checks if pairs of
|
||||||
// actors are colliding with each other, and handles their scripting
|
// actors are colliding with each other, and handles their scripting
|
||||||
// responses to such collisions.
|
// responses to such collisions.
|
||||||
//
|
|
||||||
// boxes: array of Actor bounding box rects.
|
|
||||||
func (w *Canvas) loopActorCollision() error {
|
func (w *Canvas) loopActorCollision() error {
|
||||||
|
if w.scripting == nil {
|
||||||
|
return errors.New("Canvas.loopActorCollision: scripting engine not attached to Canvas")
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// Current time of this tick so we can advance animations.
|
// Current time of this tick so we can advance animations.
|
||||||
now = time.Now()
|
now = time.Now()
|
||||||
|
|
|
@ -215,9 +215,11 @@ func (w *Canvas) Loop(ev *events.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check collisions between actors.
|
// Check collisions between actors.
|
||||||
|
if w.scripting != nil {
|
||||||
if err := w.loopActorCollision(); err != nil {
|
if err := w.loopActorCollision(); err != nil {
|
||||||
log.Error("loopActorCollision: %s", err)
|
log.Error("loopActorCollision: %s", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If the canvas is editable, only care if it's over our space.
|
// If the canvas is editable, only care if it's over our space.
|
||||||
if w.Editable {
|
if w.Editable {
|
||||||
|
|