better logging and such

This commit is contained in:
Lilian Jónsdóttir 2020-08-27 01:00:16 -07:00
parent 79f5f394b9
commit 8a640d07f3
7 changed files with 52 additions and 44 deletions

View file

@ -1,5 +1,3 @@
local inspect = require("inspect")
local this = {} local this = {}
-- {{{ Variables and such -- {{{ Variables and such
@ -65,11 +63,6 @@ end
this.log = function(...) mwse.log("[%s] %s", this.modName, string.format(...)) end this.log = function(...) mwse.log("[%s] %s", this.modName, string.format(...)) end
this.inspect = function(thing)
this.log("Inspecting a %s", thing)
this.log(inspect(thing))
end
this.vowel = function(str) this.vowel = function(str)
local s = string.sub(str, 1, 1) local s = string.sub(str, 1, 1)
local n = "" local n = ""
@ -89,7 +82,6 @@ this.keyOfLargestValue = function(t)
picked = key picked = key
end end
end end
-- return largest == 0 and nil or largest
return picked return picked
end end

View file

@ -4,49 +4,59 @@ local npcEvaluators = require("celediel.NPCsGoHome.functions.npcEvaluators")
local this = {} local this = {}
-- todo: logging
local function log(level, ...) if config.logLevel >= level then common.log(...) end end local function log(level, ...) if config.logLevel >= level then common.log(...) end end
-- cell worth is combined worth of all NPCs
this.calculateCellWorth = function(cell, proprietor) this.calculateCellWorth = function(cell, proprietor)
-- cell worth is combined worth of all NPCs
local worth = 0 local worth = 0
local msg = "breakdown:\n"
for innard in cell:iterateReferences(tes3.objectType.npc) do for innard in cell:iterateReferences(tes3.objectType.npc) do
worth = worth + npcEvaluators.calculateNPCWorth(innard, innard == proprietor and cell or nil).total local total = npcEvaluators.calculateNPCWorth(innard, innard == proprietor and cell or nil).total
worth = worth + total
msg = msg .. string.format("%s worth:%s, ", innard.object.name, total)
end end
log(common.logLevels.medium, "Calculated worth of %s for cell %s", worth, cell.id)
log(common.logLevels.large, msg:sub(1, #msg - 2)) -- strip off last ", "
return worth return worth
end end
-- iterate NPCs in the cell, if configured amount of the population is any one
-- faction, that's the cell's faction, otherwise, cell doesn't have a faction.
this.pickCellFaction = function(cell) this.pickCellFaction = function(cell)
-- iterate NPCs in the cell, if 2/3 the population is any one faction,
-- that's the cell's faction, otherwise, cell doesn't have a faction.
local npcs = {majorityFactions = {}, allFactions = {}, total = 0} local npcs = {majorityFactions = {}, allFactions = {}, total = 0}
-- count all the npcs with factions
for npc in cell:iterateReferences(tes3.objectType.npc) do for npc in cell:iterateReferences(tes3.objectType.npc) do
local faction = npc.object.faction local faction = npc.object.faction
if faction then if faction then
if not npcs.allFactions[faction] then npcs.allFactions[faction] = {total = 0, percentage = 0} end if not npcs.allFactions[faction.id] then npcs.allFactions[faction.id] = {total = 0, percentage = 0} end
if not npcs.allFactions[faction].master or npcs.allFactions[faction].master.object.factionIndex < if not npcs.allFactions[faction.id].master or npcs.allFactions[faction.id].master.object.factionIndex <
npc.object.factionIndex then npcs.allFactions[faction].master = npc end npc.object.factionIndex then npcs.allFactions[faction.id].master = npc end
npcs.allFactions[faction].total = npcs.allFactions[faction].total + 1 npcs.allFactions[faction.id].total = npcs.allFactions[faction.id].total + 1
end end
npcs.total = npcs.total + 1 npcs.total = npcs.total + 1
end end
for faction, info in pairs(npcs.allFactions) do -- pick out all the factions that make up a percentage of the cell greater than the configured value
-- as long as the cell passes the minimum requirement check
for id, info in pairs(npcs.allFactions) do
info.percentage = (info.total / npcs.total) * 100 info.percentage = (info.total / npcs.total) * 100
if info.percentage >= config.factionIgnorePercentage then if info.percentage >= config.factionIgnorePercentage and npcs.total >= config.minimumOccupancy then
-- return faction.id npcs.majorityFactions[id] = info.percentage
npcs.majorityFactions[faction] = info.percentage
end end
end end
-- no faction -- from the majority values, return the faction with the largest percentage, or "none"
return table.empty(npcs.majorityFactions) and "none" or common.keyOfLargestValue(npcs.majorityFactions) local picked = common.keyOfLargestValue(npcs.majorityFactions)
log(common.logLevels.medium, "Picked faction %s for cell %s", picked, cell.id)
log(common.logLevels.large, "breakdown:\n%s", json.encode(npcs, {indent = true}))
return picked
end end
return this return this

View file

@ -109,6 +109,8 @@ this.isIgnoredNPC = function(npc)
local isDead = false local isDead = false
local isHostile = false local isHostile = false
local isVampire = false local isVampire = false
-- some TR "Hired Guards" aren't actually "guards", ignore them as well
local isGuard = obj.isGuard or (obj.name:lower():match("guard") and true or false)
if npc.mobile then if npc.mobile then
if npc.mobile.health.current <= 0 or npc.mobile.isDead then isDead = true end if npc.mobile.health.current <= 0 or npc.mobile.isDead then isDead = true end
@ -132,13 +134,13 @@ this.isIgnoredNPC = function(npc)
"guard:%s dead:%s vampire:%s werewolf:%s dreamer:%s follower:%s hostile:%s %s%s"), -- "guard:%s dead:%s vampire:%s werewolf:%s dreamer:%s follower:%s hostile:%s %s%s"), --
obj.name, npc.object.id, npc.object.baseObject and npc.object.baseObject.id or "nil", -- obj.name, npc.object.id, npc.object.baseObject and npc.object.baseObject.id or "nil", --
config.ignored[obj.id:lower()], obj.sourceMod, config.ignored[obj.sourceMod:lower()], -- config.ignored[obj.id:lower()], obj.sourceMod, config.ignored[obj.sourceMod:lower()], --
obj.isGuard, isDead, isVampire, isWerewolf, (obj.class and obj.class.id == "Dreamers"), -- isGuard, isDead, isVampire, isWerewolf, (obj.class and obj.class.id == "Dreamers"), --
common.runtimeData.followers[npc.object.id], isHostile, obj.id:match("fargoth") and "fargoth:" or "", -- common.runtimeData.followers[npc.object.id], isHostile, obj.id:match("fargoth") and "fargoth:" or "", --
obj.id:match("fargoth") and isFargothActive or "") obj.id:match("fargoth") and isFargothActive or "")
return config.ignored[obj.id:lower()] or -- return config.ignored[obj.id:lower()] or --
config.ignored[obj.sourceMod:lower()] or -- config.ignored[obj.sourceMod:lower()] or --
obj.isGuard or -- isGuard or --
isFargothActive or -- isFargothActive or --
isDead or -- don't move dead NPCS isDead or -- don't move dead NPCS
isHostile or -- isHostile or --
@ -166,8 +168,6 @@ this.isPublicHouse = function(cell)
if not this.isInteriorCell(cell) then return false end if not this.isInteriorCell(cell) then return false end
-- gather some data about the cell -- gather some data about the cell
local worth = cellEvaluators.calculateCellWorth(cell)
local faction = cellEvaluators.pickCellFaction(cell)
local typeOfPub = common.pickPublicHouseType(cell) local typeOfPub = common.pickPublicHouseType(cell)
local city, publicHouseName local city, publicHouseName
@ -187,7 +187,9 @@ this.isPublicHouse = function(cell)
-- if it's a waistworks cell, it's public, with no proprietor -- if it's a waistworks cell, it's public, with no proprietor
if config.waistWorks == common.waist.public and cell.id:match(common.waistworks) then if config.waistWorks == common.waist.public and cell.id:match(common.waistworks) then
dataTables.createPublicHouseTableEntry(cell, nil, city, publicHouseName, worth, faction) dataTables.createPublicHouseTableEntry(cell, nil, city, publicHouseName,
cellEvaluators.calculateCellWorth(cell),
cellEvaluators.pickCellFaction(cell))
return true return true
end end
@ -199,7 +201,9 @@ this.isPublicHouse = function(cell)
log(common.logLevels.medium, "NPC:\'%s\' of class:\'%s\' made %s public", npc.object.name, log(common.logLevels.medium, "NPC:\'%s\' of class:\'%s\' made %s public", npc.object.name,
npc.object.class and npc.object.class.id or "none", cell.name) npc.object.class and npc.object.class.id or "none", cell.name)
dataTables.createPublicHouseTableEntry(cell, npc, city, publicHouseName, worth, faction) dataTables.createPublicHouseTableEntry(cell, npc, city, publicHouseName,
cellEvaluators.calculateCellWorth(cell),
cellEvaluators.pickCellFaction(cell))
return true return true
end end
@ -232,7 +236,9 @@ this.isPublicHouse = function(cell)
config.factionIgnorePercentage then config.factionIgnorePercentage then
log(common.logLevels.medium, "%s is %s%% faction %s, marking public.", cell.name, info.percentage, faction) log(common.logLevels.medium, "%s is %s%% faction %s, marking public.", cell.name, info.percentage, faction)
dataTables.createPublicHouseTableEntry(cell, npcs.factions[faction].master, city, publicHouseName, worth, faction) dataTables.createPublicHouseTableEntry(cell, npcs.factions[faction].master, city, publicHouseName,
cellEvaluators.calculateCellWorth(cell),
cellEvaluators.pickCellFaction(cell))
return true return true
end end
end end
@ -281,17 +287,18 @@ end
-- AT NIGHT -- AT NIGHT
this.checkTime = function() this.checkTime = function()
log(common.logLevels.large, "Current time is %s, things are closed between %s and %s", local night = tes3.worldController.hour.value >= config.closeTime or tes3.worldController.hour.value <= config.openTime
tes3.worldController.hour.value, config.closeTime, config.openTime) log(common.logLevels.large, "Current time is %.2f (%snight), things are closed between %s and %s",
return tes3.worldController.hour.value >= config.closeTime or tes3.worldController.hour.value <= config.openTime tes3.worldController.hour.value, night and "" or "not ", config.closeTime, config.openTime)
return night
end end
-- inclement weather -- inclement weather
this.checkWeather = function(cell) this.checkWeather = function(cell)
if not cell.region then return end if not cell.region then return end
log(common.logLevels.large, "Weather: %s >= %s == %s", cell.region.weather.index, config.worstWeather, log(common.logLevels.large, "Weather: current:%s >= configured worst:%s == %s", cell.region.weather.index,
cell.region.weather.index >= config.worstWeather) config.worstWeather, cell.region.weather.index >= config.worstWeather)
return cell.region.weather.index >= config.worstWeather return cell.region.weather.index >= config.worstWeather
end end

View file

@ -25,7 +25,7 @@ this.createHomedNPCTableEntry = function(npc, home, startingPlace, isHome, posit
if isHome and positions.npcs[npc.object.name] then if isHome and positions.npcs[npc.object.name] then
pos = positions.npcs[npc.object.name].position pos = positions.npcs[npc.object.name].position
ori = positions.npcs[npc.object.name].orientation ori = positions.npcs[npc.object.name].orientation
elseif not table.empty(common.runtimeData.positions[id]) then elseif common.runtimeData.positions[id] and not table.empty(common.runtimeData.positions[id]) then
-- pick a random position out of the positions in memory -- pick a random position out of the positions in memory
local choice, index = table.choice(common.runtimeData.positions[id]) local choice, index = table.choice(common.runtimeData.positions[id])
pos = choice.position pos = choice.position
@ -36,11 +36,11 @@ this.createHomedNPCTableEntry = function(npc, home, startingPlace, isHome, posit
ori = {0, 0, 0} ori = {0, 0, 0}
end end
log(common.logLevels.large, "Settled on position:%s, orientation:%s for %s in %s", pos, ori, npc.object.name, id)
pickedPosition = tes3vector3.new(pos[1], pos[2], pos[3]) pickedPosition = tes3vector3.new(pos[1], pos[2], pos[3])
pickedOrientation = tes3vector3.new(ori[1], ori[2], ori[3]) pickedOrientation = tes3vector3.new(ori[1], ori[2], ori[3])
log(common.logLevels.large, "Settled on position:%s, orientation:%s for %s in %s", pickedPosition, pickedOrientation, npc.object.name, id)
local ogPosition = position and (tes3vector3.new(position.x, position.y, position.z)) or local ogPosition = position and (tes3vector3.new(position.x, position.y, position.z)) or
(npc.position and npc.position:copy() or zeroVector:copy()) (npc.position and npc.position:copy() or zeroVector:copy())

View file

@ -1,5 +1,6 @@
-- handles evaluating NPCs -- handles evaluating NPCs
local common = require("celediel.NPCsGoHome.common") local common = require("celediel.NPCsGoHome.common")
local config = require("celediel.NPCsGoHome.config").getConfig()
local this = {} local this = {}
@ -41,6 +42,7 @@ this.calculateNPCWorth = function(npc, merchantCell)
-- calculate the total -- calculate the total
local total = 0 local total = 0
for _, v in pairs(worth) do total = total + v end for _, v in pairs(worth) do total = total + v end
log(common.logLevels.medium, "Calculated worth of %s for %s", total, npc.object.name)
-- then add it to the table -- then add it to the table
worth.total = total worth.total = total

View file

@ -125,9 +125,6 @@ this.processNPCs = function(cell)
for npc in cell:iterateReferences(tes3.objectType.npc) do for npc in cell:iterateReferences(tes3.objectType.npc) do
-- for npc, _ in pairs(cellsInMemory[cell].npcs) do -- for npc, _ in pairs(cellsInMemory[cell].npcs) do
if not checks.isIgnoredNPC(npc) then if not checks.isIgnoredNPC(npc) then
log(common.logLevels.large, "People change")
-- if not npc.data.NPCsGoHome then npc.data.NPCsGoHome = {} end
-- find NPC homes -- find NPC homes
local npcHome = config.moveNPCs and housing.pickHomeForNPC(cell, npc) or nil local npcHome = config.moveNPCs and housing.pickHomeForNPC(cell, npc) or nil
@ -163,8 +160,7 @@ this.processNPCs = function(cell)
end end
-- now put NPCs back -- now put NPCs back
-- if not (checks.checkTime() or checks.checkWeather(cell)) and #movedNPCs > 0 then putNPCsBack() end if not (checks.checkTime() or checks.checkWeather(cell)) and #common.runtimeData.movedNPCs > 0 then this.putNPCsBack() end
if not (checks.checkTime() or checks.checkWeather(cell)) then this.putNPCsBack() end
end end
this.processSiltStriders = function(cell) this.processSiltStriders = function(cell)

View file

@ -16,7 +16,8 @@ local category = page:createCategory(common.modName)
category:createDropdown({ category:createDropdown({
label = "Debug log level", label = "Debug log level",
description = [[Enable this if you want to flood mwse.log with nonsense. Even small is huge.]], description = "Enable this if you want to flood mwse.log with nonsense. Even small is huge." ..
"Large in Old Ebonheart spits out 16k lines each update. Don't pick that option.",
options = { options = {
{label = "None", value = common.logLevels.none}, {label = "None", value = common.logLevels.none},
{label = "Small", value = common.logLevels.small}, {label = "Small", value = common.logLevels.small},