From ad4a113642300c7cb52a2f80ff116bd2ea959a41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lilian=20J=C3=B3nsd=C3=B3ttir?= Date: Sun, 23 Aug 2020 22:02:33 -0700 Subject: [PATCH] option to treat waistworks as public + refactoring --- MWSE/mods/celediel/NPCsGoHome/common.lua | 20 ++ MWSE/mods/celediel/NPCsGoHome/config.lua | 2 +- .../celediel/NPCsGoHome/functions/checks.lua | 49 +++-- .../celediel/NPCsGoHome/functions/entry.lua | 150 +++++++++++++ .../celediel/NPCsGoHome/functions/housing.lua | 202 +++--------------- .../NPCsGoHome/functions/processors.lua | 7 +- MWSE/mods/celediel/NPCsGoHome/interop.lua | 18 +- MWSE/mods/celediel/NPCsGoHome/main.lua | 23 +- MWSE/mods/celediel/NPCsGoHome/mcm.lua | 17 +- 9 files changed, 269 insertions(+), 219 deletions(-) create mode 100644 MWSE/mods/celediel/NPCsGoHome/functions/entry.lua diff --git a/MWSE/mods/celediel/NPCsGoHome/common.lua b/MWSE/mods/celediel/NPCsGoHome/common.lua index 0924272..d6cc12c 100644 --- a/MWSE/mods/celediel/NPCsGoHome/common.lua +++ b/MWSE/mods/celediel/NPCsGoHome/common.lua @@ -10,7 +10,12 @@ this.modInfo = "Move NPCs to their homes, or public houses (or just disable them "and prevent interaction after hours, selectively disable NPCs in inclement weather" this.configPath = "NPCSGOHOME" +-- for config this.logLevels = {none = 0, small = 1, medium = 2, large = 3} +this.waist = {neither = 0, exterior = 1, public = 2} + +-- for runtime data +this.publicHouseTypes = {inns = "Inns", guildhalls = "Guildhalls", temples = "Temples", houses = "Houses", cantonworks = "Cantonworks"} -- }}} -- {{{ Filled at runtime @@ -53,6 +58,21 @@ this.vowel = function(str) return n end + +-- todo: pick this better +this.pickPublicHouseType = function(cell) + if cell.id:match("Guild") then + return this.publicHouseTypes.guildhalls + elseif cell.id:match("Temple") then + return this.publicHouseTypes.temples + elseif cell.id:match("[Cc]analworks") or cell.id:match("[Ww]aistworks") then + return this.publicHouseTypes.cantonworks + -- elseif cell.id:match("House") then + -- return publicHouseTypes.houses + else + return this.publicHouseTypes.inns + end +end -- }}} return this diff --git a/MWSE/mods/celediel/NPCsGoHome/config.lua b/MWSE/mods/celediel/NPCsGoHome/config.lua index 3e02a1f..a314ef2 100644 --- a/MWSE/mods/celediel/NPCsGoHome/config.lua +++ b/MWSE/mods/celediel/NPCsGoHome/config.lua @@ -22,7 +22,7 @@ local defaultConfig = { disableInteraction = true, -- door settings lockDoors = true, - waistWorks = true, + waistWorks = common.waist.interior, -- debug settings logLevel = common.logLevels.none, } diff --git a/MWSE/mods/celediel/NPCsGoHome/functions/checks.lua b/MWSE/mods/celediel/NPCsGoHome/functions/checks.lua index 6c95e0c..976d29e 100644 --- a/MWSE/mods/celediel/NPCsGoHome/functions/checks.lua +++ b/MWSE/mods/celediel/NPCsGoHome/functions/checks.lua @@ -1,14 +1,14 @@ local common = require("celediel.NPCsGoHome.common") local config = require("celediel.NPCsGoHome.config").getConfig() -local housing = require("celediel.NPCsGoHome.functions.housing") +local entry = require("celediel.NPCsGoHome.functions.entry") -- {{{ local variables and such --- Waistworks string match -local waistworks = { - "[Cc]analworks", -- These will match Vivec and Molag Mar - "[Ww]aistworks" -- and Almas Thirr from Tamriel Rebuilt -} --- these are separate because doors to underworks should be ignored +-- Canton string matches +-- move NPCs into waistworks +local waistworks = "[Ww]aistworks" +-- don't lock canalworks +local canalworks = "[Cc]analworks" +-- doors to underworks should be ignored -- but NPCs in underworks should not be disabled local underworks = "[Uu]nderworks" @@ -30,7 +30,6 @@ end local function getFightFromSpawnedReference(id) -- Spawn a reference of the given id in toddtest local toddTest = tes3.getCell("toddtest") - log(common.logLevels.medium, "Spawning %s in %s", id, toddTest.id) local ref = tes3.createReference({ @@ -39,7 +38,7 @@ local function getFightFromSpawnedReference(id) cell = tes3.getPlayerCell(), -- position = zeroVector, position = {0, 0, 10000}, - orientation = housing.zeroVector + orientation = tes3vector3.new(0, 0, 0) }) local fight = ref.mobile.fight @@ -97,8 +96,18 @@ this.isIgnoredCell = function(cell) return config.ignored[cell.id] or config.ignored[cell.sourceMod] -- or wilderness end -this.isCantonCell = function(cellName) - for _, str in pairs(waistworks) do if cellName:match(str) then return true end end +this.isCantonWorksCell = function(cell) + -- for _, str in pairs(waistworks) do if cell.id:match(str) then return true end end + return cell.id:match(waistworks) or cell.id:match(canalworks) or cell.id:match(underworks) +end + +this.isCantonCell = function(cell) + if this.isInteriorCell(cell) then return false end + for door in cell:iterateReferences(tes3.objectType.door) do + if door.destination and this.isCantonWorksCell(door.destination.cell) then + return true + end + end return false end @@ -177,7 +186,7 @@ this.isPublicHouse = function(cell) -- only interior cells are public "houses" if not this.isInteriorCell(cell) then return false end - local typeOfPub = housing.pickPublicHouseType(cell) + local typeOfPub = common.pickPublicHouseType(cell) local city, publicHouseName if cell.name and string.match(cell.name, ",") then @@ -191,6 +200,12 @@ this.isPublicHouse = function(cell) -- don't iterate NPCs in the cell if we've already marked it public if common.runtimeData.publicHouses[city] and (common.runtimeData.publicHouses[city][typeOfPub] and common.runtimeData.publicHouses[city][typeOfPub][cell.id]) then return true end + -- if it's a waistworks cell, it's public, with no proprietor + if config.waistWorks == common.waist.public and cell.id:match(waistworks) then + entry.createPublicHouseTableEntry(cell, nil, city, publicHouseName) + return true + end + local npcs = {factions = {}, total = 0} for npc in cell:iterateReferences(tes3.objectType.npc) do -- Check for NPCS of ignored classes first @@ -199,7 +214,7 @@ 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) - housing.createPublicHouseTableEntry(cell, npc, city, publicHouseName) + entry.createPublicHouseTableEntry(cell, npc, city, publicHouseName) return true end @@ -233,7 +248,7 @@ this.isPublicHouse = function(cell) config.factionIgnorePercentage then log(common.logLevels.medium, "%s is %s%% faction %s, marking public.", cell.name, info.percentage, faction) - housing.createPublicHouseTableEntry(cell, npcs.factions[faction].master, city, publicHouseName) + entry.createPublicHouseTableEntry(cell, npcs.factions[faction].master, city, publicHouseName) return true end end @@ -268,8 +283,8 @@ this.isIgnoredDoor = function(door, homeCellId) end end - -- don't lock doors to underworks in addition to other canton cells - local isCanton = this.isCantonCell(dest.id) or dest.id:match(underworks) + -- don't lock doors to canton cells + local isCantonWorks = this.isCantonWorksCell(dest) log(common.logLevels.large, "%s is %s, (%sin a city, is %spublic, %soccupied)", -- dest.id, this.isIgnoredCell(dest) and "ignored" or "not ignored", -- destination is ignored @@ -277,7 +292,7 @@ this.isIgnoredDoor = function(door, homeCellId) return this.isIgnoredCell(dest) or not this.isInteriorCell(dest) or - isCanton or + isCantonWorks or not inCity or leadsToPublicCell or not hasOccupants diff --git a/MWSE/mods/celediel/NPCsGoHome/functions/entry.lua b/MWSE/mods/celediel/NPCsGoHome/functions/entry.lua new file mode 100644 index 0000000..aceeabd --- /dev/null +++ b/MWSE/mods/celediel/NPCsGoHome/functions/entry.lua @@ -0,0 +1,150 @@ +local common = require("celediel.NPCsGoHome.common") +local interop = require("celediel.NPCsGoHome.interop") +local positions = require("celediel.NPCsGoHome.data.positions") +local config = require("celediel.NPCsGoHome.config").getConfig() + +local zeroVector = tes3vector3.new(0, 0, 0) +local function log(level, ...) if config.logLevel >= level then common.log(...) end end + +local this = {} + +-- {{{ npc evaluators + +-- NPCs barter gold + value of all inventory items +this.calculateNPCWorth = function(npc, merchantCell) + local worth = npc.object.barterGold + local obj = npc.baseObject and npc.baseObject or npc.object + + if npc.object.inventory then + for _, item in pairs(npc.object.inventory) do worth = worth + (item.object.value or 0) end + end + + if merchantCell then -- if we pass a cell argument + for box in merchantCell:iterateReferences(tes3.objectType.container) do -- loop over each container + if box.inventory then -- if it's not empty + for item in tes3.iterate(box.inventory) do -- loop over its items + if obj:tradesItemType(item.objectType) then -- if the NPC sells that type + worth = worth + item.object.value -- add its value to the NPCs total value + end + end + end + end + end + + return worth +end + +-- }}} + +this.checkModdedCell = function(cellId) + local id + + if cellId == "Balmora, South Wall Cornerclub" and tes3.isModActive("South Wall.ESP") then + id = "Balmora, South Wall Den Of Iniquity" + elseif cellId == "Balmora, Eight Plates" and tes3.isModActive("Eight Plates.esp") then + id = "Balmora, Seedy Eight Plates" + elseif cellId == "Hla Oad, Fatleg's Drop Off" and tes3.isModActive("Clean DR115_TheDropoff_HlaOadDocks.ESP") then + id = "Hla Oad, The Drop Off" + else + id = cellId + end + + return id +end + +this.createHomedNPCTableEntry = function(npc, home, startingPlace, isHome, position, orientation) + if npc.object and (npc.object.name == nil or npc.object.name == "") then return end + + local pickedPosition, pickedOrientation, pos, ori + + -- mod support for different positions in cells + local id = this.checkModdedCell(home.id) + + log(common.logLevels.medium, "Found %s for %s: %s... adding it to in memory table...", + isHome and "home" or "public house", npc.object.name, id) + + if isHome and positions.npcs[npc.object.name] then + pos = positions.npcs[npc.object.name].position + ori = positions.npcs[npc.object.name].orientation + -- pickedPosition = positions.npcs[npc.object.name] and tes3vector3.new(p[1], p[2], p[3]) or zeroVector:copy() + -- pickedOrientation = positions.npcs[npc.object.name] and tes3vector3.new(o[1], o[2], o[3]) or zeroVector:copy() + elseif positions.cells[id] then + pos = table.choice(positions.cells[id]).position + ori = table.choice(positions.cells[id]).orientation + -- pickedPosition = positions.cells[id] and tes3vector3.new(p[1], p[2], p[3]) or zeroVector:copy() + -- pickedOrientation = positions.cells[id] and tes3vector3.new(o[1], o[2], o[3]) or zeroVector:copy() + -- pickedPosition = tes3vector3.new(p[1], p[2], p[3]) + -- pickedOrientation = tes3vector3.new(o[1], o[2], o[3]) + else + pos = {0,0,0} + ori = {0,0,0} + -- pickedPosition = zeroVector:copy() + -- pickedOrientation = zeroVector:copy() + end + + pickedPosition = tes3vector3.new(pos[1], pos[2], pos[3]) + pickedOrientation = tes3vector3.new(ori[1], ori[2], ori[3]) + + local ogPosition = position and + (tes3vector3.new(position.x, position.y, position.z)) or + (npc.position and npc.position:copy() or zeroVector:copy()) + + local ogOrientation = orientation and + (tes3vector3.new(orientation.x, orientation.y, orientation.z)) or + (npc.orientation and npc.orientation:copy() or zeroVector:copy()) + + local entry = { + name = npc.object.name, -- string + npc = npc, -- tes3npc + isHome = isHome, -- bool + home = home, -- tes3cell + homeName = home.id, -- string + ogPlace = startingPlace, -- tes3cell + ogPlaceName = startingPlace.id, + ogPosition = ogPosition, -- tes3vector3 + ogOrientation = ogOrientation, -- tes3vector3 + homePosition = pickedPosition, -- tes3vector3 + homeOrientation = pickedOrientation, -- tes3vector3 + worth = this.calculateNPCWorth(npc) -- int + } + + common.runtimeData.homes.byName[npc.object.name] = entry + if isHome then common.runtimeData.homes.byCell[home.id] = entry end + + interop.setRuntimeData(common.runtimeData) + + return entry +end + +this.createPublicHouseTableEntry = function(publicCell, proprietor, city, name) + local typeOfPub = common.pickPublicHouseType(publicCell) + + local worth = 0 + + -- cell worth is combined worth of all NPCs + for innard in publicCell:iterateReferences(tes3.objectType.npc) do + if innard == proprietor then + worth = worth + this.calculateNPCWorth(innard, publicCell) + else + worth = worth + this.calculateNPCWorth(innard) + end + end + + local proprietorName = proprietor and proprietor.object.name or "no one" + + if not common.runtimeData.publicHouses[city] then common.runtimeData.publicHouses[city] = {} end + if not common.runtimeData.publicHouses[city][typeOfPub] then common.runtimeData.publicHouses[city][typeOfPub] = {} end + + common.runtimeData.publicHouses[city][typeOfPub][publicCell.id] = { + name = name, + city = city, + cell = publicCell, + proprietor = proprietor, + proprietorName = proprietorName, + worth = worth + } + + interop.setRuntimeData(common.runtimeData) +end + +return this diff --git a/MWSE/mods/celediel/NPCsGoHome/functions/housing.lua b/MWSE/mods/celediel/NPCsGoHome/functions/housing.lua index d160899..94786cc 100644 --- a/MWSE/mods/celediel/NPCsGoHome/functions/housing.lua +++ b/MWSE/mods/celediel/NPCsGoHome/functions/housing.lua @@ -1,75 +1,15 @@ local common = require("celediel.NPCsGoHome.common") local config = require("celediel.NPCsGoHome.config").getConfig() -local interop = require("celediel.NPCsGoHome.interop") -local positions = require("celediel.NPCsGoHome.data.positions") +local checks = require("celediel.NPCsGoHome.functions.checks") +local entry = require("celediel.NPCsGoHome.functions.entry") local function log(level, ...) if config.logLevel >= level then common.log(...) end end -local publicHouseTypes = {inns = "Inns", guildhalls = "Guildhalls", temples = "Temples", houses = "Houses"} - -- animated morrowind NPCs are contextual local contextualNPCs = {"^AM_"} local this = {} -this.zeroVector = tes3vector3.new(0, 0, 0) - -this.checkModdedCell = function(cellId) - local id - - if cellId == "Balmora, South Wall Cornerclub" and tes3.isModActive("South Wall.ESP") then - id = "Balmora, South Wall Den Of Iniquity" - elseif cellId == "Balmora, Eight Plates" and tes3.isModActive("Eight Plates.esp") then - id = "Balmora, Seedy Eight Plates" - elseif cellId == "Hla Oad, Fatleg's Drop Off" and tes3.isModActive("Clean DR115_TheDropoff_HlaOadDocks.ESP") then - id = "Hla Oad, The Drop Off" - else - id = cellId - end - - return id -end --- {{{ npc evaluators - --- NPCs barter gold + value of all inventory items -this.calculateNPCWorth = function(npc, merchantCell) - local worth = npc.object.barterGold - local obj = npc.baseObject and npc.baseObject or npc.object - - if npc.object.inventory then - for _, item in pairs(npc.object.inventory) do worth = worth + (item.object.value or 0) end - end - - if merchantCell then -- if we pass a cell argument - for box in merchantCell:iterateReferences(tes3.objectType.container) do -- loop over each container - if box.inventory then -- if it's not empty - for item in tes3.iterate(box.inventory) do -- loop over its items - if obj:tradesItemType(item.objectType) then -- if the NPC sells that type - worth = worth + item.object.value -- add its value to the NPCs total value - end - end - end - end - end - - return worth -end - --- }}} - --- todo: pick this better -this.pickPublicHouseType = function(cell) - if cell.id:match("Guild") then - return publicHouseTypes.guildhalls - elseif cell.id:match("Temple") then - return publicHouseTypes.temples - -- elseif cell.id:match("House") then - -- return publicHouseTypes.houses - else - return publicHouseTypes.inns - end -end - -- ? I honestly don't know if there are any wandering NPCs that "live" in close-by manors, but I wrote this anyway this.livesInManor = function(cellName, npcName) if not cellName or (cellName and not string.find(cellName, "Manor")) then return end @@ -94,9 +34,9 @@ this.pickInnForNPC = function(npc, city) -- ? for others, pick based on value of equipment -- but for now pick one at random - if common.runtimeData.publicHouses[city] and common.runtimeData.publicHouses[city][publicHouseTypes.inns] then - local choice = table.choice(common.runtimeData.publicHouses[city][publicHouseTypes.inns]) - if not choice then return end + if common.runtimeData.publicHouses[city] and common.runtimeData.publicHouses[city][common.publicHouseTypes.inns] then + local choice = table.choice(common.runtimeData.publicHouses[city][common.publicHouseTypes.inns]) + if not choice then return nil end log(common.logLevels.medium, "Picking inn %s, %s for %s", choice.city, choice.name, npc.object.name) return choice.cell end @@ -104,8 +44,8 @@ end this.pickPublicHouseForNPC = function(npc, city) -- look for wandering guild members - if common.runtimeData.publicHouses[city] and common.runtimeData.publicHouses[city][publicHouseTypes.guildhalls] then - for _, data in pairs(common.runtimeData.publicHouses[city][publicHouseTypes.guildhalls]) do + if common.runtimeData.publicHouses[city] and common.runtimeData.publicHouses[city][common.publicHouseTypes.guildhalls] then + for _, data in pairs(common.runtimeData.publicHouses[city][common.publicHouseTypes.guildhalls]) do -- if npc's faction and proprietor's faction match, pick that one if npc.object.faction == data.proprietor.object.faction then log(common.logLevels.medium, "Picking %s for %s based on faction", data.cell.id, npc.object.name) @@ -115,8 +55,8 @@ this.pickPublicHouseForNPC = function(npc, city) end -- temple members go to the temple - if common.runtimeData.publicHouses[city] and common.runtimeData.publicHouses[city][publicHouseTypes.temples] then - for _, data in pairs(common.runtimeData.publicHouses[city][publicHouseTypes.temples]) do + if common.runtimeData.publicHouses[city] and common.runtimeData.publicHouses[city][common.publicHouseTypes.temples] then + for _, data in pairs(common.runtimeData.publicHouses[city][common.publicHouseTypes.temples]) do if npc.object.faction == data.proprietor.object.faction then log(common.logLevels.medium, "Picking temple %s for %s based on faction", data.cell.id, npc.object.name) return data.cell @@ -128,104 +68,6 @@ this.pickPublicHouseForNPC = function(npc, city) return this.pickInnForNPC(npc, city) end -this.createHomedNPCTableEntry = function(npc, home, startingPlace, isHome, position, orientation) - if npc.object and (npc.object.name == nil or npc.object.name == "") then return end - - local pickedPosition, pickedOrientation, pos, ori - - -- mod support for different positions in cells - local id = this.checkModdedCell(home.id) - - log(common.logLevels.medium, "Found %s for %s: %s... adding it to in memory table...", - isHome and "home" or "public house", npc.object.name, id) - - if isHome and positions.npcs[npc.object.name] then - pos = positions.npcs[npc.object.name].position - ori = positions.npcs[npc.object.name].orientation - -- pickedPosition = positions.npcs[npc.object.name] and tes3vector3.new(p[1], p[2], p[3]) or zeroVector:copy() - -- pickedOrientation = positions.npcs[npc.object.name] and tes3vector3.new(o[1], o[2], o[3]) or zeroVector:copy() - elseif positions.cells[id] then - pos = table.choice(positions.cells[id]).position - ori = table.choice(positions.cells[id]).orientation - -- pickedPosition = positions.cells[id] and tes3vector3.new(p[1], p[2], p[3]) or zeroVector:copy() - -- pickedOrientation = positions.cells[id] and tes3vector3.new(o[1], o[2], o[3]) or zeroVector:copy() - -- pickedPosition = tes3vector3.new(p[1], p[2], p[3]) - -- pickedOrientation = tes3vector3.new(o[1], o[2], o[3]) - else - pos = {0,0,0} - ori = {0,0,0} - -- pickedPosition = zeroVector:copy() - -- pickedOrientation = zeroVector:copy() - end - - pickedPosition = tes3vector3.new(pos[1], pos[2], pos[3]) - pickedOrientation = tes3vector3.new(ori[1], ori[2], ori[3]) - - local ogPosition = position and - (tes3vector3.new(position.x, position.y, position.z)) or - (npc.position and npc.position:copy() or zeroVector:copy()) - - local ogOrientation = orientation and - (tes3vector3.new(orientation.x, orientation.y, orientation.z)) or - (npc.orientation and npc.orientation:copy() or zeroVector:copy()) - - local this = { - name = npc.object.name, -- string - npc = npc, -- tes3npc - isHome = isHome, -- bool - home = home, -- tes3cell - homeName = home.id, -- string - ogPlace = startingPlace, -- tes3cell - ogPlaceName = startingPlace.id, - ogPosition = ogPosition, -- tes3vector3 - ogOrientation = ogOrientation, -- tes3vector3 - homePosition = pickedPosition, -- tes3vector3 - homeOrientation = pickedOrientation, -- tes3vector3 - worth = this.calculateNPCWorth(npc) -- int - } - - common.runtimeData.homes.byName[npc.object.name] = this - if isHome then common.runtimeData.homes.byCell[home.id] = this end - - interop.setHomedNPCTable(common.runtimeData.homes.byName) - - return this -end - -this.createPublicHouseTableEntry = function(publicCell, proprietor, city, name) - local typeOfPub = this.pickPublicHouseType(publicCell) - - local worth = 0 - - -- for houses, worth is equal to NPC who lives there - -- if typeOfPub == publicHouseTypes.houses then - -- worth = calculateNPCWorth(proprietor) - -- else - -- for other types, worth is combined worth of all NPCs - for innard in publicCell:iterateReferences(tes3.objectType.npc) do - if innard == proprietor then - worth = worth + this.calculateNPCWorth(innard, publicCell) - else - worth = worth + this.calculateNPCWorth(innard) - end - end - -- end - - if not common.runtimeData.publicHouses[city] then common.runtimeData.publicHouses[city] = {} end - if not common.runtimeData.publicHouses[city][typeOfPub] then common.runtimeData.publicHouses[city][typeOfPub] = {} end - - common.runtimeData.publicHouses[city][typeOfPub][publicCell.id] = { - name = name, - city = city, - cell = publicCell, - proprietor = proprietor, - proprietorName = proprietor.object.name, - worth = worth - } - - interop.setPublicHouseTable(common.runtimeData.publicHouses) -end - -- looks through doors to find a cell that matches a wandering NPCs name this.pickHomeForNPC = function(cell, npc) -- wilderness cells don't have name @@ -234,6 +76,9 @@ this.pickHomeForNPC = function(cell, npc) -- don't move contextual, such as Animated Morrowind, NPCs at all for _, str in pairs(contextualNPCs) do if npc.object.id:match(str) then return end end + -- time to pick the "home" + local picked = nil + local name = npc.object.name local city = common.split(cell.name, ",")[1] for door in cell:iterateReferences(tes3.objectType.door) do @@ -243,23 +88,34 @@ this.pickHomeForNPC = function(cell, npc) -- essentially, if npc full name, or surname matches the cell name if dest.id:match(name) or this.livesInManor(dest.name, name) then if common.runtimeData.homes.byName[name] then -- already have a home, don't create the table entry again - return common.runtimeData.homes.byName[name] + picked = common.runtimeData.homes.byName[name] else - return this.createHomedNPCTableEntry(npc, dest, cell, true) + picked = entry.createHomedNPCTableEntry(npc, dest, cell, true) end end end end - -- haven't found a home, so put them in an inn or guildhall + -- haven't found a home, so put them in an inn or guildhall, or inside a canton if config.homelessWanderersToPublicHouses then log(common.logLevels.medium, "Didn't find a home for %s, trying inns", npc.object.name) local dest = this.pickPublicHouseForNPC(npc, city) - -- return createHomedNPCTableEntry(npc, dest, door) - if dest then return this.createHomedNPCTableEntry(npc, dest, cell, false) end + + if dest then picked = entry.createHomedNPCTableEntry(npc, dest, cell, false) end + + -- if nothing was found, then we'll settle on Canton works cell, if the cell is a Canton + if checks.isCantonCell(cell) then + if common.runtimeData.publicHouses[city] and common.runtimeData.publicHouses[city][common.publicHouseTypes.cantonworks] then + -- todo: maybe poorer NPCs in canalworks, others in waistworks ? + local canton = table.choice(common.runtimeData.publicHouses[city][common.publicHouseTypes.cantonworks]) + log(common.logLevels.medium, "Picking works %s, %s for %s", canton.city, canton.name, npc.object.name) + + if canton then picked = entry.createHomedNPCTableEntry(npc, canton.cell, cell, false) end + end + end end - return nil + return picked end return this diff --git a/MWSE/mods/celediel/NPCsGoHome/functions/processors.lua b/MWSE/mods/celediel/NPCsGoHome/functions/processors.lua index 98156da..8b30427 100644 --- a/MWSE/mods/celediel/NPCsGoHome/functions/processors.lua +++ b/MWSE/mods/celediel/NPCsGoHome/functions/processors.lua @@ -3,6 +3,7 @@ local config = require("celediel.NPCsGoHome.config").getConfig() local checks = require("celediel.NPCsGoHome.functions.checks") local interop = require("celediel.NPCsGoHome.interop") local housing = require("celediel.NPCsGoHome.functions.housing") +local entry = require("celediel.NPCsGoHome.functions.entry") local function log(level, ...) if config.logLevel >= level then common.log(...) end end @@ -16,7 +17,7 @@ this.checkForMovedNPCs = function(cell) log(common.logLevels.medium, "Looking for moved NPCs in cell %s", cell.id) for npc in cell:iterateReferences(tes3.objectType.npc) do if npc.data and npc.data.NPCsGoHome then - housing.createHomedNPCTableEntry(npc, cell, tes3.getCell(npc.data.NPCsGoHome.cell), true, npc.data.NPCsGoHome.position, npc.data.NPCsGoHome.orientation) + entry.createHomedNPCTableEntry(npc, cell, tes3.getCell(npc.data.NPCsGoHome.cell), true, npc.data.NPCsGoHome.position, npc.data.NPCsGoHome.orientation) end end end @@ -37,7 +38,7 @@ end this.moveNPC = function(homeData) -- add to in memory table table.insert(common.runtimeData.movedNPCs, homeData) - interop.setMovedNPCTable(common.runtimeData.movedNPCs) + interop.setRuntimeData(common.runtimeData) -- set npc data, so we can move NPCs back after a load local npc = homeData.npc @@ -83,7 +84,7 @@ this.putNPCsBack = function() orientation = data.ogPlace }) end - interop.setMovedNPCTable(common.runtimeData.movedNPCs) + interop.setRuntimeData(common.runtimeData) end this.processNPCs = function(cell) diff --git a/MWSE/mods/celediel/NPCsGoHome/interop.lua b/MWSE/mods/celediel/NPCsGoHome/interop.lua index c2a5568..68700c8 100644 --- a/MWSE/mods/celediel/NPCsGoHome/interop.lua +++ b/MWSE/mods/celediel/NPCsGoHome/interop.lua @@ -1,18 +1,8 @@ local this = {} --- access to the NPCs that have homes or have been assigned a public house -local homedNPCs = {} -this.setHomedNPCTable = function(t) homedNPCs = t end -this.getHomedNPCTable = function() return homedNPCs end - --- access to any cells that have been marked public -local pubs = {} -this.setPublicHouseTable = function(t) pubs = t end -this.getPublicHouseTable = function() return pubs end - --- access to NPCs that have been moved -local moved = {} -this.setMovedNPCTable = function(t) moved = t end -this.getMovedNPCTable = function() return moved end +-- access to runtime data +local runtimeData = {} +this.setRuntimeData = function(t) runtimeData = t end +this.getRuntimeData = function() return runtimeData end return this diff --git a/MWSE/mods/celediel/NPCsGoHome/main.lua b/MWSE/mods/celediel/NPCsGoHome/main.lua index a1664c0..e46bedc 100644 --- a/MWSE/mods/celediel/NPCsGoHome/main.lua +++ b/MWSE/mods/celediel/NPCsGoHome/main.lua @@ -46,14 +46,18 @@ local function checkEnteredNPCHome(cell) end local function checkEnteredPublicHouse(cell, city) - local typeOfPub = housing.pickPublicHouseType(cell) + local typeOfPub = common.pickPublicHouseType(cell) local publicHouse = publicHouses[city] and (publicHouses[city][typeOfPub] and publicHouses[city][typeOfPub][cell.name]) if publicHouse then - local msg = string.format("Entering public space %s, a%s %s in the town of %s. Talk to %s, %s for services.", - publicHouse.name, common.vowel(typeOfPub), typeOfPub:gsub("s$", ""), publicHouse.city, - publicHouse.proprietor.object.name, publicHouse.proprietor.object.class) + local msg = string.format("Entering public space %s, a%s %s in the town of %s.", + publicHouse.name, common.vowel(typeOfPub), typeOfPub:gsub("s$", ""), publicHouse.city) + + if publicHouse.proprietor then + msg = msg .. string.format(" Talk to %s, %s for services.", publicHouse.proprietor.object.name, publicHouse.proprietor.object.class) + end + log(common.logLevels.small, msg) message(msg) -- this one is more informative, and not entirely for debugging, and reminiscent of Daggerfall's messages end @@ -67,7 +71,9 @@ local function applyChanges(cell) if checks.isIgnoredCell(cell) then return end -- Interior cell, except Canton cells, don't do anything - if checks.isInteriorCell(cell) and not (config.waistWorks and checks.isCantonCell(cell.id)) then return end + if checks.isInteriorCell(cell) and not (config.waistWorks == common.waist.exterior and checks.isCantonWorksCell(cell)) then + return + end -- don't do anything to public houses if checks.isPublicHouse(cell) then return end @@ -161,15 +167,18 @@ local function onInitialized() followers = common.runtimeData.followers -- Register events + log(common.logLevels.small, "Registering events...") event.register("loaded", onLoaded) event.register("cellChanged", onCellChanged) event.register("activate", onActivated) - -- MCM - event.register("modConfigReady", function() mwse.mcm.register(require("celediel.NPCsGoHome.mcm")) end) + log(common.logLevels.none, "Successfully initialized") end event.register("initialized", onInitialized) + +-- MCM +event.register("modConfigReady", function() mwse.mcm.register(require("celediel.NPCsGoHome.mcm")) end) -- }}} -- vim:fdm=marker diff --git a/MWSE/mods/celediel/NPCsGoHome/mcm.lua b/MWSE/mods/celediel/NPCsGoHome/mcm.lua index 9ad0b56..7c6972d 100644 --- a/MWSE/mods/celediel/NPCsGoHome/mcm.lua +++ b/MWSE/mods/celediel/NPCsGoHome/mcm.lua @@ -39,12 +39,12 @@ category:createYesNoButton({ }) category:createYesNoButton({ - label = "Move NPCs with homes instead of disabling them?", + label = "Move NPCs into their homes at night and in bad weather instead of disabling them?", variable = createTableVar("moveNPCs") }) category:createYesNoButton({ - label = "Move \"homeless\" NPCs to Inns at night and in bad weather instead of disabling them?", + label = "Move \"homeless\" NPCs to public spaces at night and in bad weather instead of disabling them?", variable = createTableVar("homelessWanderersToPublicHouses") }) @@ -53,8 +53,17 @@ category:createYesNoButton({ variable = createTableVar("disableInteraction") }) -category:createYesNoButton({ - label = "Treat Canton waistworks and canalworks as exteriors (lock doors and disable NPCs)", +category:createDropdown({ + label = "Treat Canton waistworks and canalworks as exteriors, public spaces, or neither", + description = "If canton cells are treated as exterior, inside NPCs will be disabled, and doors will be locked.\n" .. + "If they're treated as public spaces, inside NPCs won't be disabled, and homeless NPCs will be moved inside ".. + "(if configured to do so).\n\nIf neither, canton cells will be treated as any other.", + options = { + {label = "Neither", value = common.waist.neither}, + {label = "Exterior", value = common.waist.exterior}, + {label = "Public", value = common.waist.public}, + }, + defaultSetting = common.waist.neither, variable = createTableVar("waistWorks") })