Добавлен , опубликован
Алгоритмы, Наработки и Способности
Способ реализации:
Zinc
Тип:
Способность
Версия Warcraft:
1.26+

Blink Strike

MUI: да
Импорт: иконка
Утечки: нет
Требования: JNGP
Описание: Герой телепортируется к каждой цели на своём пути и наносит ей 100*уровень заклинания магического урона.
  • Имеет баф, для работы с другими заклинаниями
  • Прерывается оглушением и приказами игрока
  • Учитывает неуязвимость к магии
  • Гнев деревьев блокирует заклинание
  • Не использует даммикаст
  • Не паузит героя
  • Использует хэштаблицу для хранения id таймера



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

Перенос в свою карту
Способности
  • 'AEbs' Blink Strike (герой) - способность для героя
  • 'ABbs' Blink Strike (бафф) - способность для наложения бафа
Заклинания/Эффекты
  • 'BEbs' Blink Strike - бафф способности
Триггеры
  • SpellBlinkStrike
Импорт
  • ReplaceableTextures\CommandButtons\BTNBlinkStrike.blp
  • ReplaceableTextures\CommandButtonsDisabled\DISBTNBlinkStrike.blp
  • ReplaceableTextures\PassiveButtons\PASBlinkStrike.blp
  • ReplaceableTextures\CommandButtonsDisabled\DISPASBlinkStrike.blp
Настройка
constant integer AbilityID = 'AEbs'; // Равкод способности
constant integer AbilityBuffID = 'ABbs'; // Равкод способности для баффа
constant integer BuffID = 'BEbs'; // Равкод баффа

constant integer PathItemID = 'wolg'; // Предмет для нахождения финальной точки

constant real TimerPeriod = 0.25; // Время между блинками: 1/4 секунды.
constant integer DamageDistance = 64; // Расстояние захвата юнита

constant string EffectCasterHandsPath = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"; // Еффект, который крепится к "hand left" и "hand right"
constant string EffectBlinkTarget = "Abilities\\Spells\\NightElf\\Blink\\BlinkTarget.mdl"; // Еффект блинка для точки
constant string EffectBlinkCaster = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"; // Еффект блинка для кастера

constant string CasterAnimationName = "Attack 2"; // Анимация при ударе
constant real CasterAnimationDuration = 0.9; // Длительность анимации при ударе у модели
constant integer CasterOpacity = 150; // Прозрачность кастера на момент заклинания 0 - 255

constant real CasterAnimationScale = CasterAnimationDuration/TimerPeriod*1; // Не трогать

hashtable HT = InitHashtable(); // Хэштаблица для таймера
// Можете вписать туда вашу таблицу, например:
// hashtable HT = udg_HashTable;

// Текстаг над целью. Настроен на эмуляцию критического удара
function addTextTag(widget target, string text) {
    real x = GetWidgetX(target);
    real y = GetWidgetY(target);
    texttag tt = CreateTextTag();
    SetTextTagText(tt, text+"!", 0.024);
    SetTextTagPos(tt, x, y, 0.0);
    SetTextTagColor(tt, 255, 0, 0, 255);
    SetTextTagVelocity(tt, 0.0,  0.04);
    SetTextTagFadepoint(tt, 2.0);
    SetTextTagLifespan(tt, 5.0);
    SetTextTagVisibility(tt, IsVisibleToPlayer(x, y, GetLocalPlayer()));
    SetTextTagPermanent(tt, false);
    tt = null;
}

// Нанесение урона по цели
function onDamage(unit caster, unit target, integer level) {
    // caster - юнит, использующий способность
    // target - цель
    // level - уровень способности
    real damage = level*100; // считаем урон от уровня способности
    UnitDamageTarget(
        caster,
        target,
        damage,
        true, // является ли атакой
        false, // является ли дальним боем
        ATTACK_TYPE_MAGIC,
        DAMAGE_TYPE_NORMAL,
        WEAPON_TYPE_WHOKNOWS
    );
    addTextTag(target, I2S(R2I(damage)));
}

// Дальность заклинания
function getRange(integer level) -> integer {
    // level - уровень способности
    return 800 + level*100;
}

// Ограничить количество целей. Снять ограничение: -1;
function getTargetLimit(integer level) -> integer {
    // level - уровень способности
    return -1;
}

// Проверяем, возможно ли кастовать заклинание
function canBlink(unit caster) -> boolean {
    // caster - юнит, использующий способность
    return (
        GetUnitAbilityLevel(caster, 'BEer') == 0 // Гнев деревьев
    );
}

