diff --git a/MWSE/mods/celediel/NPCsGoHome/common.lua b/MWSE/mods/celediel/NPCsGoHome/common.lua index 95144a8..e4e5be4 100644 --- a/MWSE/mods/celediel/NPCsGoHome/common.lua +++ b/MWSE/mods/celediel/NPCsGoHome/common.lua @@ -1,5 +1,3 @@ -local inspect = require("inspect") - local this = {} -- {{{ Variables and such @@ -65,11 +63,6 @@ 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) local s = string.sub(str, 1, 1) local n = "" @@ -89,7 +82,6 @@ this.keyOfLargestValue = function(t) picked = key end end - -- return largest == 0 and nil or largest return picked end diff --git a/MWSE/mods/celediel/NPCsGoHome/functions/cellEvaluators.lua b/MWSE/mods/celediel/NPCsGoHome/functions/cellEvaluators.lua index 686b9d1..b126ed1 100644 --- a/MWSE/mods/celediel/NPCsGoHome/functions/cellEvaluators.lua +++ b/MWSE/mods/celediel/NPCsGoHome/functions/cellEvaluators.lua @@ -4,49 +4,59 @@ local npcEvaluators = require("celediel.NPCsGoHome.functions.npcEvaluators") local this = {} --- todo: logging 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) - -- cell worth is combined worth of all NPCs local worth = 0 + local msg = "breakdown:\n" 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 + 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 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) - -- 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} + + -- count all the npcs with factions for npc in cell:iterateReferences(tes3.objectType.npc) do local faction = npc.object.faction 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 < - npc.object.factionIndex then npcs.allFactions[faction].master = npc end + if not npcs.allFactions[faction.id].master or npcs.allFactions[faction.id].master.object.factionIndex < + 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 npcs.total = npcs.total + 1 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 - if info.percentage >= config.factionIgnorePercentage then - -- return faction.id - npcs.majorityFactions[faction] = info.percentage + if info.percentage >= config.factionIgnorePercentage and npcs.total >= config.minimumOccupancy then + npcs.majorityFactions[id] = info.percentage end end - -- no faction - return table.empty(npcs.majorityFactions) and "none" or common.keyOfLargestValue(npcs.majorityFactions) + -- from the majority values, return the faction with the largest percentage, or "none" + 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 return this diff --git a/MWSE/mods/celediel/NPCsGoHome/functions/checks.lua b/MWSE/mods/celediel/NPCsGoHome/functions/checks.lua index f891d3e..f8a1163 100644 --- a/MWSE/mods/celediel/NPCsGoHome/functions/checks.lua +++ b/MWSE/mods/celediel/NPCsGoHome/functions/checks.lua @@ -109,6 +109,8 @@ this.isIgnoredNPC = function(npc) local isDead = false local isHostile = 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.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"), -- 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()], -- - 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 "", -- obj.id:match("fargoth") and isFargothActive or "") return config.ignored[obj.id:lower()] or -- config.ignored[obj.sourceMod:lower()] or -- - obj.isGuard or -- + isGuard or -- isFargothActive or -- isDead or -- don't move dead NPCS isHostile or -- @@ -166,8 +168,6 @@ this.isPublicHouse = function(cell) if not this.isInteriorCell(cell) then return false end -- gather some data about the cell - local worth = cellEvaluators.calculateCellWorth(cell) - local faction = cellEvaluators.pickCellFaction(cell) local typeOfPub = common.pickPublicHouseType(cell) local city, publicHouseName @@ -187,7 +187,9 @@ this.isPublicHouse = function(cell) -- 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 - dataTables.createPublicHouseTableEntry(cell, nil, city, publicHouseName, worth, faction) + dataTables.createPublicHouseTableEntry(cell, nil, city, publicHouseName, + cellEvaluators.calculateCellWorth(cell), + cellEvaluators.pickCellFaction(cell)) return true end @@ -199,7 +201,9 @@ this.isPublicHouse = function(cell) 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) - dataTables.createPublicHouseTableEntry(cell, npc, city, publicHouseName, worth, faction) + dataTables.createPublicHouseTableEntry(cell, npc, city, publicHouseName, + cellEvaluators.calculateCellWorth(cell), + cellEvaluators.pickCellFaction(cell)) return true end @@ -232,7 +236,9 @@ this.isPublicHouse = function(cell) config.factionIgnorePercentage then 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 end end @@ -281,17 +287,18 @@ end -- AT NIGHT this.checkTime = function() - log(common.logLevels.large, "Current time is %s, things are closed between %s and %s", - tes3.worldController.hour.value, config.closeTime, config.openTime) - return tes3.worldController.hour.value >= config.closeTime or tes3.worldController.hour.value <= config.openTime + local night = tes3.worldController.hour.value >= config.closeTime or tes3.worldController.hour.value <= config.openTime + log(common.logLevels.large, "Current time is %.2f (%snight), things are closed between %s and %s", + tes3.worldController.hour.value, night and "" or "not ", config.closeTime, config.openTime) + return night end -- inclement weather this.checkWeather = function(cell) if not cell.region then return end - log(common.logLevels.large, "Weather: %s >= %s == %s", cell.region.weather.index, config.worstWeather, - cell.region.weather.index >= config.worstWeather) + log(common.logLevels.large, "Weather: current:%s >= configured worst:%s == %s", cell.region.weather.index, + config.worstWeather, cell.region.weather.index >= config.worstWeather) return cell.region.weather.index >= config.worstWeather end diff --git a/MWSE/mods/celediel/NPCsGoHome/functions/dataTables.lua b/MWSE/mods/celediel/NPCsGoHome/functions/dataTables.lua index 02cd802..a295ad1 100644 --- a/MWSE/mods/celediel/NPCsGoHome/functions/dataTables.lua +++ b/MWSE/mods/celediel/NPCsGoHome/functions/dataTables.lua @@ -25,7 +25,7 @@ this.createHomedNPCTableEntry = function(npc, home, startingPlace, isHome, posit if isHome and positions.npcs[npc.object.name] then pos = positions.npcs[npc.object.name].position 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 local choice, index = table.choice(common.runtimeData.positions[id]) pos = choice.position @@ -36,11 +36,11 @@ this.createHomedNPCTableEntry = function(npc, home, startingPlace, isHome, posit ori = {0, 0, 0} 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]) 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 (npc.position and npc.position:copy() or zeroVector:copy()) diff --git a/MWSE/mods/celediel/NPCsGoHome/functions/npcEvaluators.lua b/MWSE/mods/celediel/NPCsGoHome/functions/npcEvaluators.lua index 337b4c9..a6fc17a 100644 --- a/MWSE/mods/celediel/NPCsGoHome/functions/npcEvaluators.lua +++ b/MWSE/mods/celediel/NPCsGoHome/functions/npcEvaluators.lua @@ -1,5 +1,6 @@ -- handles evaluating NPCs local common = require("celediel.NPCsGoHome.common") +local config = require("celediel.NPCsGoHome.config").getConfig() local this = {} @@ -41,6 +42,7 @@ this.calculateNPCWorth = function(npc, merchantCell) -- calculate the total local total = 0 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 worth.total = total diff --git a/MWSE/mods/celediel/NPCsGoHome/functions/processors.lua b/MWSE/mods/celediel/NPCsGoHome/functions/processors.lua index c3dde05..0fa72cf 100644 --- a/MWSE/mods/celediel/NPCsGoHome/functions/processors.lua +++ b/MWSE/mods/celediel/NPCsGoHome/functions/processors.lua @@ -125,9 +125,6 @@ this.processNPCs = function(cell) for npc in cell:iterateReferences(tes3.objectType.npc) do -- for npc, _ in pairs(cellsInMemory[cell].npcs) do 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 local npcHome = config.moveNPCs and housing.pickHomeForNPC(cell, npc) or nil @@ -163,8 +160,7 @@ this.processNPCs = function(cell) end -- 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)) then this.putNPCsBack() end + if not (checks.checkTime() or checks.checkWeather(cell)) and #common.runtimeData.movedNPCs > 0 then this.putNPCsBack() end end this.processSiltStriders = function(cell) diff --git a/MWSE/mods/celediel/NPCsGoHome/mcm.lua b/MWSE/mods/celediel/NPCsGoHome/mcm.lua index 2401a6a..5190084 100644 --- a/MWSE/mods/celediel/NPCsGoHome/mcm.lua +++ b/MWSE/mods/celediel/NPCsGoHome/mcm.lua @@ -16,7 +16,8 @@ local category = page:createCategory(common.modName) category:createDropdown({ 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 = { {label = "None", value = common.logLevels.none}, {label = "Small", value = common.logLevels.small},