Skip to content

Instantly share code, notes, and snippets.

@celediel
Last active March 9, 2022 01:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save celediel/0a14baeaf8c0bc4fb8c75f9c56a67798 to your computer and use it in GitHub Desktop.
Save celediel/0a14baeaf8c0bc4fb8c75f9c56a67798 to your computer and use it in GitHub Desktop.
Morrowind - No More Friendly Fire
-- mod info variables
local modName = "No More Friendly Fire"
local modConfig = modName:gsub("%s", "")
local modInfo = "Stop friendly fire. Player companions can't damage the player, the player " ..
"can't damage companions, and companions can't damage each other. That's it."
local author = "Celediel"
local version = "1.2.0"
-- config
local defaultConfig = {enable = true, debug = false}
local config = mwse.loadConfig(modConfig, defaultConfig)
local mag
pcall(function()
mag = require("celediel.MoreAttentiveGuards.interop")
end)
-- todo: make this not hardcoded somehow
local followMatches = {"follow", "together", "travel", "wait", "stay"}
local postDialogueTimer
local function log(...) if config.debug then mwse.log("[%s] %s", modName, string.format(...)) end end
-- keep track of followers
local followers = {}
local function buildFollowerList()
local friends = {}
local msg = ""
local magGuard = mag and mag.getGuardFollower() or nil
for friend in tes3.iterate(tes3.mobilePlayer.friendlyActors) do
if friend ~= magGuard then
friends[friend.object.id] = true
msg = msg .. friend.object.name .. " "
end
end
log("Friends: %s", msg)
return friends
end
-- Event functions
local eventFunctions = {}
eventFunctions.onDamage = function(e)
if not e.attackerReference then return end
if followers[e.attackerReference.object.id] and followers[e.reference.object.id] then
if config.enable then
log("%s hit %s for %s friendly damage, nullifying", e.attackerReference.object.name,
e.reference.object.name, e.damage)
e.damage = 0
return false -- I don't know if this makes a difference or not
else
log("%s hit %s for %s friendly damage", e.attackerReference.object.name, e.reference.object.name, e.damage)
end
-- uncomment this to see all damage done by everyone to everyone else
-- else
-- log("%s hit %s for %s damage", e.attackerReference.object.name, e.reference.object.name, e.damage)
end
end
eventFunctions.onCellChanged = function(e) followers = buildFollowerList() end
-- hopefully, when telling a follower to follow or wait, rebuild followers list
eventFunctions.onInfoResponse = function(e)
-- the dialogue option clicked on
local dialogue = tostring(e.dialogue):lower()
-- what that dialogue option triggers; this will catch AIFollow commands
local command = e.command:lower()
for _, item in pairs(followMatches) do
if command:match(item) or dialogue:match(item) then
log("Found %s in dialogue, rebuilding followers", item)
-- wait until game time restarts, and don't set multiple timers
if not postDialogueTimer or postDialogueTimer.state ~= timer.active then
postDialogueTimer = timer.start({
type = timer.simulate,
duration = 0.5,
iteration = 1,
callback = function()
followers = buildFollowerList()
end
})
end
end
end
end
-- rebuild followers list when player casts conjuration, in case its a summon spell
-- false positives are okay because we're not doing anything destructive
eventFunctions.onSpellCasted = function(e)
if e.caster == tes3.player and e.expGainSchool == tes3.magicSchool.conjuration then
log("Player cast conjuration spell %s, rebuilding followers list...", e.source.id)
-- wait for summon to be loaded
timer.start({
type = timer.simulate,
duration = 1,
iterations = 1,
callback = function() followers = buildFollowerList() end
})
end
end
-- Register events
local function onInitialized()
for name, func in pairs(eventFunctions) do
event.register(name:gsub("on(%u)", string.lower), func)
log("%s event registered", name)
end
mwse.log("[%s] Successfully initialized%s", modName, mag and " with More Attentive Guards interop" or "")
end
-- MCM
local function configMenu()
local template = mwse.mcm.createTemplate(modName)
template:saveOnClose(modConfig, config)
local page = template:createSideBarPage({
label = "Sidebar Page???",
description = string.format("%s v%s by %s\n\n%s", modName, version, author, modInfo)
})
local category = page:createCategory(modName)
category:createYesNoButton({
label = "Stop friendly fire",
variable = mwse.mcm.createTableVariable({id = "enable", table = config})
})
category:createYesNoButton({
label = "Debug logging",
variable = mwse.mcm.createTableVariable({id = "debug", table = config})
})
return template
end
event.register("modConfigReady", function() mwse.mcm.register(configMenu()) end)
event.register("initialized", onInitialized)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment