Добавлен , опубликован
Способ реализации:
Lua
Версия Warcraft:
» MUI: да
» Импорт: нет
» Утечки: нет
» Требования: нет
Герой запускает волну, которая наносит 100/200/300 урона и рикошетит от рельефа.

Скриншот

Технические подробности

Перенос в свою карту
Триггеры
  • WaterWave
Способности
  • Волна Воды 'SWaW:ANcl'
Настройка
local ABILITY_ID = AbilityId('SFiB')
local MISSILE_EFFECT = {'Abilities\\Weapons\\WaterElementalMissile\\WaterElementalMissile.mdl',2} --> model, scal
local MISSILE_HEIGHT = 100
local MISSILE_START_DISTANCE = 100

local TIMER_PERIOD = 0.03125 --> 1/32
local SPEED = 600
local DISTANCE = {1200, 1800, 2400}

local DAMAGE_UNIT = {50, 75, 100}
local DAMAGE_UNIT_RANGE = 64
local DAMAGE_UNIT_PERIOD = 0.09
local DAMAGE_UNIT_EFFECT = {'Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl', 'origin'}
local DAMAGE_EXPLODE = {100, 200, 300}
local DAMAGE_EXPLODE_RANGE = 254
local DAMAGE_EXPLODE_EFFECT = 'Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl'
Код
//! beginusercode
do
    -- На момент патча 1.31 эта функция всегда возвращает 0. Поэтому создадим её локальный аналог.
    local function AbilityId(id)
        return id:byte(1) * 0x1000000 + id:byte(2) * 0x10000 + id:byte(3) * 0x100 + id:byte(4)
    end

    -- Настройки
    local ABILITY_ID = AbilityId('SFiB')
    local MISSILE_EFFECT = {'Abilities\\Weapons\\WaterElementalMissile\\WaterElementalMissile.mdl',2} --> model, scal
    local MISSILE_HEIGHT = 100
    local MISSILE_START_DISTANCE = 100

    local TIMER_PERIOD = 0.03125 --> 1/32
    local SPEED = 600
    local DISTANCE = {1200, 1800, 2400}

    local DAMAGE_UNIT = {50, 75, 100}
    local DAMAGE_UNIT_RANGE = 64
    local DAMAGE_UNIT_PERIOD = 0.09
    local DAMAGE_UNIT_EFFECT = {'Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl', 'origin'}
    local DAMAGE_EXPLODE = {100, 200, 300}
    local DAMAGE_EXPLODE_RANGE = 254
    local DAMAGE_EXPLODE_EFFECT = 'Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl'

    -- Заклинание
    local SPEED_INC = SPEED/(1/TIMER_PERIOD)

    local function InMapXY(x, y)
        return
            x > GetRectMinX(bj_mapInitialPlayableArea)
            and
            x < GetRectMaxX(bj_mapInitialPlayableArea)
            and
            y > GetRectMinY(bj_mapInitialPlayableArea)
            and
            y < GetRectMaxY(bj_mapInitialPlayableArea)        
    end

    local GetTerrainZ_location = Location(0, 0)
    local function GetTerrainZ(x, y)
        MoveLocation(GetTerrainZ_location, x, y);
        return GetLocationZ(GetTerrainZ_location);
    end

    local TRIGGER = CreateTrigger()
    for i = 0, bj_MAX_PLAYER_SLOTS - 1, 1
    do
        TriggerRegisterPlayerUnitEvent(TRIGGER, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT)
    end
    TriggerAddCondition(t, Condition(function()
        return GetSpellAbilityId() == ABILITY_ID
    end))
    TriggerAddAction(TRIGGER, function()
        local caster = GetTriggerUnit()
        local x = GetUnitX(caster)
        local y = GetUnitY(caster)

        local angle = Atan2(GetSpellTargetY() - y, GetSpellTargetX() - x)
        local cos = Cos(angle)
        local sin = Sin(angle)

        x = x + MISSILE_START_DISTANCE*cos
        y = y + MISSILE_START_DISTANCE*sin
        local z = GetTerrainZ(x, y) + MISSILE_HEIGHT
        local zx
        local zy

        local level = GetUnitAbilityLevel(caster, ABILITY_ID)
        local distance = DISTANCE[level]

        local missile = AddSpecialEffect(MISSILE_EFFECT[1], x, y)
        BlzSetSpecialEffectYaw(missile, angle)
        BlzSetSpecialEffectHeight(missile, MISSILE_HEIGHT)
        BlzSetSpecialEffectScale(missile, MISSILE_EFFECT[2])
        
        local damaged = CreateGroup()
        local damaging = CreateGroup()

        local time = 0
        
        TimerStart(CreateTimer(), TIMER_PERIOD, true, function()
            zx = GetTerrainZ(x + 2*SPEED_INC*Cos(angle), y + 1*SPEED_INC*Sin(angle)) - z + MISSILE_HEIGHT
            zy = GetTerrainZ(x + 1*SPEED_INC*Cos(angle), y + 2*SPEED_INC*Sin(angle)) - z + MISSILE_HEIGHT
            
            if zx > z then angle = math.pi - angle end
            if zy > z then angle = 0 - angle end
            if zx > z or zy > z then
                cos = Cos(angle)
                sin = Sin(angle)
                BlzSetSpecialEffectYaw(missile, angle)
            end

            time = time + TIMER_PERIOD

            x = x + SPEED_INC*cos
            y = y + SPEED_INC*sin
            distance = distance - SPEED_INC

            if
                not InMapXY(x,y)
                or
                distance <= 0
            then
                
                DestroyEffect(AddSpecialEffect(DAMAGE_EXPLODE_EFFECT, x, y))
                GroupEnumUnitsInRange(damaging, x, y, DAMAGE_EXPLODE_RANGE, Filter(function()
                    return  
                            UnitAlive(GetFilterUnit())
                            and
                            IsPlayerEnemy(GetOwningPlayer(caster), GetOwningPlayer(GetFilterUnit()))
                            and
                            not IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE)
                            and
                            not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING)
                end))
                
                ForGroup(damaging, function()
                    GroupAddUnit(damaged, GetEnumUnit())
                    DestroyEffect(AddSpecialEffectTarget(DAMAGE_UNIT_EFFECT[1], GetEnumUnit(), DAMAGE_UNIT_EFFECT[2]))
                    UnitDamageTarget(caster, GetEnumUnit(), DAMAGE_UNIT[level], false, true, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
                end)

                DestroyGroup(damaged)
                DestroyGroup(damaging)
                DestroyEffect(missile)
                DestroyTimer(GetExpiredTimer())
                return
            end

            BlzSetSpecialEffectX(missile, x)
            BlzSetSpecialEffectY(missile, y)
            BlzSetSpecialEffectHeight(missile, z - GetTerrainZ(x, y))

            if
                time > DAMAGE_UNIT_PERIOD
            then
                time = 0
                GroupEnumUnitsInRange(damaging, x, y, DAMAGE_UNIT_RANGE, Filter(function()
                    return  UnitAlive(GetFilterUnit())
                            and
                            IsPlayerEnemy(GetOwningPlayer(caster), GetOwningPlayer(GetFilterUnit()))
                            and
                            not IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE)
                            and
                            not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING)
                            and
                            not IsUnitInGroup(GetFilterUnit(), damaged)
                end))
                
                ForGroup(damaging, function()
                    GroupAddUnit(damaged, GetEnumUnit())
                    DestroyEffect(AddSpecialEffectTarget(DAMAGE_UNIT_EFFECT[1], GetEnumUnit(), DAMAGE_UNIT_EFFECT[2]))
                    UnitDamageTarget(caster, GetEnumUnit(), DAMAGE_UNIT[level], false, true, ATTACK_TYPE_MAGIC, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
                end)

                GroupClear(damaging)
            end

        end)
        
    end)
    
