Добавлен , опубликован
Дисклеймер!
Я полный нуб/лох/помойка в джассе (называйте как хотите), ничего толком не знаю и короче юзай способность на свой страх и риск. По возможности буду стараться оптимизировать это говно
Предложения по улучшению пишите в комменты
Цель: точка или боевая единица
Тип: АОЕ урон
Суть: град стрел, но вы можете, конечно, свой снаряд поставить: метеориты, курицы, овцы, моя мать и т.д.
Подходит как для обычного юнита, так и для героя
Текущие настройки:
  • ID способности
  • ID дамми-снаряда
  • Базовый урон
  • Доп. урон с уровнем
  • Скейл урона от силы
  • Скейл урона от ловкости
  • Скейл урона от интеллекта
  • вкл|выкл бонусы к характеристикам
  • Радиус урона
  • Доп. радиус с уровнем
  • Кол-во волн
  • Доп. кол-во волн с уровнем
  • Интервал урона
  • Отступ от середины точки применения способности
  • Доп. радиус с уровнем
  • волны стрел | по 1 стреле
  • Количество стрел в волне
  • Доп. кол-во стрел в волне с уровнем
  • Интервал появления стрел
  • Интервал спавна стрел
  • Высота спавна стрел
  • Скорость падения стрел
  • Приземление стрелы
  • Тип атаки
  • Тип урона
  • Тип оружия (звук)
  • вкл|выкл эффекты при попадании
  • Эффект при попадании
  • Точка крепления эффекта
  • Время удаления эффекта
Код
scope MySc
globals
private constant integer AbilityID = 'A000' // ID способности
private constant integer DummyID = 'u000' // ID дамми-снаряда

private constant real Damage = 5 // Базовый урон
private constant real DamageLvl = 5 // Доп. урон с уровнем
private constant real DamageScaleStr = 0.10 // Скейл урона от силы
private constant real DamageScaleAgi = 0.20 // Скейл урона от ловкости
private constant real DamageScaleInt = 0.00 // Скейл урона от интеллекта
private constant boolean IncludeBonuses = true // true|false - вкл|выкл бонусы к характеристикам

private constant real DamageRadius = 175 // Радиус урона
private constant real DamageRadiusLvl = 50 // Доп. радиус с уровнем

private constant integer DamageWaves = 3 // Кол-во волн
private constant integer DamageWavesLvl = 1 // Доп. кол-во волн с уровнем
private constant real DamageInterval = 1.00 // Интервал урона

private constant real RadiusThreshold = 25 // Отступ от середины точки применения способности
private constant real RadiusThresholdLvl = 25 // Доп. радиус с уровнем
//===
private constant boolean ArrowIntervalTF = true // true|false - волны стрел | по 1 стреле
//true
private constant integer ArrowsCount = 5 // Количество стрел в волне
private constant integer ArrowsCountLvl = 5 // Доп. кол-во стрел в волне с уровнем
private constant real ArrowsInterval = 0.50 // Интервал появления стрел
//false
private constant real ArrowInterval = 0.10 // Интервал спавна стрел
//===

private constant real ArrowHeight = 700 // Высота спавна стрел
private constant real ArrowSpeed = 1500 // Скорость падения стрел
private constant real ArrowGround = 0 // Приземление стрелы

private constant attacktype AttackType = ATTACK_TYPE_NORMAL // Тип атаки
private constant damagetype DamageType = DAMAGE_TYPE_NORMAL // Тип урона
private constant weapontype WeaponType = WEAPON_TYPE_WHOKNOWS // Тип оружия (звук)

private constant boolean ArrowEffectTF = true // true|false - вкл|выкл эффекты при попадании
private constant string ArrowEffect = "Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl" // Эффект при попадании
private constant string ArrowAttachPoint = "chest" // Точка крепления эффекта
private constant real ArrowEffectRemoveTime = 1.00 // Время удаления эффекта

