New Doodad: Anvil
* The Anvil doodad is affected by gravity and becomes dangerous when falling. If it lands on the player character, you die! If it lands on any other mobile doodad, it destroys it! It can land on solid doodads such as the Electric Trapdoor and the Crumbly Floor. It will activate a Crumbly Floor if it lands on one, and can activate buttons and switches that it passes. * JavaScript API: FailLevel(message) can be called from a doodad to kill the player character. The Anvil does this if it collides with the player while it's been falling.
This commit is contained in:
parent
810ba193d9
commit
0518df226c
|
@ -41,9 +41,6 @@ function main() {
|
||||||
// When we receive power, we reset to our original position.
|
// When we receive power, we reset to our original position.
|
||||||
var origPoint = Self.Position();
|
var origPoint = Self.Position();
|
||||||
Message.Subscribe("power", function (powered) {
|
Message.Subscribe("power", function (powered) {
|
||||||
console.error("Box received power! %+v", powered);
|
|
||||||
console.error("MoveTo: %+v", origPoint);
|
|
||||||
console.error("Keys: %+v", Object.keys(Self));
|
|
||||||
Self.MoveTo(origPoint);
|
Self.MoveTo(origPoint);
|
||||||
Self.SetVelocity(Vector(0, 0));
|
Self.SetVelocity(Vector(0, 0));
|
||||||
});
|
});
|
||||||
|
|
|
@ -58,6 +58,10 @@ objects() {
|
||||||
cd box/
|
cd box/
|
||||||
make
|
make
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
cd crumbly-floor/
|
||||||
|
make
|
||||||
|
cd ..
|
||||||
}
|
}
|
||||||
|
|
||||||
onoff() {
|
onoff() {
|
||||||
|
|
|
@ -2,10 +2,16 @@ ALL: build
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build:
|
build:
|
||||||
|
# Start Flag
|
||||||
|
doodad convert -t "Start Flag" start-flag.png start-flag.doodad
|
||||||
|
|
||||||
|
# Exit Flag
|
||||||
doodad convert -t "Exit Flag" exit-flag.png exit-flag.doodad
|
doodad convert -t "Exit Flag" exit-flag.png exit-flag.doodad
|
||||||
doodad install-script exit-flag.js exit-flag.doodad
|
doodad install-script exit-flag.js exit-flag.doodad
|
||||||
|
|
||||||
doodad convert -t "Start Flag" start-flag.png start-flag.doodad
|
# Anvil
|
||||||
|
doodad convert -t "Anvil" anvil.png anvil.doodad
|
||||||
|
doodad install-script anvil.js anvil.doodad
|
||||||
|
|
||||||
for i in *.doodad; do\
|
for i in *.doodad; do\
|
||||||
doodad edit-doodad --tag "category=objects" $${i};\
|
doodad edit-doodad --tag "category=objects" $${i};\
|
||||||
|
|
51
dev-assets/doodads/objects/anvil.js
Normal file
51
dev-assets/doodads/objects/anvil.js
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
// Anvil
|
||||||
|
var falling = false;
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
// Note: doodad is not "solid" but hurts if it falls on you.
|
||||||
|
Self.SetHitbox(0, 0, 48, 25);
|
||||||
|
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 anvils!");
|
||||||
|
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));
|
||||||
|
});
|
||||||
|
}
|
BIN
dev-assets/doodads/objects/anvil.png
Normal file
BIN
dev-assets/doodads/objects/anvil.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 796 B |
|
@ -7,13 +7,13 @@ function main() {
|
||||||
var state = false;
|
var state = false;
|
||||||
var collide = false;
|
var collide = false;
|
||||||
|
|
||||||
Message.Subscribe("power", function(powered) {
|
Message.Subscribe("power", function (powered) {
|
||||||
state = powered;
|
state = powered;
|
||||||
showState(state);
|
showState(state);
|
||||||
});
|
});
|
||||||
|
|
||||||
Events.OnCollide(function(e) {
|
Events.OnCollide(function (e) {
|
||||||
if (!e.Settled) {
|
if (!e.Settled || !e.Actor.IsMobile()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ function main() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Events.OnLeave(function(e) {
|
Events.OnLeave(function (e) {
|
||||||
collide = false;
|
collide = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,11 @@ function setPoweredState(powered) {
|
||||||
Self.PlayAnimation("open", function () {
|
Self.PlayAnimation("open", function () {
|
||||||
isOpen = true;
|
isOpen = true;
|
||||||
animating = false;
|
animating = false;
|
||||||
|
|
||||||
|
// Had we lost power quickly?
|
||||||
|
if (!powerState) {
|
||||||
|
setPoweredState(false);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
animating = true;
|
animating = true;
|
||||||
|
|
|
@ -30,6 +30,7 @@ type API interface {
|
||||||
|
|
||||||
// Game functions.k
|
// Game functions.k
|
||||||
EndLevel() // Exit the current level with a victory
|
EndLevel() // Exit the current level with a victory
|
||||||
|
FailLevel(message)
|
||||||
|
|
||||||
/************************************
|
/************************************
|
||||||
* Event Handler Callback Functions *
|
* Event Handler Callback Functions *
|
||||||
|
|
|
@ -41,6 +41,7 @@ type PlayScene struct {
|
||||||
// buttons what to do next.
|
// buttons what to do next.
|
||||||
alertBox *ui.Window
|
alertBox *ui.Window
|
||||||
alertBoxLabel *ui.Label
|
alertBoxLabel *ui.Label
|
||||||
|
alertBoxValue string
|
||||||
alertReplayButton *ui.Button // Replay level
|
alertReplayButton *ui.Button // Replay level
|
||||||
alertEditButton *ui.Button // Edit Level
|
alertEditButton *ui.Button // Edit Level
|
||||||
alertNextButton *ui.Button // Next Level
|
alertNextButton *ui.Button // Next Level
|
||||||
|
@ -118,6 +119,29 @@ func (s *PlayScene) setupAsync(d *Doodle) error {
|
||||||
s.alertExitButton.Show()
|
s.alertExitButton.Show()
|
||||||
|
|
||||||
// Show the alert box.
|
// Show the alert box.
|
||||||
|
s.alertBox.Title = "Level Completed"
|
||||||
|
s.alertBoxValue = "Congratulations on clearing the level!"
|
||||||
|
s.alertBox.Show()
|
||||||
|
})
|
||||||
|
s.scripting.OnLevelFail(func(message string) {
|
||||||
|
d.Flash(message)
|
||||||
|
|
||||||
|
// Pause the simulation.
|
||||||
|
s.running = false
|
||||||
|
|
||||||
|
// Toggle the relevant buttons on.
|
||||||
|
if s.CanEdit {
|
||||||
|
s.alertEditButton.Show()
|
||||||
|
}
|
||||||
|
s.alertNextButton.Hide()
|
||||||
|
|
||||||
|
// Always-visible buttons.
|
||||||
|
s.alertReplayButton.Show()
|
||||||
|
s.alertExitButton.Show()
|
||||||
|
|
||||||
|
// Show the alert box.
|
||||||
|
s.alertBox.Title = "You've died!"
|
||||||
|
s.alertBoxValue = message
|
||||||
s.alertBox.Show()
|
s.alertBox.Show()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -310,7 +334,7 @@ func (s *PlayScene) SetupAlertbox() {
|
||||||
******************/
|
******************/
|
||||||
|
|
||||||
s.alertBoxLabel = ui.NewLabel(ui.Label{
|
s.alertBoxLabel = ui.NewLabel(ui.Label{
|
||||||
Text: "Congratulations on clearing the level!",
|
TextVariable: &s.alertBoxValue,
|
||||||
Font: balance.LabelFont,
|
Font: balance.LabelFont,
|
||||||
})
|
})
|
||||||
frame.Pack(s.alertBoxLabel, ui.Pack{
|
frame.Pack(s.alertBoxLabel, ui.Pack{
|
||||||
|
@ -390,7 +414,7 @@ func (s *PlayScene) RestartLevel() {
|
||||||
func (s *PlayScene) DieByFire(name string) {
|
func (s *PlayScene) DieByFire(name string) {
|
||||||
log.Info("Watch out for %s!", name)
|
log.Info("Watch out for %s!", name)
|
||||||
s.alertBox.Title = "You've died!"
|
s.alertBox.Title = "You've died!"
|
||||||
s.alertBoxLabel.Text = fmt.Sprintf("Watch out for %s!", name)
|
s.alertBoxValue = fmt.Sprintf("Watch out for %s!", name)
|
||||||
|
|
||||||
s.alertReplayButton.Show()
|
s.alertReplayButton.Show()
|
||||||
if s.CanEdit {
|
if s.CanEdit {
|
||||||
|
|
|
@ -19,6 +19,8 @@ This adds the global methods `Message.Subscribe(name, func)` and
|
||||||
`Message.Publish(name, args)` to the JavaScript VM's scope.
|
`Message.Publish(name, args)` to the JavaScript VM's scope.
|
||||||
*/
|
*/
|
||||||
func RegisterPublishHooks(s *Supervisor, vm *VM) {
|
func RegisterPublishHooks(s *Supervisor, vm *VM) {
|
||||||
|
log.Error("RegisterPublishHooks called with %+v %+v", s, vm)
|
||||||
|
|
||||||
// Goroutine to watch the VM's inbound channel and invoke Subscribe handlers
|
// Goroutine to watch the VM's inbound channel and invoke Subscribe handlers
|
||||||
// for any matching messages received.
|
// for any matching messages received.
|
||||||
go func() {
|
go func() {
|
||||||
|
|
|
@ -17,6 +17,7 @@ type Supervisor struct {
|
||||||
|
|
||||||
// Global event handlers.
|
// Global event handlers.
|
||||||
onLevelExit func()
|
onLevelExit func()
|
||||||
|
onLevelFail func(message string)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSupervisor creates a new JavaScript Supervior.
|
// NewSupervisor creates a new JavaScript Supervior.
|
||||||
|
|
|
@ -10,14 +10,25 @@ Names registered:
|
||||||
*/
|
*/
|
||||||
func RegisterEventHooks(s *Supervisor, vm *VM) {
|
func RegisterEventHooks(s *Supervisor, vm *VM) {
|
||||||
vm.Set("EndLevel", func() {
|
vm.Set("EndLevel", func() {
|
||||||
if s.onLevelExit == nil {
|
if s.onLevelFail == nil {
|
||||||
panic("JS EndLevel(): no OnLevelExit handler attached to script supervisor")
|
panic("JS FailLevel(): No OnLevelFail handler attached to script supervisor")
|
||||||
}
|
}
|
||||||
s.onLevelExit()
|
s.onLevelExit()
|
||||||
})
|
})
|
||||||
|
vm.Set("FailLevel", func(message string) {
|
||||||
|
if s.onLevelFail == nil {
|
||||||
|
panic("JS FailLevel(): No OnLevelFail handler attached to script supervisor")
|
||||||
|
}
|
||||||
|
s.onLevelFail(message)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnLevelExit registers an event hook for when a Level Exit doodad is reached.
|
// OnLevelExit registers an event hook for when a Level Exit doodad is reached.
|
||||||
func (s *Supervisor) OnLevelExit(handler func()) {
|
func (s *Supervisor) OnLevelExit(handler func()) {
|
||||||
s.onLevelExit = handler
|
s.onLevelExit = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnLevelFail registers an event hook for level failures (doodads killing the player).
|
||||||
|
func (s *Supervisor) OnLevelFail(handler func(string)) {
|
||||||
|
s.onLevelFail = handler
|
||||||
|
}
|
||||||
|
|
|
@ -207,6 +207,7 @@ func (w *Canvas) loopActorCollision() error {
|
||||||
// Are they on top?
|
// Are they on top?
|
||||||
aHitbox := collision.SizePlusHitbox(collision.GetBoundingRect(a), a.Hitbox())
|
aHitbox := collision.SizePlusHitbox(collision.GetBoundingRect(a), a.Hitbox())
|
||||||
if render.AbsInt(test.Y+test.H-aHitbox.Y) == 0 {
|
if render.AbsInt(test.Y+test.H-aHitbox.Y) == 0 {
|
||||||
|
// log.Error("ActorCollision: onTop=true at Y=%s", test.Y)
|
||||||
onTop = true
|
onTop = true
|
||||||
onTopY = test.Y
|
onTopY = test.Y
|
||||||
}
|
}
|
||||||
|
@ -217,6 +218,7 @@ func (w *Canvas) loopActorCollision() error {
|
||||||
lockY = lastGoodBox.Y
|
lockY = lastGoodBox.Y
|
||||||
}
|
}
|
||||||
if onTop {
|
if onTop {
|
||||||
|
// log.Error("ActorCollision: setGrounded(true)", test.Y)
|
||||||
b.SetGrounded(true)
|
b.SetGrounded(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user