Добавлен , опубликован
Раз уж я решил взяться за это дерьмо, то постарался сделать все на нормальном уровне, мб кто научится чему.
Суть спелла:
Герой запускает молот, который летит в заданном направлении и дамажит все, что попадается ему на пути, после того, как он пробежал заданное расстояние - он возвращается к кастеру.
Спелл легкий, можно было вставить понтов с молниями, притяжением юнитов, минимальной системой движения и взаимодействия с окружением и т. д., но заказчику нужен был лишь дмг в области действия.
Я не вставлял мини-систему отбивания от краев карты, как я люблю, об этом никто не просил, а жаль =(
Рельеф спецом кривой, поэтому может показаться, что молот иногда "не задевает" всех кого надо, это не более чем оптическая иллюзия, все работает как надо.
Присутствуют все настройки, которые смог придумать.
Стоит заметить, что количество дмг будет зависеть от предустановленных констант на урон.
Импорт стандартный - копипастим способность, триггер, даммик. После, устанавливаем в настройках свои равкоды, если они отличаются от моих. Для повторной компиляции требуется JNGP.
Вы безупречны!
previous
v0.01
код
    library hammer initializer Init{
    /*
    © XGM.GURU (28.09.2014)
                        By Buulichkaa
    */
        define{
        ////////////
        //SETTINGS//
        ////////////
            DUMMY_ID = 'h001' //Равкод даммика
            SPELL_ID = 'A000' //Равкод способности
            SPEED = 15 //Скорость полета снаряда
            MAX_DIST = 600 //Максимальная дистанция, которую сможет пролететь молот
            RADIUS = 80 //Радиус, в котором наносится урон
            MODEL = "Abilities\\Spells\\Human\\StormBolt\\StormBoltMissile.mdl" //Модель, которая создается на цели, при нанесении ей урона
            AMOUNT = {(GetUnitAbilityLevel(damager, SPELL_ID)*50) + 50} //Количество урона
            ATTACK_TYPE = ATTACK_TYPE_NORMAL //Тип атаки
            DAMAGE_TYPE = DAMAGE_TYPE_NORMAL //Тип урона
            WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS //Тип оружия
            
        //Keywords
            int = integer
            void = nothing
            bool = boolean
            func = function
            class = struct
            insert_class_funcs = 
            {//+class funcs
                private static integer iC = 0
                private static thistype r = 0
                private thistype rN

                static method New takes nothing returns thistype
                    thistype this
                    if (r == 0) {
                        ++iC
                        this = iC
                    }else{
                        this = r
                        r = r.rN
                    }
                    return this
                endmethod

                method Remove takes nothing returns nothing
                    set rN = r
                    set r = this
                endmethod
            //-class funcs
            }
        //***************************************************************************
        //Functions
            msg(s) = DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "|cFF00FFFFDEBUG|r " + s)
            Sqrt(r) = SquareRoot(r)
            GetX(u) = GetUnitX(u)
            GetY(u) = GetUnitY(u)
            SetXY(u, x, y) = { SetUnitX(u, x); SetUnitY(u, y) }
            GetAng(x1, y1, x2, y2) = Atan2(y2 - y1, x2 - x1)
        //***************************************************************************
        }

        globals
            private player owner = Player(0) //Игрок-владелец кастера, который обрабатывается в циклах групп
            private group enum_g = CreateGroup() //Группа в которую добавляются юниты в радиусе действия
            private group damaged_g = CreateGroup() //Продамаженные юниты
            private unit damager

            private int stack[]
            private int top = -1
            
            private timer t = CreateTimer()
            private real MaxX
            private real MaxY
            private real MinX
            private real MinY
        endglobals
        
        private real SafeX(real x){
            if (x > MaxX){return MaxX;}
            elseif (x < MinX){return MinX;}
            return x
        }

        private real SafeY(real y){
            if (y > MaxY){return MaxY;}
            elseif (y < MinY){return MinY;}
            return y
        }
        
        private bool filter(){
            return ((IsUnitEnemy(GetFilterUnit(), owner)) and (IsUnitInGroup(GetFilterUnit(), damaged_g) == false));
        }
        
        private void Group_Act(){
            UnitDamageTarget(damager, GetEnumUnit(), AMOUNT, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE);
            GroupAddUnit(damaged_g, GetEnumUnit());
            DestroyEffect(AddSpecialEffect(MODEL, GetX(GetEnumUnit()), GetY(GetEnumUnit())));
        }

        private class spell extends array{
            insert_class_funcs
            
            unit caster
            unit dummy
            group g //Группа, где хранятся уже продамаженные юниты
            real t_x //Координаты точки каста
            real t_y
            real add_x //Для движения "от" цели к точке каста
            real add_y //
            bool forward

            bool active
            
            static method Create takes unit c, real tx, real ty returns thistype
                real x = tx - GetX(c);
                real y = ty - GetY(c);
                real mod = Sqrt(x * x + y * y);
                if (mod > SPEED){
                    thistype new = thistype.New();
                    new.caster = c;
                    new.add_x = (x/mod)*SPEED;
                    new.add_y = (y/mod)*SPEED;
                    new.t_x = tx;
                    new.t_y = ty;
                    new.forward = true;
                    new.dummy = CreateUnit(GetOwningPlayer(c), DUMMY_ID, GetX(c), GetY(c), bj_RADTODEG*GetAng(GetX(c), GetY(c), tx, ty));
                    new.g = CreateGroup();
                    top++;
                    stack[top] = new;
                    new.active = true;
                    return new;
                }else{
                    return -1;
                }
            endmethod
            
            method Destroy takes void returns void
                .caster = null;
                RemoveUnit(.dummy);
                DestroyGroup(.g)
                .g = null
                .dummy = null;
                .Remove();
            endmethod
            
            method Exec takes void returns void
                owner = GetOwningPlayer(.caster);
                damaged_g = .g;
                GroupEnumUnitsInRange(enum_g, GetX(.dummy), GetY(.dummy), RADIUS, Condition(func filter));
                if (FirstOfGroup(enum_g) != null){
                    damager = .caster;
                    ForGroup(enum_g, func Group_Act);
                    GroupClear(enum_g);
                }
                if .forward{
                    real nx = GetX(.dummy)+add_x;
                    real ny = GetY(.dummy)+add_y;
                    SetXY(.dummy, SafeX(nx), SafeY(ny));
                    real x = GetX(.dummy) - GetX(.caster);
                    real y = GetY(.dummy) - GetY(.caster);
                    real mod = Sqrt(x * x + y * y);
                    if ((mod > MAX_DIST) or ((SafeX(nx)!= nx) or (SafeY(ny) != ny))){.forward = false;}
                }else{
                    real nx = GetX(.caster)-GetX(.dummy);
                    real ny = GetY(.caster)-GetY(.dummy);
                    real mod = Sqrt(nx * nx + ny * ny);
                    if (mod < SPEED){
                        .active = false;
                    }else{
                        SetXY(.dummy, GetX(.dummy)+(nx/mod)*SPEED, GetY(.dummy)+(ny/mod)*SPEED);
                        SetUnitFacing(.dummy, bj_RADTODEG*GetAng(GetX(.dummy), GetY(.dummy), GetX(.caster), GetY(.caster)));
                    }
                }
            endmethod
        }
        
        private void main(){
            int i = 0;
            loop{ 
                spell current;
                exitwhen i > top
                current = stack[i];
                if (current.active){
                    current.Exec();
                    i++;
                }else{
                    current.Destroy();
                    stack[i] = stack[top];
                    stack[top] = 0;
                    top--;
                    if(top == -1){
                        PauseTimer(t);
                    }
                }
            }
            
        }
        
        private void events(){
            if (GetSpellAbilityId() == SPELL_ID){
                if (top < 8192){
                    unit c = GetSpellAbilityUnit();
                    spell.Create(c, SafeX(GetSpellTargetX()), SafeY(GetSpellTargetY()));
                    if(top == 0){
                        TimerStart(t, .025, true, func main);
                    }
                }//top < 8192
            }
        }

        private void Init(){
            int i = 0;
            MaxX = GetRectMaxX(bj_mapInitialPlayableArea)
            MaxY = GetRectMaxY(bj_mapInitialPlayableArea)
            MinX = GetRectMinX(bj_mapInitialPlayableArea)
            MinY = GetRectMinY(bj_mapInitialPlayableArea)
            trigger t = CreateTrigger();
            loop{
                TriggerRegisterPlayerUnitEvent(t, Player(i++), ConvertPlayerUnitEvent(274), null); //spell
                exitwhen i == 16;
            }
            TriggerAddAction(t, func events);
            Preload(MODEL)
        }
    }
