Creating continuity

In this chapter we’ll create a Game object and put it into a GAME global that will track the overall state of the game. This includes managing an RNG we’ll use to seed the levelgen and each of levels, and tracking the depth of the dungeon the player is currently in.

Getting the message

First we’re going to update our Descend message to include the actor that’s descending in it.

--- @class DescendMessage : Message
--- @overload fun(descender: Actor): DescendMessage
local DescendMessage = prism.Object:extend("DescendMessage")

--- @param descender Actor
function DescendMessage:__new(descender)
   self.descender = descender
end

return DescendMessage

Let’s also change our Descend action so that we populate the message with the descending actor.

function Descend:perform(level)
   level:removeActor(self.owner)
   level:yield(prism.messages.Descend(self.owner))
end

Creating the game

We’re gonna create a new class Game, and we’re gonna include levelgen at the top.

local levelgen = require "levelgen"

--- @class Game : Object
--- @overload fun(seed: string): Game
local Game = prism.Object:extend("Game")

--- @param seed string
function Game:__new(seed)
   self.depth = 0
   self.rng = prism.RNG(seed)
end

--- @return string
function Game:getLevelSeed()
   return tostring(self.rng:getUniform())
end

--- @param player Actor
--- @return MapBuilder builder
function Game:generateNextFloor(player)
   self.depth = self.depth + 1

   local genRNG = prism.RNG(self:getLevelSeed())
   return levelgen(genRNG, player, 60, 30)
end

return Game

This is going to be where we track everything we need for the overall game in the future. Having one centralized RNG we use for generating seeds for levelgen and levels means that our game will be repeatable given the same seed.

Making it global

Head over to main.lua and right below where we’re loading all our modules we’re going to create our GAME global and seed our game.

...
prism.loadModule("modules/MyGame")

--- @module "game"
local Game = require("game")
GAME = Game(tostring(os.time()))

Now our GAME will be accessible all over our codebase.

Modifying the levelstate

The first thing we’re going to want to do is remove our require on levelgen.

-local levelgen = require "levelgen"

Then we’re going to change MyGameLevelState’s constructor.

--- @param display Display
--- @param builder MapBuilder
--- @param seed string
function MyGameLevelState:__new(display, builder, seed)
   -- Build the map and instantiate the level with systems
   local map, actors = builder:build()
   local level = prism.Level(map, actors, {
      prism.systems.Senses(),
      prism.systems.Sight(),
      prism.systems.Fall(),
   }, nil, seed)

   -- Initialize with the created level and display, the heavy lifting is done by
   -- the parent class.
   spectrum.LevelState.__new(self, level, display)
end

This sets up our Level with the map we build and the seed we’ll get from our GAME global.

--- @overload fun(display: Display, builder: MapBuilder, seed: string): MyGameLevelState
local MyGameLevelState = spectrum.LevelState:extend "MyGameLevelState"

Let’s change our overload here as well to reflect the new arguments.

if prism.messages.Descend:is(message) then
   --- @cast message DescendMessage
   self.manager:enter(MyGameLevelState(self.display, GAME:generateNextFloor(message.descender). GAME:))
end

Now we’re going to change our handler for the message to pass the player into the next level.

Moving along

We’ve got descending through the levels working, and now we’ve got everything tied together with our Game class. In the next section we’ll work on getting our inventory and a couple items working.