end
//! endusercode
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
30
Также еще заметил, что глобальные переменные можно создавать внутри функции
Глобальные переменные можно создавать откуда угодно. Но лучше так не делать
если переменная может использоваться в замыкающей функции, то она не будет очищена сборщиком мусора
Сборщик мусора очищает всё неиспользуемое. Если не уверены насчёт очистки, всегда есть nil.
Переменная var будет создана 11 раз или 1 раз, а дальше перезаписываться?
Блок do ... end создаёт область видимости и переменная будет создана один раз, потом будет перезаписываться и успешно умрёт вместе с циклом.
21
NazarPunk, спасибо большое. Теперь всё стало намного понятнее. Не могли ли бы Вы также рассказать про *in*:
for i = 0, 10, in do
Что за ключевое слово такое?

NazarPunk, еще хотел бы узнать о функциях в Lua.
function varName( ) return 0 end
varName = function( ) return 0 end
Это одно и то же или нет? Все функции в Lua - переменные? Я пришел к такому после того, как объявил две функции с одинаковыми именами, а при вызове этой функции вызывалась та, что ниже находилась, выглядит так, будто перезаписали переменную.
30
in это непонятная зверушка. Где вы такой for увидели?
Используйте лучше стандартный
for init,max/min value, increment
do
	statement(s)
end

function varName( ) return 0 end
varName = function( ) return 0 end
Это обычный сахар и таки да, вы переобъявили функцию.
Все функции в Lua - переменные?
В lua всё является объектами.
21
NazarPunk, спасибо. Поставил бы плюсиков, да кончились они.(
max = 3 на одного пользователя
28
Что за ключевое слово такое?
Хм, конструкция странная, скорее всего ошибочная. Но in может использоваться для перебора всех элементов массива, если вообще такое ключевое слово есть.
30
если вообще такое ключевое слово есть
На stackoverflow говорят что есть.
23
Странно, что волна не дамажит повторно, а пролетает сквозь юнитов вхолостую.
30
Странно, что волна не дамажит повторно, а пролетает сквозь юнитов вхолостую.
Волна летит медленно, так что нужно или часто наносить маленький урон или вешать бафф. А переусложнять не хотелось.

Наверное сделаю волну замедления с баффом, чтоб повторно накладывалось.
28
NazarPunk, волна сделана на основе Девятого Вала? Если да, то ничего удивительного, я в Bullet Hell писал про баги с этим заклинанием.
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.