Почему-то карта фаталит, если нанести триггерно несмертельный урон юниту, который был обработан данной функцией:
library SetUnitMaxState initializer Init

    globals
//      The rawcode of the life ability:
        private    constant    integer    MAX_STATE_LIFE_ABILITY    =    'A013'
        
//      The rawcode of the mana ability:
        private    constant    integer    MAX_STATE_MANA_ABILITY    =    'A014'
        
//      The maximum power of two the abilitys use:
        private    constant    integer    MAX_STATE_MAX_POWER       =    8
    endglobals

    globals
        private integer array PowersOf2
    endglobals

    function SetUnitMaxState takes unit u, unitstate state, real newValue returns nothing
        local integer stateAbility
        local integer newVal = R2I(newValue)
        local integer i = MAX_STATE_MAX_POWER
        local integer offset
        
        if state == UNIT_STATE_MAX_LIFE then
            set stateAbility = MAX_STATE_LIFE_ABILITY
        elseif state == UNIT_STATE_MAX_MANA then
            set stateAbility = MAX_STATE_MANA_ABILITY
        else
            return
        endif
        
        set newVal = newVal - R2I(GetUnitState(u, state))
        
        if newVal > 0 then
            set offset = MAX_STATE_MAX_POWER + 3
        elseif newVal < 0 then
            set offset = 2
            set newVal = -newVal
        else
            return
        endif

        loop
            exitwhen newVal == 0 or i < 0
            if newVal >= PowersOf2[i] then
                call UnitAddAbility(u, stateAbility)
                call SetUnitAbilityLevel(u, stateAbility, offset + i)
                call UnitRemoveAbility(u, stateAbility)
                set newVal = newVal - PowersOf2[i]
            else
                set i = i - 1
            endif
        endloop
    endfunction

    function AddUnitMaxState takes unit u, unitstate state, real addValue returns nothing
        call SetUnitMaxState(u, state, GetUnitState(u, state) + addValue)
    endfunction

    private function Init takes nothing returns nothing
        local integer i = 1
        
        set PowersOf2[0] = 1
        loop
            set PowersOf2[i] = PowersOf2[i - 1] * 2
            set i = i + 1
            exitwhen i == MAX_STATE_MAX_POWER + 3
        endloop
    endfunction

endlibrary

Принятый ответ

