diff --git a/MWSE/mods/celediel/NPCsGoHome/common.lua b/MWSE/mods/celediel/NPCsGoHome/common.lua index e4e5be4..bdcc1c7 100644 --- a/MWSE/mods/celediel/NPCsGoHome/common.lua +++ b/MWSE/mods/celediel/NPCsGoHome/common.lua @@ -7,6 +7,7 @@ this.version = "0.0.1" this.modInfo = "Move NPCs to their homes, or public houses (or just disable them), lock doors, " .. "and prevent interaction after hours, selectively disable NPCs in inclement weather" this.configPath = "NPCSGOHOME" +this.logString = this.modName:gsub("%s?%b()%s?","") -- for config this.logLevels = {none = 0, small = 1, medium = 2, large = 3} @@ -45,6 +46,8 @@ this.runtimeData = { }, -- NPCs who have been moved movedNPCs = {}, + -- NPCs who have been disabled + disabledNPCs = {}, -- positions that haven't been used positions = {}, -- player companions @@ -61,10 +64,10 @@ this.split = function(input, sep) return output end -this.log = function(...) mwse.log("[%s] %s", this.modName, string.format(...)) end +this.log = function(...) mwse.log("[%s] %s", this.logString, string.format(...)) end -this.vowel = function(str) - local s = string.sub(str, 1, 1) +this.vowel = function(word) + local s = string.sub(word, 1, 1) local n = "" if string.match(s, "[AOEUIaoeui]") then n = "n" end diff --git a/MWSE/mods/celediel/NPCsGoHome/functions/cellEvaluators.lua b/MWSE/mods/celediel/NPCsGoHome/functions/cellEvaluators.lua index b126ed1..3e77f89 100644 --- a/MWSE/mods/celediel/NPCsGoHome/functions/cellEvaluators.lua +++ b/MWSE/mods/celediel/NPCsGoHome/functions/cellEvaluators.lua @@ -52,7 +52,7 @@ this.pickCellFaction = function(cell) end end - -- from the majority values, return the faction with the largest percentage, or "none" + -- from the majority values, return the faction with the largest percentage, or nil local picked = common.keyOfLargestValue(npcs.majorityFactions) log(common.logLevels.medium, "Picked faction %s for cell %s", picked, cell.id) log(common.logLevels.large, "breakdown:\n%s", json.encode(npcs, {indent = true})) diff --git a/MWSE/mods/celediel/NPCsGoHome/functions/checks.lua b/MWSE/mods/celediel/NPCsGoHome/functions/checks.lua index f8a1163..fd2c32f 100644 --- a/MWSE/mods/celediel/NPCsGoHome/functions/checks.lua +++ b/MWSE/mods/celediel/NPCsGoHome/functions/checks.lua @@ -102,6 +102,14 @@ this.fargothCheck = function() return fargothJournal > 10 and fargothJournal <= 30 end +this.offersTravel = function(npc) + if not npc.object.aiConfig.travelDestinations then return false end + + for _ in tes3.iterate(npc.object.aiConfig.travelDestinations) do return true end + + return false +end + this.isIgnoredNPC = function(npc) local obj = npc.baseObject and npc.baseObject or npc.object @@ -129,33 +137,37 @@ this.isIgnoredNPC = function(npc) local isWerewolf = mwscript.getSpellEffects({reference = npc, spell = "werewolf vision"}) -- local isVampire = mwscript.getSpellEffects({reference = npc, spell = "vampire sun damage"}) + -- LuaFormatter off -- this just keeps getting uglier but it's debug logging so whatever I don't care - log(common.logLevels.large, ("Checking NPC:%s (%s or %s): id blocked:%s, %s blocked:%s " .. -- - "guard:%s dead:%s vampire:%s werewolf:%s dreamer:%s follower:%s hostile:%s %s%s"), -- - obj.name, npc.object.id, npc.object.baseObject and npc.object.baseObject.id or "nil", -- - config.ignored[obj.id:lower()], obj.sourceMod, config.ignored[obj.sourceMod:lower()], -- - isGuard, isDead, isVampire, isWerewolf, (obj.class and obj.class.id == "Dreamers"), -- - common.runtimeData.followers[npc.object.id], isHostile, obj.id:match("fargoth") and "fargoth:" or "", -- + log(common.logLevels.large, ("Checking NPC:%s (%s or %s): id blocked:%s, %s blocked:%s " .. + "guard:%s dead:%s vampire:%s werewolf:%s dreamer:%s follower:%s hostile:%s %s%s"), + obj.name, npc.object.id, npc.object.baseObject and npc.object.baseObject.id or "nil", + config.ignored[obj.id:lower()], obj.sourceMod, config.ignored[obj.sourceMod:lower()], + isGuard, isDead, isVampire, isWerewolf, (obj.class and obj.class.id == "Dreamers"), + common.runtimeData.followers[npc.object.id], isHostile, obj.id:match("fargoth") and "fargoth:" or "", obj.id:match("fargoth") and isFargothActive or "") - return config.ignored[obj.id:lower()] or -- - config.ignored[obj.sourceMod:lower()] or -- - isGuard or -- - isFargothActive or -- - isDead or -- don't move dead NPCS - isHostile or -- - common.runtimeData.followers[npc.object.id] or -- ignore followers - isVampire or -- - isWerewolf or -- - (obj.class and obj.class.id == "Dreamers") -- + return config.ignored[obj.id:lower()] or + config.ignored[obj.sourceMod:lower()] or + isGuard or + isFargothActive or + isDead or + isHostile or + common.runtimeData.followers[npc.object.id] or + isVampire or + isWerewolf or + (obj.class and obj.class.id == "Dreamers") + -- LuaFormatter on end -this.isNPCPet = function(creature) +this.isNPCPet = function(creature) -- > isPet, isLinkedToTravelNPC local obj = creature.baseObject and creature.baseObject or creature.object -- todo: more pets? if obj.id:match("guar") and obj.mesh:match("pack") then return true + elseif obj.id:match("_[Hh]rs") and obj.mesh:match("_[Hh]orse") then + return true, true else return false end @@ -230,10 +242,13 @@ this.isPublicHouse = function(cell) cell.name, faction, config.ignored[faction.id], faction.playerJoined, info.total, info.percentage, npcs.total) - -- less than 3 NPCs can't possibly be a public house unless it's a Blades house + -- log(common.logLevels.large, "ignored or joined:%s, occupants or blades:%s, faction percent:%s", (config.ignored[faction.id] or faction.playerJoined), + -- (npcs.total >= config.minimumOccupancy or faction == "Blades"), (info.percentage >= config.factionIgnorePercentage)) + + -- less than configured amount of NPCs can't be a public house unless it's a Blades house if (config.ignored[faction.id] or faction.playerJoined) and - (npcs.total >= config.minimumOccupancy or faction == "Blades") and info.percentage >= - config.factionIgnorePercentage then + (npcs.total >= config.minimumOccupancy or faction == "Blades") and + (info.percentage >= config.factionIgnorePercentage) then log(common.logLevels.medium, "%s is %s%% faction %s, marking public.", cell.name, info.percentage, faction) dataTables.createPublicHouseTableEntry(cell, npcs.factions[faction].master, city, publicHouseName, @@ -286,35 +301,33 @@ this.isIgnoredDoor = function(door, homeCellId) end -- AT NIGHT -this.checkTime = function() - local night = tes3.worldController.hour.value >= config.closeTime or tes3.worldController.hour.value <= config.openTime +this.isNight = function() + local atNight = tes3.worldController.hour.value >= config.closeTime or tes3.worldController.hour.value <= config.openTime log(common.logLevels.large, "Current time is %.2f (%snight), things are closed between %s and %s", - tes3.worldController.hour.value, night and "" or "not ", config.closeTime, config.openTime) - return night + tes3.worldController.hour.value, atNight and "" or "not ", config.closeTime, config.openTime) + + return atNight end -- inclement weather -this.checkWeather = function(cell) - if not cell.region then return end +this.isInclementWeather = function(cell) + if not cell.region then return false end + -- local index = cell.region.weather.index + local index = tes3.getCurrentWeather().index + local isBad = index >= config.worstWeather - log(common.logLevels.large, "Weather: current:%s >= configured worst:%s == %s", cell.region.weather.index, - config.worstWeather, cell.region.weather.index >= config.worstWeather) + log(common.logLevels.large, "Weather in %s: current:%s >= configured worst:%s == %s", cell.id, index, + config.worstWeather, isBad) - return cell.region.weather.index >= config.worstWeather + return isBad end -- travel agents, their steeds, and argonians stick around this.isBadWeatherNPC = function(npc) - local obj = npc.baseObject and npc.baseObject or npc.object - if not obj then return end + log(common.logLevels.large, "NPC Inclement Weather: %s is %s%s", npc.object.name, npc.object.race.id, + this.offersTravel(npc) and ", travel agent" or "") - log(common.logLevels.large, "NPC Inclement Weather: %s is %s, %s", npc.object.name, npc.object.class.name, - npc.object.race.id) - - -- todo: better detection of NPCs who offer travel services - -- found a rogue "shipmaster" in molag mar - return obj.class.name == "Caravaner" or obj.class.name == "Gondolier" or obj.class.name == "Shipmaster" or - obj.race.id == "Argonian" + return this.offersTravel(npc) or npc.object.race.id == "Argonian" end return this diff --git a/MWSE/mods/celediel/NPCsGoHome/main.lua b/MWSE/mods/celediel/NPCsGoHome/main.lua index b56de0d..74d83d0 100644 --- a/MWSE/mods/celediel/NPCsGoHome/main.lua +++ b/MWSE/mods/celediel/NPCsGoHome/main.lua @@ -11,9 +11,6 @@ local processors = require("celediel.NPCsGoHome.functions.processors") -- timers local updateTimer --- references to common.runtimeData -local publicHouses, homes, movedNPCs, followers - -- }}} -- {{{ helper functions @@ -36,7 +33,7 @@ end -- {{{ cell change checks local function checkEnteredNPCHome(cell) - local home = homes.byCell[cell.id] + local home = common.runtimeData.homes.byCell[cell.id] if home then local msg = string.format("Entering home of %s, %s", home.name, home.homeName) log(common.logLevels.small, msg) @@ -47,8 +44,8 @@ end local function checkEnteredPublicHouse(cell, city) local typeOfPub = common.pickPublicHouseType(cell) - local publicHouse = publicHouses[city] and - (publicHouses[city][typeOfPub] and publicHouses[city][typeOfPub][cell.name]) + local publicHouse = common.runtimeData.publicHouses[city] and + (common.runtimeData.publicHouses[city][typeOfPub] and common.runtimeData.publicHouses[city][typeOfPub][cell.name]) if publicHouse then local msg = string.format("Entering public space %s, a%s %s in the town of %s.", publicHouse.name, @@ -91,7 +88,7 @@ end local function updateCells() log(common.logLevels.medium, "Updating active cells!") - followers = buildFollowerList() + common.runtimeData.followers = buildFollowerList() processors.searchCellsForPositions() for _, cell in pairs(tes3.getActiveCells()) do @@ -106,7 +103,7 @@ local function updatePlayerTrespass(cell, previousCell) local inCity = previousCell and (previousCell.id:match(cell.id) or cell.id:match(previousCell.id)) if checks.isInteriorCell(cell) and not checks.isIgnoredCell(cell) and not checks.isPublicHouse(cell) and inCity then - if checks.checkTime() then + if checks.isNight() then tes3.player.data.NPCsGoHome.intruding = true else tes3.player.data.NPCsGoHome.intruding = false @@ -133,11 +130,9 @@ end local function onLoaded() tes3.player.data.NPCsGoHome = tes3.player.data.NPCsGoHome or {} - -- tes3.player.data.NPCsGoHome.movedNPCs = tes3.player.data.NPCsGoHome.movedNPCs or {} - -- movedNPCs = tes3.player.data.NPCsGoHome.movedNPCs or {} if tes3.player.cell then processors.searchCellsForNPCs() end - followers = buildFollowerList() + common.runtimeData.followers = buildFollowerList() if not updateTimer or (updateTimer and updateTimer.state ~= timer.active) then updateTimer = timer.start({ @@ -151,7 +146,7 @@ end local function onCellChanged(e) updateCells() - followers = buildFollowerList() + common.runtimeData.followers = buildFollowerList() updatePlayerTrespass(e.cell, e.previousCell) checkEnteredNPCHome(e.cell) if e.cell.name then -- exterior wilderness cells don't have name @@ -166,7 +161,7 @@ local function onKeyDown(e) -- log(common.logLevels.small, common.inspect(common.runtimeData)) log(common.logLevels.none, json.encode(common.runtimeData, { indent = true })) end - -- if ctrl log position data formattet for positions.lua + -- if ctrl log position data formatted for positions.lua if e.isControlDown then log(common.logLevels.none, "{position = %s, orientation = %s,", tes3.player.position, tes3.player.orientation) end @@ -176,12 +171,6 @@ end -- {{{ init local function onInitialized() - -- set up runtime data references - publicHouses = common.runtimeData.publicHouses - homes = common.runtimeData.homes - movedNPCs = common.runtimeData.movedNPCs - followers = common.runtimeData.followers - -- Register events log(common.logLevels.small, "Registering events...") event.register("loaded", onLoaded)