// Azulian (Red and Blue) const color = Self.GetTag("color"); var playerSpeed = color === 'blue' ? 2 : 4, swimSpeed = playerSpeed * 0.4, aggroX = 250, // X/Y distance sensitivity from player aggroY = color === 'blue' ? 100 : 200, jumpSpeed = color === 'blue' ? 10 : 11, swimJumpSpeed = jumpSpeed * 0.4, animating = false, direction = "right", lastDirection = "right"; // white Azulian is faster yet than the red if (color === 'white') { aggroX = 1000; aggroY = 400; playerSpeed = 5; swimSpeed = playerSpeed * 0.4; jumpSpeed = 12; swimJumpSpeed = jumpSpeed * 0.4; } function setupAnimations(color) { let left = color === 'blue' ? 'blu-wl' : color + '-wl', right = color === 'blue' ? 'blu-wr' : color + '-wr', leftFrames = [left + '1', left + '2', left + '3', left + '4'], rightFrames = [right + '1', right + '2', right + '3', right + '4']; Self.AddAnimation("walk-left", 100, leftFrames); Self.AddAnimation("walk-right", 100, rightFrames); } function main() { let swimJumpCooldownTick = 0, // minimum Game Tick before we can jump while swimming swimJumpCooldown = 10; // CONFIG: delta of ticks between jumps while swimming Self.SetMobile(true); Self.SetGravity(true); Self.SetInventory(true); Self.SetHitbox(0, 0, 24, 32); setupAnimations(color); if (Self.IsPlayer()) { return playerControls(); } // No A.I.? if (Self.GetOption("No A.I.") === true) { return; } // A.I. pattern: walks back and forth, turning around // when it meets resistance. // Sample our X position every few frames and detect if we've hit a solid wall. let sampleTick = 0; let sampleRate = 5; let lastSampledX = 0; // Get the player on touch. Events.OnCollide((e) => { // If we're diving and we hit the player, game over! // Azulians are friendly to Thieves though! if (e.Settled && isPlayerFood(e.Actor)) { FailLevel("Watch out for the Azulians!"); return; } }); setInterval(() => { // If the player is nearby, walk towards them. Otherwise, default pattern // is to walk back and forth. let player = Actors.FindPlayer(), followPlayer = false, jump = false; // Don't follow boring players. if (player !== null && isPlayerFood(player)) { let playerPt = player.Position(), myPt = Self.Position(); // If the player is within aggro range, move towards. if ((Math.abs(playerPt.X - myPt.X) < aggroX && Math.abs(playerPt.Y - myPt.Y) < aggroY) || (Level.Difficulty > 0)) { direction = playerPt.X < myPt.X ? "left" : "right"; followPlayer = true; if (playerPt.Y + player.Size().H < myPt.Y + Self.Size().H) { jump = true; } } } // Default AI: sample position so we turn around on obstacles. if (!followPlayer) { if (sampleTick % sampleRate === 0) { let curX = Self.Position().X; let delta = Math.abs(curX - lastSampledX); if (delta < 5) { direction = direction === "right" ? "left" : "right"; } lastSampledX = curX; } sampleTick++; } // Handle being underwater. let canJump = Self.Grounded(); if (Self.IsWet()) { let tick = GetTick(); if (tick > swimJumpCooldownTick) { canJump = true; swimJumpCooldownTick = tick + swimJumpCooldown; } } // How speedy would our movement and jump be? let xspeed = playerSpeed, yspeed = jumpSpeed; if (Self.IsWet()) { xspeed = swimSpeed; yspeed = swimJumpSpeed; } let Vx = parseFloat(xspeed * (direction === "left" ? -1 : 1)), Vy = jump && canJump ? parseFloat(-yspeed) : Self.GetVelocity().Y; Self.SetVelocity(Vector(Vx, Vy)); // If we changed directions, stop animating now so we can // turn around quickly without moonwalking. if (direction !== lastDirection) { Self.StopAnimation(); } if (!Self.IsAnimating()) { Self.PlayAnimation("walk-" + direction, null); } lastDirection = direction; }, 10); } function playerControls() { // Note: player speed is controlled by the engine. Events.OnKeypress((ev) => { if (ev.Right) { if (!Self.IsAnimating()) { Self.PlayAnimation("walk-right", null); } } else if (ev.Left) { if (!Self.IsAnimating()) { Self.PlayAnimation("walk-left", null); } } else { Self.StopAnimation(); animating = false; } }) } // Logic to decide if the player is interesting to the Azulian (aka, if the Azulian // will be hostile towards the player). Boring players will not be chased after and // the Azulian will not harm them if they make contact. function isPlayerFood(actor) { // Not a player or is invulnerable, or Peaceful difficulty. if (!actor.IsPlayer() || actor.Invulnerable() || Level.Difficulty < 0) { return false; } // On hard mode they are hostile to any player. if (Level.Difficulty > 0) { return true; } // Azulians are friendly to Thieves and other Azulians. if (actor.Doodad().Filename === "thief.doodad" || actor.Doodad().Title.indexOf("Azulian") > -1) { return false; } return true; }