ScopteRectuS, ну как я и догадывался, что идет рекурсия.
10 событий - юнит получает урон, на одного юнита... Нужны именно такого типа проверки, всегда в условии триггера проверяй от кого урон и какой этот урон с помощью флага глобалки как в примере.В блоге лича хорошо описаны костыли доты, почитай для общего развития чтобы не наступать на эти грабли еще раз.
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
0
21
7 лет назад
0
Всем спасибо за ответы, проблему я уже решил, дело было не в данной функции.
У меня была система, которая позволяла добавить событие "любой юнит атакован".
Также, был триггер, событие которого было как раз "любой юнит атакован".
В этом триггере триггерно наносился урон, который, естественно запускал этот триггер снова, и снова, и снова. Эдакая цепная реакция получалась.
Это и приводило к фаталу, хотя не понятно, почему фаталит... Цепная реакция должна сама ведь прекратиться, когда юнит умрёт. То есть, юниту наносится урон, потом еще, и еще, пока юнит не умрёт.
Или варик крашится, если триггер сам себя запускает без задержек по кд?
3
32
7 лет назад
Отредактирован quq_CCCP
3
ScopteRectuS, Дело в том что юнит неуспевает умереть, событие - юнит получает урон срабатывает до реального нанесения урона, т.е хп юнит потеряет или умрет только через фрейм, если на юнит повешано событие - юнит получает урон и в действии триггера ему еще раз наносят урон, происходит рекурсия, счетчик уронов на юнита переполняется, ну и фатал. Это хорошо описано в блоге драколича, где подробно описаны способности по блоку, ибо усиления урона. Можешь почитать на досуге и понять как лучше не делать способности.....
ScopteRectuS, думаю будет полезно ссылка
А вот сам код с пояснениями косяков ссылка
0
21
7 лет назад
Отредактирован scopterectus
0
quq_CCCP, а почему рекурсия не происходит, когда юниту наносится смертельный урон, ведь какой-бы величной не обладал нанесённый урон, он должен нанестись через фрейм.
P.S: статью еще не прочёл.
function Trig_BloodRageEffect_Actions takes nothing returns boolean
    local trigger TempTrigger = GetTriggeringTrigger( )
    local integer id = GetHandleId( TempTrigger )
    local unit BloodSeeker = ( LoadUnitHandle( udg__htb_DATA, ( id ), ( 2 ) ) )
    local unit Target = ( LoadUnitHandle( udg__htb_DATA, ( id ), ( 17 ) ) )
    local integer RageLevel = GetUnitAbilityLevel( BloodSeeker, 'A311' )
    
    if GetTriggerEventId( ) == EVENT_UNIT_DAMAGED then
        if udg_IsDamaged and( Target == GetEventDamageSource( )or Target == GetTriggerUnit( ) ) then
            set udg_IsDamaged = false
            call P6I( GetEventDamageSource( ), GetTriggerUnit( ), 3, GetEventDamage( ) * ( 20 + 5 * RageLevel ) / 100 ) //P6I функция нанесения урона
            set udg_IsDamaged = true
        endif
    elseif GetTriggerEventId( ) == EVENT_UNIT_DEATH then
    
        if GetKillingUnit( ) == Target or GetTriggerUnit( ) == Target then
            call SetUnitState( GetKillingUnit( ), UNIT_STATE_LIFE, GetUnitState( GetTriggerUnit( ), UNIT_STATE_MAX_LIFE ) * 0.25 + GetUnitState( GetKillingUnit( ), UNIT_STATE_LIFE ) )
            call DestroyEffect( AddSpecialEffectTarget( "Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl", GetKillingUnit( ), "origin" ) )
        elseif GetOwningPlayer( GetKillingUnit( ) ) == GetOwningPlayer( Target )and GetUnitAbilityLevel( GetKillingUnit( ), 'A04R' ) > 0 then
            call SetUnitState( Target, UNIT_STATE_LIFE, GetUnitState( Target, UNIT_STATE_MAX_LIFE ) * 0.25 + GetUnitState( Target, UNIT_STATE_LIFE ) )
            call DestroyEffect( AddSpecialEffectTarget( "Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl", Target, "origin" ) )
        endif
        
        if GetTriggerUnit( ) == Target then
            call DestroyEffect( ( LoadEffectHandle( udg__htb_DATA, ( id ), ( 175 ) ) ) )
            call DestroyEffect( ( LoadEffectHandle( udg__htb_DATA, ( id ), ( 176 ) ) ) )
            call FlushChildHashtable( udg__htb_DATA, ( id ) )
            call DestroyTrigger( TempTrigger ) //в оригинале используется функция SOI(...) из отдельной системы удаления триггеров
        endif
    else
        call DestroyEffect( ( LoadEffectHandle( udg__htb_DATA, ( id ), ( 175 ) ) ) )
        call DestroyEffect( ( LoadEffectHandle( udg__htb_DATA, ( id ), ( 176 ) ) ) )
        call FlushChildHashtable( udg__htb_DATA, ( id ) )
        call DestroyTrigger( TempTrigger )
    endif
    set TempTrigger = null
    set BloodSeeker = null
    set Target = null
    return false
endfunction
			set udg_IsDamaged = false
            call P6I( GetEventDamageSource( ), GetTriggerUnit( ), 3, GetEventDamage( ) * ( 20 + 5 * RageLevel ) / 100 ) //P6I функция нанесения урона
            set udg_IsDamaged = true
            call P6I( GetEventDamageSource( ), GetTriggerUnit( ), 3, GetEventDamage( ) * ( 20 + 5 * RageLevel ) / 100 ) //P6I функция нанесения урона
