more refactoring, public cells assigned a faction

This commit is contained in:
Lilian Jónsdóttir 2020-08-26 21:44:56 -07:00
parent 444e607420
commit 6dcf8bb4a8
8 changed files with 125 additions and 59 deletions

View file

@ -22,6 +22,16 @@ this.publicHouseTypes = {
houses = "Houses", houses = "Houses",
cantonworks = "Cantonworks" cantonworks = "Cantonworks"
} }
-- Canton string matches
-- move NPCs into waistworks
this.waistworks = "[Ww]aistworks"
-- don't lock canalworks
this.canalworks = "[Cc]analworks"
-- doors to underworks should be ignored
-- but NPCs in underworks should not be disabled
this.underworks = "[Uu]nderworks"
-- }}} -- }}}
-- {{{ Filled at runtime -- {{{ Filled at runtime
@ -69,13 +79,27 @@ this.vowel = function(str)
return n return n
end end
-- picks the key of the largest value out of a key:whatever, value:number table
this.keyOfLargestValue = function(t)
local picked
local largest = 0
for key, value in pairs(t) do
if value > largest then
largest = value
picked = key
end
end
-- return largest == 0 and nil or largest
return picked
end
-- todo: pick this better -- todo: pick this better
this.pickPublicHouseType = function(cell) this.pickPublicHouseType = function(cell)
if cell.id:match("Guild") then if cell.id:match("Guild") then
return this.publicHouseTypes.guildhalls return this.publicHouseTypes.guildhalls
elseif cell.id:match("Temple") then elseif cell.id:match("Temple") then
return this.publicHouseTypes.temples return this.publicHouseTypes.temples
elseif cell.id:match("[Cc]analworks") or cell.id:match("[Ww]aistworks") then elseif cell.id:match(this.canalworks) or cell.id:match(this.waistworks) then
return this.publicHouseTypes.cantonworks return this.publicHouseTypes.cantonworks
-- elseif cell.id:match("House") then -- elseif cell.id:match("House") then
-- return publicHouseTypes.houses -- return publicHouseTypes.houses
@ -99,6 +123,19 @@ this.checkModdedCell = function(cellId)
return id return id
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(this.waistworks) or cell.id:match(this.canalworks) or cell.id:match(this.underworks)
end
this.isCantonCell = function(cell)
if cell.isInterior and not cell.behavesAsExterior 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
-- }}} -- }}}
return this return this

View file

@ -0,0 +1,52 @@
local common = require("celediel.NPCsGoHome.common")
local config = require("celediel.NPCsGoHome.config").getConfig()
local npcEvaluators = require("celediel.NPCsGoHome.functions.npcEvaluators")
local this = {}
-- todo: logging
local function log(level, ...) if config.logLevel >= level then common.log(...) end end
this.calculateCellWorth = function(cell, proprietor)
-- cell worth is combined worth of all NPCs
local worth = 0
for innard in cell:iterateReferences(tes3.objectType.npc) do
worth = worth + npcEvaluators.calculateNPCWorth(innard, innard == proprietor and cell or nil).total
end
return worth
end
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}
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].master or npcs.allFactions[faction].master.object.factionIndex <
npc.object.factionIndex then npcs.allFactions[faction].master = npc end
npcs.allFactions[faction].total = npcs.allFactions[faction].total + 1
end
npcs.total = npcs.total + 1
end
for faction, 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
end
end
-- no faction
return table.empty(npcs.majorityFactions) and "none" or common.keyOfLargestValue(npcs.majorityFactions)
end
return this

View file

