Invulnerable Anvil and other fixes
* Add methods `Invulnerable() bool` and `SetInvulnerable(bool)` to the Actor API accessible in JavaScript (e.g. `Self.SetInvulnerable(true)`) * The Anvil is invulnerable - when played as, it can crush other mobs by jumping on them but is not defeated by those mobs at the same time. * Anvils don't destroy invulnerable mobs, such as other Anvils. * Bugfix: the Electric Door is considered to be opened from the first frame of animation when the door begins opening, and remains opened until the final frame of animation when it is closing. * New cheat code: `megaton weight` to play as the Anvil by default.
This commit is contained in:
parent
0fc046250e
commit
1205dc2cd3
38
Changes.md
38
Changes.md
|
@ -4,10 +4,15 @@
|
||||||
|
|
||||||
New features:
|
New features:
|
||||||
|
|
||||||
* The **JavaScript Engine** for the game has been switched from
|
* **Game Controller** support has been added! The game can now be played
|
||||||
|
with an Xbox style controller, including Nintendo Pro Controllers. The
|
||||||
|
game supports an "X Style" and "N Style" button layout, the latter of
|
||||||
|
which swaps the A/B and the X/Y buttons so gameplay controls match the
|
||||||
|
button labels in your controller.
|
||||||
|
* The **JavaScript Engine** for doodad scripts has been switched from
|
||||||
github.com/robertkrimen/otto to github.com/dop251/goja which helps
|
github.com/robertkrimen/otto to github.com/dop251/goja which helps
|
||||||
"modernize" the experience of writing doodads. Goja supports many
|
"modernize" the experience of writing doodads. Goja supports many
|
||||||
common ES6 functions already, such as:
|
common ES6 features already, such as:
|
||||||
* Arrow functions
|
* Arrow functions
|
||||||
* `let` and `const` keywords
|
* `let` and `const` keywords
|
||||||
* Promises
|
* Promises
|
||||||
|
@ -23,11 +28,21 @@ are becoming more dangerous:
|
||||||
|
|
||||||
* The **Bird** now searches for the player diagonally in front of
|
* 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 for about 240px or so. If spotted it will dive toward you and
|
||||||
it is dangerous when diving!
|
it is dangerous when diving! When playing as the bird, the dive sprite
|
||||||
|
is used when flying diagonally downwards.
|
||||||
* The **Azulians** will start to follow the player when you get
|
* The **Azulians** will start to follow the player when you get
|
||||||
close and they are dangerous when they touch you -- but not if
|
close and they are dangerous when they touch you -- but not if
|
||||||
you're the **Thief.** The red Azulian has a wider search radius,
|
you're the **Thief.** The red Azulian has a wider search radius,
|
||||||
higher jump and faster speed than the blue Azulian.
|
higher jump and faster speed than the blue Azulian.
|
||||||
|
* A new **White Azulian** has been added to the game. It is even faster
|
||||||
|
than the Red Azulian! And it can jump higher, too!
|
||||||
|
* The **Checkpoint Flag** can now re-assign the player character when
|
||||||
|
activated! Just link a doodad to the Checkpoint Flag like you do the
|
||||||
|
Start Flag. When the player reaches the checkpoint, their character
|
||||||
|
sprite is replaced with the linked doodad!
|
||||||
|
* 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.
|
||||||
|
|
||||||
New functions are available on the JavaScript API for doodads:
|
New functions are available on the JavaScript API for doodads:
|
||||||
|
|
||||||
|
@ -35,17 +50,32 @@ New functions are available on the JavaScript API for doodads:
|
||||||
* `Actors.FindPlayer() *Actor`: returns the nearest player character
|
* `Actors.FindPlayer() *Actor`: returns the nearest player character
|
||||||
* `Actors.New(filename string)`: create a new actor (NOT TESTED YET!)
|
* `Actors.New(filename string)`: create a new actor (NOT TESTED YET!)
|
||||||
* `Self.Grounded() bool`: query the grounded status of current actor
|
* `Self.Grounded() bool`: query the grounded status of current actor
|
||||||
|
* `Actors.SetPlayerCharacter(filename string)`: replace the nearest
|
||||||
|
player character with the named doodad, e.g. "boy.doodad"
|
||||||
|
* `Self.Invulnerable() bool` and `Self.SetInvulnerable(bool)`: set a
|
||||||
|
doodad is invulnerable, especially for the player character, e.g.
|
||||||
|
if playing as the Anvil you can't be defeated by mobs or fire.
|
||||||
|
|
||||||
New cheat code:
|
New cheat codes:
|
||||||
|
|
||||||
* `god mode`: toggle invincibility. When on, fire pixels and hostile
|
* `god mode`: toggle invincibility. When on, fire pixels and hostile
|
||||||
mobs can't make you fail the level.
|
mobs can't make you fail the level.
|
||||||
|
* `megaton weight`: play as the Anvil by default on levels that don't
|
||||||
|
specify a player character otherwise.
|
||||||
|
|
||||||
Other changes:
|
Other changes:
|
||||||
|
|
||||||
|
* When respawning from a checkpoint, the player is granted 3 seconds of
|
||||||
|
invulnerability; so if hostile mobs are spawn camping the player, you
|
||||||
|
don't get soft-locked!
|
||||||
* The draw order of actors on a level is now deterministic: the most
|
* The draw order of actors on a level is now deterministic: the most
|
||||||
recently added actor will always draw on top when overlapping another,
|
recently added actor will always draw on top when overlapping another,
|
||||||
and the player actor is always on top.
|
and the player actor is always on top.
|
||||||
|
* JavaScript exceptions raised in doodad scripts will be logged to the
|
||||||
|
console instead of crashing the game. In the future these will be
|
||||||
|
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.
|
||||||
* When the game checks if there's an update available via
|
* When the game checks if there's an update available via
|
||||||
<https://download.sketchymaze.com/version.json> it will send a user
|
<https://download.sketchymaze.com/version.json> it will send a user
|
||||||
agent header like: "Sketchy Maze v0.10.2 on linux/amd64" sending only
|
agent header like: "Sketchy Maze v0.10.2 on linux/amd64" sending only
|
||||||
|
|
|
@ -14,9 +14,9 @@ function setPoweredState(powered) {
|
||||||
}
|
}
|
||||||
|
|
||||||
animating = true;
|
animating = true;
|
||||||
|
opened = true;
|
||||||
Sound.Play("electric-door.wav")
|
Sound.Play("electric-door.wav")
|
||||||
Self.PlayAnimation("open", () => {
|
Self.PlayAnimation("open", () => {
|
||||||
opened = true;
|
|
||||||
animating = false;
|
animating = false;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -6,6 +6,7 @@ function main() {
|
||||||
Self.SetHitbox(0, 0, 48, 25);
|
Self.SetHitbox(0, 0, 48, 25);
|
||||||
Self.SetMobile(true);
|
Self.SetMobile(true);
|
||||||
Self.SetGravity(true);
|
Self.SetGravity(true);
|
||||||
|
Self.SetInvulnerable(true);
|
||||||
|
|
||||||
// Monitor our Y position to tell if we've been falling.
|
// Monitor our Y position to tell if we've been falling.
|
||||||
let lastPoint = Self.Position();
|
let lastPoint = Self.Position();
|
||||||
|
@ -33,7 +34,7 @@ function main() {
|
||||||
FailLevel("Watch out for anvils!");
|
FailLevel("Watch out for anvils!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (e.Actor.IsMobile()) {
|
else if (e.Actor.IsMobile() && !e.Actor.Invulnerable()) {
|
||||||
// Destroy mobile doodads.
|
// Destroy mobile doodads.
|
||||||
Sound.Play("crumbly-break.wav");
|
Sound.Play("crumbly-break.wav");
|
||||||
e.Actor.Destroy();
|
e.Actor.Destroy();
|
||||||
|
|
|
@ -29,5 +29,6 @@ var (
|
||||||
CheatPlayAsBoy = "pinocchio"
|
CheatPlayAsBoy = "pinocchio"
|
||||||
CheatPlayAsAzuBlue = "the cell"
|
CheatPlayAsAzuBlue = "the cell"
|
||||||
CheatPlayAsThief = "play as thief"
|
CheatPlayAsThief = "play as thief"
|
||||||
|
CheatPlayAsAnvil = "megaton weight"
|
||||||
CheatGodMode = "god mode"
|
CheatGodMode = "god mode"
|
||||||
)
|
)
|
||||||
|
|
|
@ -128,6 +128,10 @@ func (c Command) cheatCommand(d *Doodle) bool {
|
||||||
balance.PlayerCharacterDoodad = "thief.doodad"
|
balance.PlayerCharacterDoodad = "thief.doodad"
|
||||||
d.Flash("Set default player character to Thief")
|
d.Flash("Set default player character to Thief")
|
||||||
|
|
||||||
|
case balance.CheatPlayAsAnvil:
|
||||||
|
balance.PlayerCharacterDoodad = "anvil.doodad"
|
||||||
|
d.Flash("Set default player character to the Anvil")
|
||||||
|
|
||||||
case balance.CheatGodMode:
|
case balance.CheatGodMode:
|
||||||
if isPlay {
|
if isPlay {
|
||||||
d.Flash("God mode toggled")
|
d.Flash("God mode toggled")
|
||||||
|
|
|
@ -399,6 +399,7 @@ func (s *PlayScene) installPlayerDoodad(filename string, spawn render.Point, cen
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Player = uix.NewActor("PLAYER", &level.Actor{}, player)
|
s.Player = uix.NewActor("PLAYER", &level.Actor{}, player)
|
||||||
|
s.Player.SetInventory(true) // player always can pick up items
|
||||||
s.Player.MoveTo(spawn)
|
s.Player.MoveTo(spawn)
|
||||||
s.drawing.AddActor(s.Player)
|
s.drawing.AddActor(s.Player)
|
||||||
s.drawing.FollowActor = s.Player.ID()
|
s.drawing.FollowActor = s.Player.ID()
|
||||||
|
@ -465,7 +466,7 @@ func (s *PlayScene) BeatLevel() {
|
||||||
|
|
||||||
// FailLevel handles a level failure triggered by a doodad.
|
// FailLevel handles a level failure triggered by a doodad.
|
||||||
func (s *PlayScene) FailLevel(message string) {
|
func (s *PlayScene) FailLevel(message string) {
|
||||||
if s.godMode || s.godModeUntil.After(time.Now()) {
|
if s.Player.Invulnerable() || s.godMode || s.godModeUntil.After(time.Now()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.SetImperfect()
|
s.SetImperfect()
|
||||||
|
|
|
@ -40,6 +40,7 @@ type Actor struct {
|
||||||
noclip bool // Disable collision detection
|
noclip bool // Disable collision detection
|
||||||
hidden bool // invisible, via Hide() and Show()
|
hidden bool // invisible, via Hide() and Show()
|
||||||
frozen bool // Frozen, via Freeze() and Unfreeze()
|
frozen bool // Frozen, via Freeze() and Unfreeze()
|
||||||
|
immortal bool // Invulnerable to damage
|
||||||
hitbox render.Rect
|
hitbox render.Rect
|
||||||
inventory map[string]int // item inventory. doodad name -> quantity, 0 for key item.
|
inventory map[string]int // item inventory. doodad name -> quantity, 0 for key item.
|
||||||
data map[string]string // arbitrary key/value store. DEPRECATED ??
|
data map[string]string // arbitrary key/value store. DEPRECATED ??
|
||||||
|
@ -141,6 +142,16 @@ func (a *Actor) HasGravity() bool {
|
||||||
return a.hasGravity
|
return a.hasGravity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Invulnerable returns whether the actor is marked as immortal.
|
||||||
|
func (a *Actor) Invulnerable() bool {
|
||||||
|
return a.immortal
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInvulnerable sets the actor's immortal flag.
|
||||||
|
func (a *Actor) SetInvulnerable(v bool) {
|
||||||
|
a.immortal = v
|
||||||
|
}
|
||||||
|
|
||||||
// Size returns the size of the actor, from the underlying doodads.Drawing.
|
// Size returns the size of the actor, from the underlying doodads.Drawing.
|
||||||
func (a *Actor) Size() render.Rect {
|
func (a *Actor) Size() render.Rect {
|
||||||
return a.Drawing.Size()
|
return a.Drawing.Size()
|
||||||
|
|
|
@ -85,32 +85,34 @@ func (w *Canvas) MakeSelfAPI(actor *Actor) map[string]interface{} {
|
||||||
actor.MoveTo(p)
|
actor.MoveTo(p)
|
||||||
actor.SetGrounded(false)
|
actor.SetGrounded(false)
|
||||||
},
|
},
|
||||||
"Grounded": actor.Grounded,
|
"Grounded": actor.Grounded,
|
||||||
"SetHitbox": actor.SetHitbox,
|
"SetHitbox": actor.SetHitbox,
|
||||||
"Hitbox": actor.Hitbox,
|
"Hitbox": actor.Hitbox,
|
||||||
"SetVelocity": actor.SetVelocity,
|
"SetVelocity": actor.SetVelocity,
|
||||||
"GetVelocity": actor.Velocity,
|
"GetVelocity": actor.Velocity,
|
||||||
"SetMobile": actor.SetMobile,
|
"SetMobile": actor.SetMobile,
|
||||||
"SetInventory": actor.SetInventory,
|
"SetInventory": actor.SetInventory,
|
||||||
"HasInventory": actor.HasInventory,
|
"HasInventory": actor.HasInventory,
|
||||||
"SetGravity": actor.SetGravity,
|
"SetGravity": actor.SetGravity,
|
||||||
"AddAnimation": actor.AddAnimation,
|
"Invulnerable": actor.Invulnerable,
|
||||||
"IsAnimating": actor.IsAnimating,
|
"SetInvulnerable": actor.SetInvulnerable,
|
||||||
"IsPlayer": actor.IsPlayer,
|
"AddAnimation": actor.AddAnimation,
|
||||||
"PlayAnimation": actor.PlayAnimation,
|
"IsAnimating": actor.IsAnimating,
|
||||||
"StopAnimation": actor.StopAnimation,
|
"IsPlayer": actor.IsPlayer,
|
||||||
"ShowLayer": actor.ShowLayer,
|
"PlayAnimation": actor.PlayAnimation,
|
||||||
"ShowLayerNamed": actor.ShowLayerNamed,
|
"StopAnimation": actor.StopAnimation,
|
||||||
"Inventory": actor.Inventory,
|
"ShowLayer": actor.ShowLayer,
|
||||||
"AddItem": actor.AddItem,
|
"ShowLayerNamed": actor.ShowLayerNamed,
|
||||||
"RemoveItem": actor.RemoveItem,
|
"Inventory": actor.Inventory,
|
||||||
"HasItem": actor.HasItem,
|
"AddItem": actor.AddItem,
|
||||||
"ClearInventory": actor.ClearInventory,
|
"RemoveItem": actor.RemoveItem,
|
||||||
"Destroy": actor.Destroy,
|
"HasItem": actor.HasItem,
|
||||||
"Freeze": actor.Freeze,
|
"ClearInventory": actor.ClearInventory,
|
||||||
"Unfreeze": actor.Unfreeze,
|
"Destroy": actor.Destroy,
|
||||||
"Hide": actor.Hide,
|
"Freeze": actor.Freeze,
|
||||||
"Show": actor.Show,
|
"Unfreeze": actor.Unfreeze,
|
||||||
|
"Hide": actor.Hide,
|
||||||
|
"Show": actor.Show,
|
||||||
"GetLinks": func() []map[string]interface{} {
|
"GetLinks": func() []map[string]interface{} {
|
||||||
var result = []map[string]interface{}{}
|
var result = []map[string]interface{}{}
|
||||||
for _, linked := range w.GetLinkedActors(actor) {
|
for _, linked := range w.GetLinkedActors(actor) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user