запускается новый поток, который не нанесёт урон, возобновится работа текущего потока и переменная приравнивается к true
Чёрт,, почему это так гениально.?
0
16
7 лет назад
0
Игра имеет 32 слота под размещение данных вроде GetTriggerUnit()
Возможно, фатал случается, когда все 32 штуки заполняются из-за рекурсивного вызова, и идет попытка размещения поверх, ну либо какие-то еще внутренние структуры жестко ограничены сверху. Простой бесконечный цикл сам закрывается по истечению предоставленного лимита операций.
0
21
7 лет назад
0
Не знаю, нужно ли создавать новый вопрос, вообщем пишу сюда.
Подредактировал свой код под ваши указания, и карта перестала фаталится. Но обнаружилась другая проблема: сперва всё работает как надо, но в один момент снова начинается цепная реакция (рекурсия, но это не точно), но уже не фаталит, а просто убивает юнита. Дальше всё снова начинает работать как надо, но не надолго. После нескольких нормальных срабатываний триггера, срабатывает 1 с рекурсией, и всё по-новой.
scope StormHammer initializer Initialization

    globals
        public    constant    integer       STORM_HAMMER                          =    'A019'

        public    constant    string        AOE_EFFECT_PATH                       =    "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
        public    constant    string        TARGET_EFFECT_PATH                    =    "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"

        public    constant    string        LIGHTNING_NAME                        =    "CLPB"
        public    constant    real          LIGHTNING_HEIGHT                      =    9900.0
        public    constant    real          LIGHTNING_LIFE_TIME                   =    0.1

        public    constant    real          STORM_HAMMER_SLOW_DURATION            =    1.2
        public    constant    integer       STORM_HAMMER_SLOW_PERCENT             =    92

        public    constant    boolean       ATTACK_FLAG                           =    false
        public    constant    boolean       RANGED_FLAG                           =    false
        public    constant    attacktype    ATTACK_TYPE                           =    ATTACK_TYPE_CHAOS
        public    constant    damagetype    DAMAGE_TYPE                           =    DAMAGE_TYPE_UNIVERSAL
        public    constant    weapontype    WEAPON_TYPE                           =    null
    endglobals

    public constant function GetAbilityRadius takes integer abilityLevel returns real
        return 475.0 + 25.0 * abilityLevel
    endfunction

    public constant function GetAbilityDamage takes integer abilityLevel returns real
        return 25.0 * abilityLevel
    endfunction

    public function IsUnitAlive takes unit whichUnit returns boolean
        return ( not IsUnitType( whichUnit, UNIT_TYPE_DEAD ) ) and ( GetUnitTypeId( whichUnit ) != 0 )
    endfunction

    globals
        public    constant    group        TEMP_GROUP          =    CreateGroup( )
        public    constant    trigger      TRIGGER             =    CreateTrigger( )
        public    constant    hashtable    HASHTABLE           =    InitHashtable( )

        public                boolexpr     boolexprForGroup    =    null
        public                boolean      tempBool            =    true
    endglobals

    public function Conditions takes nothing returns boolean
        local unit    victimUnit      = GetTriggerUnit( )
        local unit    attackingUnit   = GetEventDamageSource( )

        local integer abilityLevel    = GetUnitAbilityLevel( attackingUnit, STORM_HAMMER )
//      local real    currentMana     = GetUnitState( attackingUnit, UNIT_STATE_MANA )
//      local real    abilityManaCost = GetAbilityManaCost( abilityLevel )

        local boolean a = ( abilityLevel >= 1 )
//      local boolean b = ( currentMana >= abilityManaCost )
//      local boolean c = ( not IsAbilityOnCooldown( GetUnitAbility( attackingUnit, STORM_HAMMER ) ) )
        local boolean d = ( IsUnitEnemy( attackingUnit, GetOwningPlayer( victimUnit ) ) )
        local boolean f = ( IsUnitAlive( victimUnit ) )

        set attackingUnit = null
        set victimUnit = null

        return ( tempBool ) and ( a ) and ( d ) and ( f ) // and ( c ) and ( d )
    endfunction

    public function GroupCallback takes nothing returns boolean
        local unit   filterUnit         = GetFilterUnit( )
        local unit   attackingUnit      = GetEventDamageSource( )
        local player attackingUnitOwner = GetOwningPlayer( attackingUnit )

        local boolean a = ( not IsUnitType( filterUnit, UNIT_TYPE_STRUCTURE ) )
        local boolean b = ( not IsUnitType( filterUnit, UNIT_TYPE_MECHANICAL ) )
        local boolean c = ( not IsUnitType( filterUnit, UNIT_TYPE_MAGIC_IMMUNE ) )
        local boolean d = ( IsUnitEnemy(filterUnit, GetOwningPlayer( attackingUnit ) ) )
        local boolean e = ( IsUnitAlive( filterUnit ) )

        if ( tempBool ) and ( a ) and ( b ) and ( c ) and ( d ) and ( e ) then
            call DestroyEffect( AddSpecialEffectTarget( TARGET_EFFECT_PATH, filterUnit, "origin" ) )