// Проверка целей
function checkTarget(unit caster, unit target) -> boolean {
    // caster - юнит, использующий способность
    // target - цель проверки
    return (
        caster != target // Не кастер
        &&
        !IsUnitType(target, UNIT_TYPE_STRUCTURE) // Не здание
        &&
        !IsUnitType(target, UNIT_TYPE_FLYING) // Не летающий
        &&
        !IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) // Восприимчив к магии
        &&
        IsPlayerEnemy(GetOwningPlayer(caster), GetOwningPlayer(target)) // Враг
    );
}
Код заклинания
//! zinc
library SpellBlinkStrike {
    constant integer AbilityID = 'AEbs'; // Равкод способности
    constant integer AbilityBuffID = 'ABbs'; // Равкод способности для баффа
    constant integer BuffID = 'BEbs'; // Равкод баффа
    
    constant integer PathItemID = 'wolg'; // Предмет для нахождения финальной точки
    
    constant real TimerPeriod = 0.25; // Время между блинками: 1/4 секунды.
    constant integer DamageDistance = 64; // Расстояние захвата юнита
    
    constant string EffectCasterHandsPath = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"; // Еффект, который крепится к "hand left" и "hand right"
    constant string EffectBlinkTarget = "Abilities\\Spells\\NightElf\\Blink\\BlinkTarget.mdl"; // Еффект блинка для точки
    constant string EffectBlinkCaster = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"; // Еффект блинка для кастера
    
    constant string CasterAnimationName = "Attack 2"; // Анимация при ударе
    constant real CasterAnimationDuration = 0.9; // Длительность анимации при ударе у модели
    constant integer CasterOpacity = 150; // Прозрачность кастера на момент заклинания 0 - 255
    
    constant real CasterAnimationScale = CasterAnimationDuration/TimerPeriod*1; // Не трогать

    hashtable HT = InitHashtable(); // Хэштаблица для таймера
    // Можете вписать туда вашу таблицу, например:
    // hashtable HT = udg_HashTable;
    
    // Текстаг над целью. Настроен на эмуляцию критического удара
    function addTextTag(widget target, string text) {
        real x = GetWidgetX(target);
        real y = GetWidgetY(target);
        texttag tt = CreateTextTag();
        SetTextTagText(tt, text+"!", 0.024);
        SetTextTagPos(tt, x, y, 0.0);
        SetTextTagColor(tt, 255, 0, 0, 255);
        SetTextTagVelocity(tt, 0.0,  0.04);
        SetTextTagFadepoint(tt, 2.0);
        SetTextTagLifespan(tt, 5.0);
        SetTextTagVisibility(tt, IsVisibleToPlayer(x, y, GetLocalPlayer()));
        SetTextTagPermanent(tt, false);
        tt = null;
    }
    
    // Нанесение урона по цели
    function onDamage(unit caster, unit target, integer level) {
        // caster - юнит, использующий способность
        // target - цель
        // level - уровень способности
        real damage = level*100; // считаем урон от уровня способности
        UnitDamageTarget(
            caster,
            target,
            damage,
            true, // является ли атакой
            false, // является ли дальним боем
            ATTACK_TYPE_MAGIC,
            DAMAGE_TYPE_NORMAL,
            WEAPON_TYPE_WHOKNOWS
        );
        addTextTag(target, I2S(R2I(damage)));
    }
    
    // Дальность заклинания
    function getRange(integer level) -> integer {
        // level - уровень способности
        return 800 + level*100;
    }
    
    // Ограничить количество целей. Снять ограничение: -1;
    function getTargetLimit(integer level) -> integer {
        // level - уровень способности
        return -1;
    }
    
    // Проверяем, возможно ли кастовать заклинание
    function canBlink(unit caster) -> boolean {
        // caster - юнит, использующий способность
        return (
            GetUnitAbilityLevel(caster, 'BEer') == 0 // Гнев деревьев
        );
    }
    
    // Проверка целей
    function checkTarget(unit caster, unit target) -> boolean {
        // caster - юнит, использующий способность
        // target - цель проверки
        return (
            caster != target // Не кастер
            &&
            !IsUnitType(target, UNIT_TYPE_STRUCTURE) // Не здание
            &&
            !IsUnitType(target, UNIT_TYPE_FLYING) // Не летающий
            &&
            !IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) // Восприимчив к магии
            &&
            IsPlayerEnemy(GetOwningPlayer(caster), GetOwningPlayer(target)) // Враг
        );
    }
    
    //
    // Заклинание
    //
    
    // Polar - https://xgm.guru/p/wc3/polar
        function GetPolarOffsetX(real x, real distance, real angle) -> real {
            return x + distance * Cos(angle * bj_DEGTORAD);
        }
        function GetPolarOffsetY(real y, real distance, real angle) -> real {
            return y + distance * Sin(angle * bj_DEGTORAD);
        }
        function DistanceBetweenCoords(real x1, real y1, real x2, real y2) -> real {
            real dx = x2 - x1;
            real dy = y2 - y1;
            return SquareRoot(dx*dx + dy*dy);
        }
        function AngleBetweenCoords(real x1, real y1, real x2, real y2) -> real {
            return bj_RADTODEG * Atan2(y2 - y1, x2 - x1);
        }
        function AngleDifference(real a1, real a2) -> real {
            real x;
            a1 = ModuloReal(a1, 360);
            a2 = ModuloReal(a2, 360);
            if (a1 > a2) {
                x = a1;
                a1 = a2;
                a2 = x;
            }
            x = a2 - 360;
            if (a2 - a1 > a1 - x){
                a2 = x;
            }
            return RAbsBJ(a1 - a2);
        }
        function Perpendicular (real Xa, real Ya, real Xb, real Yb, real Xc, real Yc) -> real {
            return SquareRoot((Xa - Xc) * (Xa - Xc) + (Ya - Yc) * (Ya - Yc)) * Sin(Atan2(Yc-Ya,Xc-Xa) - Atan2(Yb-Ya,Xb-Xa));
        }
    // endPolar
    
    function isUnitAlive(unit target) -> boolean{
        return GetWidgetLife(target) > 0.405;
    }
    
    struct data {
        unit caster, target[8190];
        integer targetI, targetC, limit, level, timerID;
        real xc, yc, xt, yt, angle;
        effect effectCaster[2];
        boolean isCancel;
        
        static method create(unit caster, real xt, real yt) -> data {
            data this = data.allocate();
            item it;
            integer level = GetUnitAbilityLevel(caster, AbilityID);
            real xc = GetUnitX(caster);
            real yc = GetUnitY(caster);
            real distance = DistanceBetweenCoords(xc, yc, xt, yt);
            real angle = AngleBetweenCoords(xc, yc, xt, yt);
            real range = I2R(getRange(level));
            unit u, utemp;
            integer i, j, imin;
            timer t;
            integer tid;
            group g = CreateGroup();
            if (distance > range){
                xt = GetPolarOffsetX(xc, range, angle);
                yt = GetPolarOffsetY(yc, range, angle);
            }
            range = RMinBJ(distance, range);
            
            it = CreateItem(PathItemID, xt, yt);
            
            this.isCancel = false;
            this.level = level;
            this.limit = getTargetLimit(this.level);
            this.caster = caster;
            this.xc = xc;
            this.yc = yc;
            this.xt = GetItemX(it);
            this.yt = GetItemY(it);
            RemoveItem(it); it = null;
            
            this.angle = AngleBetweenCoords(this.xc, this.yc, this.xt, this.yt);
            
            this.effectCaster[0] = AddSpecialEffectTarget(EffectCasterHandsPath, this.caster, "hand left");
            this.effectCaster[1] = AddSpecialEffectTarget(EffectCasterHandsPath, this.caster, "hand right");
            
            GroupEnumUnitsInRange(g, xc, yc, range + DamageDistance, Filter(function() -> boolean {
                return isUnitAlive(GetFilterUnit());
            }));
            
            this.targetI = -1;
            this.targetC = -1;
            
            while(true){
                u = FirstOfGroup(g);
                if (u == null) {break;}
                if (
                    checkTarget(this.caster, u)
                    &&
                    R2I(RAbsBJ(Perpendicular(this.xc, this.yc, this.xt, this.yt, GetUnitX(u), GetUnitY(u)))) <= DamageDistance
                    &&
                    AngleDifference(AngleBetweenCoords(this.xc, this.yc, this.xt, this.yt), AngleBetweenCoords(this.xc, this.yc, GetUnitX(u), GetUnitY(u))) < 60
                ){
                    this.targetI = this.targetI + 1;
                    this.target[targetI] = u;
                }
                GroupRemoveUnit(g, u);
            }
            DestroyGroup(g); g = null; u = null;
            
            SetUnitPathing(this.caster, false);
            SetUnitTimeScale(this.caster, CasterAnimationScale);
            SetUnitVertexColor(this.caster, 255, 255, 255, CasterOpacity);
            UnitAddAbility(this.caster, AbilityBuffID);
            
            if (this.targetI >= 0){
                t = CreateTimer();
                tid = GetHandleId(t);
                i = 0;
                while(i < this.targetI){
                    imin = i;
                    j = i + 1;
                    while(j <= this.targetI){
                        if (
                            DistanceBetweenCoords(this.xc, this.yc, GetUnitX(this.target[j]), GetUnitY(this.target[j]))
                            <
                            DistanceBetweenCoords(this.xc, this.yc, GetUnitX(this.target[imin]), GetUnitY(this.target[imin]))
                        ){
                            imin = j;
                        }
                        j = j + 1;
                    }
                    utemp = this.target[i];
                    this.target[i] = this.target[imin];
                    this.target[imin] = utemp;
                    i = i + 1;
                }
                
                SaveInteger(HT, tid, 0, this);
                TimerStart(t, TimerPeriod, true, function data.callback);
            } else {
                this.destroy();
            }
            
            utemp = null;
            t = null;
            return this;
        }
        
        method destroy(){
            integer i;
            
            SetUnitPathing(this.caster, true);
            UnitRemoveAbility(this.caster, AbilityBuffID);
            UnitRemoveAbility(this.caster, BuffID);
            
            if (!this.isCancel){
                SetUnitX(this.caster, this.xt);
                SetUnitY(this.caster, this.yt);
                DestroyEffect(AddSpecialEffectTarget(EffectBlinkCaster, this.caster, "origin"));
            }
            
            SetUnitTimeScale(this.caster, 1);
            if (isUnitAlive(this.caster)){
                SetUnitAnimation(this.caster, "stand");
            } else {
                SetUnitAnimation(this.caster, "death");
            }
            SetUnitVertexColor(this.caster, 255, 255, 255, 255);
            
            for(0 <= i <= 1){
                DestroyEffect(this.effectCaster[i]);
                this.effectCaster[i] = null;
            }
            for(0 <= i <= this.targetI){
                this.target[i] = null;
            }
            
            this.caster = null;
            this.deallocate();
        }
        
        static method callback(){
            timer t = GetExpiredTimer();
            integer tid = GetHandleId(t);
            data this = LoadInteger(HT, tid, 0);
            integer order = GetUnitCurrentOrder(this.caster);
            this.targetC = this.targetC + 1;
            
            if ((order != 0 && order != 851983 /* attack */ && order != 851972 /* stop */) || !isUnitAlive(this.caster)){
                this.isCancel = true;
            }

            if (this.targetC > this.targetI || this.isCancel || (this.limit >= 0 && this.targetC >= this.limit)){
                this.destroy();
                PauseTimer(t);
                DestroyTimer(t);
                FlushChildHashtable(HT, tid);
            } else {
                if (isUnitAlive(this.target[targetC])){
                    onDamage(this.caster, this.target[targetC], this.level);
                    SetUnitX(this.caster, GetUnitX(this.target[targetC]));
                    SetUnitY(this.caster, GetUnitY(this.target[targetC]));
                    IssueImmediateOrderById(this.caster, 851972 /* stop */);
                    SetUnitAnimation(this.caster, CasterAnimationName);
                }
                this.target[targetC] = null;        
            }
            
            t = null;
        }
    }
    
    function onInit(){
        integer i;
        trigger t1 = CreateTrigger();
        trigger t2 = CreateTrigger();
        
        for (0 <= i < bj_MAX_PLAYER_SLOTS){
            TriggerRegisterPlayerUnitEvent(t1, Player(i), EVENT_PLAYER_UNIT_SPELL_CAST, null);
            TriggerRegisterPlayerUnitEvent(t2, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null);
        }
        TriggerAddCondition(t1, Filter(function() -> boolean {
            if (GetSpellAbilityId() == AbilityID && GetUnitAbilityLevel(GetTriggerUnit(), AbilityBuffID) == 0){
                DestroyEffect(AddSpecialEffect(EffectBlinkTarget, GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit())));
            }
            return false;
        }));
        TriggerAddCondition(t2, Filter(function() -> boolean {
            if (
                GetSpellAbilityId() == AbilityID
                &&
                GetUnitAbilityLevel(GetTriggerUnit(), AbilityBuffID) == 0
                &&
                canBlink(GetTriggerUnit())
            ){
                data.create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY());
            }
            return false;
        }));
        
        t1 = null; t2 = null;
    }
}
//! endzinc
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
5
32
5 лет назад
Отредактирован quq_CCCP
5
NazarPunk, как почему, есть идеи как это сделать еще? С тем функционалом что подарил нам лич можно без проблем делать абилки по всем правилам варкравта, всякие копии атаки, модификаторы, и так далее, все что есть в игре у стандартных абилок - есть и у вас, неужели это так плохо?
Если вам жмет кошелек то можно найти с++ программиста, который напишет вам ддл и у вас будет то чего до этого в игре даже близко не было. Не надо ни каких лаунчеров, хренаунчеров, все работает как есть.
Спел довольно прикольно выглядит, но по мне ты усложнил его через чур.
ClotPh, можно вручать ядовитые стрелы герою и через TrowSpell кидать на врагов, механика будет как у вивера в доте.
2
29
5 лет назад
2
неужели это так плохо?
Лезть напрямую в движок без API плохой тон.
quq_CCCP:
но по мне ты усложнил его через чур.
Чего ж я такого черезчур наовэркодил, если не секрет?
4
32
5 лет назад
4
Лезть напрямую в движок без API плохой тон.
Мне кажется у нас тут появилось латентное сообщество чистокодеров, который считают использование мемхака "Дурным тоном", и только лишь 1.30 вар (плоская земля), является эталоном красоты
А что дальше?
Zinc - дурной тон
Vjass - дурной тон
Чистый Jass - дурной тон

