quq_CCCP, quq_CCCP, ну так я же хочу, чтобы не было трупа. а TimedLife просто убивает юнита, в моём случае, он попытается еще раз убить юнита, который уже мертв.
DracoL1ch, причина удаления юнитов у меня странная, - мне не нравится трупы юнитов, которые везде валяются. Если юнитов нельзя удалять, то как мне спрятать юнита? Если спрятать юнита функцией ShowUnit(), удалится ли юнит после полного разложения его трупа? или же это функция может как-то воспрепятствовать удалению юнита движком игры?
DracoL1ch, 100% рабочего способа вызвать фатал у меня действительно нету. Но я нашел участок кода, при отключении которого, карта перестает критовать. Это был обычный триггер, который удалял юнита после его смерти, но сам этот триггер не критует, он начинает критовать только если герой прокачивает данный спелл.
spell
scope Omnislash
globals
// Ability rawcode.
private constant integer SPELL_ID = 'A00K'
// Dummy image rawcode.
private constant integer DUMMY_ID = 'o000'
// Timer's period.
private constant real FPS = 0.03125
// Times between slashes.
private constant real BASE_ATTACK_TIME = 0.375
private constant real ENEMY_SEARCH_RADIUS = 600.0
// Blademaster's attack range.
private constant real ATTACK_RANGE = 100.0
// Effect on dealing damage.
private constant string EFFECT_01 = "Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageCaster.mdl"
// Effect on weapon.
private constant string EFFECT_02 = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile_mini.mdl"
// Is damage attack?
private constant boolean ATTACK_FLAG = true
// Is damage ranged?
private constant boolean RANGED_FLAG = false
// Attack type.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_HERO
// Damage type.
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
// Weapon type.
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_METAL_HEAVY_SLICE
endglobals
private function GetUnitAttackDamage takes unit blademaster returns integer
local integer agility = GetHeroAgi( blademaster, true )
local integer sides = 3
local integer dice = 3
return GetRandomInt( ( agility + dice ), ( agility + dice * sides ) )
endfunction
private function GetAnimationIndex takes nothing returns integer
local integer i = GetRandomInt( 0, 2 )
if ( i == 0 ) then
return 2
elseif ( i == 1 ) then
return 3
endif
return 8
endfunction
struct omnislashdummy
private unit dummy
private integer alpha
private effect fx
private thistype prev
private thistype next
private static timer period = CreateTimer( )
private method destroy takes nothing returns nothing
call DestroyEffect( this.fx )
call RemoveUnit( this.dummy )
set this.fx = null
set this.dummy = null
set this.prev.next = this.next
set this.next.prev = this.prev
if ( thistype(0).next == 0 ) then
call PauseTimer( period )
endif
call thistype.deallocate( this )
endmethod
private static method iterate takes nothing returns nothing
local thistype this = thistype(0).next
loop
exitwhen ( this == 0 )
if ( this.alpha <= 0 ) then
call this.destroy( )
else
set this.alpha = this.alpha - 8
call SetUnitVertexColor( this.dummy, 255, 255, 255, IMaxBJ( this.alpha, 0 ) )
endif
set this = this.next
endloop
endmethod
static method create takes unit target, unit caster returns thistype
local thistype this = thistype.allocate( )
local real x = GetUnitX( target ) + GetRandomReal( -ATTACK_RANGE, ATTACK_RANGE )
local real y = GetUnitY( target ) + GetRandomReal( -ATTACK_RANGE, ATTACK_RANGE )
local real f = bj_RADTODEG * Atan2(GetUnitY( target ) - y , GetUnitX( target ) - x )
set this.next = thistype(0)
set this.prev = thistype(0).prev
set this.next.prev = this
set this.prev.next = this
set this.dummy = CreateUnit( GetOwningPlayer( caster ), DUMMY_ID, x, y, f )
set this.fx = AddSpecialEffectTarget( EFFECT_02, this.dummy, "weapon" )
set this.alpha = 255
call PauseUnit( this.dummy, true )
call UnitAddAbility( this.dummy, 'Amrf' )
call UnitRemoveAbility( this.dummy, 'Amrf' )
call SetUnitFlyHeight( this.dummy, GetUnitFlyHeight( target ), 0.0 )
// call UnitAddAbility( this.dummy, 'Aloc' )
// call SetUnitInvulnerable( this.dummy, true )
call SetUnitPathing( this.dummy, false )
call SetUnitAnimationByIndex( this.dummy, GetAnimationIndex( ) )
if ( this.prev == 0 ) then
call TimerStart( period, FPS, true, function thistype.iterate )
endif
return this
endmethod
endstruct
struct omnislash
private unit caster
private unit target
private effect fx
private integer slash
private real bat
private real range
private real casterX
private real casterY
private real targetX
private real targetY
private thistype prev
private thistype next
private static group group = CreateGroup( )
private static timer period = CreateTimer( )
private static trigger trigger = CreateTrigger( )
private method destroy takes nothing returns nothing
call DestroyEffect( this.fx )
call PauseUnit( this.caster, false )
// call SetUnitInvulnerable( this.caster, false )
call SetUnitPathing( this.caster, true )
call SetUnitTimeScale( this.caster, 100 * 0.01)
call SetUnitVertexColor( this.caster, 255, 255, 255, 255 )
call SetUnitFlyHeight( this.caster, 0.0, 0.0 )
set this.caster = null
set this.target = null
set this.fx = null
set this.prev.next = this.next
set this.next.prev = this.prev
if ( thistype(0).next == 0 ) then
call PauseTimer( period )
endif
call thistype.deallocate( this )
endmethod
private static method getParabolaZ takes real maxHeight, real maxRange, real currentRange returns real
return ( 4 * maxHeight / maxRange ) * ( maxRange - currentRange ) * ( currentRange / maxRange )
endmethod
private method getRandomUnitFromGroup takes nothing returns unit
call GroupEnumUnitsInRange( thistype.group, this.casterX, this.casterY, ENEMY_SEARCH_RADIUS, null )
loop
set bj_groupRandomConsidered = 0
set bj_groupRandomCurrentPick = null
call ForGroup( thistype.group, function GroupPickRandomUnitEnum )
exitwhen ( bj_groupRandomCurrentPick == null ) or ( IsUnitEnemy( bj_groupRandomCurrentPick, GetOwningPlayer( this.caster ) ) and IsUnitAlive( bj_groupRandomCurrentPick ) and ( not IsUnitType( bj_groupRandomCurrentPick, UNIT_TYPE_STRUCTURE ) ) )
call GroupRemoveUnit( thistype.group, bj_groupRandomCurrentPick )
endloop
call GroupClear( thistype.group )
return bj_groupRandomCurrentPick
endmethod
private static method iterate takes nothing returns nothing
local thistype this = thistype(0).next
local real range
loop
exitwhen ( this == 0 )
if ( this.slash > 0 ) and IsUnitAlive( this.caster ) then
call SetUnitFacing( this.caster, ( bj_RADTODEG * Atan2( this.targetY - this.casterY, this.targetX - this.casterX ) ) )
if ( this.bat <= 0.0 ) then
set this.targetX = GetUnitX( this.target )
set this.targetY = GetUnitY( this.target )
set range = SquareRoot( ( this.targetX - this.casterX ) * ( this.targetX - this.casterX ) + ( this.targetY - this.casterY ) * ( this.targetY - this.casterY ) )
if ( range <= 120 ) then
set this.casterX = this.targetX + GetRandomReal( -ATTACK_RANGE, ATTACK_RANGE )
set this.casterY = this.targetY + GetRandomReal( -ATTACK_RANGE, ATTACK_RANGE )
call SetUnitX( this.caster, this.casterX )
call SetUnitY( this.caster, this.casterY )
call SetUnitFlyHeight( this.caster, 0.0, 0.0 )
call omnislashdummy.create( this.target, this.caster )
call SetUnitAnimationByIndex( this.caster, GetAnimationIndex( ) )
call DestroyEffect( AddSpecialEffectTarget( EFFECT_01, this.target, "origin" ) )
call UnitDamageTarget( this.caster, this.target, ( GetHeroAgi( this.caster, true ) + 25.0 ), ATTACK_FLAG, RANGED_FLAG, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE )
set this.bat = BASE_ATTACK_TIME
set this.target = this.getRandomUnitFromGroup( )
if ( this.target != null ) then
set this.slash = ( this.slash - 1 )
set this.targetX = GetUnitX( this.target )
set this.targetY = GetUnitY( this.target )
set this.range = SquareRoot( ( this.targetX - this.casterX ) * ( this.targetX - this.casterX ) + ( this.targetY - this.casterY ) * ( this.targetY - this.casterY ) )
else
call this.destroy( )
endif
else
set this.casterX = this.casterX + 60.0 * ( ( this.targetX - this.casterX ) / range )
set this.casterY = this.casterY + 60.0 * ( ( this.targetY - this.casterY ) / range )
call SetUnitX( this.caster, this.casterX )
call SetUnitY( this.caster, this.casterY )
call SetUnitFlyHeight( this.caster, thistype.getParabolaZ( 180.0, this.range, ( this.range - range ) ), 0.0 )
endif
else
set this.bat = this.bat - FPS
endif
else
call this.destroy( )
endif
set this = this.next
endloop
endmethod
private static method actions takes nothing returns nothing
local eventid id = GetTriggerEventId( )
local thistype this
local unit blademaster
local unit attacker
if ( id == EVENT_PLAYER_UNIT_ATTACKED ) then
set blademaster = GetTriggerUnit( )
set attacker = GetAttacker( )
call omnislashdummy.create( attacker, blademaster )
call UnitDamageTarget( blademaster, attacker, GetUnitAttackDamage( blademaster ), ATTACK_FLAG, RANGED_FLAG, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE )
set blademaster = null
set attacker = null
elseif ( id == EVENT_PLAYER_UNIT_SPELL_EFFECT ) then
set this = thistype.allocate( )
set this.next = thistype(0)
set this.prev = thistype(0).prev
set this.next.prev = this
set this.prev.next = this
set this.caster = GetSpellAbilityUnit( )
set this.casterX = GetUnitX( this.caster )
set this.casterY = GetUnitY( this.caster )
set this.target = GetSpellTargetUnit( )
set this.targetX = GetUnitX( this.target )
set this.targetY = GetUnitY( this.target )
set this.fx = AddSpecialEffectTarget( EFFECT_02, this.caster, "weapon" )
set this.slash = GetUnitAbilityLevel( this.caster, SPELL_ID ) * 2 + 10
set this.bat = 0.0
set this.range = SquareRoot( ( this.targetX - this.casterX ) * ( this.targetX - this.casterX ) + ( this.targetY - this.casterY ) * ( this.targetY - this.casterY ) )
call UnitAddAbility( this.caster, 'Amrf' )
call UnitRemoveAbility( this.caster, 'Amrf' )
call PauseUnit( this.caster, true )
// call SetUnitInvulnerable( this.caster, true )
call SetUnitPathing( this.caster, false )
call SetUnitTimeScale( this.caster, 250 * 0.01 )
call SetUnitVertexColor( this.caster, 255, 255, 255, 100 )
if ( this.prev == 0 ) then
call TimerStart( period, FPS, true, function thistype.iterate )
endif
endif
set id = null
endmethod
private static method conditions takes nothing returns boolean
local eventid eventId = GetTriggerEventId( )
local unit blademaster = GetTriggerUnit( )
local integer chance = GetUnitAbilityLevel( blademaster, SPELL_ID ) * 6 + 10
local boolean b = false
if ( eventId == EVENT_PLAYER_UNIT_ATTACKED ) then
set b = ( GetUnitAbilityLevel( blademaster, SPELL_ID ) > 0 ) and ( GetRandomReal( 0.0, 100.0 ) <= chance )
elseif ( eventId == EVENT_PLAYER_UNIT_SPELL_EFFECT ) then
set b = ( GetSpellAbilityId( ) == SPELL_ID )
endif
set eventId = null
set blademaster = null
return b
endmethod
private static method onInit takes nothing returns nothing
call PreloadAbility( SPELL_ID )
call PreloadUnit( DUMMY_ID )
call PreloadEffect( EFFECT_01 )
call TriggerRegisterAnyUnitEventBJ( thistype.trigger, EVENT_PLAYER_UNIT_ATTACKED )
call TriggerRegisterAnyUnitEventBJ( thistype.trigger, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( thistype.trigger, Condition( function thistype.conditions ) )
call TriggerAddAction( thistype.trigger, function thistype.actions )
endmethod
endstruct
endscope
также, если удалить этот триггер, удаляющий юнитов после смерти. А вместо него в редакторе констант выставить минимальные значения разложения трупа, то карта снова начинает критовать.
Но есть еще один маленький шанс, что, то, что я написал выше - ложь. Возможно, карта не критовала лишь потому что это так совпало с отключением выше указанного триггера. И данный триггер вовсе не является причиной фатала, как и сам спелл...))
P.S.: это Omnislash Juggernaut'a из доты, правда немного изменённый. Также, у данной способности есть пассивная составляющая - каждый раз, когда героя атакуют, имеется какой-то шанс, контратаковать атакующего (создается даммик, который проигрывает анимацию атаки и врагу наносится урон).
quq_CCCP, мемхак убрал с карты, а использовал героя только для того, чтобы не создавать новую боевую единицу, захламляя редактор объектов. Когда в редакторе становится много объектов становится сложно разбираться во всем этом. Ну и собственно, мой первый вопрос, мог ли этот даммик быть причиной фатала?
quq_CCCP, не могу так сделать. на самом деле мой даммик - это тот же самый герой, я не стал создавать нового юнита, а использую модель героя также и для даммика, вручая ему москитов и т.д.
quq_CCCP, ну у даммика изначально нету москитов, они добавляются после создания даммика. или это не важно? я подумал, что фатал успевает призойти между созданием дамми и добавлением москитов
DracoL1ch, на карте есть герой со способностью, которая создает дамми, проигрывающую анимацию атаки на враге, который атаковал героя. То есть дамми создается и удаляется через одну или две секунды, а при её создании ей дается неуязвимость. Может ли дамми оказаться причиной фатала?
типа дамми агрит врагов - враги агрятся. дамми получает нецязвимость - враг не может атаковать, как следствие - фатал ерор.
Возможно не совсем по теме, но хотел бы спросить: есть ли ограничение на самих триггеров (без инициализации). То есть куча триггеров, но в них код написан на JASS/vJASS и инициализация разделена на потоки (через library).
quq_CCCP, но ведь Вы написали о константных переменных, значение которых защищены от изменения, а от чего же защищены константные функции?
Если верить моим исследованиям, то они защищены от неконстантных глобальных переменных и от неконстантных функций, которые могут находиться в них, но зачем?
quq_CCCP, благодарю, но всё же хотелось бы именно с thistype.next, thistype.prev.
кажется, я сделаль:
struct data
static timer period = CreateTimer( )
thistype prev
thistype next
unit u
integer a
method destroy takes nothing returns nothing
set this.prev.next = this.next
set this.next.prev = this.prev
if ( thistype(0).next == 0 ) then
call PauseTimer( period )
endif
call thistype.deallocate( this )
endmethod
static method iterate takes nothing returns nothing
local thistype this = thistype( 0 ).next
loop
exitwhen ( this == 0 )
set this.a = this.a + 1
call SetUnitVortexColor( this.u, 255, 255, 255, this.a )
if ( this.a > 255 ) then
call this.destroy( )
endif
set this = this.next
endloop
endmethod
static method createUnit takes nothing returns thistype
local thistype this = thistype.allocate( )
set this.next = 0
set this.prev = thistype(0).prev
set this.next.prev = this
set this.prev.next = this
set this.u = CreateUnit(...)
set this.a = 0
call SetUnitVortexColor( this.u, 255, 255, 255, this.a )
if ( this.prev == 0 ) then
call TimerStart( period, 0.03125, true, function thistype.iterate )
endif
return this
endmethod
endstruct
Clamp, если я правильно понял, то почему это работает?
code
constant function sum takes integer a , integer b returns integer
return a + b
endfunction
function test takes nothing returns nothing
call BJDebugMsg( I2S( sum (1, 1 ) ))
call BJDebugMsg( I2S( sum (2, 2 ) ))
endfunction
//===========================================================================
function InitTrig_test takes nothing returns nothing
set gg_trg_test = CreateTrigger( )
call TriggerRegisterTimerEventSingle( gg_trg_test, 1.00 )
call TriggerAddAction( gg_trg_test, function test )
endfunction
вас должно заботить не то, быстрее ли такая функция или медленнее, а оптимизация узких мест, вот где важна скорость выполнения. не в ту сторону смотрите
Я про это и думал. Ведь можно сделать функцию константной, которая вызывается таймером с малым периодом.
code
function IsUnitAlive takes unit whichUnit returns boolean
return not ( IsUnitType( whichUnit, UNIT_TYPE_DEAD ) or ( GetUnitTypeId( whichUnit ) == 0 ) )
endfunction
function TimerCallback takes nothing returns nothing
...
if ( IsUnitAlive( unit ) ) then
...
endif
...
endfunction
function Start takes nothing returns nothing
call TimerStart( CreateTimer( ), 0.03125, true, function TimerCallback )
endfunction
» WarCraft 3 / Фаталит карта
» WarCraft 3 / Фаталит карта
Ред. scopterectus
» WarCraft 3 / Фаталит карта
Ред. scopterectus
» WarCraft 3 / Высота Z
» WarCraft 3 / Фаталит карта
» WarCraft 3 / Фаталит карта
» WarCraft 3 / Фаталит карта
» WarCraft 3 / Фаталит карта
типа дамми агрит врагов - враги агрятся. дамми получает нецязвимость - враг не может атаковать, как следствие - фатал ерор.
» WarCraft 3 / Есть ли ограничение по числу триггеров, областей или переменных?
» WarCraft 3 / Иконки способностей для предметов
Я спрашивал, как это исправить (если возможно) ?
Ред. scopterectus
» WarCraft 3 / PreloadAbility( integer )
Ред. scopterectus
» WarCraft 3 / constant function
» WarCraft 3 / constant function
» WarCraft 3 / структура и перебирание данных в ней
» WarCraft 3 / структура и перебирание данных в ней
» WarCraft 3 / структура и перебирание данных в ней
» WarCraft 3 / constant function
» WarCraft 3 / constant function
» WarCraft 3 / constant function
» WarCraft 3 / constant function
Ред. scopterectus
» WarCraft 3 / constant function
Ред. scopterectus
» WarCraft 3 / constant function
» WarCraft 3 / Определённый предмет с инвентаря
» WarCraft 3 / PreloadAbility( integer )
Ред. scopterectus
» WarCraft 3 / PreloadAbility( integer )