White Azulian, Respawn invincibility timer

* Respawning from a checkpoint grants 3 seconds of immunity in case
  enemies are spawn camping.
* Add the white Azulian as an even faster and harder enemy than the red
  Azulian: twice as fast, jumps higher, and can detect the player from
  further away.
This commit is contained in:
Noah 2022-01-18 18:32:15 -08:00
parent 3f7e384633
commit 44aba8f1b4
15 changed files with 40 additions and 16 deletions

View File

@ -14,6 +14,11 @@ build:
doodad edit-doodad --tag "color=red" azu-red.doodad doodad edit-doodad --tag "color=red" azu-red.doodad
doodad install-script azulian.js azu-red.doodad doodad install-script azulian.js azu-red.doodad
doodad convert -t "White Azulian" white-front.png white-back.png \
white-wr{1,2,3,4}.png white-wl{1,2,3,4}.png azu-white.doodad
doodad edit-doodad --tag "color=white" azu-white.doodad
doodad install-script azulian.js azu-white.doodad
# Tag the category for these doodads # Tag the category for these doodads
for i in *.doodad; do\ for i in *.doodad; do\
doodad edit-doodad --tag "category=creatures" $${i};\ doodad edit-doodad --tag "category=creatures" $${i};\

View File

@ -9,9 +9,17 @@ var playerSpeed = color === 'blue' ? 2 : 4,
direction = "right", direction = "right",
lastDirection = "right"; lastDirection = "right";
// white Azulian is faster yet than the red
if (color === 'white') {
aggroX = 1000;
aggroY = 400;
playerSpeed = 8;
jumpSpeed = 16;
}
function setupAnimations(color) { function setupAnimations(color) {
let left = color === 'blue' ? 'blu-wl' : 'red-wl', let left = color === 'blue' ? 'blu-wl' : color+'-wl',
right = color === 'blue' ? 'blu-wr' : 'red-wr', right = color === 'blue' ? 'blu-wr' : color+'-wr',
leftFrames = [left + '1', left + '2', left + '3', left + '4'], leftFrames = [left + '1', left + '2', left + '3', left + '4'],
rightFrames = [right + '1', right + '2', right + '3', right + '4']; rightFrames = [right + '1', right + '2', right + '3', right + '4'];

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1002 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 985 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 999 B

View File

@ -4,13 +4,13 @@ let speed = 4,
Vx = Vy = 0, Vx = Vy = 0,
altitude = Self.Position().Y; // original height in the level altitude = Self.Position().Y; // original height in the level
let direction = "left", let direction = "left",
lastDirection = "left"; lastDirection = "left";
let states = { let states = {
flying: 0, flying: 0,
diving: 1, diving: 1,
}; };
let state = states.flying; let state = states.flying;
function main() { function main() {
Self.SetMobile(true); Self.SetMobile(true);
@ -106,6 +106,8 @@ function main() {
// A.I. subroutine: scan for the player character. // A.I. subroutine: scan for the player character.
// The bird scans in a 45 degree angle downwards, if the // The bird scans in a 45 degree angle downwards, if the
// player is seen nearby in that scan it will begin a dive. // player is seen nearby in that scan it will begin a dive.
// It's not hostile towards characters that can fly (having
// no gravity).
function AI_ScanForPlayer() { function AI_ScanForPlayer() {
let stepY = 12, // number of pixels to skip let stepY = 12, // number of pixels to skip
stepX = stepY, stepX = stepY,
@ -130,7 +132,7 @@ function AI_ScanForPlayer() {
scanX += stepX; scanX += stepX;
scanY += stepY; scanY += stepY;
for (let actor of Actors.At(Point(scanX, scanY))) { for (let actor of Actors.At(Point(scanX, scanY))) {
if (actor.IsPlayer()) { if (actor.IsPlayer() && actor.HasGravity()) {
state = states.diving; state = states.diving;
return; return;
} }

View File

@ -88,6 +88,10 @@ var (
PlayModeIdleTimeout = 2200 * time.Millisecond PlayModeIdleTimeout = 2200 * time.Millisecond
PlayModeAlphaStep = 8 // 0-255 alpha, steps per tick for fade in PlayModeAlphaStep = 8 // 0-255 alpha, steps per tick for fade in
PlayModeAlphaMax = 220 PlayModeAlphaMax = 220
// Invulnerability time in seconds at respawn from checkpoint, in case
// enemies are spawn camping.
RespawnGodModeTimer = 3 * time.Second
) )
// Edit Mode Values // Edit Mode Values

View File

@ -66,11 +66,12 @@ type PlayScene struct {
Player *uix.Actor Player *uix.Actor
playerPhysics *physics.Mover playerPhysics *physics.Mover
lastCheckpoint render.Point lastCheckpoint render.Point
playerLastDirection float64 // player's heading last tick playerLastDirection float64 // player's heading last tick
antigravity bool // Cheat: disable player gravity antigravity bool // Cheat: disable player gravity
noclip bool // Cheat: disable player clipping noclip bool // Cheat: disable player clipping
godMode bool // Cheat: player can't die godMode bool // Cheat: player can't die
playerJumpCounter int // limit jump length godModeUntil time.Time // Invulnerability timer at respawn.
playerJumpCounter int // limit jump length
// Inventory HUD. Impl. in play_inventory.go // Inventory HUD. Impl. in play_inventory.go
invenFrame *ui.Frame invenFrame *ui.Frame
@ -404,6 +405,9 @@ func (s *PlayScene) SetCheckpoint(where render.Point) {
// RetryCheckpoint moves the player back to their last checkpoint. // RetryCheckpoint moves the player back to their last checkpoint.
func (s *PlayScene) RetryCheckpoint() { func (s *PlayScene) RetryCheckpoint() {
// Grant the player invulnerability for 5 seconds
s.godModeUntil = time.Now().Add(balance.RespawnGodModeTimer)
log.Info("Move player back to last checkpoint") log.Info("Move player back to last checkpoint")
s.Player.MoveTo(s.lastCheckpoint) s.Player.MoveTo(s.lastCheckpoint)
s.running = true s.running = true
@ -421,11 +425,12 @@ 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 { if s.godMode || s.godModeUntil.After(time.Now()) {
return return
} }
s.SetImperfect() s.SetImperfect()
s.d.FlashError(message) s.d.FlashError(message)
s.ShowEndLevelModal( s.ShowEndLevelModal(
false, false,
"You've died!", "You've died!",