Reset Timer Doodad + Various Fixes
* Bird is not solid when colliding with other birds. * If the dev shell is used to run JavaScript during Play Mode, consider it cheating (so player can't `$ d.Scene.ResetTimer()` for example) * On Survival Mode levels, DieByFire immediately opens the End Level (silver score) modal rather than respawn from checkpoint, so levels don't need checkpoint contraptions to end the level. * During level loading screens, wait and call doodads' main() function until the very end.
This commit is contained in:
parent
af6b8625d6
commit
38a23f00b2
50
Changes.md
50
Changes.md
|
@ -2,15 +2,26 @@
|
|||
|
||||
## v0.12.0 (TBD)
|
||||
|
||||
New features:
|
||||
This update adds several new features to gameplay and the Level Editor.
|
||||
|
||||
* **Level Difficulty Setting:** in the Level Properties you can choose
|
||||
from Peaceful, Normal or Hard for your level.
|
||||
* On Peaceful, Azulians and Birds don't attack the player, acting like
|
||||
pre-0.11.0 versions that ignored the player character.
|
||||
* On Hard difficulty, Azulians have an infinite aggro radius (they'll
|
||||
immediately hunt the player from any distance on level start) and
|
||||
they are hostile to all player creatures.
|
||||
A **Game Rules** feature has been added to the Level Editor which allows
|
||||
customizing certain gameplay features while that level is being played.
|
||||
These settings are available in the Level Properties window of the editor:
|
||||
|
||||
* The **Difficulty** rule can modify the behavior of enemy doodads
|
||||
when the level is played. Choose between Peaceful, Normal, or Hard.
|
||||
* On Peaceful, Azulians and Birds don't attack the player, acting
|
||||
like pre-0.11.0 versions that ignored the player character.
|
||||
* On Hard difficulty, Azulians have an infinite aggro radius
|
||||
(they'll immediately hunt the player from any distance on
|
||||
level start) and they are hostile to _all_ player creatures.
|
||||
* **Survival Mode** changes the definition of "high score" for levels
|
||||
where the player is very likely to die at least once.
|
||||
* The silver high score (respawned at checkpoint) will be for the
|
||||
_longest_ time survived on the level rather than the fastest time
|
||||
to complete it.
|
||||
* The gold high score (got to an Exit Flag without dying once) is
|
||||
still rewarded to the fastest time completing the level.
|
||||
|
||||
An update to the Level Editor's toolbar:
|
||||
|
||||
|
@ -18,11 +29,20 @@ An update to the Level Editor's toolbar:
|
|||
from the game's built-in fonts.
|
||||
* New **Pan Tool** to be able to scroll the level safely by dragging with
|
||||
your mouse or finger.
|
||||
* New **Flood Tool** (or paint bucket tool) can be used to replace
|
||||
contiguous areas of your level from one color to another.
|
||||
* The toolbar buttons are smaller and rearranged. On medium-size screens
|
||||
or larger, the toolbar buttons are drawn side-by-side in two columns.
|
||||
On narrower screens with less real estate, it will use a single column
|
||||
when it fits better.
|
||||
|
||||
New doodads:
|
||||
|
||||
* A technical doodad for **Reset Level Timer** resets the timer to zero,
|
||||
one time, when touched by the player. If the doodad receives a power
|
||||
signal from a linked doodad, it can reset the level timer again if the
|
||||
player touches it once more.
|
||||
|
||||
Updates to the JavaScript API for custom doodads:
|
||||
|
||||
* New global integer `Level.Difficulty` is available to doodad scripts to
|
||||
|
@ -30,10 +50,13 @@ Updates to the JavaScript API for custom doodads:
|
|||
* Peaceful (-1): `Level.Difficulty < 0`
|
||||
* Normal (0): `Level.Difficulty == 0`
|
||||
* Hard (1): `Level.Difficulty > 1`
|
||||
* New function `Level.ResetTimer()` resets the in-game timer to zero.
|
||||
|
||||
New cheat codes:
|
||||
|
||||
* `test load screen` tests the loading screen UI for a few seconds.
|
||||
* `master key` allows playing locked Story Mode levels without unlocking
|
||||
them first by completing the earlier levels.
|
||||
|
||||
Other changes:
|
||||
|
||||
|
@ -42,6 +65,17 @@ Other changes:
|
|||
* Fixed a bug where making the app window bigger during a loading screen
|
||||
caused the Editor to not adapt to the larger window.
|
||||
* Don't show the _autosave.doodad in the Doodad Dropper.
|
||||
* The Azulians have had their jump heights buffed slightly.
|
||||
* Birds no longer register as solid when colliding with other birds (or
|
||||
more generally, characters unaffected by gravity).
|
||||
|
||||
Bugs fixed:
|
||||
|
||||
* When modifying your Palette to rename a color or add an additional
|
||||
color, it wasn't possible to draw with that new color without fully
|
||||
exiting and reloading the editor - this is now resolved.
|
||||
* The palette editor will try and prevent the user from giving the same
|
||||
name to different colors.
|
||||
|
||||
## v0.11.0 (Feb 21 2022)
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ function main() {
|
|||
return;
|
||||
}
|
||||
|
||||
if (e.Actor.IsMobile() && e.InHitbox) {
|
||||
if (e.Actor.IsMobile() && e.Actor.HasGravity() && e.InHitbox) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -28,6 +28,10 @@ build:
|
|||
doodad edit-doodad --tag "color=invisible" reg-warp-door.doodad
|
||||
doodad install-script ../warp-door/warp-door.js reg-warp-door.doodad
|
||||
|
||||
# Reset Level Timer
|
||||
doodad convert -t "Reset Level Timer" timer-64.png reg-reset-timer.doodad
|
||||
doodad install-script reset-timer.js reg-reset-timer.doodad
|
||||
|
||||
for i in *.doodad; do\
|
||||
doodad edit-doodad --tag "category=technical" $${i};\
|
||||
done
|
||||
|
|
30
dev-assets/doodads/regions/reset-timer.js
Normal file
30
dev-assets/doodads/regions/reset-timer.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Reset Level Timer.
|
||||
function main() {
|
||||
Self.Hide();
|
||||
|
||||
// Reset the level timer only once.
|
||||
let hasReset = false;
|
||||
|
||||
Events.OnCollide((e) => {
|
||||
if (!e.Settled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only care if it's the player.
|
||||
if (!e.Actor.IsPlayer()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.InHitbox && !hasReset) {
|
||||
Level.ResetTimer();
|
||||
hasReset = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Receive a power signal resets the doodad.
|
||||
Message.Subscribe("power", (powered) => {
|
||||
if (powered) {
|
||||
hasReset = true;
|
||||
}
|
||||
});
|
||||
}
|
BIN
dev-assets/doodads/regions/timer-64.png
Normal file
BIN
dev-assets/doodads/regions/timer-64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 901 B |
|
@ -336,6 +336,13 @@ func (c Command) RunScript(d *Doodle, code string) (goja.Value, error) {
|
|||
d.FlashError("Command.RunScript: Panic: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// If we're in Play Mode, consider it cheating if the player is
|
||||
// messing with any in-game structures.
|
||||
if scene, ok := d.Scene.(*PlayScene); ok {
|
||||
scene.SetCheated()
|
||||
}
|
||||
|
||||
out, err := d.shell.js.RunString(code)
|
||||
return out, err
|
||||
}
|
||||
|
|
|
@ -175,8 +175,8 @@ func makeEndLevel(m *Modal, cfg ConfigEndLevel) *ui.Window {
|
|||
Font: balance.MenuFont,
|
||||
}))
|
||||
button.Handle(ui.Click, func(ed ui.EventData) error {
|
||||
btn.F()
|
||||
m.Dismiss(false)
|
||||
btn.F()
|
||||
return nil
|
||||
})
|
||||
button.Compute(engine)
|
||||
|
|
|
@ -138,8 +138,8 @@ func (m *Modal) Then(f func()) *Modal {
|
|||
|
||||
// Dismiss the modal and optionally call the callback function.
|
||||
func (m *Modal) Dismiss(call bool) {
|
||||
Reset()
|
||||
if call && m.callback != nil {
|
||||
m.callback()
|
||||
}
|
||||
Reset()
|
||||
}
|
||||
|
|
|
@ -224,6 +224,7 @@ func (s *PlayScene) setupAsync(d *Doodle) error {
|
|||
|
||||
// Handle a doodad changing the player character.
|
||||
s.drawing.OnSetPlayerCharacter = s.SetPlayerCharacter
|
||||
s.drawing.OnResetTimer = s.ResetTimer
|
||||
|
||||
// Given a filename or map data to play?
|
||||
if s.Level != nil {
|
||||
|
@ -265,11 +266,6 @@ func (s *PlayScene) setupAsync(d *Doodle) error {
|
|||
// Load in the player character.
|
||||
s.setupPlayer(balance.PlayerCharacterDoodad)
|
||||
|
||||
// Run all the actor scripts' main() functions.
|
||||
if err := s.drawing.InstallScripts(); err != nil {
|
||||
log.Error("PlayScene.Setup: failed to drawing.InstallScripts: %s", err)
|
||||
}
|
||||
|
||||
if s.CanEdit {
|
||||
d.Flash("Entered Play Mode. Press 'E' to edit this map.")
|
||||
} else {
|
||||
|
@ -286,6 +282,11 @@ func (s *PlayScene) setupAsync(d *Doodle) error {
|
|||
// Gamepad: put into GameplayMode.
|
||||
gamepad.SetMode(gamepad.GameplayMode)
|
||||
|
||||
// Run all the actor scripts' main() functions.
|
||||
if err := s.drawing.InstallScripts(); err != nil {
|
||||
log.Error("PlayScene.Setup: failed to drawing.InstallScripts: %s", err)
|
||||
}
|
||||
|
||||
s.startTime = time.Now()
|
||||
s.perfectRun = true
|
||||
s.running = true
|
||||
|
@ -321,6 +322,11 @@ func (s *PlayScene) SetPlayerCharacter(filename string) {
|
|||
}
|
||||
}
|
||||
|
||||
// ResetTimer sets the level elapsed timer back to zero.
|
||||
func (s *PlayScene) ResetTimer() {
|
||||
s.startTime = time.Now()
|
||||
}
|
||||
|
||||
// setupPlayer creates and configures the Player Character in the level.
|
||||
func (s *PlayScene) setupPlayer(playerCharacterFilename string) {
|
||||
// Find the spawn point of the player. Search the level for the
|
||||
|
@ -492,7 +498,12 @@ func (s *PlayScene) BeatLevel() {
|
|||
)
|
||||
}
|
||||
|
||||
// FailLevel handles a level failure triggered by a doodad.
|
||||
/*
|
||||
FailLevel handles a level failure triggered by a doodad or fire pixel.
|
||||
|
||||
If the Survival GameRule is set, this ends the level with a note on how long the
|
||||
player had survived for and they get a silver rating.
|
||||
*/
|
||||
func (s *PlayScene) FailLevel(message string) {
|
||||
if s.Player.Invulnerable() || s.godMode || s.godModeUntil.After(time.Now()) {
|
||||
return
|
||||
|
@ -500,6 +511,19 @@ func (s *PlayScene) FailLevel(message string) {
|
|||
s.SetImperfect()
|
||||
s.d.FlashError(message)
|
||||
|
||||
if s.Level.GameRule.Survival {
|
||||
s.ShowEndLevelModal(
|
||||
true,
|
||||
"Level Completed",
|
||||
fmt.Sprintf(
|
||||
"%s\nCongrats on surviving for %s!",
|
||||
message,
|
||||
savegame.FormatDuration(time.Since(s.startTime)),
|
||||
),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
s.ShowEndLevelModal(
|
||||
false,
|
||||
"You've died!",
|
||||
|
|
|
@ -95,6 +95,9 @@ type Canvas struct {
|
|||
// The filename.doodad is given.
|
||||
OnSetPlayerCharacter func(filename string)
|
||||
|
||||
// Handler for when a doodad script calls Level.ResetTimer().
|
||||
OnResetTimer func()
|
||||
|
||||
/********
|
||||
* Editable canvas private variables.
|
||||
********/
|
||||
|
|
|
@ -67,6 +67,13 @@ func (w *Canvas) MakeScriptAPI(vm *scripting.VM) {
|
|||
|
||||
vm.Set("Level", map[string]interface{}{
|
||||
"Difficulty": w.level.GameRule.Difficulty,
|
||||
"ResetTimer": func() {
|
||||
if w.OnResetTimer != nil {
|
||||
w.OnResetTimer()
|
||||
} else {
|
||||
log.Error("Level.ResetTimer: caller was not ready")
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user