Только GUI и дефолтные скилы = истинные инструменты Вар3Модмейкера

так и до милишных карт не далеко
0
29
5 лет назад
0
Мне кажется у нас тут появилось латентное сообщество чистокодеров
Должны же быть люди, которые против пихания мемхака всюду по делу и без. Да и предоставлять доступ к своему WinApi абы кому я не горю желанием(
2
32
5 лет назад
2
NazarPunk:
неужели это так плохо?
Лезть напрямую в движок без API плохой тон.
quq_CCCP:
но по мне ты усложнил его через чур.
Чего ж я такого черезчур наовэркодил, если не секрет?
Ну как по мне, уж много импортных библиотек которые бы если честно "не усрались", про твой личный код ничего говорить не буду, тут на вкус и цвет все фломастеры разные, а вот таймер утилс - мусор, драколич подтвердит. Таймер самый легкий хендл в игре, аттач и ресайкл работает медленне чем просто создание таймера + хештаблица. Потом ты делаешь спелл на показ, в своей карте хоть на gamecache + RB, тут на публику.
Лично я считаю дурным тоном пихать 100500 сторонних либ в спеллы на показ + еще и сомнительные наработки.
NazarPunk, Насчет мемхака:
1 - ты сам себе осмысленно собрался выстрелить в ногу?
2 - если бы хотели, вы бы уже сидели и смотрели на надпись на экране "Перевидите 100usd на кошлек xxxxxxxxxxxxxxxxx, иначе вашим фалам хана!" а так это никому не интересно, потом написать уж прям тру вирус - не так уж и просто, потом "ЗАЧЕМ" - ? Ради потроллить пару человек сидеть и кодить много кода? Ну те кто страдает такими вот шуточками, на это попросту неспособен, а тот кто способен подобным никогда заниматься не будет.
0
22
5 лет назад
0
Bergi_Bear:
Лезть напрямую в движок без API плохой тон.
Мне кажется у нас тут появилось латентное сообщество чистокодеров, который считают использование мемхака "Дурным тоном", и только лишь 1.30 вар (плоская земля), является эталоном красоты
А что дальше?
Zinc - дурной тон
Vjass - дурной тон
Чистый Jass - дурной тон

Только GUI и дефолтные скилы = истинные инструменты Вар3Модмейкера

так и до милишных карт не далеко
Я из тех кто считает хаки и даже в какой-то степени vJass дурным тоном.
0
29
5 лет назад
0
Лично я считаю дурным тоном пихать 100500 сторонних либ в спеллы на показ + еще и сомнительные наработки.
В следующий раз нужные функции из либ в спэлл скопирую.
quq_CCCP:
а вот таймер утилс - мусор
Поверю на слово. Только нужно будет подумать, как структуру к таймеру приаттачить.
quq_CCCP:
Насчет мемхака
Все фломастеры разные))
1
28
5 лет назад
Отредактирован PT153
1
Как приаттачить: кину код через некоторое время.

