Есть ли в 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
`
ОЖИДАНИЕ РЕКЛАМЫ...
0
21
5 лет назад
0
Предполагаю, что делается через какой-то цикл перебором всех элементов таблицы, если элемент является функцией, то запустить.
1
29
5 лет назад
1
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
Принятый ответ
0
21
5 лет назад
0
NazarPunk, простите за наглость. А как еще получить название функции?
1
29
5 лет назад
1
А как еще получить название функции?
На код внимательно посмотреть))
Загруженные файлы
0
21
5 лет назад
0
NazarPunk, v - это сам объект, а k - имя?
NazarPunk:
На код внимательно посмотреть))
Я код не запускал, поэтому такой вопрос получился)
0
29
5 лет назад
0
v - это сам объект, а k - имя?
Это сокращённо от key, value.
3
24
5 лет назад
3
ScopteRectuS, в k в этом коде попадает ключ по которому что-то лежит в таблице. В общем случае - ключ не обязательно строка.
0
21
5 лет назад
0
Нет, я понимаю, что это переменная. Я имел ввиду то, что в ней хранится.
Сейчас уже всё понял. Спасибо.
Написал свой код, но не работает. Почему?
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( )
3
24
5 лет назад
Отредактирован prog
3
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 передавать.
0
21
5 лет назад
Отредактирован scopterectus
0
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
Кстати, да. Можно добавить то, что Вы предложили. И использовать один триггер, который отлавливает касты способностей и запускать нужные функции.
1
24
5 лет назад
1
Я бы еще рекомендовал хранить в разных таблицах сами способности и привязку способностей к героям.
Грубо говоря, герои в таблице Heroes, способности в таблице Abilities. Позволит ускорить перебор и избежать ненужных проверок.
0
21
5 лет назад
0
prog, А как реализовать таблицу для героев, если герои - это ключи для таблицы Ability?
1
24
5 лет назад
Отредактирован prog
1
ScopteRectuS, идея в том чтобы просто их разделить. Heroes - примерно так как сейчас, с ключами по равкодам героев, а в значениях таблицы с инфой по героям, включая перечисление их способностей. А в Abilities - ключи это равкоды способностей, а значения - таблицы способностей.
Сейчас у тебя в Ability складывается два типа данных - по равкоду героя его список способностей, а по каким-то другим ключам таблицы с данными конкретных способностей.
Естественно, я исхожу из предположения что триггеры срабатывания способностей переделаны на получение таблицы способности по равкоду, вместо отдельных триггеров на каждую способность.
Конструкция "if GetSpellAbilityId( ) ~= Ability[ HERO_DEATH_KNIGHT ][ Q ] then return end" ужасна - это вызов функции и два обращения к таблице/массиву в глобальной переменной. И хуже всего - эта конструкция вызывается каждый раз когда срабатывает триггер, а срабатывает он на все способности. Когда способностей будет сто и все на разных триггерах - аналогичная конструкция будет вызываться уже сто раз при каждом касте любой способности.
0
21
5 лет назад
0
prog, понятно. Спасибо за предложение. Постараюсь это реализовать.
0
29
5 лет назад
0
ScopteRectuS, можно же просто опорную таблицу сделать.
if IDS[GetSpellAbilityId()] ~= nil then IDS[GetSpellAbilityId()]() end
0
24
5 лет назад
Отредактирован prog
0
ScopteRectuS, таблица Heroes тебе еще пригодится если у героев появятся механики, требующие что-то делать, например, при входе героя на карту или при смерти, индивидуально для разных героев. Эти действия можно будет занести в таблицу Heroes аналогично тому как можно занести действия способностей. Или, например, если для героев нужно будет хранить какие-то дополнительные данные по равкоду героя.

if IDS[GetSpellAbilityId()] ~= nil then IDS[GetSpellAbilityId()]() end
Срочно кешировать в локалку результат вызова GetSpellAbilityId()! Вызов функций намного-намного-намного дороже обращений к переменным.
0
21
5 лет назад
Отредактирован scopterectus
0
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
Норм или нет?
1
24
5 лет назад
1
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
Как-то так.
0
21
5 лет назад
0
prog, prog, неужели обращение к ячейке таблицы настолько тяжелый процесс, что лучше сохранять её значение в локалку?
1
24
5 лет назад
1
ScopteRectuS, в зависимости от кол-ва обращений, конечно. onInit в локалку я завернул больше за компанию, а вот в случае с глобальной таблицей AbilityIndex - да, смысл есть.
0
21
5 лет назад
0
prog, понятно. А цикл разве так плох? Если на карте максимум штук 50 способностей будет?
1
24
5 лет назад
1
ScopteRectuS, не то чтобы прям плох, но зачем елозить циклом, если прямое обращение реализовать ничего не стоит.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.