Spell Optic Blast

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Optic Blast is more or less [pretty much =)] a clone of the Starcraft 2's Colossus Thermal Lance attack.
The underlying struct requires feeding it some points and other data after that it caries the actions.

Required libraries:

TimerUtils
GroupUtils
LightningRecycle
TimedEffect

JASS:
//! zinc

library opticblast requires TimerUtils, GroupUtils, LightningRecycle, TimedEffect
{
    constant integer    ENERGY_BEAM_COUNT                = 4;
    constant real       BEAM_MOVEMENT_UPDATES_PER_SECOND = 32;
    constant string     DEFAULT_SOUND_LOCATION           = "Abilities\\Spells\\Human\\AerialShackles\\MagicLariatLoop1.wav";
    constant string     DEFAULT_EFFECT_LOCATION          = "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl";
    constant attacktype ATTACK_TYPE                      = ATTACK_TYPE_NORMAL;
    constant damagetype DAMAGE_TYPE                      = DAMAGE_TYPE_NORMAL;

    location loc = Location(0, 0);
    unit     tmpu = null;

    boolexpr filter;
    function targets_allowed() -> boolean
    {
        unit u = GetFilterUnit();
        boolean b;
        b = IsUnitEnemy(u, GetOwningPlayer(tmpu));

        return b;
    }

    struct sounddata
    {
        sound s;
    }

    function StopLoopingSoundTimed()
    {
        timer t = GetExpiredTimer();
        sounddata sd = GetTimerData(t);
        StopSound(sd.s, false, false);
        sd.destroy();
        ReleaseTimer(t);
    }

    function PlayLoopingSoundTimed(string file_location, real x, real y, real z, real duration, integer volume)
    {
        timer t = NewTimer();
        sounddata sd = sounddata.create();
        sound s = CreateSound(file_location, true, true, true, 10, 10, "SpellsEAX");
        SetSoundVolume(s, volume);
        SetSoundDistances(s, 2000, 2000);
        /*SetSoundDistanceCutoff(s, 2000);*/
        SetSoundPosition(s, x, y, z);
        StartSound(s);
    
        sd.s = s;
        SetTimerData(t, sd);
        TimerStart(t, duration, false, function StopLoopingSoundTimed);
    }

    function dummy()
    {
        opticblast(GetTimerData(GetExpiredTimer())).exe();
    }

    public struct opticblast
    {

    private
    {
        lightning beam[ENERGY_BEAM_COUNT];
        real      beam_distance_from_hit_to_move_to[ENERGY_BEAM_COUNT];
        real      beam_traveled_distance[ENERGY_BEAM_COUNT];
        real      t[ENERGY_BEAM_COUNT];
        real      t_inc[ENERGY_BEAM_COUNT];
        integer   slowest_beam_index;
        timer     executefuncer;
        group     damaged_units;
    }

        unit   caster;
        widget target;

        string beam_model[ENERGY_BEAM_COUNT];

        real beam_start_x[ENERGY_BEAM_COUNT];
        real beam_start_y[ENERGY_BEAM_COUNT];
        real beam_start_z[ENERGY_BEAM_COUNT];

        real beam_hit_x[ENERGY_BEAM_COUNT];
        real beam_hit_y[ENERGY_BEAM_COUNT];

        real beam_move_to_x[ENERGY_BEAM_COUNT];
        real beam_move_to_y[ENERGY_BEAM_COUNT];

        real beam_duration[ENERGY_BEAM_COUNT];

        integer beam_count;
        boolean play_sound;
        string  sound_location = "";

        boolean beam_spawn_effect[ENERGY_BEAM_COUNT];
        string  beam_effect_location[ENERGY_BEAM_COUNT];

        real beam_hit_radius = 0;
        real damage          = 0;

        method setup()
        {
            integer i    = 0;
            real    dx   = 0;
            real    dy   = 0;
            real    highest_duration = 0;
            integer highest_duration_index = 0;

            PauseUnit(caster, true);
            
            for (0 <= i < beam_count)
            {
                t<i> = 0;
                t_inc<i> = 1.0 / (beam_duration<i> * BEAM_MOVEMENT_UPDATES_PER_SECOND);

                dx = beam_move_to_x<i> - beam_hit_x<i>;
                dy = beam_move_to_y<i> - beam_hit_y<i>;
                beam_distance_from_hit_to_move_to<i> = SquareRoot(dx * dx + dy * dy);

                if (highest_duration &lt; beam_duration<i>)
                {
                    highest_duration = beam_duration<i>;
                    highest_duration_index = i;
                }

                beam<i> = GetLightning(GetLightningIdByCodeName(beam_model<i>));
                MoveLightningEx(beam<i>, false, beam_start_x<i>, beam_start_y<i>, beam_start_z<i>, beam_hit_x<i>, beam_hit_y<i>, 0);
            }
            slowest_beam_index = highest_duration_index;

            damaged_units = NewGroup();
            executefuncer = NewTimer();
            SetTimerData(executefuncer, this);
            TimerStart(executefuncer, 1.0 / BEAM_MOVEMENT_UPDATES_PER_SECOND, true, function dummy);

            if (play_sound)
            {
                dx = GetWidgetX(target);
                dy = GetWidgetY(target);
                MoveLocation(loc, dx, dy);
                if (sound_location == &quot;&quot; || sound_location == null)
                    PlayLoopingSoundTimed(DEFAULT_SOUND_LOCATION, dx, dy, GetLocationZ(loc), beam_duration[slowest_beam_index], 100);
                else
                    PlayLoopingSoundTimed(sound_location, dx, dy, GetLocationZ(loc), beam_duration[slowest_beam_index], 100);
            }
        }
        
        method exe()
        {
            integer i = 0;
            real    x = 0;
            real    y = 0;

            for (0 &lt;= i &lt; beam_count)
            {
                t<i> = t<i> + t_inc<i>;
                if (t<i> &gt; 1)
                {
                    if (beam<i> != null)
                    {
                        RecycleLightning(beam<i>, GetLightningIdByCodeName(beam_model<i>));
                        beam<i> = null;
                    }
                }
                else
                {
                    x = beam_hit_x<i> + t<i> * (beam_move_to_x<i> - beam_hit_x<i>);
                    y = beam_hit_y<i> + t<i> * (beam_move_to_y<i> - beam_hit_y<i>);

                    MoveLocation(loc, x, y);
                    MoveLightningEx(beam<i>, false, beam_start_x<i>, beam_start_y<i>, beam_start_z<i>, x, y, GetLocationZ(loc));

                    beam_traveled_distance<i> = beam_traveled_distance<i> + t_inc<i> * beam_distance_from_hit_to_move_to<i>;
                    // if (ModuloReal(beam_traveled_distance<i>, beam_hit_radius) &lt;= 16)
                    // { 
                            tmpu = caster;
                            GroupEnumUnitsInArea(ENUM_GROUP, x, y, beam_hit_radius, filter);
                            tmpu = FirstOfGroup(ENUM_GROUP);
                            while (tmpu != null)
                            {
                                if (!IsUnitInGroup(tmpu, damaged_units))
                                {
                                    UnitDamageTarget(caster, tmpu, damage, true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE_WHOKNOWS);
                                    GroupAddUnit(damaged_units, tmpu);
                                }
                                GroupRemoveUnit(ENUM_GROUP, tmpu);
                                tmpu = FirstOfGroup(ENUM_GROUP);
                            }
                    // }

                    // if (ModuloReal(beam_traveled_distance<i>, 32.0) &lt;= 8)
                    // {
                        if (beam_spawn_effect<i>)
                        {
                            if (beam_effect_location<i> == &quot;&quot; || beam_effect_location<i> == null)
                            {
                                AddSpecialEffectTimed(DEFAULT_EFFECT_LOCATION, x, y, beam_duration[slowest_beam_index] * (1 - t[slowest_beam_index]));
                            }
                            else
                            {
                                AddSpecialEffectTimed(beam_effect_location<i>, x, y, beam_duration[slowest_beam_index] * (1 - t[slowest_beam_index]));
                            }
                        }
                    // }
                }

                if (t[slowest_beam_index] &gt; 1)
                {
                    stop();
                    i = beam_count; // break;
                } 
            }
        }
        
        method stop()
        {
            destroy();
            ReleaseTimer(executefuncer);
            ReleaseGroup(damaged_units);
            PauseUnit(caster, false);
        }
    }
    
    function spell_optic_blast() -&gt; boolean
    {
        opticblast ob;
        unit       caster;
        real       cx;
        real       cy;
        unit       target;
        real       tx;
        real       ty;
        real       dx;
        real       dy;
        real       dirx;
        real       diry;
        real       dist;
        real       line_length;

        caster  = GetEventDamageSource();

        if (GetUnitName(caster) == &quot;Colossus&quot; &amp;&amp; !IsUnitPaused(caster))
        {
            cx          = GetUnitX(caster);
            cy          = GetUnitY(caster);
            target      = GetTriggerUnit();
            tx          = GetUnitX(target);
            ty          = GetUnitY(target);
            dx          = tx - cx;
            dy          = ty - cy;
            dist        = SquareRoot(dx * dx + dy * dy);
            dirx        = dx / dist;
            diry        = dy / dist;
            line_length = 256;

            ob = opticblast.create();

            ob.caster = caster;
            ob.target = target;

            ob.beam_model[0]           = LIGHTNING_DRAIN_MANA;
            ob.beam_start_x[0]         = dirx * 128 + cx - 24;
            ob.beam_start_y[0]         = diry * 128 + cy;
            ob.beam_start_z[0]         = 295;
            ob.beam_hit_x[0]           = -diry * line_length * 0.5 + tx;
            ob.beam_hit_y[0]           = dirx  * line_length * 0.5 + ty;
            ob.beam_move_to_x[0]       = diry  * line_length * 0.5 + tx;
            ob.beam_move_to_y[0]       = -dirx * line_length * 0.5 + ty;
            ob.beam_duration[0]        = 0.49;
            ob.beam_spawn_effect[0]    = true;

            ob.beam_model[1]           = LIGHTNING_DRAIN_MANA;
            ob.beam_start_x[1]         = dirx * 128 + cx + 24;
            ob.beam_start_y[1]         = diry * 128 + cy;
            ob.beam_start_z[1]         = 295;
            ob.beam_hit_x[1]           =  diry * line_length * 0.5 + tx;
            ob.beam_hit_y[1]           = -dirx * line_length * 0.5 + ty;
            ob.beam_move_to_x[1]       = -diry * line_length * 0.5 + tx;
            ob.beam_move_to_y[1]       =  dirx * line_length * 0.5 + ty;
            ob.beam_duration[1]        = 0.5;
            ob.beam_spawn_effect[1]    = true;

            ob.beam_count              = 2;
            ob.play_sound              = true;
            ob.beam_hit_radius         = 128;
            ob.damage = 100;

            ob.setup();
        }

        return false;
    }

    function onInit()
    {
        unit u;
        trigger t = CreateTrigger();
        GroupEnumUnitsInRect(ENUM_GROUP, GetPlayableMapRect(), null);

        u = FirstOfGroup(ENUM_GROUP);
        while (u != null)
        {
            TriggerRegisterUnitEvent(t, u, EVENT_UNIT_DAMAGED);
            GroupRemoveUnit(ENUM_GROUP, u);
            u = FirstOfGroup(ENUM_GROUP);
        }
        TriggerAddCondition(t, Condition(function spell_optic_blast));       

        filter = Filter(function targets_allowed);
    }       

}

//! endzinc
</i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i></i>
 

Attachments

  • optic_blast.w3x
    46.5 KB · Views: 371

Laiev

Hey Listen!!
Reaction score
188
You really should use some Damage System.

Leak:
JASS:
function onInit()
    {
        unit u;

(and other function that you don't null locals)

Why this:
JASS:

if (GetUnitName(caster) == &quot;Colossus&quot; &amp;&amp; !IsUnitPaused(caster))


This should be based on ability or unit type id, also this SHOULD be configurable.
And unit paused?????

This is a bad practice

Also, your system/spell/library/something just register preplaced units in the map, not any other entering unit, so this will fail if you use dynamic spawn (which happen in 99% of maps)
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top