Добавлен , опубликован
Алгоритмы, Наработки и Способности
Способ реализации:
cJass
Тип:
Способность
Версия Warcraft:
1.26+
Требования: JNGP (1.26-1.28) / SharpCraft (1.28.2+)
Список изменений
Версия 1.1+
  1. исправлен маленький баг, когда при шансе chance выпадало число chance и способность не срабатывала (пропустил в условии <= меньше ИЛИ РАВНО);
  2. высота снаряда при появлении теперь также зависит от высоты кастера.
Версия 1.1
  1. убраны все требования, кроме JNGP;
  2. убрана вариативность использования урона из-за ненадобности/собственной надстройки;
  3. добавлена возможность блокировать способности, чтобы те не вызывали эффект;
  4. добавлена возможность вызывать эффект исключительно разрешенным способностям;
  5. добавлен шанс срабатывания (по умолчанию – 100%);
  6. чуть больше комментариев.
Небольшая наработка немного импровизированной способности. cJass + MUI.
Идея by A.W.K., реализация Atesla. Некоторые наработки взяты из карты NazarPunk для более углубленной настройки библиотеки.
Инструкция по импорту:
  1. скопировать способность "Парад огня" и юнита "Дамми_Фаербол";
  2. скопировать папку со скриптами "Spell";
  3. настроить в библиотеке id способности и дамми, если они отличны от оригинала;
  4. выдать способность необходимым юнитам;
  5. опционально: настроить разрешенные/заблокированные способности, которые вызывают эффект "Парада огня", функции:
AddSpellActive(id способности) / AddSpellBlocked(id способности)
Примечание: у AddSpellActive() приоритет над AddSpellBlocked(), поэтому нужно использовать что-то одно.
Описание:
При использовании ЛЮБОЙ/РАЗРЕШЕННОЙ (в зависимости от настройки) способности мгновенно выпускает в случайного врага в радиусе radius (по умолчанию – 800м) огненный шар. Шар наносит урон, зависящий от потраченной на заклинание маны. Пассивная способность.
В карте для примера были заблокированы способности: Огненный столб (AHfs), Винные пары (ANdh), Леденящий крик (ANht)
Код способности
#include "cj_types.j"
#include "cj_antibj_base.j"
native UnitAlive takes unit id returns boolean