//          call SlowUnit( filterUnit, STORM_HAMMER_SLOW_PERCENT, STORM_HAMMER_SLOW_DURATION )
            set tempBool = false
            call UnitDamageTarget( attackingUnit, filterUnit, GetAbilityDamage( GetUnitAbilityLevel( attackingUnit, STORM_HAMMER ) ), ATTACK_FLAG, RANGED_FLAG, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE )
            set tempBool = true
        endif
        
        set filterUnit    = null
        set attackingUnit = null

        return false
    endfunction

    public function Actions takes nothing returns nothing
        local unit    victim          = GetTriggerUnit( )
        local unit    attacker        = GetEventDamageSource( )

        local real    victimZ         = GetUnitFlyHeight( victim )
        local real    victimX         = GetUnitX( victim )
        local real    victimY         = GetUnitY( victim )

        local integer abilityLevel    = GetUnitAbilityLevel( attacker, STORM_HAMMER )
//      local integer abilityManaCost = GetAbilityManaCost( abilityLevel )
//      local real    abilityCooldown = GetAbilityDataCooldown( GetUnitAbility( attacker, STORM_HAMMER ), abilityLevel )

//      local real    newManaState    = GetUnitState( attacker, UNIT_STATE_MANA ) - abilityManaCost

        call SaveInteger( HASHTABLE, GetHandleId( attacker ), STORM_HAMMER, LoadInteger( HASHTABLE, GetHandleId( attacker ), STORM_HAMMER ) + 1 )
        call CreateFloatingTextTag( GetOwningPlayer( attacker ), FLOATING_TEXTTAG_CRITICAL_STRIKE, LoadInteger( HASHTABLE, GetHandleId( attacker ), STORM_HAMMER ), GetUnitX( attacker ), GetUnitY( attacker ) )

        if ( LoadInteger( HASHTABLE, GetHandleId( attacker ), STORM_HAMMER ) >= 1 ) then
            call SaveInteger( HASHTABLE, GetHandleId( attacker ), STORM_HAMMER, 0 )
//          call IssueImmediateOrder( attacker, "stop" )
//          call StartAbilityCooldown( attacker, STORM_HAMMER, 1 )
//          call SetUnitState( attacker, UNIT_STATE_MANA, newManaState )
            call GroupEnumUnitsInRange( TEMP_GROUP, victimX, victimY, GetAbilityRadius( abilityLevel ), boolexprForGroup )
//          call DestroyLightningTimed( AddLightningEx( LIGHTNING_NAME, true, victimX, victimY, LIGHTNING_HEIGHT, victimX, victimY, victimZ ), LIGHTNING_LIFE_TIME )
            call DestroyEffect( AddSpecialEffectTarget( AOE_EFFECT_PATH, victim, "origin" ) )
        endif

        set victim        = null
        set attacker      = null
    endfunction

    public function Initialization takes nothing returns nothing
        set boolexprForGroup   = Condition(function GroupCallback)

        call PreloadAbility( STORM_HAMMER )
        call PreloadEffect( AOE_EFFECT_PATH )
        call PreloadEffect( TARGET_EFFECT_PATH )
        call PreloadLightning( LIGHTNING_NAME )

//      call TriggerRegisterAnyUnitEventBJ( TRIGGER, EVENT_PLAYER_UNIT_ATTACKED ) // данный триггер запускается извне с событием EVENT_UNIT_DAMAGED.
        call TriggerAddCondition( TRIGGER, Condition( function StormHammer_Conditions ) )
        call TriggerAddAction( TRIGGER, function StormHammer_Actions )
    endfunction