Вот код. Добавил также функцию по дебагу, она не десинкает по сети.
раскрыть
globals
    key TimerParentKey
    constant hashtable Hash = InitHashtable()
endglobals

function DebugMsg takes string s returns nothing
    debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0., 0., 5., s)
endfunction

function GetCustomTimer takes nothing returns integer
    return LoadInteger(Hash, TimerParentKey, GetHandleId(GetExpiredTimer()))
endfunction

function BindTimerToStruct takes timer t, integer this returns nothing
    call SaveInteger(Hash, TimerParentKey, GetHandleId(t), this)
endfunction

function UnbindTimerFromStruct takes timer t returns nothing
    call PauseTimer(t)
    call RemoveSavedInteger(Hash, TimerParentKey, GetHandleId(t))
    call DestroyTimer(t)
endfunction

//! textmacro CustomTimer takes var, fname, name
    readonly timer $var$

    private method Init$fname$ takes nothing returns nothing
        set $var$ = CreateTimer()
        call BindTimerToStruct($var$, this)
        //call DebugMsg("$fname$ " + I2S(this) + " of $name$ is created.")
    endmethod
        
    private method Delete$fname$ takes nothing returns nothing
        call UnbindTimerFromStruct($var$)
        set $var$ = null
        //call DebugMsg("$fname$ " + I2S(this) + " of $name$ is deleted.")
    endmethod
