# Hooks

## Do Action / Add Action

#### Definitions

```swift
doAction(name: string, context: any [, context2 ...]) -> nil
```

```swift
addAction(name: string, callback: function, opt priority: integer) -> nil
```

#### Usage example

{% code title="init.lua" %}

```lua
registerForEvent('oninit', function()
    
    -- execute all addAction() using this name
    doAction('my_action', 'init', 'something')
    
end)
```

{% endcode %}

{% code title="my\_script.lua" %}

```lua
addAction('my_action', function(context, context2)

    -- context  = 'init'
    -- context2 = 'something'
    -- do something
    
end, 10)
```

{% endcode %}

{% code title="my\_script\_2.lua" %}

```lua
addAction('my_action', function(context, context2)

    -- context  = 'init'
    -- context2 = 'something'
    -- do something before (note the priority: 5)
    
end, 5)
```

{% endcode %}

## Apply Filters / Add Filter

#### Definitions

```swift
applyFilters(name: string, value: any, context: any [, context2 ...]) -> nil
```

```swift
addFilter(name: string, callback: function, opt priority: integer) -> nil
```

#### Usage example

{% code title="init.lua" %}

```lua
registerForEvent('oninit', function()

    local myVar = 'yes'
    
    myVar = applyFilters('my_filter', myVar, 'init', 'something')
    
    print(myVar)
    -- no
    
end)
```

{% endcode %}

{% code title="my\_script.lua" %}

```lua
addFilter('my_filter', function(myVar, context, context2)

    -- myVar    = 'maybe' (set by the other filter with priortiy: 5)
    -- context  = 'init'
    -- context2 = 'something'
    
    -- update myVar to 'no'
    return 'no'
    
end, 10)
```

{% endcode %}

{% code title="my\_script\_2.lua" %}

```lua
addFilter('my_filter', function(myVar, context, context2)
    
    -- myVar    = 'yes' (set by initial applyFilters())
    -- context  = 'init'
    -- context2 = 'something'
    
    -- update myVar to 'maybe' (note the priority: 5)
    return 'maybe'
    
end, 5)
```

{% endcode %}

## Source Code

```lua
local this

this = {

    storage = {
        actions = {},
        filters = {}
    },

    addAction = function(action, callback, priority, context)

        if type(action) == 'string' and type(callback) == 'function' then
            priority = tonumber(priority or 10)

            this._addHook('actions', action, callback, priority, context)
        end

        return this

    end,

    doAction = function(...)
        local args = { ... }
        local action = table.remove(args, 1)

        if type(action) == 'string' then
            this._runHook('actions', action, args)
        end

        return this
    end,

    removeAction = function(action, callback)
        if type(action) == 'string' then
            this._removeHook('actions', action, callback)
        end

        return this
    end,

    addFilter = function(filter, callback, priority, context)
        if type(filter) == 'string' and type(callback) == 'function' then
            priority = tonumber(priority or 10)

            this._addHook('filters', filter, callback, priority, context)
        end

        return this
    end,

    applyFilters = function(...)
        local args = { ... }
        local filter = table.remove(args, 1)

        if type(filter) == 'string' then
            return this._runHook('filters', filter, args)
        end

        return this
    end,

    removeFilter = function(filter, callback)
        if type(filter) == 'string' then
            this._removeHook('filters', filter, callback)
        end

        return this
    end,

    _removeHook = function(type, hook, callback, context)

        if not this.storage[type][hook] then
            return
        end

        if not callback then
            this.storage[type][hook] = {}

        else
            local handlers = this.storage[type][hook]
            --local i

            if not context then
                for i = #handlers, 1, -1 do
                    if handlers[i].callback == callback then
                        table.remove(handlers, i)
                    end
                end
            else
                for i = #handlers, 1, -1 do
                    local handler = handlers[i]

                    if handler.callback == callback and handler.context == context then
                        table.remove(handlers, i)
                    end
                end
            end
        end
    end,

    _addHook = function(type, hook, callback, priority, context)

        local hookObject = {
            callback = callback,
            priority = priority,
            context = context
        }

        local hooks = this.storage[type][hook]

        if hooks then
            table.insert(hooks, hookObject)
            hooks = this._hookInsertSort(hooks)
        else
            hooks = { hookObject }
        end

        this.storage[type][hook] = hooks
    end,

    _hookInsertSort = function(hooks)
        local tmpHook, j, prevHook

        for i = 2, #hooks do
            tmpHook = hooks[i]
            j = i

            prevHook = hooks[j - 1]
            while prevHook and prevHook.priority > tmpHook.priority do
                hooks[j] = hooks[j - 1]
                j = j - 1
                prevHook = hooks[j - 1]
            end

            hooks[j] = tmpHook
        end

        return hooks
    end,

    _runHook = function(type, hook, args)

        local handlers = this.storage[type][hook]

        if not handlers then
            return type == 'filters' and args[1] or false
        end

        local i = 1
        local len = #handlers

        if type == 'filters' then
            for i = 1, len do
                args[1] = handlers[i].callback(unpack(args))
            end
        else
            for i = 1, len do
                handlers[i].callback(unpack(args))
            end
        end

        return type == 'filters' and args[1] or true

    end


}

-- history storage
local actionHistory = {}

addAction = function(...)
    this.addAction(...)
    return this
end

removeAction = function(...)
    this.removeAction(...)
    return this
end

doAction = function(...)
    local args = {...}
    local action = args[1]

    actionHistory[action] = 1
    this.doAction(...)
    actionHistory[action] = 0
    return this
end

doingAction = function(action)
    return actionHistory[action] == 1
end

didAction = function(action)
    return actionHistory[action] ~= nil
end

currentAction = function()
    for k in pairs(actionHistory) do
        if actionHistory[k] then
            return k
        end
    end

    return false
end

addFilter = function(...)
    this.addFilter(...)
    return this
end

removeFilter = function(...)
    this.removeFilter(...)
    return this
end

applyFilters = function(...)
    return this.applyFilters(...)
end
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wiki.redmodding.org/cyber-engine-tweaks/snippets-examples/utilities/hooks.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
