don't move multiple NPCs into the same spot

This commit is contained in:
Lilian Jónsdóttir 2020-08-23 23:54:58 -07:00
parent 4ed6648ca8
commit ec2a570b30
6 changed files with 77 additions and 41 deletions

View file

@ -29,6 +29,8 @@ this.runtimeData = {
}, },
-- NPCs who have been moved -- NPCs who have been moved
movedNPCs = {}, movedNPCs = {},
-- positions that haven't been used
positions = {},
-- player companions -- player companions
followers = {} followers = {}
} }
@ -73,6 +75,22 @@ this.pickPublicHouseType = function(cell)
return this.publicHouseTypes.inns return this.publicHouseTypes.inns
end end
end 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
-- }}} -- }}}
return this return this

View file

@ -37,29 +37,13 @@ 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) this.createHomedNPCTableEntry = function(npc, home, startingPlace, isHome, position, orientation)
if npc.object and (npc.object.name == nil or npc.object.name == "") then return end if npc.object and (npc.object.name == nil or npc.object.name == "") then return end
local pickedPosition, pickedOrientation, pos, ori local pickedPosition, pickedOrientation, pos, ori
-- mod support for different positions in cells -- mod support for different positions in cells
local id = this.checkModdedCell(home.id) local id = common.checkModdedCell(home.id)
log(common.logLevels.medium, "Found %s for %s: %s... adding it to in memory table...", 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) isHome and "home" or "public house", npc.object.name, id)
@ -67,22 +51,21 @@ 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
-- pickedPosition = positions.npcs[npc.object.name] and tes3vector3.new(p[1], p[2], p[3]) or zeroVector:copy() -- elseif positions.cells[id] then
-- pickedOrientation = positions.npcs[npc.object.name] and tes3vector3.new(o[1], o[2], o[3]) or zeroVector:copy() elseif common.runtimeData.positions[id] then
elseif positions.cells[id] then -- pos = table.choice(positions.cells[id]).position
pos = table.choice(positions.cells[id]).position -- ori = table.choice(positions.cells[id]).orientation
ori = table.choice(positions.cells[id]).orientation local choice, index = table.choice(common.runtimeData.positions[id])
-- pickedPosition = positions.cells[id] and tes3vector3.new(p[1], p[2], p[3]) or zeroVector:copy() pos = choice.position
-- pickedOrientation = positions.cells[id] and tes3vector3.new(o[1], o[2], o[3]) or zeroVector:copy() ori = choice.orientation
-- pickedPosition = tes3vector3.new(p[1], p[2], p[3]) table.remove(common.runtimeData.positions[id], index)
-- pickedOrientation = tes3vector3.new(o[1], o[2], o[3])
else else
pos = {0,0,0} pos = {0,0,0}
ori = {0,0,0} ori = {0,0,0}
-- pickedPosition = zeroVector:copy()
-- pickedOrientation = zeroVector:copy()
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])

View file

@ -45,7 +45,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][common.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][common.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
@ -78,8 +79,6 @@ this.pickHomeForNPC = function(cell, npc)
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" -- 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
@ -88,11 +87,9 @@ 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 dataTables again -- already have a home, don't create the table dataTables again
picked = common.runtimeData.homes.byName[name] return common.runtimeData.homes.byName[name] and common.runtimeData.homes.byName[name] or
else dataTables.createHomedNPCTableEntry(npc, dest, cell, true)
picked = dataTables.createHomedNPCTableEntry(npc, dest, cell, true)
end
end end
end end
end end
@ -102,21 +99,23 @@ this.pickHomeForNPC = function(cell, npc)
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)
if dest then picked = dataTables.createHomedNPCTableEntry(npc, dest, cell, false) end if dest then return dataTables.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 nothing was found, then we'll settle on Canton works cell, if the cell is a Canton
if checks.isCantonCell(cell) then if checks.isCantonCell(cell) then
if common.runtimeData.publicHouses[city] and common.runtimeData.publicHouses[city][common.publicHouseTypes.cantonworks] then if common.runtimeData.publicHouses[city] and
common.runtimeData.publicHouses[city][common.publicHouseTypes.cantonworks] then
-- todo: maybe poorer NPCs in canalworks, others in waistworks ? -- todo: maybe poorer NPCs in canalworks, others in waistworks ?
local canton = table.choice(common.runtimeData.publicHouses[city][common.publicHouseTypes.cantonworks]) 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) log(common.logLevels.medium, "Picking works %s, %s for %s", canton.city, canton.name, npc.object.name)
if canton then picked = dataTables.createHomedNPCTableEntry(npc, canton.cell, cell, false) end if canton then return dataTables.createHomedNPCTableEntry(npc, canton.cell, cell, false) end
end end
end end
end end
return picked -- didn't find anything
return nil
end end
return this return this

