<aside> 💡

implementation:

base

player:

local core = require('openmw.core')
local self = require('openmw.self')
local types = require('openmw.types')
local I = require('openmw.interfaces')
local async = require('openmw.async')

I.AnimationController.addTextKeyHandler('spellcast', function(groupname, key)

    local spell_id = "night-eye"

    if key.sub(key, #key - 6) == 'release' and types.Actor.getSelectedSpell(self).id == spell_id  then 
        print("nightime")
        
        core.sendGlobalEvent("sjek_add_stuff", { cell = self.cell.id })
        
          local name_callback = async:registerTimerCallback('arbitary name',
                function(data)
                    core.sendGlobalEvent("sjek_remove_stuff", { cell = data.cell }) --player = self
                    print("remove", data.cell)
                end)
          
          local effects = core.magic.spells.records[spell_id].effects
          local duration = effects[1].duration
          
          async:newSimulationTimer( duration , name_callback, {cell = self.cell.id}) 
    end
end)

Global:

local world = require('openmw.world')
local types = require('openmw.types')
local core = require('openmw.core')

local function sjek_add_stuff(event)
        
        local cont = world.getCellById(event.cell):getAll(types.Container)   
																					    -- object type to place at
        local bubbles = world.createObject("active_bubbles00", #cont ) 
																				      -- what to add, with type count

         for a, _ in pairs(cont) do
          local bubblesone = bubbles:split(1) -- one object for teleport
          bubblesone:teleport( event.cell, cont[a].position ) -- placement
         end
end

local function sjek_remove_stuff(event)
    if event.cell then
      local bubble = world.getCellById(event.cell):getAll(types.Activator)   --type to remove
       for i, _ in pairs(bubble) do
         if bubble[i].recordId == "active_bubbles00" then 
												         -- what specifically
            bubble[i]:remove() 
									            --remove in loop
         end
       end
    end
end

return { eventHandlers = { sjek_add_stuff = sjek_add_stuff, sjek_remove_stuff = sjek_remove_stuff }  } 

defining object in local

ie. for local partly:

I.AnimationController.addTextKeyHandler('spellcast', function(groupname, key)

    local spell_id = "night-eye"
    local object_id = "active_bubbles00"

    if key.sub(key, #key - 6) == 'release' and types.Actor.getSelectedSpell(self).id == spell_id  then 
        print("nightime")
        
        core.sendGlobalEvent("sjek_add_stuff", { cell = self.cell.id, object_id = object_id })
        
          local name_callback = async:registerTimerCallback('arbitary name',
                function(data)
                    core.sendGlobalEvent("sjek_remove_stuff", { cell = data.cell, object_id = data.object_id }) --player = self
                    print("remove", data.cell)
                end)
          
          local effects = core.magic.spells.records[spell_id].effects
          local duration = effects[1].duration
          
          async:newSimulationTimer( duration , name_callback, {cell = self.cell.id, object_id = object_id}) 
    end
end)

using VFX:

With loop setting on event