//! endtextmacro

//! textmacro CustomTimerSafe takes var, fname, name
    readonly timer $var$

    private method Init$fname$ takes nothing returns nothing
        if $var$ == null then
            set $var$ = CreateTimer()
            call BindTimerToStruct($var$, this)
            //call DebugMsg("$fname$ " + I2S(this) + " of $name$ is created.")
        endif
    endmethod
        
    private method Delete$fname$ takes nothing returns nothing
        if $var$ != null then
            call UnbindTimerFromStruct($var$)
            set $var$ = null
            //call DebugMsg("$fname$ " + I2S(this) + " of $name$ is deleted.")
        endif
    endmethod
//! endtextmacro
Пример использования.
раскрыть
struct TowerAbility
    static Tower Caster

    TAD data
    Tower caster
    
    static method create takes Tower m, integer T returns thistype
        local thistype this = allocate()
        set data = TAD[T]
        set caster = m
        set m.abil = this
        call UnitAddAbility(m.tower, data.info_id)
        return this
    endmethod
    
    stub method incLevel takes nothing returns nothing
    endmethod
endstruct

struct AbilGoldMining extends TowerAbility
    static constant real period = 1.
    // Defaults
    static constant integer defincome = 1
    // Increments
    static constant integer incincome = 1
    // Additions
    static constant integer b1_income = 1
    static constant integer b2_income = 3
    
    integer income = defincome
    CustomPlayer owner

