Skip to content

Instantly share code, notes, and snippets.

Last active March 9, 2022 01:00
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
What would you like to do?
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
mag = require("celediel.MoreAttentiveGuards.interop")
-- 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[] = true
msg = msg .. .. " "
log("Friends: %s", msg)
return friends
-- Event functions
local eventFunctions = {}
eventFunctions.onDamage = function(e)
if not e.attackerReference then return end
if followers[] and followers[] then
if config.enable then
log("%s hit %s for %s friendly damage, nullifying",,, e.damage)
e.damage = 0
return false -- I don't know if this makes a difference or not
log("%s hit %s for %s friendly damage",,, e.damage)
-- uncomment this to see all damage done by everyone to everyone else
-- else
-- log("%s hit %s for %s damage",,, e.damage)
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 ~= then
postDialogueTimer = timer.start({
type = timer.simulate,
duration = 0.5,
iteration = 1,
callback = function()
followers = buildFollowerList()
-- 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...",
-- wait for summon to be loaded
type = timer.simulate,
duration = 1,
iterations = 1,
callback = function() followers = buildFollowerList() 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)
mwse.log("[%s] Successfully initialized%s", modName, mag and " with More Attentive Guards interop" or "")
-- 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)
label = "Stop friendly fire",
variable = mwse.mcm.createTableVariable({id = "enable", table = config})
label = "Debug logging",
variable = mwse.mcm.createTableVariable({id = "debug", table = config})
return template
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