@ -2,16 +2,9 @@
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 dataTables = require("celediel.NPCsGoHome.functions.dataTables") local dataTables = require("celediel.NPCsGoHome.functions.dataTables")
local cellEvaluators = require("celediel.NPCsGoHome.functions.cellEvaluators")
-- {{{ local variables and such -- {{{ local variables and such
-- 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"
-- city name if cell.name is nil -- city name if cell.name is nil
local wilderness = "Wilderness" local wilderness = "Wilderness"
@ -22,7 +15,7 @@ local function log(level, ...) if config.logLevel >= level then common.log(...)
-- patented by Merlord -- patented by Merlord
local yeet = function(reference) local yeet = function(reference)
-- tes3.positionCell({reference = reference, position = {0, 0, 10000}}) tes3.positionCell({reference = reference, position = {0, 0, 10000}})
reference:disable() reference:disable()
timer.delayOneFrame(function() mwscript.setDelete({reference = reference}) end) timer.delayOneFrame(function() mwscript.setDelete({reference = reference}) end)
end end
@ -35,10 +28,10 @@ local function getFightFromSpawnedReference(id)
local ref = tes3.createReference({ local ref = tes3.createReference({
object = id, object = id,
-- cell = toddTest, cell = toddTest,
cell = tes3.getPlayerCell(), -- cell = tes3.getPlayerCell(),
-- position = zeroVector, position = tes3vector3.new(0, 0, 0),
position = {0, 0, 10000}, -- position = {0, 0, 10000},
orientation = tes3vector3.new(0, 0, 0) orientation = tes3vector3.new(0, 0, 0)
}) })
@ -97,19 +90,6 @@ 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.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
-- ! this one depends on tes3 ! -- -- ! this one depends on tes3 ! --
this.fargothCheck = function() this.fargothCheck = function()
local fargothJournal = tes3.getJournalIndex({id = "MS_Lookout"}) local fargothJournal = tes3.getJournalIndex({id = "MS_Lookout"})
@ -186,6 +166,8 @@ this.isPublicHouse = function(cell)
if not this.isInteriorCell(cell) then return false end if not this.isInteriorCell(cell) then return false end
-- gather some data about the cell -- gather some data about the cell
local worth = cellEvaluators.calculateCellWorth(cell)
local faction = cellEvaluators.pickCellFaction(cell)
local typeOfPub = common.pickPublicHouseType(cell) local typeOfPub = common.pickPublicHouseType(cell)
local city, publicHouseName local city, publicHouseName
@ -204,8 +186,8 @@ this.isPublicHouse = function(cell)
end end
-- if it's a waistworks cell, it's public, with no proprietor -- if it's a waistworks cell, it's public, with no proprietor
if config.waistWorks == common.waist.public and cell.id:match(waistworks) then if config.waistWorks == common.waist.public and cell.id:match(common.waistworks) then
dataTables.createPublicHouseTableEntry(cell, nil, city, publicHouseName) dataTables.createPublicHouseTableEntry(cell, nil, city, publicHouseName, worth, faction)
return true return true
end end
@ -217,8 +199,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)
dataTables.createPublicHouseTableEntry(cell, npc, city, publicHouseName) dataTables.createPublicHouseTableEntry(cell, npc, city, publicHouseName, worth, faction)
return true return true
end end
@ -251,7 +232,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)
dataTables.createPublicHouseTableEntry(cell, npcs.factions[faction].master, city, publicHouseName) dataTables.createPublicHouseTableEntry(cell, npcs.factions[faction].master, city, publicHouseName, worth, faction)
return true return true
end end
end end
@ -287,7 +268,7 @@ this.isIgnoredDoor = function(door, homeCellId)
end end
-- don't lock doors to canton cells -- don't lock doors to canton cells
local isCantonWorks = this.isCantonWorksCell(dest) local isCantonWorks = common.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

View file

@ -3,7 +3,7 @@ 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 interop = require("celediel.NPCsGoHome.interop")
local positions = require("celediel.NPCsGoHome.data.positions") local positions = require("celediel.NPCsGoHome.data.positions")
local evaluators = require("celediel.NPCsGoHome.functions.npcEvaluators") local npcEvaluators = require("celediel.NPCsGoHome.functions.npcEvaluators")
local zeroVector = tes3vector3.new(0, 0, 0) local zeroVector = tes3vector3.new(0, 0, 0)
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
@ -13,18 +13,20 @@ local this = {}
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
-- mod support for different positions in cells -- mod support for different positions in cells
local id = common.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)
-- pick the position and orientation the NPC will be placed at
local pickedPosition, pickedOrientation, pos, ori
if isHome and positions.npcs[npc.object.name] then if isHome and positions.npcs[npc.object.name] then
pos = positions.npcs[npc.object.name].position pos = positions.npcs[npc.object.name].position
ori = positions.npcs[npc.object.name].orientation ori = positions.npcs[npc.object.name].orientation
elseif not table.empty(common.runtimeData.positions[id]) then elseif 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]) local choice, index = table.choice(common.runtimeData.positions[id])
pos = choice.position pos = choice.position
ori = choice.orientation ori = choice.orientation
@ -57,7 +59,7 @@ this.createHomedNPCTableEntry = function(npc, home, startingPlace, isHome, posit
ogOrientation = ogOrientation, -- tes3vector3 ogOrientation = ogOrientation, -- tes3vector3
homePosition = pickedPosition, -- tes3vector3 homePosition = pickedPosition, -- tes3vector3
homeOrientation = pickedOrientation, -- tes3vector3 homeOrientation = pickedOrientation, -- tes3vector3
worth = evaluators.calculateNPCWorth(npc) -- int worth = npcEvaluators.calculateNPCWorth(npc) -- int
} }
common.runtimeData.homes.byName[npc.object.name] = entry common.runtimeData.homes.byName[npc.object.name] = entry
@ -68,20 +70,9 @@ this.createHomedNPCTableEntry = function(npc, home, startingPlace, isHome, posit
return entry return entry
end end
this.createPublicHouseTableEntry = function(publicCell, proprietor, city, name) this.createPublicHouseTableEntry = function(publicCell, proprietor, city, name, cellWorth, cellFaction)
local typeOfPub = common.pickPublicHouseType(publicCell) 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 + evaluators.calculateNPCWorth(innard, publicCell).total
else
worth = worth + evaluators.calculateNPCWorth(innard).total
end
end
local proprietorName = proprietor and proprietor.object.name or "no one" 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] then common.runtimeData.publicHouses[city] = {} end
@ -96,7 +87,8 @@ this.createPublicHouseTableEntry = function(publicCell, proprietor, city, name)
cell = publicCell, cell = publicCell,
proprietor = proprietor, proprietor = proprietor,
proprietorName = proprietorName, proprietorName = proprietorName,
worth = worth worth = cellWorth,
faction = cellFaction
} }
interop.setRuntimeData(common.runtimeData) interop.setRuntimeData(common.runtimeData)

