Есть ли в Lua способ запустить все функции указанной таблицы?
test = { }

test.a = 123
test.b = true

function test.func0( )
end

function test.func1( )
end

function test.qab( )
end
Нужно сделать так, чтобы все функции test.func0( ), test.func1( ), test.qab( ) были запущены. При этом заранее не известно количество функций.

ScopteRectuS:
Предполагаю, что делается через какой-то цикл перебором всех элементов таблицы, если элемент является функцией, то запустить.
Правильно предполагаете, но мануалы же никому неинтересны, проже же вопрос задать.
test   = { }
test.a = 123
test.b = true
function test.func0()
	print(0)
end
function test.func1()
	print(1)
end
function test.qab()
	print(2)
end

for k, v in pairs(test) do
	if type(v) == 'function' then
		print(k, ':')
		v()
	end
end
`
ОЖИДАНИЕ РЕКЛАМЫ...
21
Предполагаю, что делается через какой-то цикл перебором всех элементов таблицы, если элемент является функцией, то запустить.
30
ScopteRectuS:
Предполагаю, что делается через какой-то цикл перебором всех элементов таблицы, если элемент является функцией, то запустить.
Правильно предполагаете, но мануалы же никому неинтересны, проже же вопрос задать.
test   = { }
test.a = 123
test.b = true
function test.func0()
	print(0)
end
function test.func1()
	print(1)
end
function test.qab()
	print(2)
end

for k, v in pairs(test) do
	if type(v) == 'function' then
		print(k, ':')
		v()
	end
end
Принятый ответ
21
NazarPunk, простите за наглость. А как еще получить название функции?
30
А как еще получить название функции?
На код внимательно посмотреть))
Загруженные файлы
21
NazarPunk, v - это сам объект, а k - имя?
NazarPunk:
На код внимательно посмотреть))
Я код не запускал, поэтому такой вопрос получился)
30
v - это сам объект, а k - имя?
Это сокращённо от key, value.
24
ScopteRectuS, в k в этом коде попадает ключ по которому что-то лежит в таблице. В общем случае - ключ не обязательно строка.
21
Нет, я понимаю, что это переменная. Я имел ввиду то, что в ней хранится.
Сейчас уже всё понял. Спасибо.
Написал свой код, но не работает. Почему?
Ability = { }
Ability.DeathCoil = { }

function Ability.DeathCoil.init( )
    print( 321 )
end

function InitAllAbilities( )
    for parentKey, parentValue in pairs( Ability ) do

        if type( parentValue ) == "table" then

            for childKey, chilValue in pairs( parentValue ) do
                if type( childValue ) == "function" and childKey == "init" then
                    childValue( )
                end
            end

        end
    end
end

InitAllAbilities( )
24
ScopteRectuS, Зачем так сложно то?

Ability = { }
Ability.DeathCoil = { }

function Ability.DeathCoil.init( )
    print( 321 )
end

function InitAllAbilities( )
    for parentKey, parentValue in pairs( Ability ) do
        if (type( parentValue ) == "table") then
			if(parentValue.init and type( parentValue.init ) == "function" ) then
				parentValue.init()
			end
       end
    end
end

InitAllAbilities( ) -- Этот вызов лучше бы перенести куда-то после инициализации карты, иначе будут проблемы.
Более того, я бы и от проверок на table и function избавился бы, условившись с самим собой что в таблице Ability могут лежать только таблицы спелов, а в таблицах спелов init либо функция либо его нет.
Ну и самое важное - я бы ключами в таблице Ability взял равкоды абилок, чтобы по равкоду абилки можно было сразу получить доступ к связанной с этим равкодом таблице.
Как-то так
	Ability = {}
	local DeathCoil = {}
	Ability[FourCC('A000')] = DeathCoil

	function DeathCoil.init( )
	...
	end
	
	function DeathCoil.cast( )
	...
	end
	...
	
function SomeTriggerAction()
	local spellId = GetSpellAbilityId()
	if(	Ability[spellId]) then
		if(Ability[spellId].cast)then
			Ability[spellId].cast()
		end
	end
end
...
А при желании можно еще и параметры в cast передавать.
21
prog, да, без второго цикла действительно лучше!
Таблицы у меня так реализованы:
-----------------------------------------------------------------------------
--  H E R O E S                                                            --
-----------------------------------------------------------------------------
HERO_BLADEMASTER                                                    = FourCC( "Her0" )
HERO_DEATH_KNIGHT                                                   = FourCC( "Her2" )

-----------------------------------------------------------------------------
--  A B I L I T I E S                                                      --
-----------------------------------------------------------------------------
Q                                                                   = 0x000001
W                                                                   = 0x000002
E                                                                   = 0x000003
R                                                                   = 0x000004

Ability = {
    [ HERO_BLADEMASTER ] = { 
        [ Q ]                                                       = FourCC( "A006" ),
        [ W ]                                                       = FourCC( "A001" ),
        [ E ]                                                       = FourCC( "A000" ),
        [ R ]                                                       = FourCC( "A007" )
    },
    [ HERO_DEATH_KNIGHT ] = { 
        [ Q ]                                                       = FourCC( "A00I" ),
        [ W ]                                                       = 0,
        [ E ]                                                       = 0,
        [ R ]                                                       = 0
    },
    [ HERO_CRYPT_LORD ] = { 
        [ Q ]                                                       = FourCC( "A00B" ),
        [ W ]                                                       = 0,
        [ E ]                                                       = 0,
        [ R ]                                                       = 0
    },
    [ HERO_PALADIN ] = {
        [ Q ]                                                       = FourCC( "A004" ),
        [ W ]                                                       = 0,
        [ E ]                                                       = 0,
        [ R ]                                                       = 0
    }
}
И собственно сама способность:
-----------------------------------------------------------------------------
--  D E A T H   K N I G H T :   D E A T H   C O I L ,   ( Q )              --
-----------------------------------------------------------------------------

do
    Ability.DeathCoil = { }
    
    function Ability.DeathCoil.init( )
        RegisterAnyUnitEvent( EVENT_PLAYER_UNIT_SPELL_EFFECT, nil,
            function( )
                if GetSpellAbilityId( ) ~= Ability[ HERO_DEATH_KNIGHT ][ Q ] then return end

                local caster    = GetSpellAbilityUnit( )
                local player    = GetOwningPlayer( caster )
                local coilX     = GetUnitX( caster )
                local coilY     = GetUnitY( caster )
                local angle     = Atan2( GetSpellTargetY( ) - coilY, GetSpellTargetX( ) - coilX )
                local targetX   = Cos( angle ) * 800.0 + coilX
                local targetY   = Sin( angle ) * 800.0 + coilY
                local coil      = AddSpecialEffect( "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilMissile.mdl", coilX, coilY )
                local stock     = CreateGroup( )
                local enumGroup = CreateGroup( )

                local level     = GetUnitAbilityLevel( caster, GetSpellAbilityId( ) )
                local damage    = 75.0 * level
                local aoe       = 150.0

                BlzSetSpecialEffectYaw( coil, Atan2( targetY - coilY, targetX - coilX ) )
                    
                TimerStart( CreateTimer( ), 0.03125, true, 
                    function( )
                        local dx   = targetX - coilX
                        local dy   = targetY - coilY
                        local dist = math.sqrt( dx ^ 2 + dy ^ 2 )
                            
                        if dist > 32.8125 then
                            coilX = coilX + 32.8125 * ( dx / dist )
                            coilY = coilY + 32.8125 * ( dy / dist )

                            BlzSetSpecialEffectX( coil, coilX )
                            BlzSetSpecialEffectY( coil, coilY )

                            GroupEnumUnitsInRange( enumGroup, coilX, coilY, aoe + MAX_COLLISION_SIZE, nil ) 
                            while true do
                                local enumUnit = FirstOfGroup( enumGroup ) 
                                    
                                if enumUnit == nil then break else GroupRemoveUnit( enumGroup, enumUnit ) end

                                if 
                                    UnitAlive( enumUnit ) 
                                    and not IsUnitInGroup( enumUnit, stock ) 
                                    and not IsUnitBuilding( enumUnit ) 
                                    and enumUnit ~= caster 
                                    and IsUnitInRangeXY( enumUnit, coilX, coilY, aoe ) 
                                then
                                    DestroyEffect( AddSpecialEffectTarget( "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl", enumUnit, "origin" ) )
                                    GroupAddUnit( stock, enumUnit )
                                    GroupRemoveUnit( enumGroup, enumUnit )

                                    if IsUnitEnemy( enumUnit, player ) then
                                        UnitDamageTarget( caster, enumUnit, damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, nil )

                                    elseif IsUnitAlly( enumUnit, player ) then
                                        UnitHealTarget( caster, enumUnit, damage )
                                    end
                                end
                            end
                                    
                        else
                            DestroyEffect( coil )
                            DestroyTimer( GetExpiredTimer( ) )
                            DestroyGroup( stock )
                            DestroyGroup( enumGroup )
                        end
                    end 
                )
            end 
        )
    end

end
Кстати, да. Можно добавить то, что Вы предложили. И использовать один триггер, который отлавливает касты способностей и запускать нужные функции.
24
Я бы еще рекомендовал хранить в разных таблицах сами способности и привязку способностей к героям.
Грубо говоря, герои в таблице Heroes, способности в таблице Abilities. Позволит ускорить перебор и избежать ненужных проверок.
21
prog, А как реализовать таблицу для героев, если герои - это ключи для таблицы Ability?
24
ScopteRectuS, идея в том чтобы просто их разделить. Heroes - примерно так как сейчас, с ключами по равкодам героев, а в значениях таблицы с инфой по героям, включая перечисление их способностей. А в Abilities - ключи это равкоды способностей, а значения - таблицы способностей.
Сейчас у тебя в Ability складывается два типа данных - по равкоду героя его список способностей, а по каким-то другим ключам таблицы с данными конкретных способностей.
Естественно, я исхожу из предположения что триггеры срабатывания способностей переделаны на получение таблицы способности по равкоду, вместо отдельных триггеров на каждую способность.
Конструкция "if GetSpellAbilityId( ) ~= Ability[ HERO_DEATH_KNIGHT ][ Q ] then return end" ужасна - это вызов функции и два обращения к таблице/массиву в глобальной переменной. И хуже всего - эта конструкция вызывается каждый раз когда срабатывает триггер, а срабатывает он на все способности. Когда способностей будет сто и все на разных триггерах - аналогичная конструкция будет вызываться уже сто раз при каждом касте любой способности.
21
prog, понятно. Спасибо за предложение. Постараюсь это реализовать.
30
ScopteRectuS, можно же просто опорную таблицу сделать.
if IDS[GetSpellAbilityId()] ~= nil then IDS[GetSpellAbilityId()]() end
24
ScopteRectuS, таблица Heroes тебе еще пригодится если у героев появятся механики, требующие что-то делать, например, при входе героя на карту или при смерти, индивидуально для разных героев. Эти действия можно будет занести в таблицу Heroes аналогично тому как можно занести действия способностей. Или, например, если для героев нужно будет хранить какие-то дополнительные данные по равкоду героя.

if IDS[GetSpellAbilityId()] ~= nil then IDS[GetSpellAbilityId()]() end
Срочно кешировать в локалку результат вызова GetSpellAbilityId()! Вызов функций намного-намного-намного дороже обращений к переменным.
21
prog, сделал так:
сама способность:
do
    Ability.DeathCoil = { }
    Ability.DeathCoil.id = FourCC( "A00I" ) -- < обязательная переменная для работы системы.
    
    function Ability.DeathCoil.onSpellCast( )
    end 

    function Ability.DeathCoil.onSpellEffect( )
    end 

    function Ability.DeathCoil.onInit( )
        -- preload...
    end

end
вызов функций:
function InitAllAbilities( )
    for key, value in pairs( Ability ) do

        if type( value ) == "table" then

            if value.onInit and type( value.onInit ) == "function" then value.onInit( ) end
        end
        
    end

    local trigger = CreateTrigger( )

    TriggerRegisterAnyUnitEventBJ( trigger, EVENT_PLAYER_UNIT_SPELL_EFFECT  )

    TriggerAddAction( trigger, 
        function( )
            local abilityId = GetSpellAbilityId( )
            
            for key, value in pairs( Ability ) do

                if type( value ) == "table" then

                    if value.id and value.id == abilityId then
                        if value.onSpellEffect  and type( value.onSpellEffect  ) == "function" then value.onSpellEffect ( ) end
                    end

                end

            end
        end
    )
end
Норм или нет?
24
ScopteRectuS, Не норм, конечно, потому что лишний цикл.
способность
AbilityIndex = {} -- Переменная для хранения способностей по равкоду
Ability = {} -- Переменная для хранения способностей по имени
do

    Ability.DeathCoil = { }
    AbilityIndex[FourCC( "A00I" )] = AbilityIndex.DeathCoil
    
    function Ability.DeathCoil.onSpellCast( )
    end 

    function Ability.DeathCoil.onSpellEffect( )
    end 

    function Ability.DeathCoil.onInit( )
        -- preload...
    end

end
вызов функций
function InitAllAbilities( )
    for key, value in pairs( Ability ) do
        if type( value ) == "table" then
            -- Если у способности нужно что-то инициализировать (прелоад, создание объектов), 
            -- то нужно в таблице способности создать функцию "onInit".
			local onInit  = value.onInit
            if  onInit  and type( onInit  ) == "function" then onInit( ) end
        end
    end
    local trigger = CreateTrigger( )
    TriggerRegisterAnyUnitEventBJ( trigger, EVENT_PLAYER_UNIT_SPELL_EFFECT  )
    TriggerAddAction( trigger, 
        function( )
            local abilityId = GetSpellAbilityId( )
			local spelldata = AbilityIndex[abilityId]
            if spelldata and type( spelldata ) == "table" then
				local onSpellEffect  = spelldata.onSpellEffect 
                if  onSpellEffect   and type( onSpellEffect    ) == "function" then onSpellEffect ( ) end
           end
		end
    )
end
Как-то так.
21
prog, prog, неужели обращение к ячейке таблицы настолько тяжелый процесс, что лучше сохранять её значение в локалку?
24
ScopteRectuS, в зависимости от кол-ва обращений, конечно. onInit в локалку я завернул больше за компанию, а вот в случае с глобальной таблицей AbilityIndex - да, смысл есть.
21
prog, понятно. А цикл разве так плох? Если на карте максимум штук 50 способностей будет?
24
ScopteRectuS, не то чтобы прям плох, но зачем елозить циклом, если прямое обращение реализовать ничего не стоит.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.