Почему-то карта фаталит, если нанести триггерно несмертельный урон юниту, который был обработан данной функцией:
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
19
7 лет назад
0
Похожие вопросы:

ответ
Ну тут все очевидно, я абсолютно точно установил источник проблемы и нашел легкодоступное решение, специально для Вас.
Насколько я понимаю, мне совершенно не обязательно его описывать, так как Вы, справедливо полагая что Все люди на Земле, как и Вы, обладают экстрасенсорными способностями, можете подсмотреть его из моей головы.
ответ
почему так долго грузится карта
Потому что огромный Нестандартные объекты может тысяч или больше или Спелл герой до 100 уровень.
из них 60 загрузка просто висит на 1/5.
Если нетак? Проверь если есть файлы war3map.j папка Game\WarcraftIII, надо удалить файл. очень давно тоже 1 раз что за вылет или неправильно выполняет триггер.
Есть способы ускорить загрузку карты не экономя в редакторе объектов
Есть Widgetizer, там очень быстрая загрузка игра, но плохо работает спелл или текст.
ответ
Проблема решена.
ответ
Было создано 22 366 679 экземпляров класса CUnitListNode, которые заняли 255.9 МБ памяти.
При очередной попытке выделения, игра упала.
Виной всему утечки памяти: за 21 минуту набралось 80 тысяч групп и 20 тысяч точек.
Также, из-за выполнения большого количества кода, сильно лагает.
На стадии выбора героя (первые две минуты), выполняется 550 000 операций в секунду, а далее — 1 200 000.
Для сравнения: лимит потока — 300 000 операций.
Хорошо, что ты приложил карту, так как в логе маловато информации.

1
28
7 лет назад
Отредактирован nvc123
1
любой несмертельный урон?
даже если 1 единица урона?
1
16
7 лет назад
1
лог фатала приложи
1
29
7 лет назад
1
ScopteRectuS,
героям я бы не советовал таким способам хп менять, у меня были с этим фаталы, и я переделывал
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 на код в фигурных скобках
0
32
7 лет назад
0
ScopteRectuS, ну как я и догадывался, что идет рекурсия.
10 событий - юнит получает урон, на одного юнита... Нужны именно такого типа проверки, всегда в условии триггера проверяй от кого урон и какой этот урон с помощью флага глобалки как в примере.В блоге лича хорошо описаны костыли доты, почитай для общего развития чтобы не наступать на эти грабли еще раз.
Принятый ответ
0
23
7 лет назад
0
а чем не угодил менять урон числом а потом уже наносить последный урон... А то события ловит каждый урон так и появилось рекурсия
Чтобы оставить комментарий, пожалуйста, войдите на сайт.