//***************************************************************************
v0.02
Изменения: по просьбе заказчика добавлено следующее:
  1. Молот при полете от кастера будет отталкивать противников
  2. Новые настройки
  3. Немножко пофикшен код
код
    library hammer initializer Init{
    /*
    © XGM.GURU (30.09.2014)
                        By Buulichkaa
    */
        define{
        ////////////
        //SETTINGS//
        ////////////
            DUMMY_ID = 'h001' //Равкод даммика
            SPELL_ID = 'A000' //Равкод способности
            SPEED = 15 //Скорость полета снаряда
            MAX_DIST = 600 //Максимальная дистанция, которую сможет пролететь молот
            RADIUS = 80 //Радиус вокруг молота, в котором наносится урон
            MODEL = "Abilities\\Spells\\Human\\StormBolt\\StormBoltMissile.mdl" //Модель, которая создается на цели, при нанесении ей урона
            ATTACK_TYPE = ATTACK_TYPE_NORMAL //Тип атаки
            DAMAGE_TYPE = DAMAGE_TYPE_NORMAL //Тип урона
            WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS //Тип оружия
            //advanced{
                //Настройки, которые лучше не менять, если не знаешь что делаешь
                T_PERIOD = .025 //Период таймера
                AMOUNT = {(GetUnitAbilityLevel(damager, SPELL_ID)*50) + 50} //Количество урона
                KNOCKBACK = MAX_DIST/2 //Расстояние, на которое будет отброшен юнит
                KB_SPEED = 15. //Начальная скорость, с которой юнит будет отброшен
                //Переменной ниже можно установить свое константное значение
                //Оно будет отниматься от начальной скорости с каждым периодом
                //Сейчас установлено уравнение, которое зависит от установленного вами расстояния
                //Установив свое значение этой переменной вы игнорируете переменную KNOCKBACK
                //А расстояние, которое сможет пролететь отброшенный юнит будет зависить от начальной скорости
                KB_FADE_SPEED = KB_SPEED/((KNOCKBACK*2/KB_SPEED)-1) //Скорость затухания отбрасывания за каждый "тик" таймера
            //}
        //Keywords
            int = integer
            void = nothing
            bool = boolean
            func = function
            class = struct
            insert_class_funcs = 
            {//+class funcs
                private static integer iC = 0
                private static thistype r = 0
                private thistype rN

                static method New takes nothing returns thistype
                    thistype this
                    if (r == 0) {
                        ++iC
                        this = iC
                    }else{
                        this = r
                        r = r.rN
                    }
                    return this
                endmethod

                method Remove takes nothing returns nothing
                    set rN = r
                    set r = this
                endmethod
            //-class funcs
            }
        //***************************************************************************
        //Functions
            msg(s) = DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "|cFF00FFFFDEBUG|r " + s)
            Sqrt(r) = SquareRoot(r)
            GetX(u) = GetUnitX(u)
            GetY(u) = GetUnitY(u)
            SetXY(u, x, y) = { SetUnitX(u, x); SetUnitY(u, y) }
            GetAng(x1, y1, x2, y2) = Atan2(y2 - y1, x2 - x1)
        //***************************************************************************
        }

        globals
            private player owner //Игрок-владелец кастера, который обрабатывается в циклах групп
            private group enum_g = CreateGroup() //Группа в которую добавляются юниты в радиусе действия
            private group kb_g
            private group damaged_g //Продамаженные юниты
            private bool forward_g
            private real vec_x //Направление в котором отталкиваются юниты
            private real vec_y
            private unit damager

            private int stack[]
            private int top = -1
            
            private timer t = CreateTimer()
            private real MaxX
            private real MaxY
            private real MinX
            private real MinY
        endglobals
        
        private real SafeX(real x){
            if (x > MaxX){return MaxX;}
            elseif (x < MinX){return MinX;}
            return x
        }

        private real SafeY(real y){
            if (y > MaxY){return MaxY;}
            elseif (y < MinY){return MinY;}
            return y
        }
        
        private bool filter(){
            return ((IsUnitEnemy(GetFilterUnit(), owner)) and (IsUnitInGroup(GetFilterUnit(), damaged_g) == false));
        }
        
        private void Group_Act_Damage(){
            UnitDamageTarget(damager, GetEnumUnit(), AMOUNT, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE);
            GroupAddUnit(damaged_g, GetEnumUnit());
            if forward_g{
                GroupAddUnit(kb_g, GetEnumUnit());
            }
            DestroyEffect(AddSpecialEffect(MODEL, GetX(GetEnumUnit()), GetY(GetEnumUnit())));
        }
        
        private void Group_Act_Knockback(){
            SetXY(GetEnumUnit(), SafeX(GetUnitX(GetEnumUnit())+vec_x), SafeY(GetUnitY(GetEnumUnit())+vec_y));
        }
        
        private class spell extends array{
            insert_class_funcs
            
            unit caster
            unit dummy
            group g //Группа, где хранятся уже продамаженные юниты (они же являются теми, кто обрабатывается в процессе отбрасывания)
            group g_kb
            real t_x //Координаты точки каста
            real t_y
            real add_x //Для движения "от" цели к точке каста
            real add_y //
            real fade_speed
            bool dummy_active
            bool forward
            bool knockback

            bool active
            
            static method Create takes unit c, real tx, real ty returns thistype
                real x = tx - GetX(c);
                real y = ty - GetY(c);
                real mod = Sqrt(x * x + y * y);
                if (mod > SPEED){
                    thistype new = thistype.New();
                    new.caster = c;
                    new.add_x = (x/mod)*SPEED;
                    new.add_y = (y/mod)*SPEED;
                    new.t_x = tx;
                    new.t_y = ty;
                    new.forward = true;
                    new.dummy_active = true;
                    new.knockback = false;
                    new.fade_speed = KB_SPEED;
                    new.dummy = CreateUnit(GetOwningPlayer(c), DUMMY_ID, GetX(c), GetY(c), bj_RADTODEG*GetAng(GetX(c), GetY(c), tx, ty));
                    new.g = CreateGroup();
                    new.g_kb = CreateGroup();
                    top++;
                    stack[top] = new;
                    new.active = true;
                    return new;
                }else{
                    return -1;
                }
            endmethod
            
            method Destroy takes void returns void
                .caster = null;
                DestroyGroup(.g);
                DestroyGroup(.g_kb);
                .g = null;
                .dummy = null;
                .Remove();
            endmethod
            
            method Exec takes void returns void
                owner = GetOwningPlayer(.caster);
                damaged_g = .g;
                kb_g = .g_kb;
                forward_g = .forward;
                GroupEnumUnitsInRange(enum_g, GetX(.dummy), GetY(.dummy), RADIUS, Condition(func filter));
                if (FirstOfGroup(enum_g) != null){
                    damager = .caster;
                    ForGroup(enum_g, func Group_Act_Damage);
                    GroupClear(enum_g);
                    if .forward{
                        .knockback = true;
                    }
                }
                if .dummy_active{
                    if .forward{
                        real nx = GetX(.dummy)+add_x;
                        real ny = GetY(.dummy)+add_y;
                        SetXY(.dummy, SafeX(nx), SafeY(ny));
                        real x = GetX(.dummy) - GetX(.caster);
                        real y = GetY(.dummy) - GetY(.caster);
                        real mod = Sqrt(x * x + y * y);
                        if ((mod > MAX_DIST) or ((SafeX(nx)!= nx) or (SafeY(ny) != ny))){.forward = false;}
                    }else{
                        real nx = GetX(.caster)-GetX(.dummy);
                        real ny = GetY(.caster)-GetY(.dummy);
                        real mod = Sqrt(nx * nx + ny * ny);
                        if (mod < SPEED){
                            RemoveUnit(.dummy);
                            if (.knockback){
                                .dummy_active = false;
                            }else{
                                .active = false;
                            }
                        }else{
                            SetXY(.dummy, GetX(.dummy)+(nx/mod)*SPEED, GetY(.dummy)+(ny/mod)*SPEED);
                            SetUnitFacing(.dummy, bj_RADTODEG*GetAng(GetX(.dummy), GetY(.dummy), GetX(.caster), GetY(.caster)));
                        }
                    }
                }
                if (.knockback){
                    if (.fade_speed > 0){
                        .fade_speed = .fade_speed - KB_FADE_SPEED;
                        vec_x = .add_x/SPEED*.fade_speed;
                        vec_y = .add_y/SPEED*.fade_speed;
                        ForGroup(.g_kb, func Group_Act_Knockback);
                    }else{
                        if (.dummy_active){}else{
                            .active = false;
                        }
                    }
                }
            endmethod
        }
        
        private void main(){
            int i = 0;
            loop{ 
                spell current;
                exitwhen i > top
                current = stack[i];
                if (current.active){
                    current.Exec();
                    i++;
                }else{
                    current.Destroy();
                    stack[i] = stack[top];
                    top--;
                    if(top == -1){
                        PauseTimer(t);
                    }
                }
            }
            
        }
        
        private void events(){
            if (GetSpellAbilityId() == SPELL_ID){
                if (top < 8192){
                    unit c = GetSpellAbilityUnit();
                    spell.Create(c, SafeX(GetSpellTargetX()), SafeY(GetSpellTargetY()));
                    if(top == 0){
                        TimerStart(t, T_PERIOD, true, func main);
                    }
                }//top < 8192
            }
        }

        private void Init(){
            int i = 0;
            MaxX = GetRectMaxX(bj_mapInitialPlayableArea)
            MaxY = GetRectMaxY(bj_mapInitialPlayableArea)
            MinX = GetRectMinX(bj_mapInitialPlayableArea)
            MinY = GetRectMinY(bj_mapInitialPlayableArea)
            trigger t = CreateTrigger();
            loop{
                TriggerRegisterPlayerUnitEvent(t, Player(i++), ConvertPlayerUnitEvent(274), null); //spell
                exitwhen i == 16;
            }
            TriggerAddAction(t, func events);
            Preload(MODEL)
        }
    }
