option to treat waistworks as public + refactoring
This commit is contained in:
parent
1e4c733b0a
commit
ad4a113642
|
@ -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"
|
"and prevent interaction after hours, selectively disable NPCs in inclement weather"
|
||||||
this.configPath = "NPCSGOHOME"
|
this.configPath = "NPCSGOHOME"
|
||||||
|
|
||||||
|
-- for config
|
||||||
this.logLevels = {none = 0, small = 1, medium = 2, large = 3}
|
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
|
-- {{{ Filled at runtime
|
||||||
|
@ -53,6 +58,21 @@ this.vowel = function(str)
|
||||||
|
|
||||||
return n
|
return n
|
||||||
end
|
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
|
return this
|
||||||
|
|
|
@ -22,7 +22,7 @@ local defaultConfig = {
|
||||||
disableInteraction = true,
|
disableInteraction = true,
|
||||||
-- door settings
|
-- door settings
|
||||||
lockDoors = true,
|
lockDoors = true,
|
||||||
waistWorks = true,
|
waistWorks = common.waist.interior,
|
||||||
-- debug settings
|
-- debug settings
|
||||||
logLevel = common.logLevels.none,
|
logLevel = common.logLevels.none,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
local common = require("celediel.NPCsGoHome.common")
|
local common = require("celediel.NPCsGoHome.common")
|
||||||
local config = require("celediel.NPCsGoHome.config").getConfig()
|
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
|
-- {{{ local variables and such
|
||||||
-- Waistworks string match
|
-- Canton string matches
|
||||||
local waistworks = {
|
-- move NPCs into waistworks
|
||||||
"[Cc]analworks", -- These will match Vivec and Molag Mar
|
local waistworks = "[Ww]aistworks"
|
||||||
"[Ww]aistworks" -- and Almas Thirr from Tamriel Rebuilt
|
-- don't lock canalworks
|
||||||
}
|
local canalworks = "[Cc]analworks"
|
||||||
-- these are separate because doors to underworks should be ignored
|
-- doors to underworks should be ignored
|
||||||
-- but NPCs in underworks should not be disabled
|
-- but NPCs in underworks should not be disabled
|
||||||
local underworks = "[Uu]nderworks"
|
local underworks = "[Uu]nderworks"
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@ end
|
||||||
local function getFightFromSpawnedReference(id)
|
local function getFightFromSpawnedReference(id)
|
||||||
-- Spawn a reference of the given id in toddtest
|
-- Spawn a reference of the given id in toddtest
|
||||||
local toddTest = tes3.getCell("toddtest")
|
local toddTest = tes3.getCell("toddtest")
|
||||||
|
|
||||||
log(common.logLevels.medium, "Spawning %s in %s", id, toddTest.id)
|
log(common.logLevels.medium, "Spawning %s in %s", id, toddTest.id)
|
||||||
|
|
||||||
local ref = tes3.createReference({
|
local ref = tes3.createReference({
|
||||||
|
@ -39,7 +38,7 @@ local function getFightFromSpawnedReference(id)
|
||||||
cell = tes3.getPlayerCell(),
|
cell = tes3.getPlayerCell(),
|
||||||
-- position = zeroVector,
|
-- position = zeroVector,
|
||||||
position = {0, 0, 10000},
|
position = {0, 0, 10000},
|
||||||
orientation = housing.zeroVector
|
orientation = tes3vector3.new(0, 0, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
local fight = ref.mobile.fight
|
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
|
return config.ignored[cell.id] or config.ignored[cell.sourceMod] -- or wilderness
|
||||||
end
|
end
|
||||||
|
|
||||||
this.isCantonCell = function(cellName)
|
this.isCantonWorksCell = function(cell)
|
||||||
for _, str in pairs(waistworks) do if cellName:match(str) then return true end end
|
-- 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
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -177,7 +186,7 @@ this.isPublicHouse = function(cell)
|
||||||
-- only interior cells are public "houses"
|
-- only interior cells are public "houses"
|
||||||
if not this.isInteriorCell(cell) then return false end
|
if not this.isInteriorCell(cell) then return false end
|
||||||
|
|
||||||
local typeOfPub = housing.pickPublicHouseType(cell)
|
local typeOfPub = common.pickPublicHouseType(cell)
|
||||||
local city, publicHouseName
|
local city, publicHouseName
|
||||||
|
|
||||||
if cell.name and string.match(cell.name, ",") then
|
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
|
-- 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 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}
|
local npcs = {factions = {}, total = 0}
|
||||||
for npc in cell:iterateReferences(tes3.objectType.npc) do
|
for npc in cell:iterateReferences(tes3.objectType.npc) do
|
||||||
-- Check for NPCS of ignored classes first
|
-- 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,
|
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)
|
||||||
|
|
||||||
housing.createPublicHouseTableEntry(cell, npc, city, publicHouseName)
|
entry.createPublicHouseTableEntry(cell, npc, city, publicHouseName)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -233,7 +248,7 @@ 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)
|
||||||
|
|
||||||
housing.createPublicHouseTableEntry(cell, npcs.factions[faction].master, city, publicHouseName)
|
entry.createPublicHouseTableEntry(cell, npcs.factions[faction].master, city, publicHouseName)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -268,8 +283,8 @@ this.isIgnoredDoor = function(door, homeCellId)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- don't lock doors to underworks in addition to other canton cells
|
-- don't lock doors to canton cells
|
||||||
local isCanton = this.isCantonCell(dest.id) or dest.id:match(underworks)
|
local isCantonWorks = this.isCantonWorksCell(dest)
|
||||||
|
|
||||||
log(common.logLevels.large, "%s is %s, (%sin a city, is %spublic, %soccupied)", --
|
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
|
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
|
return this.isIgnoredCell(dest) or
|
||||||
not this.isInteriorCell(dest) or
|
not this.isInteriorCell(dest) or
|
||||||
isCanton or
|
isCantonWorks or
|
||||||
not inCity or
|
not inCity or
|
||||||
leadsToPublicCell or
|
leadsToPublicCell or
|
||||||
not hasOccupants
|
not hasOccupants
|
||||||
|
|
150
MWSE/mods/celediel/NPCsGoHome/functions/entry.lua
Normal file
150
MWSE/mods/celediel/NPCsGoHome/functions/entry.lua
Normal file
|
@ -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
|
|
@ -1,75 +1,15 @@
|
||||||
local common = require("celediel.NPCsGoHome.common")
|
local common = require("celediel.NPCsGoHome.common")
|
||||||
local config = require("celediel.NPCsGoHome.config").getConfig()
|
local config = require("celediel.NPCsGoHome.config").getConfig()
|
||||||
local interop = require("celediel.NPCsGoHome.interop")
|
local checks = require("celediel.NPCsGoHome.functions.checks")
|
||||||
local positions = require("celediel.NPCsGoHome.data.positions")
|
local entry = require("celediel.NPCsGoHome.functions.entry")
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
local publicHouseTypes = {inns = "Inns", guildhalls = "Guildhalls", temples = "Temples", houses = "Houses"}
|
|
||||||
|
|
||||||
-- animated morrowind NPCs are contextual
|
-- animated morrowind NPCs are contextual
|
||||||
local contextualNPCs = {"^AM_"}
|
local contextualNPCs = {"^AM_"}
|
||||||
|
|
||||||
local this = {}
|
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
|
-- ? 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)
|
this.livesInManor = function(cellName, npcName)
|
||||||
if not cellName or (cellName and not string.find(cellName, "Manor")) then return end
|
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
|
-- ? for others, pick based on value of equipment
|
||||||
|
|
||||||
-- but for now pick one at random
|
-- but for now pick one at random
|
||||||
if common.runtimeData.publicHouses[city] and common.runtimeData.publicHouses[city][publicHouseTypes.inns] then
|
if common.runtimeData.publicHouses[city] and common.runtimeData.publicHouses[city][common.publicHouseTypes.inns] then
|
||||||
local choice = table.choice(common.runtimeData.publicHouses[city][publicHouseTypes.inns])
|
local choice = table.choice(common.runtimeData.publicHouses[city][common.publicHouseTypes.inns])
|
||||||
if not choice then return end
|
if not choice then return nil end
|
||||||
log(common.logLevels.medium, "Picking inn %s, %s for %s", choice.city, choice.name, npc.object.name)
|
log(common.logLevels.medium, "Picking inn %s, %s for %s", choice.city, choice.name, npc.object.name)
|
||||||
return choice.cell
|
return choice.cell
|
||||||
end
|
end
|
||||||
|
@ -104,8 +44,8 @@ end
|
||||||
|
|
||||||
this.pickPublicHouseForNPC = function(npc, city)
|
this.pickPublicHouseForNPC = function(npc, city)
|
||||||
-- look for wandering guild members
|
-- look for wandering guild members
|
||||||
if common.runtimeData.publicHouses[city] and common.runtimeData.publicHouses[city][publicHouseTypes.guildhalls] then
|
if common.runtimeData.publicHouses[city] and common.runtimeData.publicHouses[city][common.publicHouseTypes.guildhalls] then
|
||||||
for _, data in pairs(common.runtimeData.publicHouses[city][publicHouseTypes.guildhalls]) do
|
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's faction and proprietor's faction match, pick that one
|
||||||
if npc.object.faction == data.proprietor.object.faction then
|
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)
|
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
|
end
|
||||||
|
|
||||||
-- temple members go to the temple
|
-- temple members go to the temple
|
||||||
if common.runtimeData.publicHouses[city] and common.runtimeData.publicHouses[city][publicHouseTypes.temples] then
|
if common.runtimeData.publicHouses[city] and common.runtimeData.publicHouses[city][common.publicHouseTypes.temples] then
|
||||||
for _, data in pairs(common.runtimeData.publicHouses[city][publicHouseTypes.temples]) do
|
for _, data in pairs(common.runtimeData.publicHouses[city][common.publicHouseTypes.temples]) do
|
||||||
if npc.object.faction == data.proprietor.object.faction then
|
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)
|
log(common.logLevels.medium, "Picking temple %s for %s based on faction", data.cell.id, npc.object.name)
|
||||||
return data.cell
|
return data.cell
|
||||||
|
@ -128,104 +68,6 @@ this.pickPublicHouseForNPC = function(npc, city)
|
||||||
return this.pickInnForNPC(npc, city)
|
return this.pickInnForNPC(npc, city)
|
||||||
end
|
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
|
-- looks through doors to find a cell that matches a wandering NPCs name
|
||||||
this.pickHomeForNPC = function(cell, npc)
|
this.pickHomeForNPC = function(cell, npc)
|
||||||
-- wilderness cells don't have name
|
-- 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
|
-- 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
|
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 name = npc.object.name
|
||||||
local city = common.split(cell.name, ",")[1]
|
local city = common.split(cell.name, ",")[1]
|
||||||
for door in cell:iterateReferences(tes3.objectType.door) do
|
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
|
-- essentially, if npc full name, or surname matches the cell name
|
||||||
if dest.id:match(name) or this.livesInManor(dest.name, name) then
|
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
|
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
|
else
|
||||||
return this.createHomedNPCTableEntry(npc, dest, cell, true)
|
picked = entry.createHomedNPCTableEntry(npc, dest, cell, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
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
|
if config.homelessWanderersToPublicHouses then
|
||||||
log(common.logLevels.medium, "Didn't find a home for %s, trying inns", npc.object.name)
|
log(common.logLevels.medium, "Didn't find a home for %s, trying inns", npc.object.name)
|
||||||
local dest = this.pickPublicHouseForNPC(npc, city)
|
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
|
end
|
||||||
|
|
||||||
return nil
|
return picked
|
||||||
end
|
end
|
||||||
|
|
||||||
return this
|
return this
|
||||||
|
|
|
@ -3,6 +3,7 @@ local config = require("celediel.NPCsGoHome.config").getConfig()
|
||||||
local checks = require("celediel.NPCsGoHome.functions.checks")
|
local checks = require("celediel.NPCsGoHome.functions.checks")
|
||||||
local interop = require("celediel.NPCsGoHome.interop")
|
local interop = require("celediel.NPCsGoHome.interop")
|
||||||
local housing = require("celediel.NPCsGoHome.functions.housing")
|
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
|
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)
|
log(common.logLevels.medium, "Looking for moved NPCs in cell %s", cell.id)
|
||||||
for npc in cell:iterateReferences(tes3.objectType.npc) do
|
for npc in cell:iterateReferences(tes3.objectType.npc) do
|
||||||
if npc.data and npc.data.NPCsGoHome then
|
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
|
end
|
||||||
end
|
end
|
||||||
|
@ -37,7 +38,7 @@ end
|
||||||
this.moveNPC = function(homeData)
|
this.moveNPC = function(homeData)
|
||||||
-- add to in memory table
|
-- add to in memory table
|
||||||
table.insert(common.runtimeData.movedNPCs, homeData)
|
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
|
-- set npc data, so we can move NPCs back after a load
|
||||||
local npc = homeData.npc
|
local npc = homeData.npc
|
||||||
|
@ -83,7 +84,7 @@ this.putNPCsBack = function()
|
||||||
orientation = data.ogPlace
|
orientation = data.ogPlace
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
interop.setMovedNPCTable(common.runtimeData.movedNPCs)
|
interop.setRuntimeData(common.runtimeData)
|
||||||
end
|
end
|
||||||
|
|
||||||
this.processNPCs = function(cell)
|
this.processNPCs = function(cell)
|
||||||
|
|
|
@ -1,18 +1,8 @@
|
||||||
local this = {}
|
local this = {}
|
||||||
|
|
||||||
-- access to the NPCs that have homes or have been assigned a public house
|
-- access to runtime data
|
||||||
local homedNPCs = {}
|
local runtimeData = {}
|
||||||
this.setHomedNPCTable = function(t) homedNPCs = t end
|
this.setRuntimeData = function(t) runtimeData = t end
|
||||||
this.getHomedNPCTable = function() return homedNPCs end
|
this.getRuntimeData = function() return runtimeData 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
|
|
||||||
|
|
||||||
return this
|
return this
|
||||||
|
|
|
@ -46,14 +46,18 @@ local function checkEnteredNPCHome(cell)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function checkEnteredPublicHouse(cell, city)
|
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])
|
local publicHouse = publicHouses[city] and (publicHouses[city][typeOfPub] and publicHouses[city][typeOfPub][cell.name])
|
||||||
|
|
||||||
if publicHouse then
|
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.",
|
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,
|
publicHouse.name, common.vowel(typeOfPub), typeOfPub:gsub("s$", ""), publicHouse.city)
|
||||||
publicHouse.proprietor.object.name, publicHouse.proprietor.object.class)
|
|
||||||
|
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)
|
log(common.logLevels.small, msg)
|
||||||
message(msg) -- this one is more informative, and not entirely for debugging, and reminiscent of Daggerfall's messages
|
message(msg) -- this one is more informative, and not entirely for debugging, and reminiscent of Daggerfall's messages
|
||||||
end
|
end
|
||||||
|
@ -67,7 +71,9 @@ local function applyChanges(cell)
|
||||||
if checks.isIgnoredCell(cell) then return end
|
if checks.isIgnoredCell(cell) then return end
|
||||||
|
|
||||||
-- Interior cell, except Canton cells, don't do anything
|
-- 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
|
-- don't do anything to public houses
|
||||||
if checks.isPublicHouse(cell) then return end
|
if checks.isPublicHouse(cell) then return end
|
||||||
|
@ -161,15 +167,18 @@ local function onInitialized()
|
||||||
followers = common.runtimeData.followers
|
followers = common.runtimeData.followers
|
||||||
|
|
||||||
-- Register events
|
-- Register events
|
||||||
|
log(common.logLevels.small, "Registering events...")
|
||||||
event.register("loaded", onLoaded)
|
event.register("loaded", onLoaded)
|
||||||
event.register("cellChanged", onCellChanged)
|
event.register("cellChanged", onCellChanged)
|
||||||
event.register("activate", onActivated)
|
event.register("activate", onActivated)
|
||||||
|
|
||||||
-- MCM
|
log(common.logLevels.none, "Successfully initialized")
|
||||||
event.register("modConfigReady", function() mwse.mcm.register(require("celediel.NPCsGoHome.mcm")) end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
event.register("initialized", onInitialized)
|
event.register("initialized", onInitialized)
|
||||||
|
|
||||||
|
-- MCM
|
||||||
|
event.register("modConfigReady", function() mwse.mcm.register(require("celediel.NPCsGoHome.mcm")) end)
|
||||||
-- }}}
|
-- }}}
|
||||||
|
|
||||||
-- vim:fdm=marker
|
-- vim:fdm=marker
|
||||||
|
|
|
@ -39,12 +39,12 @@ category:createYesNoButton({
|
||||||
})
|
})
|
||||||
|
|
||||||
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")
|
variable = createTableVar("moveNPCs")
|
||||||
})
|
})
|
||||||
|
|
||||||
category:createYesNoButton({
|
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")
|
variable = createTableVar("homelessWanderersToPublicHouses")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -53,8 +53,17 @@ category:createYesNoButton({
|
||||||
variable = createTableVar("disableInteraction")
|
variable = createTableVar("disableInteraction")
|
||||||
})
|
})
|
||||||
|
|
||||||
category:createYesNoButton({
|
category:createDropdown({
|
||||||
label = "Treat Canton waistworks and canalworks as exteriors (lock doors and disable NPCs)",
|
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")
|
variable = createTableVar("waistWorks")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue