13

» WarCraft 3 / Дебаггер Lua

Bergi_Bear, принтов там выходит 4-5 строк.
Есть класс Action с методом public:run(...). Для всех колбеков во всех системах использую его.
Похожую задачу решаю 3мя классами.
Action
------=========
-- Include
--=========

local lib_path = Lib.curPath()
local lib_dep = Lib.curDepencies()

local Class = lib_dep.Class or error('')
---@type UtilsFunctions
local Functions = require(lib_path..'Functions') or error('')
local isTypeErr = Functions.isTypeErr or error('')
---@type UtilsSettings
local Settings = require(lib_path..'Settings') or error('')
local Log = Settings.default_logger or error('')

--=======
-- Class
--=======

local Action = Class.new('Action')
---@class Action
local public = Action.public
---@class ActionClass
local static = Action.static
---@type ActionClass
local override = Action.override
local private = {}

--========
-- Static
--========

---@alias Callback fun(vararg:any[]):any

---@param callback Callback
---@param owner any
---@param child Action | nil
---@return Action
function override.new(callback, owner, child)
    isTypeErr(callback, 'function', 'callback')
    if child then isTypeErr(child, 'Action', 'child') end

    local list = private.callback2list[callback]
    if list and list[owner] then
        return list[owner]
    end

    local instance = child or Class.allocate(Action)
    private.newData(instance, callback, owner)

    return instance
end

--========
-- Public
--========

---@return any
function public:run(...)
    if Settings.isDebug() then
        local success, result = pcall(private.data[self].callback, ...)
        if success then
            return result
        else
            Log:err(result)
        end
    else
        return private.data[self].callback(...)
    end
end

---@return any
function public:getOwner()
    return private.data[self].owner
end

--=========
-- Private
--=========

private.data = setmetatable({}, {__mode = 'k'})
private.callback2list = setmetatable({}, {__mode = 'v'})

---@param self Action
---@param callback Callback
---@param owner any
function private.newData(self, callback, owner)
    local priv = {
        callback = callback,
        owner = owner
    }

    private.data[self] = priv
    if not private.callback2list[callback] then
        private.callback2list[callback] = setmetatable({}, {__mode = 'v'})
    end
    local list = private.callback2list[callback]
    list[owner or ''] = self
end

return static
ActionList
--=========
-- Include
--=========

local lib_path = Lib.curPath()
local lib_dep = Lib.curDepencies()

local Class = lib_dep.Class or error('')
---@type ActionClass
local Action = require(lib_path..'Action') or error('')
---@type UtilsFunctions
local Functions = require(lib_path..'Functions') or error('')
local isTypeErr = Functions.isTypeErr or error('')

--=======
-- Class
--=======

local ActionList = Class.new('ActionList')
---@class ActionList : Handle
local public = ActionList.public
---@class ActionListClass : HandleClass
local static = ActionList.static
---@type ActionListClass
local override = ActionList.override
local private = {}

--========
-- Static
--========

---@param owner any
---@param child ActionList | nil
---@return ActionList
function override.new(owner, child)
    if child then isTypeErr(child, ActionList, 'child') end

    local instance = child or Class.allocate(ActionList)
    private.newData(instance, owner)

    return instance
end

--========
-- Public
--========

---@param callback Callback
---@return Action
function public:add(callback)
    isTypeErr(callback, 'function', 'callback')
    local priv = private.data[self]

    local action = Action.new(callback, priv.owner)
    table.insert(priv.actions, action)

    return action
end

---@param action Action
---@return boolean
function public:remove(action)
    isTypeErr(action, Action, 'action')

    local priv = private.data[self]
    if action:getOwner() ~= priv.owner then return false end

    for i = 1, #priv.actions do
        if priv.actions[i] == action then
            table.remove(priv.actions, i)
            return true
        end
    end

    return false
end

---@param pos number
---@return Action | nil
function public:get(pos)
    return private.data[self].actions[pos]
end

---@return number
function public:count()
    return #private.data[self].actions
end

--- Remove all actions from list.
function public:clear()
    private.data[self].actions = {}
end

--- Run all actions.
---@return table<Action, any>
function public:run(...)
    local priv = private.data[self]

    local res = {}
    for i = 1, #priv.actions do
        res[priv.actions[i]] = priv.actions[i]:run(...)
    end

    return res
end

--=========
-- Private
--=========

private.data = setmetatable({}, {__mode = 'k'})

---@param self ActionList
---@param owner any
function private.newData(self, owner)
    local priv = {
        owner = owner,
        actions = {}
    }
    private.data[self] = priv
end

return static
Trigger
--=========
-- Include
--=========

local lib_path = Lib.curPath()
local lib_dep = Lib.curDepencies()

local Class = lib_dep.Class or error('')
---@type UtilsLib
local UtilsLib = lib_dep.Utils or error('')
local ActionList = UtilsLib.ActionList or error('')
local isTypeErr = UtilsLib.isTypeErr or error('')

---@type HandleClass
local Handle = require(lib_path..'Base') or error('')

--=======
-- Class
--=======

local Trigger = Class.new('Trigger', Handle)
---@class Trigger : Handle
local public = Trigger.public
---@class TriggerClass : HandleClass
local static = Trigger.static
---@type TriggerClass
local override = Trigger.override
local private = {}

--========
-- Static
--========

---@param child Trigger | nil
---@return Trigger
function override.new(child)
    if child then isTypeErr(child, Trigger, 'child') end

    local instance = child or Class.allocate(Trigger)
    instance = Handle.new(CreateTrigger(), DestroyTrigger, instance)
    private.newData(instance)

    return instance
end

--========
-- Public
--========

---@param callback Callback
---@return Action
function public:addAction(callback)
    return private.data[self].action_list:add(callback)
end

---@param action Action
---@return boolean
function public:removeAction(action)
    return private.data[self].action_list:remove(action)
end

---@return number
function public:countActions()
    return private.data[self].action_list:count()
end

---Function removes all actions from trigger without removing trigger.
function public:clearActions()
    private.data[self].action_list:clear()
end

---Function executes trigger like event do.
function public:execute()
    TriggerExecute(self:getData())
end

---@param var_name string
---@param opcode limitop
---@param limitval number
function public:addVariableEvent(var_name, opcode, limitval)
    isTypeErr(var_name, 'string', 'var_name')
    isTypeErr(opcode, 'limitop', 'opcode')
    isTypeErr(limitval, 'number', 'limitval')
    TriggerRegisterVariableEvent(self:getData(), var_name, opcode, limitval)
end

---@param timeout number
---@param periodic boolean
function public:addTimerEvent(timeout, periodic)
    TriggerRegisterTimerEvent(self:getData(), timeout, periodic)
end

---@param timer timer
function public:addTimerExpireEvent(timer)
    TriggerRegisterTimerExpireEvent(self:getData(), timer)
end

---@param game_state gamestate
---@param opcode limitop
---@param limitval number
function public:addGameStateEvent(game_state, opcode, limitval)
    TriggerRegisterGameStateEvent(self:getData(), game_state, opcode, limitval)
end

---@param dialog dialog
function public:addDialogEvent(dialog)
    TriggerRegisterDialogEvent(self:getData(), dialog)
end

---@param button button
function public:addDialogButtonEvent(button)
    TriggerRegisterDialogButtonEvent(self:getData(), button)
end

---@param game_event gameevent
function public:addGameEvent(game_event)
    TriggerRegisterGameEvent(self:getData(), game_event)
end

---@param region region
function public:addEnterRegion(region)
    TriggerRegisterEnterRegion(self:getData(), region)
end

---@param region region
function public:addLeaveRegion(region)
    TriggerRegisterLeaveRegion(self:getData(), region)
end

---@param trackable trackable
function public:addTrackableHitEvent(trackable)
    TriggerRegisterTrackableHitEvent(self:getData(), trackable)
end

---@param trackable trackable
function public:addTrackableTrackEvent(trackable)
    TriggerRegisterTrackableTrackEvent(self:getData(), trackable)
end

---@param player_event_type playerevent
---@param player player
function public:addPlayerEvent(player_event_type, player)
    TriggerRegisterPlayerEvent(self:getData(), player, player_event_type)
end

---@param player_unit_event playerunitevent
---@param player player
function public:addPlayerUnitEvent(player_unit_event, player)
    TriggerRegisterPlayerUnitEvent(self:getData(), player, player_unit_event, nil)
end

---@param player player
---@param alliancetype alliancetype
function public:addPlayerAllianceChange(player, alliancetype)
    TriggerRegisterPlayerAllianceChange(self:getData(), player, alliancetype)
end

---@param player player
---@param player_state playerstate
---@param opcode limitop
---@param limitval number
function public:addPlayerStateEvent(player, player_state, opcode, limitval)
    TriggerRegisterPlayerStateEvent(self:getData(), player, player_state, opcode, limitval)
end

---@param player player
---@param message string
---@param exact_match boolean
function public:addPlayerChatEvent(player, message, exact_match)
    TriggerRegisterPlayerChatEvent(self:getData(), player, message, exact_match)
end

---@param widget widget
function public:addDeathEvent(widget)
    TriggerRegisterDeathEvent(self:getData(), widget)
end

---@param unit unit
---@param unit_state unitstate
---@param opcode limitop
---@param limitval number
function public:addUnitStateEvent(unit, unit_state, opcode, limitval)
    TriggerRegisterUnitStateEvent(self:getData(), unit, unit_state, opcode, limitval)
end

---@param unit_event unitevent
---@param unit unit
function public:addUnitEvent(unit_event, unit)
    TriggerRegisterUnitEvent(self:getData(), unit_event, unit)
end

---@param unit unit
---@param range number
function public:addUnitInRange(unit, range)
    TriggerRegisterUnitInRange(self:getData(), unit, range)
end

---@param frame framehandle
---@param frame_event frameeventtype
function public:addFrameEvent(frame, frame_event)
    BlzTriggerRegisterFrameEvent(self:getData(), frame, frame_event)
end

---@param player player
---@param prefix string
---@param from_server boolean
function public:addPlayerSyncEvent(player, prefix, from_server)
    BlzTriggerRegisterPlayerSyncEvent(self:getData(), player, prefix, from_server)
end

---@param player player
---@param key oskeytype
---@param meta_key integer
---@param key_down boolean
function public:addPlayerKeyEvent(player, key, meta_key, key_down)
    BlzTriggerRegisterPlayerKeyEvent(self:getData(), player, key, meta_key, key_down)
end

--=========
-- Private
--=========

private.data = setmetatable({}, {__mode = 'k'})

---@param self Trigger
function private.newData(self)
    local priv = {
        action_list = ActionList.new()
    }
    private.data[self] = priv

    TriggerAddAction(self:getData(), function() priv.action_list:run() end)
end

return static

Но тут ничего не понятно стороннему человеку, как мне кажется.
13

» WarCraft 3 / Дебаггер Lua

Bergi_Bear, Да будет молчать, но pcall требует накладных расходов. Я ее использую немного по-другому, и заметил, что достаточно сильно пролагивает при выдаче ошибки с вложенностью больше 3. А значит функция не бесплатная.
13

» WarCraft 3 / [lua] Каст бар

Bergi_Bear, создание фрейма лучше перенести в инициализацию, а при необходимости его прятать/показывать.
13

» WarCraft 3 / Несколько вопросов об утечках

1, 2. Утечки бывают разные, какие-то более тяжелые, какие-то менее. Стоит попробовать потестить на слабом компе, например в VirtualBox. А стоит ли это фиксить зависит от длительности сессии игры.
  1. Насколько знаю - нет.
  2. Хендл - ССЫЛКА на почти любой объект на карте: юниты, декорации и даже элементы интерфейса, в т.ч. стандартные. Счетчик лишь показывает количество этих ссылок. Утечками же можно считать только необоснованное увеличение их количества. Чаще всего это вызвано тем, что остались ссылки на пустые объекты.
13

» WarCraft 3 / Цикл от 1 до 5 не всегда до 5-ти