library FireBall initializer Init {

    /* Эти переменные не трогаем */
    private timer fb_timer = CreateTimer(); // Обязательная переменная. Глобальный таймер, который проходит по массиву
    private timer fb_timer_mana = CreateTimer(); // Таймер для маны
    private int fb_count = 0; // Количество срабатываний способности. Для поддержки MUI
    private bool fb_timer_active = false; // Активен ли таймер в данный момент
    private float fb_mana[]; // Сюда заносим ману
    private unit fb_unit[]; // Здесь хранятся кастеры
    private int spells_blocked[]; // Сюда заносим способности, которые не срабатывают с пассивкой
    private int spells_blocked_count = 0; // Кол-во заблоченных способностей
    private int spells_active[]; // Сюда заносятся способности, которые срабатывают с пассивкой. Игнорирует переменную выше
    private int spells_active_count = 0; // Кол-во срабатываемых способностей
    
   /* Настраиваемые переменные */
    private constant integer id = 'u000'; // Дамми-снаряд
    private constant player owner = Player( PLAYER_NEUTRAL_PASSIVE ); // Владелец дамми-снаряда
    private constant int pf = 'APOF'; // ID необходимой способности
    private constant int chance = 100; // Шанс срабатывания способности в процентах, от 1 до 100
    private constant string eff = "Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl"; // Эффект при взрыве шара
    private constant string att = "chest"; // На каком участке тела взрывается эффект
    private constant float period = .03125; // Период таймера снаряда: 1/32 секунды
    private constant float speed = 800. / ( 1. / period ); // Расстояние, которое снаряд пройдёт за каждый тик таймера
    private constant float radius = 800.; // Радиус применения шара
    private constant float distance = 50.; // Необходимое расстояние между шаром и целью, чтобы шар взорвался и нанес урон
    private constant float z = 75.; // Высота появления снаряда
    
    private void AddSpellBlocked( int spell ) {
        spells_blocked[spells_blocked_count] = spell;
        spells_blocked_count++;
    }
    
    private void AddSpellActive( int spell ) {
        spells_active[spells_active_count] = spell;
        spells_active_count++;
    }
    
    private float DistanceBetweenCoords( float x1, float y1, float x2, float y2 ) { // Расстояние между двумя точками
        float dx = x2 - x1;
        float dy = y2 - y1;
        return SquareRoot( dx * dx + dy * dy );
    }
    
    private void onDamage( unit hero, unit enemy, float damage ) { // Нанесение урона и вызов эффекта
        UnitDamageTarget( hero, enemy, damage, false, true, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_FIRE, null );
        DestroyEffect( AddSpecialEffectTarget( eff, enemy, att ) );
    }
    
    private void GetAbilityManaCost() { // Проходимся по массиву и отмеряем ману, передаем ее в стуктуру
        int count = fb_count - 1;
        loop {
        exitwhen count < 0
            fb_mana[count] = fb_mana[count] - GetUnitState( fb_unit[count], UNIT_STATE_MANA ) + 0.5;
            fb.new( fb_unit[count], count );
            count--;
        }
        if count < 0 {
            fb_count = 0;
            fb_timer_active = false;
            PauseTimer( fb_timer_mana );
        }
    }

    struct fb extends array{ // Структура способности
        unit caster;
        unit dummy;
        unit target;
        float mana_cost;
        float x;
        float y;

        static thistype count = 0;

        static thistype new( unit caster, int count_fb ) {
			thistype this = count;
            count++;
            this.caster = caster;
            this.x = GetUnitX( caster );
            this.y = GetUnitY( caster );
            this.target = null;
            this.mana_cost = I2R( R2I( fb_mana[count_fb] ) );
            group G = CreateGroup();
            unit enemy = null;
            player pc = GetOwningPlayer( caster );
            GroupEnumUnitsInRange( G, x, y, radius, null );
            loop{
                enemy = GroupPickRandomUnit( G );
                if ( UnitAlive( enemy ) && IsPlayerEnemy( GetOwningPlayer( enemy ), pc ) ){
                    this.target = enemy;
                    this.dummy = CreateUnit( owner, id, x, y, GetUnitFacing( this.caster ) );
                    SetUnitState( this.dummy, UNIT_STATE_MANA, GetUnitState( this.dummy, UNIT_STATE_MAX_MANA ) );
                    SetUnitFlyHeight( this.dummy, GetUnitFlyHeight( caster ) + z, 0. );
                }
                GroupRemoveUnit( G, enemy );
            exitwhen ( this.target != null || enemy == null )
            }
            if ( this.dummy == null ) { delete(); }
            GroupClear( G );
            DestroyGroup( G );
            G = null;
            return 0;
        }
        
        bool continue( float x, float y, float xc, float yc ) { return ( DistanceBetweenCoords( x, y, xc, yc ) <= distance || !UnitAlive( target ) ) }
        bool actions() {
            float xd = GetUnitX( dummy );
            float yd = GetUnitY( dummy );
            float xt = GetUnitX( target );
            float yt = GetUnitY( target );
            if ( continue( xd, yd, xt, yt ) ) { return delete(); }
            float zt = GetUnitFlyHeight( target ) + z;
            float a = Atan2( yt - yd, xt - xd );
            float d = DistanceBetweenCoords( xd, yd, xt, yt );
            SetUnitX(dummy, xd + speed * Cos( a ) );
            SetUnitY(dummy, yd + speed * Sin( a ) );
            SetUnitFlyHeight( dummy, d + zt, 1000. );
            SetUnitFacing( dummy, a );
            return true;
        }

        bool delete() {
            RemoveUnit( dummy );
            if ( UnitAlive( target ) ) { onDamage( caster, target, mana_cost ); }
            caster = null;
            dummy = null;
            target = null;
            count--;
            if ( ( this ) < ( count ) ){
                caster = count.caster;
                dummy = count.dummy;
                target = count.target;
                mana_cost = count.mana_cost;
                x = count.x;
                y = count.y;
                return false;
            }
            return true;
        }

    }
    
    private void update() {
        fb i = fb.count - 1;
        loop{
        exitwhen i < 0
            if ( i.actions() ) { i--; }
        }
    }

    private void FireBall_Init() { // Функция, вызываемая при срабатывании
        if ( GetRandomInt( 1, 100 ) <= chance ) {
            fb_unit[fb_count] = GetTriggerUnit();
            fb_mana[fb_count] = GetUnitState( fb_unit[fb_count], UNIT_STATE_MANA );
            fb_count++;
            if !fb_timer_active {
                fb_timer_active = true;
                TimerStart( fb_timer_mana, .010, true, function GetAbilityManaCost );
            }
        }
    }
    private bool GetFBSpells( int spell ) {
        int count = 0;
        bool have_spell = false;
        if ( spells_active_count > 0 ) {
            count = spells_active_count - 1;
            loop{
            exitwhen have_spell || count < 0
                if ( spells_active[count] == spell ) { have_spell = true; }
                count--;
            }
        return have_spell;
        } if ( spells_blocked_count > 0 ) {
            have_spell = true;
            count = spells_blocked_count - 1;
            loop{
            exitwhen !have_spell || count < 0
                if ( spells_blocked[count] == spell ) { have_spell = false; }
                count--;
            }
        } else { have_spell = true; }
        return have_spell;
    }
    private bool FireBall_Cond()  { return ( GetUnitAbilityLevel( GetTriggerUnit(), pf ) > 0 && GetFBSpells( GetSpellAbilityId() ) ); }

    private void Init() {
        DoNotSaveReplay();
        FogEnable( false );
        FogMaskEnable( false );
        Preload( eff );
        
        AddSpellBlocked( 'AHfs' ); AddSpellBlocked( 'ANdh' ); AddSpellBlocked( 'ANht' );
        
        trigger t = CreateTrigger();
        TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT );
        TriggerAddCondition( t, Condition( function FireBall_Cond ) );
        TriggerAddAction( t, function FireBall_Init );
        TimerStart( fb_timer, period, true, function update );
    }

}
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
0
8
5 лет назад
Отредактирован Atesla
0
PrincePhoenix:
Atesla, наработки делаются для людей, чтобы упростить ту или иную задачу.
Здесь всё только усложнили, да и при чём она актуальна только для 1.26
Потому и делалось для 1.26 версии, т.к. на более новых патчах такая реализация уже на блюдечке от самих Близзов.
UPD: Впрочем, у кого будет идея безболезненно и без багов сделать систему, которая узнает затраты маны на спеллы, то можно писать сюда.
0
32
5 лет назад
0
я как то делал но не могу вспомнить, на ум приходит только периодом 0,01 чекать количество маны текущее, и сравнивать изменения, как только изменение произошло значит мана потратилась, не думаю что это будет накладно для 10 героев, врядли способность будет более чем у 1 героя
0
8
5 лет назад
0
Bergi_Bear:
я как то делал но не могу вспомнить, на ум приходит только периодом 0,01 чекать количество маны текущее, и сравнивать изменения, как только изменение произошло значит мана потратилась, не думаю что это будет накладно для 10 героев, врядли способность будет более чем у 1 героя
Я бы хотел так сделать и не париться, но тут можно легко вызвать баг при одновременном касте спеллов (например, один юзает скилл, другой восстанавливает ману). Сами люди обсуждали это и выясняли. Если честно, я не хочу лезть на грабли.
Кому надо будет: xgm.guru/p/wc3/153351?postid=285485
0
28
5 лет назад
Отредактирован Феникс
0
Ну вот насколько 0.03 маны стоят внедрения MH и одновременно ломания поддержки всех новых патчей?
Если перевести это по уму в jass вообще избежишь любых проблем абуза
Загруженные файлы
0
29
5 лет назад
Отредактирован nazarpunk
0
Хорошо, что убрали мемхак, теперь люди смогут нормально в свою карту импортировать.
3
8
5 лет назад
3
NazarPunk:
Хорошо, что убрали мемхак, теперь люди смогут нормально в свою карту импортировать.
Счас еще и гифка подъедет :D
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.