//***************************************************************************
v0.03
Изменения:
  1. Прокомментирована каждая строка, которая, в теории, может вызывать вопросы.
  2. Код в некоторых местах переписан.
код
    library hammer initializer Init{
    /*
    © XGM.GURU (01.10.2014)
                        By Buulichkaa
    */
        define{
        ////////////
        //SETTINGS//
        ////////////
            private DUMMY_ID = 'h001' //Равкод даммика
            private SPELL_ID = 'A000' //Равкод способности
            private SPEED = 15. //Скорость полета снаряда
            private MAX_DIST = 600. //Максимальная дистанция, которую сможет пролететь молот
            private RADIUS = 80. //Радиус вокруг молота, в котором наносится урон
            private MODEL = "Abilities\\Spells\\Human\\StormBolt\\StormBoltMissile.mdl" //Модель, которая создается на цели, при нанесении ей урона
            private ATTACK_TYPE = ATTACK_TYPE_NORMAL //Тип атаки
            private DAMAGE_TYPE = DAMAGE_TYPE_NORMAL //Тип урона
            private WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS //Тип оружия
            //advanced{
                //Настройки, которые лучше не менять, если не знаешь что делаешь
                private MAX_PLAYERS = 16 //Количество возможных игроков карты
                private T_PERIOD = .025 //Период таймера
                private AMOUNT = {(GetUnitAbilityLevel(damager, SPELL_ID)*50.) + 50.} //Количество урона
                private KNOCKBACK = MAX_DIST/2 //Расстояние, на которое будет отброшен юнит
                private KB_SPEED = 15. //Начальная скорость, с которой юнит будет отброшен
                //Переменной ниже можно установить свое константное значение
                //Оно будет отниматься от начальной скорости с каждым периодом
                //Сейчас установлено уравнение, которое зависит от установленного вами расстояния
                //Установив свое значение этой переменной вы игнорируете переменную KNOCKBACK
                //А расстояние, которое сможет пролететь отброшенный юнит, будет зависеть от начальной скорости
                private KB_FADE_SPEED = KB_SPEED/((KNOCKBACK*2/KB_SPEED)-1) //Скорость затухания отбрасывания за каждый "тик" таймера
            //}
        //Keywords
            private int = integer
            private void = nothing
            private bool = boolean
            private func = function
            private class = struct
            private insert_class_funcs = //Вставка функций выделения свободной ячейки массива
            {//+class funcs
                //Может я немного криво объясню эту часть, если непонятно - запишите на бумажке результаты переменных после нескольких вызовов
                //Говоря простым языком, это - генератор неповторяющихся чисел от 1 до (в данном случае) 8191
                private static integer C = 0 //Вершина списка, формально - это переменная, обозначающая максимальное количество активных элементов
                private static thistype r = 0 //Следующий элемент, который будет удален
                private thistype Next //Массив, который хранит элементы в порядке удаления

                static method New takes nothing returns thistype
                    thistype this 
                    if (r != 0) {
                        this = r;
                        r = r.Next;
                    } else {
                        ++C
                        this = C;
                    }
                    return this;
                endmethod

                method Remove takes nothing returns nothing
                    .Next = r;
                    r = this;
                endmethod
            //-class funcs
            }
        //***************************************************************************
        //Functions
            private msg(s) = DisplayTimedTextToPlayer(Player(0), 0, 0, 10, "|cFF00FFFFDEBUG|r " + s)
            private Sqrt(r) = SquareRoot(r)
            private GetX(u) = GetUnitX(u)
            private GetY(u) = GetUnitY(u)
            private SetXY(u, x, y) = { SetUnitX(u, x); SetUnitY(u, y) }
            private GetAng(x1, y1, x2, y2) = Atan2(y2 - y1, x2 - x1) //радианы
        //***************************************************************************
        }

        globals
            //передача данных в цикл ForGroup
                private player owner //Игрок-владелец кастера, который обрабатывается в циклах групп
                private group enum_g = CreateGroup() //Группа в которую добавляются юниты в радиусе действия
                private group kb_g //Юниты, которые отталкиваются
                private group damaged_g //Продамаженные юниты
                private bool forward_g
                private real vec_x //Направление в котором отталкиваются юниты
                private real vec_y
                private unit damager
            //

            private int stack[] //Массив хранения всех элементов
            private int top = -1 //Последний элемент
            
            private timer t = CreateTimer()
            //Границы игровой карты, для проверки координат
                private real MaxX
                private real MaxY
                private real MinX
                private real MinY
            //
        endglobals
        
        //Проверка координат юнитов
            private real SafeX(real x){
                if (x > MaxX){return MaxX;}
                elseif (x < MinX){return MinX;}
                return x
            }

            private real SafeY(real y){
                if (y > MaxY){return MaxY;}
                elseif (y < MinY){return MinY;}
                return y
            }
        //
        
        private bool filter(){ //Для фильтрации пикаемых в группу юнитов, для дальнейшего нанесения им урона\отталкивания
            return ((IsUnitEnemy(GetFilterUnit(), owner)) and (IsUnitInGroup(GetFilterUnit(), damaged_g) == false));
        }
        
        private void Group_Act_Damage(){ //Цикл нанесения урона юнитам
            UnitDamageTarget(damager, GetEnumUnit(), AMOUNT, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE);
            GroupAddUnit(damaged_g, GetEnumUnit());
            if forward_g{ //Проверка в какую сторону движется молот, добавляем в группу для отталкивания лишь при условии, что молот движется вперед
                GroupAddUnit(kb_g, GetEnumUnit());
            }
            DestroyEffect(AddSpecialEffect(MODEL, GetX(GetEnumUnit()), GetY(GetEnumUnit()))); //Добавление эффекта продамаженным юнитам
        }
        
        private void Group_Act_Knockback() { //Цикл отталкивания
            SetXY(GetEnumUnit(), SafeX(GetUnitX(GetEnumUnit())+vec_x), SafeY(GetUnitY(GetEnumUnit())+vec_y));
        }
        
        private class spell extends array {
            insert_class_funcs
            
            unit caster
            unit dummy
            group g //Группа, где хранятся уже продамаженные юниты
            group g_kb //Юниты, которых отталкивает молотом
            real add_x //Вектор для движения от кастера к точке каста
            real add_y 
            real fade_speed //Скорость затухания конкретного молота
            bool dummy_active //Существует ли даммик, бывают случаи, когда даммик удалился, а юниты все ещё отталкиваются, все зависит от KNOCKBACK
            bool forward //В какую сторону движется молот
            bool knockback //Активно ли отталкивание

            bool active //Активен ли экземпляр спелла
            
            static method Create takes unit c, real tx, real ty returns thistype
                real x = tx - GetX(c); //Получаем направление от точки каста к кастеру
                real y = ty - GetY(c);
                real mod = x * x + y * y;
                if (mod > SPEED*SPEED){
                    thistype new = thistype.New();
                    new.caster = c;
                    new.add_x = (x/Sqrt(mod))*SPEED; //Записываем вектор для движения молота от кастера
                    new.add_y = (y/Sqrt(mod))*SPEED;
                    new.forward = true;
                    new.dummy_active = true;
                    new.knockback = false;
                    new.fade_speed = KB_SPEED;
                    new.dummy = CreateUnit(GetOwningPlayer(c), DUMMY_ID, GetX(c), GetY(c), bj_RADTODEG*GetAng(GetX(c), GetY(c), tx, ty));
                    new.g = CreateGroup();
                    new.g_kb = CreateGroup();
                    top++;
                    stack[top] = new; //Помещаем новосозданный спелл в вершину стека
                    new.active = true;
                    return new;
                }else{
                    return -1;
                }
            endmethod
            
            method Destroy takes void returns void
                .caster = null;
                DestroyGroup(.g);
                DestroyGroup(.g_kb);
                .g = null;
                .dummy = null;
                .Remove();
            endmethod
            
            method Exec takes void returns void
                if .dummy_active {
                    owner = GetOwningPlayer(.caster);
                    damaged_g = .g;
                    GroupEnumUnitsInRange(enum_g, GetX(.dummy), GetY(.dummy), RADIUS, Condition(func filter)); //Пикаем юнитов
                    if (FirstOfGroup(enum_g) != null) {
                        forward_g = .forward;
                        damager = .caster;
                        kb_g = .g_kb;
                        ForGroup(enum_g, func Group_Act_Damage);
                        GroupClear(enum_g);
                        .knockback = .forward; //Если молот зацепил цель летя вперед, то активируется отталкивание
                    }
                    if .forward {
                        real nx = GetX(.dummy) + add_x; //Обсчитываем новые координаты даммика
                        real ny = GetY(.dummy) + add_y;
                        SetXY(.dummy, SafeX(nx), SafeY(ny));
                        real x = GetX(.dummy) - GetX(.caster);
                        real y = GetY(.dummy) - GetY(.caster);
                        real mod = x * x + y * y; //Обсчитываем расстояние от кастера к молоту
                        //Если оно больше чем максимальное или же молот коснулся границы карты, то устанавливаем движение назад
                        if ((mod > MAX_DIST*MAX_DIST) or ((SafeX(nx)!= nx) or (SafeY(ny) != ny))) {.forward = false;}
                    } else { //Молот движется назад
                        real nx = GetX(.caster)-GetX(.dummy); //Обсчитываем новые координаты даммика
                        real ny = GetY(.caster)-GetY(.dummy);
                        real mod = nx * nx + ny * ny;
                        if (mod < SPEED*SPEED) { //Если расстояние от даммика меньше скорости
                            RemoveUnit(.dummy);
                            if (.knockback) { //и включено отталкивание, то выключаем движение даммика, которого же и удаляем
                                .dummy_active = false;
                            } else {
                                .active = false; //если отталкивание неактивно, то помечаем экземпляр как готовый к удалению
                            }
                        } else {
                            SetXY(.dummy, GetX(.dummy)+(nx/Sqrt(mod))*SPEED, GetY(.dummy)+(ny/Sqrt(mod))*SPEED); //Если с расстоянием все впорядке, то двигаем даммик
                            SetUnitFacing(.dummy, bj_RADTODEG*GetAng(GetX(.dummy), GetY(.dummy), GetX(.caster), GetY(.caster))); //И разворачиваем его "лицом" к кастеру
                        }
                    }
                }
                if (.knockback){ //Активно ли отталкивание
                    if (.fade_speed > 0) { //Не иссяк ли импульс от отталкивания
                        .fade_speed = .fade_speed - KB_FADE_SPEED; //Отнимаем ещё "часть" импульса
                        vec_x = .add_x/SPEED*.fade_speed; //Обсчитываем координаты для всех отталкиваемых юнитов
                        vec_y = .add_y/SPEED*.fade_speed;
                        ForGroup(.g_kb, func Group_Act_Knockback); //Обрабатываем их в цикле
                    } else {
                        if (.dummy_active){}else { //Если импульс иссяк и даммик уже неактивен, то помечаем экземпляр спелла как неактивный
                            .active = false;
                        }
                    }
                }
            endmethod
        }
        
        private void main(){ //Функция обработки всех экземлпяров спелла
            int i = 0; //текущий обрабатываемый элемент в стеке
            loop{ 
                spell current; //текущий экземпляр спелла
                exitwhen i > top //выход, когда мы прошли весь стек
                current = stack[i];
                if (current.active) {
                    current.Exec();
                    i++;//важная часть, переходим к следующему элементу, лишь когда обработали предыдущий, при условии что мы нашли удаленный элемент
                    //переменная не увеличется, иначе мы не обработаем верхний элемент стека, которым закрыли дырку
                } else { //Если мы нашли удаленный изнутри элемент, то удаляем текущий, устанавливаем в текущую ячейку стека верхний, а также уменьшаем счетчик вершины на 1
                    current.Destroy();
                    stack[i] = stack[top];
                    top--;
                    if(top == -1){ //Паузим таймер, если экземпляры кончились
                        PauseTimer(t);
                    }
                }
            }
            
        }
        
        private void events(){
            if (GetSpellAbilityId() == SPELL_ID) {
                if (top < 8192) { //все же для приличия
                    unit c = GetSpellAbilityUnit(); //получаем кастера
                    spell.Create(c, SafeX(GetSpellTargetX()), SafeY(GetSpellTargetY())); //создаем новый экземпляр
                    if (top == 0) { //если это первый элемент в стеке, то запускаем таймер
                        TimerStart(t, T_PERIOD, true, func main);
                    }
                }//top < 8192
            }
        }

        private void Init(){
            int i = 0;
            MaxX = GetRectMaxX(bj_mapInitialPlayableArea) //Установка значений границ карты
            MaxY = GetRectMaxY(bj_mapInitialPlayableArea)
            MinX = GetRectMinX(bj_mapInitialPlayableArea)
            MinY = GetRectMinY(bj_mapInitialPlayableArea)
            trigger t = CreateTrigger();
            loop{
                TriggerRegisterPlayerUnitEvent(t, Player(i++), ConvertPlayerUnitEvent(274), null); //Событие, что юнит скастовал абилку, мана снята, кд запущен
                exitwhen i == MAX_PLAYERS;
            }
            TriggerAddAction(t, func events);
            Preload(MODEL); //загружаем нашу модель во время полосы загрузки, чтобы избежать минилага при первом касте
        }
    }