Дело в недостатке вашего понимая. Выложите кусок кода или скриншот с проблемной частью триггера, тогда можно будет поговорить предметно.
13

» WarCraft 3 / Экспорт/импорт информации между картой и сервером

Теоретически в плюс к хостботу можно сделать генерацию .bat файла ( с накоплением статистики по всем играть с участием данного игрока) и просить игроков его запустить для отправки статистики и/или для заявки на бан игрока.
13

» WarCraft 3 / Какая проверка будет наиболее подходящей выхода юнита из круга

Можно заполнить этот "овал" регионами с пересечениями. При входе в регион добавлять к счетчику для юнита +1, при выходе -1. Тогда если юнит не находится ни в одном регионе, его счетчик равен 0. А пронумеровав регионы степенями двойки и прибавляя/вычитая эти номера из счетчика можно еще и определить последний регион в котором был юнит.
Если твоя арена имеет форму близкую к овалу, можно использовать формулу для эллипса. Точно не припомню, но надо найти константы двух его фокусов и его "радиус", а условием пребывания юнита внутри эллипса будет что сумма расстояний до этих фокусов меньше, чем "радиус" эллипса.
13

» WarCraft 3 / что то не так с LUA

Pashka5, немного упростил. Для того чтобы протестировать функцию, нужно вызвать savetyRun(функция, ее аргументы), тогда при получении ошибки она будет напечатана в чат. Если в последних патчах попробовать вызвать функцию collectgarbage, то получишь однозначный ответ, что ее не существует.
P.S. внутри pcall удобно использовать функцию error, про нее можно почитать в мануалах к луа

Например, savetyRun(collectgarbage, "collect")
13

» WarCraft 3 / что то не так с LUA

Принятый ответ
В патче 1.32 ликвидировали ручной сборщик мусора

Чтобы увидеть ошибку можно использовать обертку
function savetyRun(func,...)
        local success, result = pcall(func, ...)
        if success then
            return result
        else
            print(result)
        end
end
13

» WarCraft 3 / lua переменная и таблица

Принятый ответ
Pashka5, можно так:
local TABLE
function someFunction()
    TABLE = TABLE or {}
    for i=0,10 do
        TABLE[i]="assa"
    end
end
Таким образом если TABLE == nil, будет инициализирована новая таблица. Но не стоит экономить на спичках во время пожара.
13

» WarCraft 3 / Релиз Reforged, оценки метакритиков, блогеры не могут ошибаться

Zetox, не проверял, но слышал можно. И виртуалка не нужна, теперь можно несколько окон открывать
13

» WarCraft 3 / garbagecollect

prog, хм... Даже в голову не приходило, пожалуй при таком раскаде вовсе откажусь от автосборки такого мусора
13

» WarCraft 3 / Новые туториалы

Принятый ответ
Туториалов по lua в сети много, и Nazar регулярно добавляет наглядные примеры в наработки.
13

» WarCraft 3 / ООП lua

NazarPunk, да, ООП ради ООП. Да он только создает лишнюю нагрузку и да в wc3 можно спокойно жить без него. Вообще ООП нахер не нужен и иногда только усложняет. Мне так проще видеть структуру проекта и определять необходимый функционал, плюс это ограничивает некоторые мои ошибки. Зачем используется, например, glib? Который вообще весьма уродлив, на мой взгляд.
13

» WarCraft 3 / ООП lua

ScorpioT1000, в луа это можно решить либами на основе таблиц. Тут больше в мировоззрении дело и в поставленной задаче. Где-то удобнее ООП, где-то функциональное...
13

» WarCraft 3 / ООП lua

ScorpioT1000, да, было бы неплохо всю инфу по lua разжевать и залить в одно место. А я только пару дней назад узнал, что у функции error есть второй параметр, который крайне необходим в реалиях wc3
13

» WarCraft 3 / Заряды способности

Bergi_Bear, действительно, не пришло в голову... Можно попробовать на фреймы кнопок абилок повесить скрытие зарядов, а на кнопку 11 скрытие/показ в зависимости от текущего состояния. Но все эти навороты приводят к тому что через замену иконок проще, правда нужно много иконок.