feat: extend game with Game.new and Game:metaStats
This commit is contained in:
1 parent
e188355386
commit
2d1cf09afa
1 file changed
+185
-4
+185
-4
@@ -10,51 +10,232 @@ local Los = require("los")
|
|||||||
local Light = require("light")
|
local Light = require("light")
|
||||||
local Combat = require("combat")
|
local Combat = require("combat")
|
||||||
local Items = require("items")
|
local Items = require("items")
|
||||||
|
local Shop = require("shop")
|
||||||
local Effects = require("effects")
|
local Effects = require("effects")
|
||||||
local Status = require("status")
|
local Status = require("status")
|
||||||
local Identify = require("identify")
|
local Identify = require("identify")
|
||||||
|
local Builder = require("builder")
|
||||||
local Dungeon = require("dungeon")
|
local Dungeon = require("dungeon")
|
||||||
local Entity = require("entity")
|
local Entity = require("entity")
|
||||||
local MessageLog = require("messagelog")
|
local MessageLog = require("messagelog")
|
||||||
local Scheduler = require("scheduler")
|
local Scheduler = require("scheduler")
|
||||||
local Ai = require("ai")
|
local Ai = require("ai")
|
||||||
local Serialize = require("serialize")
|
local SaveGame = require("savegame")
|
||||||
local Render = require("render")
|
local Render = require("render")
|
||||||
|
local Progression = require("progression")
|
||||||
|
local Monsters = require("monsters")
|
||||||
|
local Spells = require("spells")
|
||||||
|
local Throwing = require("throwing")
|
||||||
|
local Ranged = require("ranged")
|
||||||
|
local Abilities = require("abilities")
|
||||||
|
local Hazards = require("hazards")
|
||||||
|
local Terrain = require("terrain")
|
||||||
|
local Shrines = require("shrines")
|
||||||
|
local Classes = require("classes")
|
||||||
|
local Skills = require("skills")
|
||||||
|
local Crafting = require("crafting")
|
||||||
|
local Companions = require("companions")
|
||||||
|
local Mercenaries = require("mercenaries")
|
||||||
|
local Artifacts = require("artifacts")
|
||||||
|
local Sets = require("sets")
|
||||||
|
local Hunger = require("hunger")
|
||||||
|
local Mimics = require("mimics")
|
||||||
|
local Bosses = require("bosses")
|
||||||
|
local Phases = require("phases")
|
||||||
|
local Quests = require("quests")
|
||||||
|
local Meta = require("meta")
|
||||||
|
local Alchemy = require("alchemy")
|
||||||
|
local Resistances = require("resistances")
|
||||||
|
local Factions = require("factions")
|
||||||
|
local Encumbrance = require("encumbrance")
|
||||||
|
local Elites = require("elites")
|
||||||
|
local Vaults = require("vaults")
|
||||||
|
local Runewords = require("runewords")
|
||||||
|
local Atmosphere = require("atmosphere")
|
||||||
|
local Events = require("events")
|
||||||
|
local Stealth = require("stealth")
|
||||||
|
local Compendium = require("compendium")
|
||||||
|
local Nodes = require("nodes")
|
||||||
|
local Curses = require("curses")
|
||||||
|
local Momentum = require("momentum")
|
||||||
|
local Corpses = require("corpses")
|
||||||
|
local Digging = require("digging")
|
||||||
|
local Sanity = require("sanity")
|
||||||
|
local Graves = require("graves")
|
||||||
|
local Proficiency = require("proficiency")
|
||||||
|
local Ascension = require("ascension")
|
||||||
|
local Durability = require("durability")
|
||||||
|
local Conductivity = require("conductivity")
|
||||||
|
local Disarm = require("disarm")
|
||||||
|
local Reanimation = require("reanimation")
|
||||||
|
local Secrets = require("secrets")
|
||||||
|
local Chests = require("chests")
|
||||||
|
local Nemesis = require("nemesis")
|
||||||
|
local Scavenging = require("scavenging")
|
||||||
|
local Infighting = require("infighting")
|
||||||
|
local Blocks = require("blocks")
|
||||||
|
local Noise = require("noise")
|
||||||
|
local Disease = require("disease")
|
||||||
|
local Captives = require("captives")
|
||||||
|
local Level = require("level")
|
||||||
|
local Horde = require("horde")
|
||||||
|
local Branches = require("branches")
|
||||||
|
local Commands = require("commands")
|
||||||
|
|
||||||
|
-- Real feature history, oldest first, matching the fields Game.fromData
|
||||||
|
-- below has always had to guard with "or" because they arrived after the
|
||||||
|
-- original snapshot shape: essence and the vault/set-piece tallies, then the
|
||||||
|
-- skill tree and reputation, then sanity. A run save from before any of
|
||||||
|
-- those landed is missing exactly the fields each step below fills in.
|
||||||
|
local GameSave = SaveGame.new({
|
||||||
|
[1] = function(data)
|
||||||
|
data.playerExtra.essence = data.playerExtra.essence or 0
|
||||||
|
data.stats = data.stats or { kills = 0, bossKills = 0, questsCompleted = 0, artifactsFound = 0 }
|
||||||
|
data.stats.vaultsOpened = data.stats.vaultsOpened or 0
|
||||||
|
data.stats.setPiecesFound = data.stats.setPiecesFound or 0
|
||||||
|
return data
|
||||||
|
end,
|
||||||
|
[2] = function(data)
|
||||||
|
data.playerExtra.skills = data.playerExtra.skills or {}
|
||||||
|
data.playerExtra.skillPoints = data.playerExtra.skillPoints or 0
|
||||||
|
data.playerExtra.skillCooldowns = data.playerExtra.skillCooldowns or {}
|
||||||
|
data.playerExtra.reputation = data.playerExtra.reputation or {}
|
||||||
|
return data
|
||||||
|
end,
|
||||||
|
[3] = function(data)
|
||||||
|
data.playerExtra.sanity = data.playerExtra.sanity or Sanity.MAX
|
||||||
|
return data
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
local Game = {}
|
local Game = {}
|
||||||
Game.__index = Game
|
Game.__index = Game
|
||||||
|
|
||||||
local FOV_RADIUS = 8
|
local FOV_RADIUS = 8
|
||||||
|
|
||||||
|
-- Which equip slot each wearable item kind occupies, used to check a slot
|
||||||
|
-- for a bound curse before letting a new piece take its place.
|
||||||
|
local slotByItemKind = {
|
||||||
|
[Items.WEAPON] = "weapon", [Items.ARMOR] = "armor",
|
||||||
|
[Items.RING] = "ring", [Items.BOW] = "bow",
|
||||||
|
}
|
||||||
|
|
||||||
local function sign(n)
|
local function sign(n)
|
||||||
if n > 0 then return 1 elseif n < 0 then return -1 else return 0 end
|
if n > 0 then return 1 elseif n < 0 then return -1 else return 0 end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Game.new(seed)
|
function Game.new(seed, options)
|
||||||
local game = setmetatable({}, Game)
|
local game = setmetatable({}, Game)
|
||||||
game.seed = seed
|
game.seed = seed
|
||||||
game.rng = Rng.new(seed)
|
game.rng = Rng.new(seed)
|
||||||
game.depth = 1
|
game.depth = 1
|
||||||
game.turn = 0
|
game.turn = 0
|
||||||
|
game.won = false
|
||||||
game.log = MessageLog.new(32)
|
game.log = MessageLog.new(32)
|
||||||
game.identify = Identify.new(game.rng)
|
game.identify = Identify.new(game.rng)
|
||||||
game.dungeon = Dungeon.new(seed)
|
game.dungeon = Dungeon.new(seed, options)
|
||||||
|
game.maxDepth = Builder.maxDepth(options)
|
||||||
game.level = game.dungeon:get(1)
|
game.level = game.dungeon:get(1)
|
||||||
game.player = Entity.new({
|
game.player = Entity.new({
|
||||||
id = 0, x = game.level.upX, y = game.level.upY,
|
id = 0, x = game.level.upX, y = game.level.upY,
|
||||||
glyph = "@", name = "you", kind = "player",
|
glyph = "@", name = "you", kind = "player",
|
||||||
hp = 30, maxHp = 30, attack = 5, defense = 1, speed = 100,
|
hp = 30, maxHp = 30, attack = 5, defense = 1, speed = 100,
|
||||||
})
|
})
|
||||||
game.player.satiation = 1000
|
game.player.satiation = Hunger.START
|
||||||
|
game.player.sanity = Sanity.MAX
|
||||||
|
game.player.gold = 20
|
||||||
|
game.player.essence = 0
|
||||||
game.player.light = { fuel = 400, radius = 8 }
|
game.player.light = { fuel = 400, radius = 8 }
|
||||||
|
game.player.level = 1
|
||||||
|
game.player.xp = 0
|
||||||
|
game.player.xpNext = Progression.xpToNext(1)
|
||||||
|
game.player.mana = 10
|
||||||
|
game.player.maxMana = 10
|
||||||
|
game.player.spellsKnown = { "missile" }
|
||||||
game.player.inventory = {
|
game.player.inventory = {
|
||||||
Items.potion(10), Items.potion(10), Items.food("ration", 400), Items.scroll("mapping"),
|
Items.potion(10), Items.potion(10), Items.food("ration", 400), Items.scroll("mapping"),
|
||||||
}
|
}
|
||||||
|
game.player.skills = {}
|
||||||
|
game.player.skillPoints = 1
|
||||||
|
game.player.skillCooldowns = {}
|
||||||
|
game.player.reputation = {}
|
||||||
|
game.deepestDepth = 1
|
||||||
|
game.stats = {
|
||||||
|
kills = 0, bossKills = 0, questsCompleted = 0, artifactsFound = 0, vaultsOpened = 0,
|
||||||
|
setPiecesFound = 0, chestsOpened = 0,
|
||||||
|
}
|
||||||
|
if options and options.meta then
|
||||||
|
Meta.applyAll(options.meta, game.player)
|
||||||
|
-- Reuses options.meta's own table (rather than copying it) so every
|
||||||
|
-- sighting/kill recorded mid-run is already sitting in the table
|
||||||
|
-- warren.lua writes back out with Meta.save at the end of the run.
|
||||||
|
options.meta.compendium = options.meta.compendium or Compendium.new()
|
||||||
|
game.compendium = options.meta.compendium
|
||||||
|
else
|
||||||
|
game.compendium = Compendium.new()
|
||||||
|
end
|
||||||
|
if options and options.class and Classes.isValid(options.class) then
|
||||||
|
Classes.apply(game.player, options.class)
|
||||||
|
end
|
||||||
|
game.ascension = Ascension.resolve(options and options.meta, options and options.mutators)
|
||||||
|
Ascension.applyToPlayer(game.ascension, game.player)
|
||||||
game.visible = {}
|
game.visible = {}
|
||||||
|
game.monsterSeq = 0
|
||||||
|
game.nemesis = nil
|
||||||
game:computeVisible()
|
game:computeVisible()
|
||||||
return game
|
return game
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Snapshot fed to meta.lua at the end of a run (see warren.lua's --meta
|
||||||
|
-- flag): everything Meta.renownFor and every achievement condition read,
|
||||||
|
-- gathered in one place so neither has to know Game's own field names.
|
||||||
|
function Game:metaStats()
|
||||||
|
return {
|
||||||
|
deepestDepth = self.deepestDepth,
|
||||||
|
kills = self.stats.kills,
|
||||||
|
bossKills = self.stats.bossKills,
|
||||||
|
questsCompleted = self.stats.questsCompleted,
|
||||||
|
artifactsFound = self.stats.artifactsFound,
|
||||||
|
vaultsOpened = self.stats.vaultsOpened,
|
||||||
|
setPiecesFound = self.stats.setPiecesFound,
|
||||||
|
chestsOpened = self.stats.chestsOpened,
|
||||||
|
won = self.won,
|
||||||
|
mutators = self.ascension,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- The grave this run leaves behind for a future one to stumble across (see
|
||||||
|
-- graves.lua and Builder's options.graves): only warren.lua's --meta path
|
||||||
|
-- ever calls this, and only once a run has actually ended in death -
|
||||||
|
-- winning walks out with the Amulet, not a grave. className falls back to
|
||||||
|
-- "wanderer" for a run that never picked a class at all.
|
||||||
|
function Game:graveEntry()
|
||||||
|
return {
|
||||||
|
className = self.player.class or "wanderer",
|
||||||
|
depth = self.deepestDepth,
|
||||||
|
gold = self.player.gold,
|
||||||
|
essence = self.player.essence,
|
||||||
|
turn = self.turn,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fresh id for a monster spawned mid-game (summon, split), disjoint from the
|
||||||
|
-- small sequential ids the builder hands out at level generation.
|
||||||
|
function Game:nextMonsterId()
|
||||||
|
self.monsterSeq = self.monsterSeq + 1
|
||||||
|
return 100000 + self.monsterSeq
|
||||||
|
end
|
||||||
|
|
||||||
|
function Game:spawnMinion(speciesKey, x, y)
|
||||||
|
local spec = Monsters.byKey[speciesKey]
|
||||||
|
local minion = Monsters.create(spec, self:nextMonsterId(), x, y, self.depth)
|
||||||
|
-- Summoned or split mid-fight, so it is already aware rather than asleep -
|
||||||
|
-- see stealth.lua's header for why builder-placed wild monsters differ.
|
||||||
|
minion.awareness = Stealth.HUNTING
|
||||||
|
self.level.entities[#self.level.entities + 1] = minion
|
||||||
|
return minion
|
||||||
|
end
|
||||||
|
|
||||||
function Game:currentEntities()
|
function Game:currentEntities()
|
||||||
return self.level.entities
|
return self.level.entities
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in new issue
Block a user