v0.11.0 last minute tweaks
* When playing as the Bird, the dive attack is able to destroy other mobile doodads such as Azulians and Thieves. * The Box has been made invulnerable so it can't be destroyed by Anvils or player-controlled Birds. * Bugfixes with pop-up modals: * The quit game confirm modal doesn't appear if another modal is already active on screen. * The Escape key can dismiss Alert and Confirm modals. * Add "Level" menu items to Play Mode to restart the level or retry from the last checkpoint (in case of softlocks, etc.)
This commit is contained in:
parent
40cb9f15cb
commit
962098d4e7
11
Changes.md
11
Changes.md
|
@ -1,6 +1,6 @@
|
|||
# Changes
|
||||
|
||||
## v0.11.0 (Feb 20 2022)
|
||||
## v0.11.0 (Feb 21 2022)
|
||||
|
||||
New features:
|
||||
|
||||
|
@ -34,8 +34,9 @@ are becoming more dangerous:
|
|||
|
||||
* The **Bird** now searches for the player diagonally in front of
|
||||
it for about 240px or so. If spotted it will dive toward you and
|
||||
it is dangerous when diving! When playing as the bird, the dive sprite
|
||||
is used when flying diagonally downwards.
|
||||
it is dangerous when diving! When _playing_ as the bird, the dive sprite
|
||||
is used when flying diagonally downwards. The player-controlled bird
|
||||
can kill mobile doodads by diving into them.
|
||||
* The **Azulians** will start to follow the player when you get
|
||||
close and they are dangerous when they touch you -- but not if
|
||||
you're the **Thief.** The red Azulian has a wider search radius,
|
||||
|
@ -49,6 +50,8 @@ are becoming more dangerous:
|
|||
* The **Anvil** is invulnerable -- if the player character is the Anvil
|
||||
it can not die by fire or hostile enemies, and Anvils can not destroy
|
||||
other Anvils.
|
||||
* The **Box** is also made invulnerable so it can't be destroyed by a
|
||||
player-controlled Anvil or Bird.
|
||||
|
||||
New functions are available on the JavaScript API for doodads:
|
||||
|
||||
|
@ -82,6 +85,8 @@ Other changes:
|
|||
caught and presented nicely in an in-game popup window.
|
||||
* When playing as the Bird, the flying animation now loops while the
|
||||
player is staying still rather than pausing.
|
||||
* The "Level" menu in Play Mode has options to restart the level or
|
||||
retry from last checkpoint, in case a player got softlocked.
|
||||
* When the game checks if there's an update available via
|
||||
<https://download.sketchymaze.com/version.json> it will send a user
|
||||
agent header like: "Sketchy Maze v0.10.2 on linux/amd64" sending only
|
||||
|
|
|
@ -144,9 +144,24 @@ function AI_ScanForPlayer() {
|
|||
|
||||
// If under control of the player character.
|
||||
function player() {
|
||||
var playerSpeed = 12;
|
||||
let playerSpeed = 12,
|
||||
diving = false,
|
||||
falling = false;
|
||||
|
||||
// The player can dive by moving downwards and laterally, but
|
||||
// de-cheese their ability to just sweep across the ground; if
|
||||
// they aren't seen to be moving downwards, cancel the dive.
|
||||
let lastPoint = Self.Position();
|
||||
setInterval(() => {
|
||||
let nowAt = Self.Position();
|
||||
if (nowAt.Y > lastPoint.Y) {
|
||||
falling = true;
|
||||
} else {
|
||||
falling = false;
|
||||
}
|
||||
lastPoint = nowAt;
|
||||
}, 100);
|
||||
|
||||
Self.SetInventory(true);
|
||||
Events.OnKeypress((ev) => {
|
||||
Vx = 0;
|
||||
Vy = 0;
|
||||
|
@ -158,31 +173,45 @@ function player() {
|
|||
}
|
||||
|
||||
// Dive!
|
||||
if (ev.Down && ev.Right) {
|
||||
if (ev.Down && ev.Right && falling) {
|
||||
Self.StopAnimation();
|
||||
Self.ShowLayerNamed("dive-right");
|
||||
} else if (ev.Down && ev.Left) {
|
||||
diving = falling;
|
||||
} else if (ev.Down && ev.Left && falling) {
|
||||
Self.StopAnimation();
|
||||
Self.ShowLayerNamed("dive-left");
|
||||
diving = falling;
|
||||
} else if (ev.Right) {
|
||||
// Fly right.
|
||||
if (!Self.IsAnimating()) {
|
||||
Self.PlayAnimation("fly-right", null);
|
||||
}
|
||||
Vx = playerSpeed;
|
||||
diving = false;
|
||||
} else if (ev.Left) {
|
||||
// Fly left.
|
||||
if (!Self.IsAnimating()) {
|
||||
Self.PlayAnimation("fly-left", null);
|
||||
}
|
||||
Vx = -playerSpeed;
|
||||
diving = false;
|
||||
} else {
|
||||
// Hover in place.
|
||||
if (!Self.IsAnimating()) {
|
||||
Self.PlayAnimation("fly-"+direction);
|
||||
}
|
||||
diving = false;
|
||||
}
|
||||
|
||||
// Self.SetVelocity(Vector(Vx, Vy));
|
||||
})
|
||||
// Player is invulnerable while diving.
|
||||
Self.SetInvulnerable(diving);
|
||||
});
|
||||
|
||||
Events.OnCollide((e) => {
|
||||
// If the player is diving at an enemy mob, destroy it.
|
||||
if (diving && e.Settled && e.Actor.IsMobile() && !e.Actor.Invulnerable()) {
|
||||
Sound.Play("crumbly-break.wav");
|
||||
e.Actor.Destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ const speed = 4,
|
|||
|
||||
function main() {
|
||||
Self.SetMobile(true);
|
||||
Self.SetInvulnerable(true);
|
||||
Self.SetGravity(true);
|
||||
Self.SetHitbox(0, 0, size, size);
|
||||
|
||||
|
|
|
@ -152,16 +152,6 @@ func (d *Doodle) Run() error {
|
|||
log.Debug("Shell: opening shell")
|
||||
d.shell.Open = true
|
||||
} else {
|
||||
// Global event handlers.
|
||||
if keybind.Shutdown(ev) {
|
||||
if d.Debug { // fast exit in -debug mode.
|
||||
d.running = false
|
||||
} else {
|
||||
d.ConfirmExit()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if keybind.Help(ev) {
|
||||
// Launch the local guidebook
|
||||
native.OpenLocalURL(balance.GuidebookPath)
|
||||
|
@ -174,6 +164,16 @@ func (d *Doodle) Run() error {
|
|||
// Make sure no UI modals (alerts, confirms)
|
||||
// or loadscreen are currently visible.
|
||||
if !modal.Handled(ev) {
|
||||
// Global event handlers.
|
||||
if keybind.Shutdown(ev) {
|
||||
if d.Debug { // fast exit in -debug mode.
|
||||
d.running = false
|
||||
} else {
|
||||
d.ConfirmExit()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Run the scene's logic.
|
||||
err = d.Scene.Loop(d, ev)
|
||||
if err != nil {
|
||||
|
|
|
@ -85,7 +85,9 @@ func FromEvent(ev *event.State) State {
|
|||
|
||||
// Shutdown (Escape) signals the game to start closing down.
|
||||
func Shutdown(ev *event.State) bool {
|
||||
return ev.Escape
|
||||
result := ev.Escape
|
||||
ev.Escape = false
|
||||
return result
|
||||
}
|
||||
|
||||
// Help (F1) can be checked one time.
|
||||
|
|
|
@ -13,7 +13,7 @@ func Alert(message string, args ...interface{}) *Modal {
|
|||
if !ready {
|
||||
panic("modal.Alert(): not ready")
|
||||
} else if current != nil {
|
||||
return current
|
||||
current.Dismiss(false)
|
||||
}
|
||||
|
||||
// Reset the supervisor.
|
||||
|
@ -22,6 +22,7 @@ func Alert(message string, args ...interface{}) *Modal {
|
|||
m := &Modal{
|
||||
title: "Alert",
|
||||
message: fmt.Sprintf(message, args...),
|
||||
cancelable: true,
|
||||
}
|
||||
m.window = makeAlert(m)
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ func Confirm(message string, args ...interface{}) *Modal {
|
|||
if !ready {
|
||||
panic("modal.Confirm(): not ready")
|
||||
} else if current != nil {
|
||||
return current
|
||||
current.Dismiss(false)
|
||||
}
|
||||
|
||||
// Reset the supervisor.
|
||||
|
@ -21,6 +21,7 @@ func Confirm(message string, args ...interface{}) *Modal {
|
|||
m := &Modal{
|
||||
title: "Confirm",
|
||||
message: fmt.Sprintf(message, args...),
|
||||
cancelable: true,
|
||||
}
|
||||
m.window = makeConfirm(m)
|
||||
|
||||
|
|
|
@ -66,6 +66,12 @@ func Handled(ev *event.State) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Escape key cancels the modal.
|
||||
if keybind.Shutdown(ev) && current.cancelable {
|
||||
current.Dismiss(false)
|
||||
return true
|
||||
}
|
||||
|
||||
supervisor.Loop(ev)
|
||||
|
||||
// Has the window changed size?
|
||||
|
@ -111,6 +117,7 @@ type Modal struct {
|
|||
message string
|
||||
window *ui.Window
|
||||
callback func()
|
||||
cancelable bool // Escape key can cancel the modal
|
||||
}
|
||||
|
||||
// WithTitle sets the title of the modal.
|
||||
|
|
|
@ -435,6 +435,19 @@ func (s *PlayScene) installPlayerDoodad(filename string, spawn render.Point, cen
|
|||
// EditLevel toggles out of Play Mode to edit the level.
|
||||
func (s *PlayScene) EditLevel() {
|
||||
log.Info("Edit Mode, Go!")
|
||||
|
||||
// If they didn't come from the Level Editor originally, e.g. they are in Story Mode,
|
||||
// confirm they want the editor in case they accidentally hit the "E" key due to
|
||||
// its proximity to the WASD keys.
|
||||
if !s.CanEdit {
|
||||
modal.Confirm("Open this level in the editor?").Then(s.doEditLevel)
|
||||
} else {
|
||||
s.doEditLevel()
|
||||
}
|
||||
}
|
||||
|
||||
// Common logic to transition into the Editor.
|
||||
func (s *PlayScene) doEditLevel() {
|
||||
gamepad.SetMode(gamepad.MouseMode)
|
||||
s.d.Goto(&EditorScene{
|
||||
Filename: s.Filename,
|
||||
|
@ -447,6 +460,7 @@ func (s *PlayScene) EditLevel() {
|
|||
func (s *PlayScene) RestartLevel() {
|
||||
log.Info("Restart Level")
|
||||
s.d.Goto(&PlayScene{
|
||||
LevelPack: s.LevelPack,
|
||||
Filename: s.Filename,
|
||||
Level: s.Level,
|
||||
CanEdit: s.CanEdit,
|
||||
|
@ -637,8 +651,7 @@ func (s *PlayScene) Loop(d *Doodle, ev *event.State) error {
|
|||
}
|
||||
|
||||
// Switching to Edit Mode?
|
||||
if s.CanEdit && keybind.GotoEdit(ev) {
|
||||
gamepad.SetMode(gamepad.MouseMode)
|
||||
if keybind.GotoEdit(ev) {
|
||||
s.EditLevel()
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -53,6 +53,12 @@ func (u *PlayScene) setupMenuBar(d *Doodle) *ui.MenuBar {
|
|||
////////
|
||||
// Level menu
|
||||
levelMenu := menu.AddMenu("Level")
|
||||
levelMenu.AddItem("Restart level", u.RestartLevel)
|
||||
levelMenu.AddItem("Retry from checkpoint", func() {
|
||||
u.SetImperfect()
|
||||
u.RetryCheckpoint()
|
||||
})
|
||||
levelMenu.AddSeparator()
|
||||
levelMenu.AddItemAccel("Edit level", "E", u.EditLevel)
|
||||
|
||||
// Hilariously broken, someday!
|
||||
|
|
Loading…
Reference in New Issue
Block a user