View file

@ -1,7 +1,8 @@
-- handles finding homes or picking public spaces NPCs -- handles finding homes or picking public spaces for NPCs
-- * home cell is chosen according to the following order:
-- * NPC's actual home, i.e.: home cell name contains NPC name
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 checks = require("celediel.NPCsGoHome.functions.checks")
local dataTables = require("celediel.NPCsGoHome.functions.dataTables") local dataTables = require("celediel.NPCsGoHome.functions.dataTables")
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
@ -31,8 +32,6 @@ this.pickInnForNPC = function(npc, city)
-- high class inns for nobles and rich merchants and such -- high class inns for nobles and rich merchants and such
-- lower class inns for middle class npcs and merchants -- lower class inns for middle class npcs and merchants
-- temple for commoners and the poorest people -- temple for commoners and the poorest people
-- ? pick based on barterGold and value of equipment for merchants ?
-- ? 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][common.publicHouseTypes.inns] then if common.runtimeData.publicHouses[city] and common.runtimeData.publicHouses[city][common.publicHouseTypes.inns] then
@ -81,6 +80,8 @@ this.pickHomeForNPC = function(cell, npc)
-- time to pick the "home" -- time to pick the "home"
local name = npc.object.name local name = npc.object.name
local city = common.split(cell.name, ",")[1] local city = common.split(cell.name, ",")[1]
-- check if the NPC already has a house
for door in cell:iterateReferences(tes3.objectType.door) do for door in cell:iterateReferences(tes3.objectType.door) do
if door.destination then if door.destination then
local dest = door.destination.cell local dest = door.destination.cell
@ -102,10 +103,9 @@ this.pickHomeForNPC = function(cell, npc)
if dest then return 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 common.isCantonCell(cell) then
if common.runtimeData.publicHouses[city] and if common.runtimeData.publicHouses[city] and
common.runtimeData.publicHouses[city][common.publicHouseTypes.cantonworks] then 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]) 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)

View file

@ -1,6 +1,9 @@
-- handles evalutating NPCs -- handles evaluating NPCs
local this = {} local this = {}
-- todo: logging
local function log(level, ...) if config.logLevel >= level then common.log(...) end end
-- NPCs barter gold + value of all inventory items -- NPCs barter gold + value of all inventory items
this.calculateNPCWorth = function(npc, merchantCell) this.calculateNPCWorth = function(npc, merchantCell)
-- start with this -- start with this
@ -33,7 +36,7 @@ this.calculateNPCWorth = function(npc, merchantCell)
end end
end end
-- caculate the total -- calculate the total
local total = 0 local total = 0
for _, v in pairs(worth) do total = total + v end for _, v in pairs(worth) do total = total + v end

View file

@ -11,6 +11,7 @@ local function log(level, ...) if config.logLevel >= level then common.log(...)
local this = {} local this = {}
-- create an in memory list of positions for a cell, to ensure multiple NPCs aren't placed in the same spot
this.updatePositions = function(cell) this.updatePositions = function(cell)
local id = cell.id local id = cell.id
-- update runtime positions in cell, but don't overwrite loaded positions -- update runtime positions in cell, but don't overwrite loaded positions

View file

@ -1,5 +1,5 @@
-- {{{ other files -- {{{ other files
-- ? could probably split this file out to others as well -- todo: too many things require too many other things, needs fix
local config = require("celediel.NPCsGoHome.config").getConfig() local config = require("celediel.NPCsGoHome.config").getConfig()
local common = require("celediel.NPCsGoHome.common") local common = require("celediel.NPCsGoHome.common")
local checks = require("celediel.NPCsGoHome.functions.checks") local checks = require("celediel.NPCsGoHome.functions.checks")