//***************************************************************************
`
ОЖИДАНИЕ РЕКЛАМЫ...
0
21
10 лет назад
0
Лучше не трогать формулу урона, если не знаешь, с чем имеешь дело, сейчас стоит 100\150\200\250, по заказу
0
18
10 лет назад
0
Что-то типо топоров у бестмастера
0
21
10 лет назад
0
Molecyla, у топоров хоть какой-то интересный аспект есть - само движение топоров, а тут совсем все плохо
2
28
10 лет назад
2
 static method New takes nothing returns thistype
                    thistype this
                    if (r == 0) {
                        ++iC
                        this = iC
                    }else{
                        this = r
                        r = r.rN
                    }
                    return this
                endmethod
чем стандартный .create() плох?
2
21
10 лет назад
Отредактирован Buulichkaa
2
nvc123, кастомизация \O/
а вообще - защита от повторного освобождения (и ещё некоторые методы) мне там нафиг не нужны, либа же полностью запривачена, даже таймер внутри :D
Это я ещё на чистом джассе не писал, времени не было особо =)
кстати, в v0.01 там при инициализации глобалки группа утекает, я, видимо копировал строку с предыдущей группой.
damaged_g сделана для передачи данных в forgroup цикл, а я ей значение присвоил)
7
28
10 лет назад
7
Buulichkaa, да ты и на бинарном коде мог написать походу
и если тебя так волнует оптимизация кода(что ты переписал 2 функции ради целого ифа) то вот те интересная инфа
квадратный корень высчитывается в 10 раз медленнее чем квадрат числа(в проверке что расстояние больше скорости выгоднее было бы возвести скорость в квадрат)
4
21
10 лет назад
4
nvc123, ну, не до такой степени, хотя все же перепишу :D четкая поправка
Чтобы оставить комментарий, пожалуйста, войдите на сайт.