No game like this is complete without an awe-inspiring boss craft or base that offers a more complex fight. Because boss crafts don't typically fly around in circles, a new action is called for in plan.lua
; hover
. It's very similar to turn
in its implementation, so we won't cover it in too much detail.
First, open plan.lua
and add the hover function to the environment:
self.Speed = speed end function actions.hover(xMove, yMove, duration) duration = duration * 1000 local start = system.getTimer() while duration > 0 do local elapsed = coroutine.yield() elapsed, start = elapsed.time - start, elapsed.time local fraction = math.min(1, (elapsed / duration)) local xDrift, yDrift = xMove * fraction, yMove * fraction xMove, yMove = xMove - xDrift, yMove - yDrift self.x, self.y = self.x + xDrift, self.y + yDrift duration = duration - elapsed end end function actions.turn(angle, duration) duration = duration * 1000
Now that the AI script actions are complete, we'll create the file that will accept these actions and advance the game when a boss is destroyed. Make a copy of dogfighter.lua
in the top level of the project and name it boss.lua
. The following are the steps to create a boss object:
module
function will accept a flight plan
function like the dogfighter
module does, but also accepts a function that dictates what will happen when the boss is defeated:return function (game, x, y, path, advance) local self = ship.dreadnought(game, game.Mobs.Air, enemyFilter)
advance
function in the boss's table so that we can retrieve it and run it when the boss dies: self.Speed = 0
self.Advance = advance
local plan = require "plan" (self, path)
Using a supplied function gives us choices about how bosses appear. For instance, you could make a level where a boss appeared halfway through, and its defeat started a new schedule to proceed through the rest of the level.
In order for the schedule to be able to spawn a new boss object, the game's spawn table needs a reference to the module. Open game.lua
and add a new line to the table near the top of the file.
scene.Spawn = {
turret = require "turret";
dogfighter = require "dogfighter";
boss = require "boss";
}
To launch the boss at the end of the level, we'll modify the level schedule. Open marsh-enemies.lua
and find the end of the function being passed to the schedule.
boss
object: at (58) spawn.boss(160, -40, orbit(), bossOneDestroyed)
end
)
return duration
end local function bossOneDestroyed(self, game) game:dispatchEvent{name = 'Game'; action = 'ended', outcome = 'won'} end return function(game)
local function orbit() return function() hover(0, 100, 2) after (1.5) hover(55, 0, 1.5) end end local function bossOneDestroyed(self, game)
return function() hover(0, 100, 2) after (1.5) hover(55, 0, 1.5) while true do after(1) hover(40, 50, 1.5) after(1) hover(-150, -50, 4) after(1) hover(-40, 50, 1.5) after(1) hover(150, -50, 4) end end
after (1.5) hover(55, 0, 1.5) local function crossFan() fire(1, 50, 80); fire(2, 50, -80) release(3); release(4) fire(5, 50, 13); fire(6, 50, -13) end local function focusForward() release(1); release(2) fire(3, 48, -15); fire(4, 48, 15) release(5); release(6) end while true do
while true do focusForward() after(1) hover(40, 50, 1.5) crossFan() after(1) hover(-150, -50, 4) focusForward() after(1) hover(-40, 50, 1.5) crossFan() after(1) hover(150, -50, 4) end
The default onDestroy
handler falls short in two regards where the boss is concerned. It needs to end the level (as determined by the boss's advance function), and frankly, one little explosion isn't impressive enough for a boss's victory. The following are the steps to handle the boss's defeat:
ship.lua
and frame in an onDestroy
override for the dreadnought
entry:dreadnought = ship { sprite = chassis [4]; MaxHealth = 1000; onDestroy = function(game, event) local self = event.unit end; Weapons = {
onDestroy
function, so we'll start by bringing them in. However, we'll hold off on removing the boss
sprite as shown in the following code:onDestroy = function(game, event) local self = event.unit timer.cancel(self.Guidance) for id, weapon in pairs(self.Weapons) do weapon:stop() end end
boss
sprite:local w, h = self.width / 2, self.height / 2 local chainReaction = timer.performWithDelay(100, function(event) explosion(self.parent, self.xOrigin + math.random(-w, w), self.yOrigin + math.random(-h, h)) end, 0 )
Advance
function.self.Destruction = timer.performWithDelay(1000, function(event) timer.cancel(chainReaction) self:Advance(game) self:removeSelf() end )
Test things out! Once you make it past the other enemies and dodge the boss's guns, you'll see the boss going down in a blaze of glory. Having your character be invulnerable during development makes things like this much easier to test out.
We expanded the selection of actions available to the enemy's AI code. We created a new object type with a continuously repeating set of controls and internal logic to its actions. We also used one timer to regulate another, in order to create a visually interesting effect with a fixed duration.
3.144.40.189