View file

@ -5,11 +5,40 @@ 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 dataTables = require("celediel.NPCsGoHome.functions.dataTables") local dataTables = require("celediel.NPCsGoHome.functions.dataTables")
local positions = require("celediel.NPCsGoHome.data.positions")
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 this = {} local this = {}
this.updatePositions = function(cell)
local id = cell.id
-- update runtime positions in cell, but don't overwrite loaded positions
if not common.runtimeData.positions[id] and positions.cells[id] then
common.runtimeData.positions[id] = {}
for _, data in pairs(positions.cells[id]) do
table.insert(common.runtimeData.positions[id], data)
end
end
end
this.searchCellsForPositions = function()
for _, cell in pairs(tes3.getActiveCells()) do
-- check active cells
this.updatePositions(cell)
for door in cell:iterateReferences(tes3.objectType.door) do
if door.destination then
-- then check cells attached to active cells
this.updatePositions(door.destination.cell)
-- one more time
for internalDoor in door.destination.cell:iterateReferences(tes3.objectType.door) do
if internalDoor.destination then this.updatePositions(internalDoor.destination.cell) end
end
end
end
end
end
-- search in a specific cell for moved NPCs -- search in a specific cell for moved NPCs
this.checkForMovedNPCs = function(cell) this.checkForMovedNPCs = function(cell)
-- NPCs don't get moved to exterior cells, so no need to check them for moved NPCs -- NPCs don't get moved to exterior cells, so no need to check them for moved NPCs

View file

@ -91,6 +91,7 @@ local function updateCells()
log(common.logLevels.medium, "Updating active cells!") log(common.logLevels.medium, "Updating active cells!")
followers = buildFollowerList() followers = buildFollowerList()
processors.searchCellsForPositions()
for _, cell in pairs(tes3.getActiveCells()) do for _, cell in pairs(tes3.getActiveCells()) do
log(common.logLevels.large, "Applying changes to cell %s", cell.id) log(common.logLevels.large, "Applying changes to cell %s", cell.id)

View file

@ -16,6 +16,7 @@ ignored class or faction will not be locked, or have its NPCS disabled.
* NPC "homes" * NPC "homes"
* Outside NPCs who have homes are currently paired with the inside cell of their home * Outside NPCs who have homes are currently paired with the inside cell of their home
* Other NPCs are configurably paired with local public houses (Inns, temples, and guildhalls of their faction) * Other NPCs are configurably paired with local public houses (Inns, temples, and guildhalls of their faction)
* Moved NPCs persist on save/load
## WIP ## ## WIP ##
@ -29,3 +30,8 @@ ignored class or faction will not be locked, or have its NPCS disabled.
* move non-faction NPCs who don't have homes to temples or inns based on their "worth" * move non-faction NPCs who don't have homes to temples or inns based on their "worth"
* pick temple for the poorest NPCs, or classed inns based on NPC/inn "worth" * pick temple for the poorest NPCs, or classed inns based on NPC/inn "worth"
## Known issues ##
* If NPCs in a town are moved, and the player moves far away from that town before they're moved back, then
saves and reloads, those NPCs will probably stay moved.