endscope
0
32
7 лет назад
0
ScopteRectuS, как то ты события криво вешаешь, сразу по несколько на 1 юнита походу. Карту в студию.
0
21
7 лет назад
0
    function RegisterUnitDamaged takes unit whichUnit returns nothing
        local integer unitId = GetHandleId( whichUnit )

        if HaveSavedHandle( REGISTER_UNIT_DAMAGED_HASHTABLE, unitId, HASHTABLE_FLAG_KEY ) then
//          call BJDebugMsg("TriggerRegisterDamagedEvent(...) :    Attempt to add new event.")

        else
            call SaveTriggerEventHandle( REGISTER_UNIT_DAMAGED_HASHTABLE, unitId, HASHTABLE_FLAG_KEY, TriggerRegisterUnitEvent( gg_trg_UnitDamaged, whichUnit, EVENT_UNIT_DAMAGED ) )
        endif
    endfunction
на одном юните не может быть более одного повешанного события.
Загруженные файлы
0
28
7 лет назад
0
ScopteRectuS, что за костыль?
нафига вообще тут хештейбл
ты когда вызываешь эту функцию?
и вообще
готовых систем 100500
нафига свои костыли придумывать?
0
21
7 лет назад
Отредактирован scopterectus
0
Готовые системы вешают событие на всех юнитов, а я сделал так, чтобы вешало лишь на тех, на кого мне нужно
library RegisterUnitDamaged

//  GetEventDamageSource() - Источник урона (атаковавший юнит).
//  GetEventDamage()       - Величина нанесённого урона.
//  GetTriggerUnit()       - Цель урона (атакованный юнит).

    globals
        public    constant    hashtable    REGISTER_UNIT_DAMAGED_HASHTABLE    =    InitHashtable( )
        public    constant    integer      HASHTABLE_FLAG_KEY                 =    0
    endglobals

    function RegisterUnitDamaged takes unit whichUnit returns nothing
        local integer unitId = GetHandleId( whichUnit )

        if HaveSavedHandle( REGISTER_UNIT_DAMAGED_HASHTABLE, unitId, HASHTABLE_FLAG_KEY ) then
//          call BJDebugMsg("TriggerRegisterDamagedEvent(...) :    Attempt to add new event.")

        else
            call SaveTriggerEventHandle( REGISTER_UNIT_DAMAGED_HASHTABLE, unitId, HASHTABLE_FLAG_KEY, TriggerRegisterUnitEvent( gg_trg_UnitDamaged, whichUnit, EVENT_UNIT_DAMAGED ) )
        endif
    endfunction

    public function Actions takes nothing returns nothing
        call ConditionalTriggerExecute( gg_trg_GreedIsGood )
        call ConditionalTriggerExecute( StormHammer_TRIGGER )
    endfunction

endlibrary

function InitTrig_UnitDamaged takes nothing returns nothing
    set gg_trg_UnitDamaged = CreateTrigger()
    call TriggerAddAction( gg_trg_UnitDamaged, function RegisterUnitDamaged_Actions )
endfunction
0
21
7 лет назад
Отредактирован scopterectus
0
Не знаю, нужно ли создавать новый вопрос, вообщем пишу сюда.
Подредактировал свой код под ваши указания, и карта перестала фаталится. Но обнаружилась другая проблема: сперва всё работает как надо, но в один момент снова начинается цепная реакция (рекурсия, но это не точно), но уже не фаталит, а просто убивает юнита. Дальше всё снова начинает работать как надо, но не надолго. После нескольких нормальных срабатываний триггера, срабатывает 1 с рекурсией, и всё по-новой.
Причину бага я нашёл, это происходило потому что юниту снова наносился триггерный урон, но уже совсем в другом триггере.
    set udg_IsDamaged = false
    call UnitDamageTarget( whichUnit, target, amount, attack, ranged, attackType, damageType, weaponType )
    set udg_IsDamaged = true
2
28
7 лет назад
2
ScopteRectuS, можно юзать дефайны
define UnitDamageTarget(whichUnit, target, amount, attack, ranged, attackType, damageType, weaponType ) ={
	udg_IsDamaged = false;
	call Unit##DamageTarget( whichUnit, target, amount, attack, ranged, attackType, damageType, weaponType );
	udg_IsDamaged = true;
}
вставь это в шапку карты и если я написал правильно то оно при компиляции заменит все UnitDamageTarget на код в фигурных скобках
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.