//! runtextmacro CustomTimer("t", "Timer", "AbilGoldMining")

    method onDestroy takes nothing returns nothing
        call DeleteTimer()
    endmethod
    
    method incLevel takes nothing returns nothing
        set income = income + incincome
        if caster.level == data.b1 then
            set income = income + b1_income
        elseif caster.level == data.b2 then
            set income = income + b2_income
            call caster.removeSelfUpgrade()
        endif
    endmethod
    
    static method addGold takes nothing returns nothing
        local thistype this = GetCustomTimer()
        call owner.addGold(income)
        call owner.createGoldTextUnit(I2S(income), caster.tower)
    endmethod
    
    static method init takes nothing returns nothing
        local Tower m = Caster
        local thistype this = allocate(m, AbilGoldMiningId)
        set owner = m.owner
        call InitTimer()
        call TimerStart(t, period, true, function thistype.addGold)
        call DebugMsg("AbilGoldMining " + I2S(this) + " is created.")
    endmethod
endstruct
0
32
5 лет назад
0
NazarPunk, сколько ты на сайте знаешь людей, делающих карты на мемхаке, едины просто, а сколько тех кто вирус в состоянии написать?
quq_CCCP, поддерживаю, еще бы в 16 майнеров с шифровальщиками написали и гг гарене
Мемхак = максимальный функционал, и я считаю дурным тоном это не использование этого функционала, а эту способность в примере можно и на гуи сделать, чтобы визуально было так же, зачем вообще делать то, что уже было сделано, когда можно просто заняться новым, даже в варкрафте
1
28
5 лет назад
1
Считаю дурным тоном называть что-то дурным тоном.
0
32
5 лет назад
0
Zahanc, дурно тон использовать мап хака и читов для достижения преимуществ над над соперником, МЕМХАК это всего лишь функционал
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.