private real TempReal = 0.
endglobals
//==========================================================================
private function IsUnitDead takes unit u returns boolean
    return IsUnitType(u,UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0
endfunction

private function filt takes nothing returns boolean // Тут ставим свои условия кому наносить урон, на данный момент: Юнит жив, Юнит враг применяющему юниту, Юнит не здание
    set bj_lastReplacedUnit = GetFilterUnit()
    return not IsUnitDead(bj_lastReplacedUnit) and IsUnitEnemy(bj_lastReplacedUnit,bj_groupEnumOwningPlayer) and not IsUnitType(bj_lastReplacedUnit,UNIT_TYPE_STRUCTURE)
endfunction

private function remeff takes nothing returns nothing
    local timer t = GetExpiredTimer()
    call DestroyEffect(LoadEffectHandle(udg_Hash,GetHandleId(t),0))
    call FlushChildHashtable(udg_Hash,GetHandleId(t))
    call DestroyTimer(t)
    set t = null
endfunction

private function dmgf takes nothing returns nothing
    local timer t = null
    call UnitDamageTarget(bj_lastCreatedUnit,GetEnumUnit(),TempReal,false,true,AttackType,DamageType,WeaponType)
    if ArrowEffectTF == true then
        set t = CreateTimer()
        set bj_lastCreatedEffect = AddSpecialEffectTarget(ArrowEffect,GetEnumUnit(),ArrowAttachPoint)
        call SaveEffectHandle(udg_Hash,GetHandleId(t),0,bj_lastCreatedEffect)
        call TimerStart(t,ArrowEffectRemoveTime,false,function remeff)
        set t = null
        set bj_lastCreatedEffect = null
    endif
endfunction

private function createunit takes real x, real y returns nothing
    set bj_lastCreatedUnit = CreateUnit(bj_groupEnumOwningPlayer,DummyID,x,y,GetRandomReal(0,360))
    call SetUnitFlyHeight(bj_lastCreatedUnit,ArrowHeight,0)
    call SetUnitFlyHeight(bj_lastCreatedUnit,ArrowGround,ArrowSpeed)
    call UnitApplyTimedLife(bj_lastCreatedUnit,'BTLF',(ArrowHeight-ArrowGround)/ArrowSpeed)
    set bj_lastCreatedUnit = null
endfunction

private function Arrow takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer i = GetHandleId(t)
    local integer dmgw = LoadInteger(udg_Hash,i,7)
    local integer dmgw1 = LoadInteger(udg_Hash,i,8)
    local integer k = LoadInteger(udg_Hash,i,11)
    local real x = LoadReal(udg_Hash,i,1)
    local real y = LoadReal(udg_Hash,i,2)
    local real DI = LoadReal(udg_Hash,i,3)+.01
    local real AI = LoadReal(udg_Hash,i,4)+.01
    local real r = LoadReal(udg_Hash,i,5)
    local real a = GetRandomReal(0,360)
    local real aa
    local group g = null
    
    if dmgw1 == dmgw then
        call PauseTimer(t)
        call DestroyTimer(t)
        call FlushChildHashtable(udg_Hash,i)
    else 
        set bj_groupEnumOwningPlayer = LoadPlayerHandle(udg_Hash,i,6)
        
        if ArrowIntervalTF == false then
            if AI >= ArrowInterval then
                set TempReal = GetRandomReal(LoadReal(udg_Hash,i,9),r)
                call createunit(x+TempReal*Cos(a*bj_DEGTORAD),y+TempReal*Sin(a*bj_DEGTORAD))
                set AI = 0.
            endif
        elseif DI >= DamageInterval-ArrowsInterval and k == 0 then
            set aa = LoadReal(udg_Hash,i,9)
            set bj_forLoopAIndex = 0
            set bj_forLoopAIndexEnd = LoadInteger(udg_Hash,i,10)
            set k = 1
            loop
                exitwhen bj_forLoopAIndex == bj_forLoopAIndexEnd
                set bj_forLoopAIndex = bj_forLoopAIndex+1
                set TempReal = GetRandomReal(aa,r)
                call createunit(x+TempReal*Cos(a*bj_DEGTORAD),y+TempReal*Sin(a*bj_DEGTORAD))
                set a = GetRandomReal(0,360)
            endloop
        endif
        
        if DI >= DamageInterval then
            set g = CreateGroup()
            set TempReal = LoadReal(udg_Hash,i,0)
            set bj_lastCreatedUnit = CreateUnit(bj_groupEnumOwningPlayer,DummyID,x,y,0)
            call GroupEnumUnitsInRange(g,x,y,r,Condition(function filt))
            call ForGroup(g,function dmgf)
            call GroupClear(g)
            call DestroyGroup(g)
            call RemoveUnit(bj_lastCreatedUnit)
            set g = null
            set bj_lastCreatedUnit = null
            set DI = 0.
            set k = 0
            call SaveInteger(udg_Hash,i,8,dmgw1+1)
        endif
        
        call SaveReal(udg_Hash,i,3,DI)
        call SaveReal(udg_Hash,i,4,AI)
        call SaveInteger(udg_Hash,i,11,k)
        set bj_groupEnumOwningPlayer = null
    endif
    
    set t = null
endfunction

private function Actions takes nothing returns nothing
    local unit u = GetSpellAbilityUnit()
    local timer t = CreateTimer()
    local integer i = GetHandleId(t)
    local integer lvl = GetUnitAbilityLevel(u,AbilityID)
    
    call SaveReal(udg_Hash,i,0,Damage+(lvl*DamageLvl)+(GetHeroStr(u,IncludeBonuses)*DamageScaleStr)+(GetHeroAgi(u,IncludeBonuses)*DamageScaleAgi)+(GetHeroInt(u,IncludeBonuses)*DamageScaleInt))
    call SaveReal(udg_Hash,i,1,GetSpellTargetX())
    call SaveReal(udg_Hash,i,2,GetSpellTargetY())
    call SaveReal(udg_Hash,i,3,0.)
    call SaveReal(udg_Hash,i,4,0.)
    call SaveReal(udg_Hash,i,5,DamageRadius+(lvl*DamageRadiusLvl))
    call SaveReal(udg_Hash,i,9,RadiusThreshold+(lvl*RadiusThresholdLvl))
    call SavePlayerHandle(udg_Hash,i,6,GetOwningPlayer(u))
    call SaveInteger(udg_Hash,i,7,DamageWaves+(lvl*DamageWavesLvl))
    call SaveInteger(udg_Hash,i,8,0)
    call SaveInteger(udg_Hash,i,10,ArrowsCount+(ArrowsCountLvl*lvl))
    call TimerStart(t,.01,true,function Arrow)
    
    set u = null
    set t = null
endfunction

//===========================================================================
private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == AbilityID
endfunction

function InitTrig_Phoenix takes nothing returns nothing // Phoenix переименовать на название твоего триггера
    local trigger t = CreateTrigger()
    local integer index = 0
    loop
        call TriggerRegisterPlayerUnitEvent(t,Player(index),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
        set index = index + 1
        exitwhen index == bj_MAX_PLAYER_SLOTS
    endloop
    call TriggerAddCondition(t,Condition(function Conditions))
    call TriggerAddAction(t,function Actions)
    set t = null
endfunction
endscope
В планах доработать, чтобы стрелы не могли спавнится за границей карты (если какой-то дурачок решит юзануть туда спелл и крашнуть всем игру)
Upd: Я в курсе что у некоторых могла возникнуть ошибка отсутствия функции в базе данных из-за кастом гуи, я конвертировал текст в код и теперь всё должно быть нормально, прошу прощения
Если при запуске карты через редактор тебя выбивает в главное меню, то просто нажми Ctrl+S в редакторе, если возникнет ошибка, пожалуйста, сделай скрин и пришли мне, если ошибки не будет, то после этого карта должна запустится
`
ОЖИДАНИЕ РЕКЛАМЫ...
2
rsfghd, действительно пованивает, утекает и лагает, использует глобалки близард чтобы обойти отсутствие возможности передать аргументы через предикаты, из-за чего половина стрел при малом интервале не обнулится, утекает и юнит и локация ну и т.д. Да и урон через волны такое себе. Фильтр и дамаг в луп засунуть осталось и через firstofgroup обрабатывать чтобы избавиться от приватной глобалки и не было конкурентности. Оно сейчас с 1000 стрел х 12 кастеров плавно едет. В целом спасибо, пока думал как заставить это плавно без тормозов работать написал полезную вспомогательную библиотеку. Ну а это почищеный код с переработаной логикой до моих шаманств с оптимизацией, моя версия сейчас без лагов поддерживает 2000 снарядов с интервалом 0.010, и каждый снаряд гарантированно обрабатывается, но оно уже требует самописную библиотеку.
Открыть
scope ArrowsSc
globals
//=============================== КОНФИГУРАЦИЯ СПОСОБНОСТИ =====================================
private constant integer aID = 'A000' // ID способности
private constant integer dID = 'u000' // ID дамми-юнита для снаряда и источника урона
private constant real Damage = 50 // Базовое значение урона (на стрелу)
private constant real MaxR = 230 // Радиус действия заклинания (спавна стрел)
private constant real MinR = 65 // Минимальный радиус спавна стрел (от центра)
private constant real DmgR = 70 // Радиус урона стрелы
private constant integer ArrowsCount = 1000 // Количество стрел (снарядов)
private constant real SpwnInt = 0.020 // Минимальный интервал между появлением стрел (к нему прибавится рандомный разброс)
private constant real SpwnIntR = 0.020 // Максимальный разброс интервала
private constant real ArrowHeight = 700 // Начальная высота спавна стрел
private constant real ArrowSpeed = 1500 // Скорость падения (SetUnitFlyHeight)
private constant real ArrowGround = 0 // Конечная высота падения (земля)
private constant attacktype AttackType = ATTACK_TYPE_PIERCE
private constant damagetype DamageType = DAMAGE_TYPE_NORMAL
private constant weapontype WeaponType = WEAPON_TYPE_WHOKNOWS
private constant string ArrowEffect = "Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl"
private constant string ArrowAttachPoint = "chest"
private constant real ArrowEffectRemoveTime = 1.00
// Глобальные переменные
private unit TempArrow = null
endglobals
//==========================================================================
private function IsUnitDead takes unit u returns boolean
    return IsUnitType(u,UNIT_TYPE_DEAD) or GetUnitTypeId(u) == 0
endfunction

private function filt takes nothing returns boolean
    return not IsUnitDead(GetFilterUnit()) and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TempArrow)) and not IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)
endfunction

private function remeff takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer i = GetHandleId(t)
    call DestroyEffect(LoadEffectHandle(udg_Hash,i,0))
    call FlushChildHashtable(udg_Hash,i)
    call DestroyTimer(t)
    set t = null
endfunction

private function dmgf takes nothing returns nothing
    local timer t = null
    local effect e = null
    call UnitDamageTarget(TempArrow,GetEnumUnit(),Damage,false,true,AttackType,DamageType,WeaponType)
    set t = CreateTimer()
    set e = AddSpecialEffectTarget(ArrowEffect,GetEnumUnit(),ArrowAttachPoint)
    call SaveEffectHandle(udg_Hash,GetHandleId(t),0,e)
    call TimerStart(t,ArrowEffectRemoveTime,false,function remeff)
    set t = null
    set e = null
endfunction

private function ArrowDamage takes nothing returns nothing
    local timer t = GetExpiredTimer()//Получаем таймер снаряда
    local integer apk = GetHandleId(t)//Получаем arrow parent key
    local unit u = LoadUnitHandle(udg_Hash, apk, 0) //Получаем снаряд (источник урона)
    local player Owner = LoadPlayerHandle(udg_Hash, apk, 1) // Владелец
    local group g = null
    local real ux
    local real uy
    // 1. Получаем точные координаты снаряда в момент срабатывания
    set ux = GetUnitX(u)
    set uy = GetUnitY(u)
    // 3. Создаем группу целей
    set g = CreateGroup()
    set TempArrow = u
    // 4. Группируем цели и наносим урон
    call GroupEnumUnitsInRange(g, ux, uy, DmgR, Condition(function filt))
    call ForGroup(g, function dmgf)
    // 5. КРИТИЧЕСКИ ВАЖНО: Очистка
    call DestroyGroup(g)
    set TempArrow = null
    set g = null
    set u = null
    // 6. Финальная очистка хэш-таблицы и таймера
    call FlushChildHashtable(udg_Hash, apk)
    call DestroyTimer(t)
    set t = null
    set Owner = null
endfunction

private function SpawnArrows takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer pk = GetHandleId(t)
    local integer apk
    local unit Caster
    local player Owner
    local real x = LoadReal(udg_Hash, pk, 2) //X
    local real y = LoadReal(udg_Hash, pk, 3)//Y
    local integer AS = LoadInteger(udg_Hash, pk, 4)//Arrows Shooted
    local location Tloc = null
    local real RandR
    local real Interval
    local timer FlyTime
    local unit u
    local real lifetime = (ArrowHeight-ArrowGround)/ArrowSpeed
    if AS == ArrowsCount then//Условие выхода из цикла, остановка и очистка.
        local location l = LoadLocationHandle(udg_Hash, pk, 5) //Загружаем хендл локации
        call PauseTimer(t)
        call RemoveLocation(l) //Удаление объекта Location из памяти игры
        call FlushChildHashtable(udg_Hash, pk) // Очистка ссылок из хэша
        call DestroyTimer(t)
        set t = null
        set l = null
        return
    endif
    //Инициализируем переменные
    set Caster = LoadUnitHandle(udg_Hash,pk,0) //Кастер
    set Owner = LoadPlayerHandle(udg_Hash,pk,1) //Овнер
    set Tloc = LoadLocationHandle(udg_Hash,pk,5) //TempLocation
    set RandR = GetRandomReal(MinR,MaxR)  //Рандомный радиус между минимальным и максимальным радиусом
    //Получаем рандомную точку в радиусе
    call MoveLocation(Tloc, x+RandR*Cos(GetRandomReal(0,360)*bj_DEGTORAD), y+RandR*Sin(GetRandomReal(0,360)*bj_DEGTORAD))
    //создание юнита-стрелы
    set u = CreateUnitAtLoc(Owner,dID,Tloc,GetRandomReal(0,360))
// Установка TimedLife. Это удалит юнит после lifetime, проиграв звук.
    call UnitApplyTimedLife(u,'BTLF',lifetime)
    call SetUnitFlyHeight(u,ArrowHeight,0)
    call SetUnitFlyHeight(u,ArrowGround,ArrowSpeed)
    set FlyTime = CreateTimer() //Таймер для стрелы
    set apk = GetHandleId(FlyTime) //arrow parent key
    //Что передаём функции урона:
    call SaveUnitHandle(udg_Hash,apk,0,u)//apk 0 - стрела
    call SavePlayerHandle(udg_Hash,apk,1,Owner)//apk 1 ее владелец
    call TimerStart(FlyTime, lifetime - 0.036, false, function ArrowDamage) //нанесение урона и спецэфектов перед "смертью" стрелы.
    // снаряд запущен
    call SaveInteger(udg_Hash,pk,4,AS+1)//Запущено снарядов +1
    set Interval = SpwnInt + GetRandomReal(0.001, SpwnIntR)//Интервал с разбросом
    call TimerStart(t, Interval, true, function SpawnArrows)//Таймер запуска снарядов
//утилизация
    set Tloc = null
    set u = null
    set Caster = null
    set FlyTime = null
    set Owner = null
endfunction

// --- ИНИЦИАЛИЗАЦИЯ ТАБЛИЦЫ ---
private function Actions takes nothing returns nothing
    local unit u = GetSpellAbilityUnit() 
    local timer t = CreateTimer() 
    local integer pk = GetHandleId(t) //ключ таблицы
    local player o = GetOwningPlayer(u)
    local location l = Location(0,0)
    call SaveUnitHandle(udg_Hash,pk,0,u)  //Caster (слот 0)
    call SavePlayerHandle(udg_Hash,pk,1,o) //Owner (слот 1)
    call SaveReal(udg_Hash,pk,2,GetSpellTargetX())  // X (слот 2)
    call SaveReal(udg_Hash,pk,3,GetSpellTargetY())  // Y (слот 3)
    call SaveInteger(udg_Hash,pk,4,0) //Выпущено стрел (слот 4)
    call SaveLocationHandle(udg_Hash,pk,5,l) //Точка локации используемая для этого каста
    call TimerStart(t,0.01,false,function SpawnArrows)
    set l = null
    set o = null
    set u = null
    set t = null
endfunction

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == aID
endfunction
    
function InitTrig_arrows takes nothing returns nothing
    local trigger t = CreateTrigger()
    local integer index = 0
    loop
        call TriggerRegisterPlayerUnitEvent(t,Player(index),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
        set index = index + 1
        exitwhen index == bj_MAX_PLAYER_SLOTS
    endloop
    call TriggerAddCondition(t,Condition(function Conditions))
    call TriggerAddAction(t,function Actions)
    set t = null
endfunction
endscope
2
rsfghd, благодаря этому пованивающему мусору я многому научился))) В твоём коде очень много примеров того как делать надо, и как делать категорически НЕНАДО XD
28
AnimoMori, хы, это было 5 лет назад думаешь я бы не переделал это с нуля если бы захотел?)
но впрочем спасибо что увлёкся, я поржал <3
Чтобы оставить комментарий, пожалуйста, войдите на сайт.