• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Spell - Shimmering Portal v4.2b


Description

Converted 'Shimmering Portal' from Frozen Throne campaign.

Teleports any unit that pass through the portal to targeted portals location.



System

Creates an portal near caster and one at targeted point! channels until portal is created.
If caster moves (stops channeling the spell) portals will be destroyed. First portal faces angle between caster and target point, while second portal faces approximately angle of caster upon casting



Spell is recoded in Warcraft 3 v1.30 and does not require JNGP!


Additional Info:

To install spell go to trigger editor and read coments at the top code of the spell on how to install it to your map.

However spell is editable through globals just at beginning of the code (comments expalin what you need to do and how to make an specific change in spell)


If you need any future help feel free to comment here:



Version 1.0 - Release

Version 1.1 - Now portal sides have a visibility trigger action which expires when portals close and event is changed to spell effect ability not begins casing... that fixes some bugs.

Version 2.0 - Mass update to my spell! Spell is MUI, most GUI, has custom script, header code + GUI triggers, i as well added readme for this who are completely new to Warcraft can install this spell in there map... well ofc you need to know english. Spell does not leak event filters or any other game objects!

Version 3.0 - Final update spell is fully recoded in vJass. Fixed 'Handle Stack Corruption' this was the only my spell remaining effected by that corruption...
And well since its in vjass its faster, MUI (any amount of portals at same time is allowed), does not cause/create memory leaks and is compatible with any version of warcraft since user can change that in users setup part!

Version 3.0b - Additional update to 3.0!
* Fixed bug with timed life starting at time when you start channeling portal insted of starting once portal is summoned.
* Made portal (unit more like dummy portal should be)
* Added 2 triggers insted of one (users now pick which trigger they want, insted of having hard time removing/adding //! novjass preprocessors)
* Added script externals, which will install my spell through jass code in your map (once you save your map) so no need for rawcodes and copy and pasting abilities...
* Minor script change

Version 4.0 - Shimmering Portal Remasterd
* Totally recoded in zinc
* Now is MUI
* Has a lot of extra details when units are teleported, camera pan, selection...
* Now has extra features for spell leveling up (cooldown, mana cost, portal duration, transition time and n units to be ported)
* Now uses recycle engine and my new mini library
* Was one of hardest spells to make

Version 4.1
* Fixed bug where map fails to compile because of lua script.

Version 4.2
* Added some hints and explanations on how portals work
* Compiled in Warcraft 3 v1.30, no longer requires JNGP

Version 4.2b - Fixed desync sound bug









JASS:
//==========================================================================
//                  Dark Dragon Library Code v1.3
//
//      * Made on Warcraft III v1.30.4
//
//  Installation:
//                 
//                  1) Export instantdummy.mdx from this map and import it to your map, leave path at "war3mapImported/instantdummy.mdx"
//                  2) Copy this trigger to your map, save your map and then change below line "// external ... " or copy "DD Dummy" and paste it in your map
//     3) Copy and paste "Unit Chill" ability from this map to your map
//     4) Match the rawcodes below to your map or use same ones as below
//
// Credits:
//          ('Vexorian' - dummy.mdx)
//============================================================================

// *** Change "// external" to "//! external", save your map, close map, change back from "//!" to "//" and save map again.
// *** This will create dummy in your map
//
// ==================================
// external ObjectMerger w3u ushd dumy uabi "Aloc,Amrf" uble 0 ucbs 0 ucpt 0 umxp 0 umxr 0 umdl "war3mapImported\instantdummy.mdx" ushu "None" umvh 0 umvs 1 umas 1 umis 1 ucol 0 ufoo 0 uhom 1 umpi 10000 umpm 10000 usid 1 usin 1 unam "DD Dummy"
// ==================================


//! zinc

library DDLib requires optional TimerUtils, optional GroupUtils
{
   
    // -----------------------------------------------------------------------
    // -----------------------------------------------------------------------
    // *** Lib constants ***
    public {
       
  // ----------------------------------------------------------------------
  // * Start modify/match rawcodes to your map
        constant        integer     DD_DUMMYCODE       = 'dumy';
        constant        integer     DD_ABILITY_CROWN_FORM     = 'Amrf';
  constant   integer  DD_CHILL     = 'Achl';
  constant   integer  DD_CHILL_BUFF    = 'Bfro';
  // * End modify
  // ----------------------------------------------------------------------
 
 
  constant        integer     p_null             = (0x0);
        constant        real        DD_INTERVAL       = .017;
       
                        // map min and max coords
                        real        DDMinX             = 0.;
                        real        DDMinY             = 0.;
                        real        DDMaxX             = 0.;
                        real        DDMaxY             = 0.;
    }
 
 private {
  constant   integer  HARVEST_ID     = 'Ahrl';
  constant  real  TRIGGER_REFRESH_RATE  = (60.)*3.; /// damage detection trigger
                       
                        unit   TreeChecker              = null;
      trigger  TempTrig      = null;
      integer  NTrig       = 0;
      trigger  DmgTrig[];
      p_real   EnumVec      = p_null;
      boolexpr  EnumFilter      = null;
      sound  ErrorSound     = null;
      timer  GameElapsedTimer   = null;
  constant   integer  RND_INT_MAX_ARRAY_N   = 100;
      integer  RndInt[], RndIntWriteN = 00, RndIntReadN = 00;
      trigger  TrigMouseEvent     = null;
      force   RndGenForce     = null;
      real   RndElapsedTime     = 0.;
 }
    // -----------------------------------------------------------------------
    // -----------------------------------------------------------------------
    // -----------------------------------------------------------------------
   
    // * types
    public {
       
        // *** pointer to list of data ***
        type    p_integer           extends     integer[8];
        type    p_real              extends     real[8];
        type    p_unit              extends     unit[8];
       
  function H2ID(handle h) -> integer {
   return GetHandleId(h) - 0x100000;
  }
 
        function New_pInteger(integer i) -> p_integer
        { p_integer p = p_integer.create(); p[0] = i; return p; }
        function New_pReal(real r) -> p_real
  { p_real p = p_real.create(); p[0] = r; return p; }
        function New_pUnit(unit u) -> p_unit
  { p_unit p = p_unit.create(); p[0] = u; return p; }
       
  function pVector(real x, real y, real z) -> p_real {
   p_real v = p_real.create();
   v[0] = x; v[1] = y; v[2] = z;
   return v;
  }
 
  // --------------------------------------------------------------------------------------
 
  function DDMsg(string str) {
   DisplayTimedTextFromPlayer(GetLocalPlayer(), 0., 0., 30., str);
  }
 
  // --------------------------------------------------------------------------------------
 
  function DisplayErrorMsgPlayer(player p, real dur, string msg) {
   if (GetLocalPlayer() == p) {
    StartSound(ErrorSound);
    DisplayTimedTextFromPlayer(p, 0., 0., dur, "|cffffcc00"+ msg +"|r");
   }
  }

    }
   
    // -----------------------------------------------------------------------
    // ->           ***** private globals *****
    // -----------------------------------------------------------------------
    private {
        location    TempLoc         = Location(0., 0.);
        timer       TimerStack[];
        integer     TimN            = 0;
        group       GroupStack[];
        integer     GrpN            = 0;
        unit        DummyStack[];
        integer     DumN            = 0;
  integer  TimTicks[];
  integer  TimData[];
  timer  TimTim1[];
  timer  TimTim2[];
 
 
  integer  UnitStackData = 0;
  unit  UnitStack[];
  integer  US_N   = 0;
       
        public hashtable   DDHT            = InitHashtable();
    }
    // -----------------------------------------------------------------------
   
   public {
  
        // *** Global funcs
       
        function Pw_2(real x) -> real {
            return x*x;
        }
 
  function DDHypot(real x, real y) -> real {
   return (x*x) + (y*y);
  }
       
        function DDTerrZ(real x, real y) -> real {
            MoveLocation(TempLoc, x, y);
            return GetLocationZ(TempLoc);
        }
       
        function DDWidgetTerrZ(widget w) -> real {
   MoveLocation(TempLoc, GetWidgetX(w), GetWidgetY(w));
            return GetLocationZ(TempLoc);
        }
 
  function DDEffectTerrZ(effect e) -> real {
   MoveLocation(TempLoc, BlzGetLocalSpecialEffectX(e), BlzGetLocalSpecialEffectY(e));
            return GetLocationZ(TempLoc);
  }
 
  function DDGetUnitZ(unit u) -> real {
   return BlzGetUnitZ(u) + GetUnitFlyHeight(u);
  }
       
        // =================================================================
        //              *** Save Handle data ****
        // =================================================================
       
        function DDSet(handle h, integer id, integer val) {
            SaveInteger(DDHT, id+1, GetHandleId(h), val);
        }
       
        function DDGet(handle h, integer id) -> integer {
            return LoadInteger(DDHT, id+1, GetHandleId(h));
        }
 
  function DDHas(handle h, integer id) -> boolean {
   return HaveSavedInteger(DDHT, id+1, GetHandleId(h));
  }
 
  function DDFlush(integer id) {
   FlushChildHashtable(DDHT, id+1);
  }
       
        // =================================================================
        //              *** Timer Handling ****
        // =================================================================
 
  // -> check if timer is recycled
  function DDIsTimRecycled(timer t) -> boolean {
   integer i;
   for(i=TimN-01; i >= 00; i-=01)
    if (TimerStack[i] == t)
     return true;
   return false;
  }
 
        // -> Load timer for recycling
        function DDLoadTim() -> timer {
            static if (LIBRARY_TimerUtils) { return NewTimer(); }
            else {
                if (TimN > 0) {
                    TimN -= 1;
                    return TimerStack[TimN];
                }
                return CreateTimer();
            }
        }
       
        // -> recycle loaded timer
        function DDRecycleTim(timer t) {
            static if (LIBRARY_TimerUtils) { ReleaseTimer(t); }
            else {
    static if (DEBUG_MODE)
     if (DDIsTimRecycled(t)) {
      DDMsg("Multiple recycle of timer!");
      return;
     }
                TimerStack[TimN] = t;
                TimN += 1;
            }
        }
       
        // ** Get data stored on expired timer
        function DDTimData() -> integer {
            return TimData[H2ID(GetExpiredTimer())];
        }
 
  // *** Custom timer tick
        function DDCTimTick(timer t) -> integer {
            return TimTicks[H2ID(t)];
        }
 
  // *** Gets current tick and adds next one ***
        function DDTimTick() -> integer {
   integer id = H2ID(GetExpiredTimer());
   TimTicks[id] += 01;
            return TimTicks[id];
        }
       
        // ** start timer with data storage
        function DDStartTim(real secs, boolean looping, integer pdata, code func) -> timer {
            timer t = DDLoadTim();
  
   TimData[H2ID(t)] = pdata;
            TimerStart(t, secs, looping, func);
            return t;
        }
       
        // ** start timer with data storage, and launches it instantly
        function DDStartTimInst(real secs, boolean looping, integer pdata, code func) -> timer {
            timer t1 = DDLoadTim(), t2 = DDLoadTim(), t3 = DDLoadTim();
           
   TimData[H2ID(t2)] = pdata;
            TimerStart(t2, 0., false, func);
  
   TimTim1[H2ID(t3)] = t1;
   TimTim2[H2ID(t3)] = t2;
   TimerStart(t3, .005, false, function() {
    timer t = GetExpiredTimer();
    integer id = H2ID(t);
   
    PauseTimer(t);
    static if (LIBRARY_TimerUtils)
     ReleaseTimer(t);
    else {
     TimerStack[TimN] = t;
     TimN += 1;
    }
   
    t = TimTim2[id];
    if (DDIsTimRecycled(t))
     t = TimTim1[id];
    TimTicks[H2ID(t)] = 00;
    PauseTimer(t);
    static if (LIBRARY_TimerUtils)
     ReleaseTimer(t);
    else {
     TimerStack[TimN] = t;
     TimN += 1;
    }
   });
  
   TimData[H2ID(t1)] = pdata;
            TimerStart(t1, secs, looping, func);
           
            return t1;
        }
       
        // *** Quit expired timer ***
        function DDQuitTim() {
   timer t = GetExpiredTimer();
   TimTicks[H2ID(t)] = 00;
            PauseTimer(t);
   static if (LIBRARY_TimerUtils)
    ReleaseTimer(t);
            else {
                TimerStack[TimN] = t;
                TimN += 1;
            }
        }
 
  function DDQuitTimEx(timer t) {
   TimTicks[H2ID(t)] = 00;
            PauseTimer(t);
   static if (LIBRARY_TimerUtils)
    ReleaseTimer(t);
            else {
                TimerStack[TimN] = t;
                TimN += 1;
            }
        }
       
        // =================================================================
        //              *** Group Handling ****
        // =================================================================
       
        // -> Load timer for recycling
        function DDLoadGroup() -> group {
            static if (LIBRARY_GroupUtils) { return NewGroup(); }
            else {
                if (GrpN > 0) {
                    GrpN -= 1;
                    return GroupStack[GrpN];
                }
                return CreateGroup();
            }
        }
       
        // -> Recycle group
        function DDRecycleGroup(group g) {
            static if (LIBRARY_GroupUtils) { ReleaseGroup(g); }
            else {
    GroupClear(g);
                GroupStack[GrpN] = g;
                GrpN += 1;
            }
        }
       
        // --------------------------------------------------------
        // -- Quick filter area
  private integer GroupFilterData = 00;
 
        function DDGroupFilterArea(real x, real y, real radius, integer data, code func) {
            group g = DDLoadGroup();
   GroupFilterData = data;
            GroupEnumUnitsInRange(g, x, y, radius, Filter(func));
            DDRecycleGroup(g);
        }
       
        // --------------------------------------------------------
        // -- Quick filter rect
        function DDGroupFilterRect(rect r, integer data, code func) {
            group g = DDLoadGroup();
   GroupFilterData = data;
            GroupEnumUnitsInRect(g, r, Filter(func));
            DDRecycleGroup(g);
        }
       
  function DDGFilterData() -> integer {
   return GroupFilterData;
  }
 
  function DDGFilterDataSet(integer data) {
   GroupFilterData = data;
  }
 
  // --------------------------------------------------------
        // *** Filtrates and fills units in to memory
        function DDGroupFillMemArea(real x, real y, real radius, integer data, code filter) {
   group g = DDLoadGroup();
   boolexpr exp = And(Filter(filter), Filter(function() -> boolean {
    UnitStack[US_N] = GetFilterUnit();
    US_N += 1;
    return false;
   }));
   US_N = 0;
   UnitStack[0] = null;
   UnitStackData = data;
   GroupEnumUnitsInRange(g, x, y, radius, exp);
   DDRecycleGroup(g);
   DestroyBoolExpr(exp);
   exp = null;
  }
 
  function DDGroupFillMemRect(rect r, integer data, code filter) {
   group g = DDLoadGroup();
   boolexpr exp = And(Filter(filter), Filter(function() -> boolean {
    UnitStack[US_N] = GetFilterUnit();
    US_N += 1;
    return false;
   }));
   US_N = 0;
   UnitStack[0] = null;
   UnitStackData = data;
   GroupEnumUnitsInRect(g, r, exp);
   DDRecycleGroup(g);
   DestroyBoolExpr(exp);
   exp = null;
  }
 
  function DDMemUnitN() -> integer { return US_N; }
  function DDMemUnitData() -> integer { return UnitStackData; }
 
  function DDMemUnit(integer index) -> unit {
   if (US_N == 0) return null;
   debug {
    if (index < 0) {
     BJDebugMsg("DDMemUnit: index less than 0");
     index = 0;
    } else if (index >= US_N) {
     BJDebugMsg("DDMemUnit: index greater than units alloc size");
     index = 0;
    }
   }
   return UnitStack[index];
  }
  // --------------------------------------------------------
 
  // --------------------------------------------------------
  // ***
       
        // =================================================================
        //              *** Dummy Handling ****
        // =================================================================
       
        // -> Load dummy for recycling
        function DDLoadDummy() -> unit {
            if (DumN > 0) {
                DumN -= 1;
                PauseUnit(DummyStack[DumN], false);
                return DummyStack[DumN];
            }
            return CreateUnit(Player(0xF), DD_DUMMYCODE, DDMaxX, DDMaxY, 0.);
        }
       
        // *** prepares/setups dummy for spell casting
        function DDLoadSpellDummy(player owner, real x, real y, integer abil, integer abilLevel) -> unit {
            unit dummy = DDLoadDummy();
            SetUnitOwner(dummy, owner, false);
            SetUnitX(dummy, x);
            SetUnitY(dummy, y);
            if (abil != p_null) {
                UnitAddAbility(dummy, abil);
                SetUnitAbilityLevel(dummy, abil, abilLevel);
            }
            return dummy;
        }
       
        // -> Recycle dummy
        function DDRecycleDummy(unit u) {
            PauseUnit(u, true);
            DummyStack[DumN] = u;
            DumN += 1;
        }
       
        // -> Recycle dummy timed
        function DDRecycleDummyTimed(unit u, real secs) {
            DDStartTim(secs, false, New_pUnit(u), function() {
                DDRecycleDummy(p_unit(DDTimData())[0]);
                p_unit(DDTimData()).destroy();
                DDQuitTim();
            });
        }
       
        // *** shares vision for timed amount, usually for dummy casting
        function DDUnitShareVisionTimed(unit u, player toP, real secs) {
            p_integer pi = p_integer.create();
           
            pi[0] = New_pUnit(u);
            pi[1] = GetPlayerId(toP);
            UnitShareVision(u, toP, true);
            DDStartTim(secs, false, pi, function() {
                p_integer pi = DDTimData();
               
                UnitShareVision(p_unit(pi[0])[0], Player(pi[1]), false);
                p_unit(pi[0])[0] = null;
                p_unit(pi[0]).destroy();
                pi.destroy();
                DDQuitTim();
            });
           
        }
 
        // *** Remove ability timed ***
        private struct uratimed {
            private {
                unit u;
                integer abil;
            }
           
            static method create(unit whichUnit, integer id, real time) -> uratimed {
                thistype this = allocate();
               
                u = whichUnit;
                abil = id;
                DDStartTim(time, false, this, function() {
                    thistype this = DDTimData();
                    UnitRemoveAbility(u, abil);
                    DDQuitTim();
                    deallocate();
                });
               
                return 0;
            }
        }
        function DDRemoveAbilityTimed(unit u, integer abil, real secs) { uratimed.create(u, abil, secs); }
       
  function DDSpellDamage(unit u, unit v, real dmg) {
   real life = GetWidgetLife(v);
   real dmgfactor = 1.;
  
   if (IsUnitType(v, UNIT_TYPE_HERO)) {
    if (UnitHasItemOfTypeBJ(v, 'brac'))
     dmgfactor = .5;
    else
     dmgfactor = .75;
   }
  
   if (life > dmg*dmgfactor) {
    SetWidgetLife(v, life-(dmg*dmgfactor));
   } else
    UnitDamageTarget(u, v, 99999., false, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
  }
 
  // -------------------------------------------------------------------------------------
  // *** Chills target unit
  private struct chill {
   unit u, dmy;
   real dur;
  
   static chill Data[];
   //static key CHILL_KEY;
  }
 
  function DDUnitChill(player p, unit u, real dur) -> boolean {
   //chill c = DDGet(u, chill.CHILL_KEY);
   chill c = chill.Data[H2ID(u)];
   unit d;
   real rad;
  
  
   if (c == p_null) {
    c = chill.create();
    c.u = u; c.dur = dur;
    chill.Data[H2ID(u)] = c;
    //DDSet(u, chill.CHILL_KEY, c);
   
    d = DDLoadDummy();
    c.dmy = d;
    rad = GetUnitFacing(d) * bj_DEGTORAD;
    SetUnitOwner(d, p, false);
    UnitAddAbility(d, DD_CHILL);
    SetUnitX(d, GetUnitX(u) - 20.*Cos(rad));
    SetUnitY(d, GetUnitY(u) - 20.*Sin(rad));
   
    if (IssueTargetOrder(d, "frostnova", u)) {
     DDStartTim(.1, true, c, function() {
      chill c = DDTimData();
     
      c.dur -= .1;
      if (c.dur <= 0. || GetUnitAbilityLevel(c.u, DD_CHILL_BUFF) == 00) {
       UnitRemoveAbility(c.u, DD_CHILL_BUFF);
       UnitRemoveAbility(c.dmy, DD_CHILL);
       DDRecycleDummy(c.dmy);
       chill.Data[H2ID(c.u)] = p_null;
       //DDSet(c.u, chill.CHILL_KEY, p_null);
       c.u = null;
       c.destroy();
       DDQuitTim();
      }
     });
     return true;
    }
   
    return false;
   }
  
   c.dur = dur;
  
   return true;
  }
 
  // ------------------------------------------------------------------------------------------------
 
  struct fade {
   unit u;
   real trans;
   real rate, e_trans, dur;
  
   static constant real INTERVAL = .1;
  
   static method create(unit u, real dur, real s_trans, real e_trans) -> fade {
    fade this = allocate();
   
    this.u = u;
    this.trans = s_trans;
    this.rate = ((e_trans-s_trans)/dur)*fade.INTERVAL;
    this.e_trans = e_trans;
    this.dur = dur;
   
    return this;
   }
  }
 
  // *** Fades unit over time ***
  public function DDFadeUnit(unit u, integer from_alpha, integer to_alpha, real duration) {
   fade f = fade.create(u,
         duration,
         from_alpha/2.55,
         to_alpha/2.55);
  
   SetUnitVertexColor(u, 255, 255, 255, from_alpha);
   // --- Start thread ---
   DDStartTim(fade.INTERVAL, true, f, function() {
    fade f = DDTimData();
   
    f.trans += f.rate;
    f.dur -= fade.INTERVAL;
    SetUnitVertexColor(f.u, 255, 255, 255, R2I(f.trans*2.55));
   
    if (f.dur < 0.) {
     SetUnitVertexColor(f.u, 255, 255, 255, R2I(f.e_trans*2.55));
     f.u = null;
     f.destroy();
     DDQuitTim();
    }
   });
       
  }
 
  // ------------------------------------------------------------------------------------------------
 
        // Check if unit is invulnerable
        function DDIsUnitInvulnerable(unit u) -> boolean {
            unit d = DDLoadDummy();
            real hp = GetWidgetLife(u);
            boolean flag;
           
            UnitDamageTarget(d, u, 1., true, false, null, null, null);
            flag = GetWidgetLife(u) == hp;
            SetWidgetLife(u, hp);
            DDRecycleDummy(d);
           
            return flag;
        }
       
        // *** check if unit is ward
        function DDIsUnitWard(unit whichUnit) -> boolean {
            return GetUnitDefaultMoveSpeed(whichUnit) == 0.;
        }
 
        // =================================================================
        //              *** Effect Handling ****
        // =================================================================
       
        // -----------------------------------------------
        // *** Define movable effect
        struct ddeffect {
            private {
                effect e;
                real fac; // facing angle in radians
                real effZ; // pitch in radians
                real decay;
    real stepTrans, cTrans, eTrans;
               
                static constant real        EFFECT_DECAY        = 5.;
            }
           
            // =========================================================================================
            // =========================================================================================
            static method create(string mdl, real x, real y, real facRad, real size) -> ddeffect {
                ddeffect this = allocate();
               
                this.e   = AddSpecialEffect(mdl, x, y);
    this.fac  = facRad;
    this.effZ = 0.;
                BlzSetSpecialEffectRoll(this.e, facRad);
    BlzSetSpecialEffectScale(this.e, size);
               
                return this;
            }
  
   static method createZ(string mdl, real x, real y, real z, real facRad, real size) -> ddeffect {
                ddeffect this = allocate();
               
                this.e   = AddSpecialEffect(mdl, x, y);
    this.fac  = facRad;
    this.effZ = z-DDTerrZ(x, y);
                BlzSetSpecialEffectRoll(this.e, facRad);
    BlzSetSpecialEffectScale(this.e, size);
    BlzSetSpecialEffectZ(this.e, z);
               
                return this;
            }
           
            // -----------------
            method destroy() {
                DestroyEffect(this.e);
                this.e = null;
                deallocate();
            }
           
            // *** destroys effect timed
            method destroyx(real decayTime) {
                DDStartTim(decayTime, false, this, function() {
     ddeffect se = DDTimData();
     BlzSetSpecialEffectPosition(se.e, DDMaxX, DDMaxY, 0.);
                    DestroyEffect(se.e);
     se.e = null;
     se.deallocate();
                    DDQuitTim();
                });
            }
            // =========================================================================================
            // =========================================================================================
           
  
           
   method operator Z=(real z)     { BlzSetSpecialEffectZ(this.e, z); }
  
            method operator X() -> real                 { return BlzGetLocalSpecialEffectX(this.e); }
            method operator Y() -> real                 { return BlzGetLocalSpecialEffectY(this.e); }
            method operator Z() -> real                 { return BlzGetLocalSpecialEffectZ(this.e); }
            method operator WZ() -> real                { return DDEffectTerrZ(this.e); }
            method operator Height() -> real            { return this.Z-this.WZ; }
           
            method operator Facing=(real facRad)        { BlzSetSpecialEffectRoll(this.e, facRad); this.fac = facRad; }
            method operator Facing() -> real            { return this.fac; }
  
   method Position(real x, real y)    { BlzSetSpecialEffectPosition(this.e, x, y, this.effZ+this.WZ); }
   method PositionZ(real x, real y, real z) { BlzSetSpecialEffectPosition(this.e, x, y, z); }
   method Animation(animtype at)    { BlzPlaySpecialEffect(this.e, at); }
   method AnimationSpeed(real animSpeed)  { BlzSetSpecialEffectTimeScale(this.e, animSpeed/100.); }
  
   //method operator Pitch=(integer pitch)  { SetUnitAnimationByIndex(u, pitch); }
           
            //method Face(widget w) { Facing = Atan2(GetWidgetY(w)-Y, GetWidgetX(w)-X)*bj_RADTODEG; }
  
   method Fade(real startTransparency, real endTransparency, real duration) {
    this.cTrans = startTransparency;
    this.eTrans = endTransparency;
    this.stepTrans = .1*(endTransparency-startTransparency) / duration;
   
    BlzSetSpecialEffectAlpha(this.e, R2I(startTransparency*2.55));
   
    DDStartTim(.1, true, this, function() {
     ddeffect dde = DDTimData();
    
     dde.cTrans += dde.stepTrans;
     if (dde.stepTrans > 0.)
      if (dde.cTrans >= dde.eTrans) {
       BlzSetSpecialEffectAlpha(dde.e, R2I(dde.eTrans*2.55));
       DDQuitTim();
       return;
      }
     else
      if (dde.cTrans <= dde.eTrans) {
       BlzSetSpecialEffectAlpha(dde.e, R2I(dde.eTrans*2.55));
       DDQuitTim();
       return;
      }
     BlzSetSpecialEffectAlpha(dde.e, R2I(dde.cTrans*2.55));
    });
   }
  
        }
       
  private type timedeffect extends effect[01];
 
  function DDDestroyEffectTimed(effect e, real secs) {
   timedeffect te = timedeffect.create();
   te[00] = e;
   DDStartTim(secs, true, te, function() {
    timedeffect te = DDTimData();
    DestroyEffect(te[00]);
    te.destroy();
    DDQuitTim();
   });
  }
    }
 
 // ----------------------------------------------------------------------------
    // *** Main damage detection function, registers any damage dealt to units ***
    public function DDTriggerRegisterAnyUnitDamaged(trigger t) {
        DmgTrig[NTrig] = t;
        NTrig += 1;
    }
   
    function InitDamageDetection() {
  code c = function() {
   if (TempTrig != null)
    DestroyTrigger(TempTrig);
  
   TempTrig = CreateTrigger();
   TriggerRegisterEnterRectSimple(TempTrig, bj_mapInitialPlayableArea);
   TriggerAddCondition(TempTrig, function() -> boolean {
    integer i;
   
    // *** Check if we need to exec triggers or register an unit ***
    if (GetTriggerEventId() == EVENT_UNIT_DAMAGED) {
     for(i=0; i < NTrig; i+=1)
      if (IsTriggerEnabled(DmgTrig[i]))
       if (TriggerEvaluate(DmgTrig[i]))
        TriggerExecute(DmgTrig[i]);
    }
    else
     // *** Register new unit ***
     TriggerRegisterUnitEvent(GetTriggeringTrigger(),
            GetTriggerUnit(),
            EVENT_UNIT_DAMAGED);
   
    return false;
   });
  
   DDGroupFilterRect(bj_mapInitialPlayableArea, 00, function() -> boolean {
    TriggerRegisterUnitEvent(TempTrig, GetFilterUnit(), EVENT_UNIT_DAMAGED);
    return false;
   });
  };
  trigger t = CreateTrigger();
 
  TriggerAddAction(t, c);
  TriggerExecute(t);
  DestroyTrigger(t);
  TimerStart(CreateTimer(), TRIGGER_REFRESH_RATE, true, c);
  t = null;
    }
 
 // ---------------------------------------------------------------------------------
 
 // *** Main enum dests in range function ***
    public function DDEnumDestsInRange(p_real vec, real radius, boolexpr filter, code pfunc) {
        rect r = Rect(vec[0]-radius, vec[1]-radius, vec[0]+radius, vec[1]+radius);
 
   EnumVec[0] = vec[0];
   EnumVec[1] = vec[1];
   EnumVec[2] = radius;
           
        if (filter != null) filter = And(EnumFilter, filter);
        else filter = EnumFilter;
        EnumDestructablesInRect(r, filter, pfunc);
 
        if (filter != EnumFilter) { DestroyBoolExpr(filter); filter = null; }
  RemoveRect(r);
  r = null;
    }
       
    function InitEnumDests() {
        EnumVec = p_real.create();
        EnumFilter = Filter(function() -> boolean {
            return Pw_2(EnumVec[0]-GetDestructableX(GetFilterDestructable())) + Pw_2(EnumVec[1]-GetDestructableY(GetFilterDestructable())) < Pw_2(EnumVec[2]);
        });
    }
   
 // --------------------------------------------------------------------------------------
 
 // *** checks is destruct tree ***
    public function DDIsDestructableTree(destructable d) -> boolean {
        if (d != null) {
            PauseUnit(TreeChecker, false);
            if (IssueTargetOrder(TreeChecker, "harvest", d)) {
                PauseUnit(TreeChecker, true);
                return true;
            }
            PauseUnit(TreeChecker, true);
        }
        return false;
    }

    function InitDestTreeCheck() {
        TreeChecker = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA), DD_DUMMYCODE, DDMaxX, DDMaxY, 0.);
        UnitAddAbility(TreeChecker, HARVEST_ID);
        PauseUnit(TreeChecker, true);
    }
 
 // --------------------------------------------------------------------------------------
   
 public function DDNewTextTagUnit(unit whichUnit, string text, real dur, real red, real green, real blue, real transparency) {
  CreateTextTagUnitBJ( text, whichUnit, 0., 11.00, red, green, blue, transparency );
  SetTextTagPermanentBJ( bj_lastCreatedTextTag, false );
  SetTextTagVelocityBJ( bj_lastCreatedTextTag, 48.00, 90 );
  SetTextTagFadepointBJ( bj_lastCreatedTextTag, dur-1.25 );
  SetTextTagLifespanBJ( bj_lastCreatedTextTag, dur );
 }
 
 // --------------------------------------------------------------------------------------
 
 struct cameranoisedata {
  player p[12];
  integer n=00;
 }
 
 public function DDCameraSetSourceNoiseForPlayers(real x, real y, real mag, real vel, real maxDist, real duration) {
  integer i;
  real d;
  cameranoisedata cnd = cameranoisedata.create();
 
  for (i=00; i < bj_MAX_PLAYERS; i+=01) {
   if (GetLocalPlayer() == Player(i)) {
    d = SquareRoot( Pw_2(GetCameraTargetPositionX()-x) + Pw_2(GetCameraTargetPositionY()-y) );
    if (d < maxDist) {
     cnd.p[cnd.n] = Player(i);
     CameraSetSourceNoise(mag-(d*(mag/maxDist)), vel-(d*(vel/maxDist)));
     CameraSetTargetNoise(mag-(d*(mag/maxDist)), vel-(d*(vel/maxDist)));
     cnd.n += 01;
    }
   }
  }
 
  DDStartTim(duration, false, cnd, function() {
   cameranoisedata cnd = DDTimData();
  
   while(cnd.n != 00) {
    cnd.n -= 01;
    if (GetLocalPlayer() == cnd.p[cnd.n])
     CameraSetSourceNoise(0., 0.);
     CameraSetTargetNoise(0., 0.);
   }
  
   cnd.destroy();
   DDQuitTim();
  });
 }
 
 // --------------------------------------------------------------------------------------
   
 hashtable GenSndTable = null;
 
    public function DDGenericSound(string file, real vol, real x, real y, real mxDist, real pitch) {
  sound s;
  real d;
  integer i;
  integer snd_n, sh;
       
  // Play sound and shake camera for players within spell cast range
  for (i=00; i < bj_MAX_PLAYERS; i+=01) {
   if (GetLocalPlayer() == Player(i)) {
    d = SquareRoot( Pw_2(GetCameraTargetPositionX()-x) + Pw_2(GetCameraTargetPositionY()-y) );
    if (d < mxDist) {
     sh = StringHash(file);
     snd_n = LoadInteger(GenSndTable, sh, 04);
     s = LoadSoundHandle(GenSndTable, sh, snd_n);
     if (s == null) {
      s = CreateSound(file, false, false, false, 10, 10, "");
      SaveSoundHandle(GenSndTable, sh, snd_n, s);
     } else if (GetSoundIsPlaying(s)) {
      StopSound(s, false, false);
     }
     SetSoundPitch(s, pitch);
     SetSoundVolume(s, R2I((vol-d*(vol/mxDist))*1.27));
     StartSound(s);
     snd_n += 01;
     if (snd_n == 04)
      snd_n = 00;
     SaveInteger(GenSndTable, sh, 04, snd_n);
    }
   }
  }
    }
 
 public function DDGetGameElapsedTime() -> real {
  return TimerGetElapsed(GameElapsedTimer);
 }
 
 public function DDGetRndReal(real min, real max) -> real {
  real rnd = ((max-min)/1000000.)*I2R(RndInt[RndIntReadN]);
  debug if (max > 1000000.)
   DDMsg("ERROR: \"DDGetRndNumber\" - 'max' variable is greater than 1000000!");
  RndIntReadN += 01; if (RndIntReadN == RND_INT_MAX_ARRAY_N) RndIntReadN = 00;
  return min + rnd;
 }
 
 public function DDGetRndInt(integer min, integer max) -> integer {
  return R2I( DDGetRndReal(I2R(min), I2R(max)) );
 }
 
 // ====================================================================
    function onInit() {
  InitDamageDetection();
  InitDestTreeCheck();
  InitEnumDests();
 
        DDMinX = GetRectMinX(bj_mapInitialPlayableArea);
        DDMinY = GetRectMinY(bj_mapInitialPlayableArea);
        DDMaxX = GetRectMaxX(bj_mapInitialPlayableArea);
        DDMaxY = GetRectMaxY(bj_mapInitialPlayableArea);
 
  GenSndTable = InitHashtable();
 
  ErrorSound = CreateSound( "Sound\\Interface\\Error.wav", false, false, false, 10, 10, "" );
  SetSoundParamsFromLabel( ErrorSound, "InterfaceError" );
  SetSoundDuration( ErrorSound, 614 );
  SetSoundVolume(ErrorSound, 127);
 
  GameElapsedTimer = CreateTimer();
  TimerStart(GameElapsedTimer, 10800., false, null);
 
  for(RndIntWriteN=00; RndIntWriteN < RND_INT_MAX_ARRAY_N; RndIntWriteN+=01)
   RndInt[RndIntWriteN] = GetRandomInt(00, 1000000);
 
  RndIntWriteN = 00;
  RndGenForce = CreateForce();
  TrigMouseEvent = CreateTrigger();
  ForForce(bj_FORCE_ALL_PLAYERS, function() {
   if (GetPlayerController(GetEnumPlayer()) == MAP_CONTROL_USER)
    TriggerRegisterPlayerEvent(TrigMouseEvent, GetEnumPlayer(), EVENT_PLAYER_MOUSE_MOVE);
  });
  TriggerAddCondition(TrigMouseEvent, Condition(function() -> boolean {
   real mouseN;
   boolean xFirst = GetRandomInt(00, 01) == 01;
  
   if (!IsPlayerInForce(GetTriggerPlayer(), RndGenForce)) {
    // example: input x = 578.4571496
    //   output rnd n = 4571498
    if (xFirst)
     mouseN = RAbsBJ(BlzGetTriggerPlayerMouseX()) * 100.;
    else
     mouseN = RAbsBJ(BlzGetTriggerPlayerMouseY()) * 100.;
    if (mouseN == 0.)
     return false;
    //mouseN *= 100.;
    RndInt[RndIntWriteN] = R2I((mouseN - I2R(R2I(mouseN))) * 1000.);
    //DDMsg(I2S(RndInt[RndIntWriteN]));
    //RndIntWriteN += 01; if (RndIntWriteN == RND_INT_MAX_ARRAY_N) RndIntWriteN = 00;
   
    if (xFirst)
     mouseN = RAbsBJ(BlzGetTriggerPlayerMouseY()) * 100.;
    else
     mouseN = RAbsBJ(BlzGetTriggerPlayerMouseX()) * 100.;
    RndInt[RndIntWriteN] += R2I((mouseN - I2R(R2I(mouseN))) * 1000.)*1000;
    //DDMsg(I2S(RndInt[RndIntWriteN]));
    RndIntWriteN += 01; if (RndIntWriteN == RND_INT_MAX_ARRAY_N) RndIntWriteN = 00;
    ForceAddPlayer(RndGenForce, GetTriggerPlayer());
   }
  
   if (DDGetGameElapsedTime()-RndElapsedTime > .125) {
    ForceClear(RndGenForce);
    RndElapsedTime = DDGetGameElapsedTime();
   }
  
   return false;
  }));
    }
   
}

//! endzinc
======================================

JASS:
//TESH.scrollpos=0
//TESH.alwaysfold=0
// =====================================================================================================
//       * Spell - Shimmering Portal
//         by Dark Dragon
//
//
// Installation:
//
// (1) Install "DD Library" to your map if you have not done so already from my previous spells
// (2) Copy "Shimmering Portal" trigger to your map
// (3) Copy "Shimmering Portal" and "Portal (Dummy)" to your map
// (4) Edit spell to your will in object editor and here below in trigger editor
// (5) Enjoy!
// =====================================================================================================


//! zinc
library ShimmeringPortal requires DDLib
{

 
 // =============================================================
 //   *** Spell editable constants ***
 // =============================================================
 
 // ==========================================================================
 //
 //      *** Primary Spell Constants ***
 //
 // ==========================================================================
 
 // ----------------------------------------------------------------
 // *** The id of shimmering portal spell
 constant  integer      SPELL_ID      = 'SPrt';
 
 // ----------------------------------------------------------------
 // *** The id of shimmering portal dummy
 constant  integer      PORTAL_ID      = 'port';
 
 // ----------------------------------------------------------------
 // *** The length of portal, shrinking the value will force units to enter closer to center of portal
 constant  real      PORTAL_LENGTH     = 450.;
 
 // ----------------------------------------------------------------
 // *** Width of portal, units will enter the portal more far away if value is increased,
 //     decreassing this value too much can make unit pass portal faster than check interval can scan, thous causing spell to bug
 constant  real      PORTAL_WIDTH     = 110.;
 
 // ----------------------------------------------------------------
 // *** Units that are teleported to target portal, will walk away from there position offseted by random value between this (min and max)
 constant  real      UNIT_PORT_WALK_OFFSET_MIN  = 200.;
 constant  real      UNIT_PORT_WALK_OFFSET_MAX  = 350.;
 
 // ----------------------------------------------------------------
 // *** The offset of first portal near caster
 constant  real      FIRST_PORTAL_OFFSET    = 300.;
 
 // ----------------------------------------------------------------
 // *** The minimum casting range for shimmering portal spell
 constant  real      SECOND_PORTAL_MIN_RANGE   = 1000.;
 
 // ----------------------------------------------------------------
 // *** Time it takes to destroy portal after it expires or last unit exits
 constant  real      DESTROY_PORTAL_DELAY   = .7;
 
 // ----------------------------------------------------------------
 // *** The warning message, displayed in red color, and sound played with it, volume is in percentage
 constant  string      PORTAL_WARNING_MESSAGE   = "Warning: Too close cast of Shimmering portal!";
 constant  string      PORTAL_WARNING_SOUND_FILE  = "Sound\\Interface\\Error.wav";
 constant  real      PORTAL_WARNING_SOUND_VOLUME  = 90.;
 
 // ----------------------------------------------------------------
 // *** The duration of warning message
 constant  real      PORTAL_WARNING_MESSAGE_DURATION = 5.;
 
 // ----------------------------------------------------------------
 // *** This is animation of dummy portal, birth duration must be equeal to spell channel time
 constant  string      PORTAL_ANIM_BIRTH    = "birth";
 constant  string      PORTAL_ANIM_DEATH    = "death";
 constant  string      PORTAL_ANIM_STAND    = "stand";
 constant  real      PORTAL_ANIM_BIRTH_DURATION  = 7.5;
 
 // ----------------------------------------------------------------
 // *** How often to scan for units entry, greater value slows down CPU, while too low can cause units entered to not be registerd
 constant  real      PORT_CHECK_INTERVAL    = .1;
 
 // ----------------------------------------------------------------
 // *** Prevents units that are teleported to target portal to be teleported back, causing infinite loop of teleporting,
 //     this timing says that any unit will not be able to use any portals (MUI included)
 constant  real      UNIT_NEXT_PORT_DELAY   = 2.;
 
 // ----------------------------------------------------------------
 // *** Portal custom sounds files and volume in percentage
 constant  real      SOUND_MAX_DISTANCE    = 3500.;
 constant  string      PORTAL_BIRTH_SOUND_FILE   = "Sound\\Ambient\\DoodadEffects\\ShimmeringPortalBirth.wav";
 constant  real      PORTAL_BIRTH_SOUND_VOLUME  = 95.;
 constant  string      PORTAL_DEATH_SOUND_FILE   = "Sound\\Ambient\\DoodadEffects\\ShimmeringPortalDeath.wav";
 constant  real      PORTAL_DEATH_SOUND_VOLUME  = 95.;
 constant  string      PORTAL_ENTRANCE_SOUND_FILE  = "Sound\\Ambient\\DoodadEffects\\ShimmeringPortalEntrance.wav";
 constant  real      PORTAL_ENTRANCE_SOUND_VOLUME = 95.;
 constant  string      PORTAL_AMBIENCE_SOUND_FILE  = "Sound\\Ambient\\DoodadEffects\\CityScapeMagicRunesLoop1.wav";
 constant  real      PORTAL_AMBIENCE_SOUND_VOLUME = 100.;
 
 
 // ==========================================================================
 //
 //      *** Minimap Ping Constants ***
 //
 // ==========================================================================
 
 // ----------------------------------------------------------------
 // *** Do use minimap ping system for portals?
 constant  boolean      PORTAL_USE_MINIMAP_PING   = true;
 
 // ----------------------------------------------------------------
 // *** How often to ping minimap
 constant  real      PORTAL_MINIMAP_PING_INTERVAL = 4.5;
 
 // ----------------------------------------------------------------
 // *** How long to ping minimap
 constant  real      PORTAL_MINIMAP_PING_DURATION = 3.;
 
 // ----------------------------------------------------------------
 // *** The color of minimap ping
 constant  integer      PORTAL_MINIMAP_PING_RED   = 180;
 constant  integer      PORTAL_MINIMAP_PING_GREEN  = 23;
 constant  integer      PORTAL_MINIMAP_PING_BLUE  = 240;
 
 // ==========================================================================
 //
 //      *** Camera pan Constants ***
 //
 // ==========================================================================
 
 
 // ----------------------------------------------------------------
 // *** Camera pan is triggered when there are no more player units in area around source portal
 constant  boolean      DO_CAMERA_PAN     = true;
 
 // ----------------------------------------------------------------
 // *** This is the radius of camera scanning, where it will search for player units
 constant  real      CAMERA_PAN_AREA_SCAN   = 700.;
 
 // ----------------------------------------------------------------
 // *** The time it takes after last unit enters the portal to pan the camera
 constant  real      CAMERA_PAN_DELAY    = 1.5;
 
 
 // ==========================================================================
 //
 //      *** After Image Constants ***
 //
 // ==========================================================================
 
 // ----------------------------------------------------------------
 // *** Do use after imaging, will show transparent unit entering the portal
 constant  boolean      USE_UNIT_AFTER_IMAGING   = true;
 
 // ----------------------------------------------------------------
 // *** The time image lasts
 constant  real      UNIT_AFTER_IMAGE_DURATION  = .85;
 
 // ----------------------------------------------------------------
 // *** Animation speed of image, 1. is 100. percent
 constant  real      UNIT_AFTER_IMAGE_TIMESCALE  = .5;
 
 // ----------------------------------------------------------------
 // *** The color of image
 constant  integer      UNIT_AFTER_IMAGE_RED   = 128;
 constant  integer      UNIT_AFTER_IMAGE_GREEN   = 128;
 constant  integer      UNIT_AFTER_IMAGE_BLUE   = 128;
 // ----------------------------------------------------------------
 // *** The starting transparency in percentage of image, 100. is fully visible
 constant  real      UNIT_AFTER_IMAGE_TRANSPARENCY_MIN = 60.;
 constant  real      UNIT_AFTER_IMAGE_TRANSPARENCY_MAX = 70.;
 
 // =================================================================================
 //  
 //    *** Enable custom selection, units that are teleported to target portal get
 //        selected by owning player if they where selected when they entered source portal
 constant  boolean      DO_UNIT_SELECTION_JOB   = true;
 
 // ----------------------------------------------------------------
 // *** How often to check should units be selected, that is did all units enter source portal
 constant  real      SELECTION_CHECK_INTERVAL  = .5;
 
 // ----------------------------------------------------------------
 // *** non-Editable, do not change ***
 //constant  integer      BLIZZARD_MAX_SOUND_INSTANCES = 4;
 constant  key       CASTER_FACING_KEY;
 constant  key       UNIT_PORT_KEY;
 constant  key       CASTER_PORTS_KEY;
 constant  key       UNIT_SELECTED_KEY;
 constant  key       UNIT_SELECTION_GROUPING_KEY;
 
 
 
 // -------------------------------------------
 // *** Portal level constants
 // -------------------------------------------
 
 // Portal duration in seconds
 function PORTAL_DURATION(integer level) -> real {
  real dur[];
 
  dur[1] = 10.;
  dur[2] = 15.;
  dur[3] = 20.;
 
  return dur[level];
 }
 
 // Portal units transition time in seconds
 function PORTAL_TRANSITION_TIME(integer level) -> real {
  real trans_tim[];
 
  trans_tim[1] = 5.;
  trans_tim[2] = 4.;
  trans_tim[3] = 2.;
 
  return trans_tim[level];
 }
 
 // Portal max units pass
 function PORTAL_MAX_PASS(integer level) -> integer {
  integer max_pass[];
 
  max_pass[1] = 3;
  max_pass[2] = 7;
  max_pass[3] = 15;
 
  return max_pass[level];
 }
 
 // ----------------------------------------
 // **** Unit filter for portal
 function PortFilter(unit filter_unit, player cast_player) -> boolean {
  return  !IsUnitType(filter_unit, UNIT_TYPE_DEAD)    &&
    !IsUnitType(filter_unit, UNIT_TYPE_STRUCTURE)  &&
    !IsUnitType(filter_unit, UNIT_TYPE_ANCIENT)   &&
    !IsUnitType(filter_unit, UNIT_TYPE_POLYMORPHED)  &&
    !IsUnitType(filter_unit, UNIT_TYPE_FLYING)   &&
    !DDIsUnitWard(filter_unit)       &&
    IsUnitAlly(filter_unit, cast_player);
 }
 
 
 // ==================================================================================================
 //   --------------            --------------
 //     *** End constant spell modification, start spell code ***
 //   --------------            --------------
 // ==================================================================================================
 
 
 
 /// Check if target unit is in rectangle, x, y and degrees depend on portal, while length and width are constants
 function IsUnitInRectangle(unit u, real x, real y, real degrees) -> boolean {
  real ux = GetUnitX(u), uy = GetUnitY(u);
 
  real minx = x-(PORTAL_LENGTH*.5), miny = y-(PORTAL_WIDTH*.5); // Min and max x,y from normal rect
  real maxx = x+(PORTAL_LENGTH*.5), maxy = y+(PORTAL_WIDTH*.5);
 
  real range   = SquareRoot(Pw_2(x-ux) + Pw_2(y-uy)); // Distance between center of rect and target unit
  real radians  = Atan2(uy-y, ux-x); // Angle between center of rect and target unit
 
  /// convert unit angle to rect normal
  ux = x + range * Cos(radians-(degrees*bj_DEGTORAD));
  uy = y + range * Sin(radians-(degrees*bj_DEGTORAD));
 
  return (ux > minx && ux < maxx) && (uy > miny && uy < maxy);
 }
 
 // --------------------------------------------------------
 // Since portals are rects under angle, proper movement/teleportation from one portal to another has to be calculated
 // sx = source rectangle x
 // tx = target rectangle x
 function TranslateUnitFromRectangleToRectangle( unit u,
             real sx, real sy, real sdegrees,
             real tx, real ty, real tdegrees)
 {
 
  real ux = GetUnitX(u), uy = GetUnitY(u);
  real range = SquareRoot(Pw_2(sx-ux) + Pw_2(sy-uy)); // Distance between center of source rect and target unit
  real tradians_shift = -Atan2(uy-sy, ux-sx) + (tdegrees+sdegrees+180.)*bj_DEGTORAD; // Target angle between center of source rect and target unit shifted to target rect
 
  /*
  BJDebugMsg(R2S(sdegrees));
  BJDebugMsg(R2S(tdegrees));
  BJDebugMsg(R2S(-Atan2(uy-sy, ux-sx)*bj_RADTODEG));
  BJDebugMsg(R2S(tradians_shift*bj_RADTODEG));
  */
 
  /// convert unit to target rect
  ux = tx + range * Cos(tradians_shift);
  uy = ty + range * Sin(tradians_shift);
 
  // ** Set unit position and shift his angle to cause portal unit shattering, so that units are more organised
  // * Unit will continue moving in direction of his shift facing when ported
  SetUnitX(u, ux); SetUnitY(u, uy);
  SetUnitFacing(u, -GetUnitFacing(u) + (tdegrees+sdegrees));
 }
 
 // ========================================================================
 // *** After imaging code
 struct unitimage {
  private {
   unit u;
   real trans;
   real t_spd;
  
   static  constant  player   IMAGE_OWNER    = Player(15);
  }
 
  public static method CreateAfterImage(unit target, real x, real y) {
   thistype this = allocate();
   real fac = GetUnitFacing(target);
 
   u = CreateUnit(IMAGE_OWNER, GetUnitTypeId(target), 0., 0., fac);
   UnitAddAbility(u, 'Aloc');
   SetUnitPathing(u, false);
   //PauseUnit(u, true);
   SetUnitTimeScale(u, UNIT_AFTER_IMAGE_TIMESCALE);
   SetUnitX(u, x - 90. * Cos(fac*bj_DEGTORAD)); SetUnitY(u, y - 90. * Sin(fac*bj_DEGTORAD));
   SetUnitVertexColor(u, UNIT_AFTER_IMAGE_RED, UNIT_AFTER_IMAGE_GREEN, UNIT_AFTER_IMAGE_BLUE, 255);
   //SetUnitAnimation(u, UNIT_AFTER_IMAGE_ANIMATION);
   //QueueUnitAnimation(u, UNIT_AFTER_IMAGE_ANIMATION);
   SetUnitMoveSpeed(u, 1.);
   IssuePointOrder(u, "move", x + 100. * Cos(fac*bj_DEGTORAD), y + 100. * Sin(fac*bj_DEGTORAD));
   SetUnitColor(u, GetPlayerColor(GetOwningPlayer(target)));
  
   trans = GetRandomReal(UNIT_AFTER_IMAGE_TRANSPARENCY_MIN, UNIT_AFTER_IMAGE_TRANSPARENCY_MAX);
   t_spd = DD_INTERVAL*trans/UNIT_AFTER_IMAGE_DURATION;
   DDStartTim(DD_INTERVAL, true, this, function() {
    thistype this = DDTimData();
   
    trans -= t_spd;
    SetUnitVertexColor(u, UNIT_AFTER_IMAGE_RED, UNIT_AFTER_IMAGE_GREEN, UNIT_AFTER_IMAGE_BLUE, R2I(trans*2.55));
   
    if (trans <= 0.) {
     RemoveUnit(u);
     u = null;
     destroy();
     DDQuitTim();
    }
   });
  }
 }
 
 // ========================================================================
 // *** Primary portal code
 struct portal {
  private {
   unit   p;
   real   x, y, degrees;
   real   tx, ty, tdegrees;
   integer  lvl;
   integer  cpass; // current unit passed through portal
   timer   t;
   rect   pr;  // pick rect
   portal   lnk;
   sound  amb;
   integer  trans_units; // number of units being transiting at a time
   real  cpass_timeout;
  
  
   static constant  real  DIAGONAL   = SquareRoot((PORTAL_LENGTH*PORTAL_LENGTH) + (PORTAL_WIDTH*PORTAL_WIDTH));
   static constant  real  ALPHA    = Atan(PORTAL_WIDTH/PORTAL_LENGTH);
  
   //static    sound  SList[];
   //static    integer  SN     = 0;
  
   //static    player  TempP   = null;
   static    boolean  TempBool  = false;
  
   // *** selection vars ***
   static    group  SctG   = CreateGroup();
   static    timer  SctT   = CreateTimer();
  }
 
  private static method Min4x(real a, real b, real c, real d) -> real {
   if (a < b && a < c && a < d)
    return a;
   else if (b < c && b < d)
    return b;
   else if (c < d)
    return c;
   return d;
  }
 
  private static method Max4x(real a, real b, real c, real d) -> real {
   if (a > b && a > c && a > d)
    return a;
   else if (b > c && b > d)
    return b;
   else if (c > d)
    return c;
   return d;
  }
 
  private static method RectangleToRect(real x, real y, real rrad) -> rect {
  
   real x1 = x + (DIAGONAL * Cos(rrad+ALPHA)*.5),
     x2 = x + (DIAGONAL * Cos(rrad-ALPHA)*.5),
     x3 = x + (DIAGONAL * Cos(rrad+bj_PI+ALPHA)*.5),
     x4 = x + (DIAGONAL * Cos(rrad+bj_PI-ALPHA)*.5);
  
   real y1 = y + (DIAGONAL * Sin(rrad+ALPHA)*.5),
     y2 = y + (DIAGONAL * Sin(rrad-ALPHA)*.5),
     y3 = y + (DIAGONAL * Sin(rrad+bj_PI+ALPHA)*.5),
     y4 = y + (DIAGONAL * Sin(rrad+bj_PI-ALPHA)*.5);
    
   return Rect(Min4x(x1, x2, x3, x4), Min4x(y1, y2, y3, y4),
      Max4x(x1, x2, x3, x4), Max4x(y1, y2, y3, y4));
  }
 
  // ----------------------------------
  // * Constructor
  static method create(player p, integer lvl, real x, real y, real degrees, real tx, real ty, real tdegrees) -> thistype {
   thistype this = allocate();
  
   this.p = CreateUnit(p, PORTAL_ID, x, y, degrees+90.);
   PauseUnit(this.p, true);
   SetUnitAnimation(this.p, PORTAL_ANIM_BIRTH);
   QueueUnitAnimation(this.p, PORTAL_ANIM_STAND);
  
   this.x     = x;
   this.y     = y;
   this.degrees   = degrees;
   this.tx    = tx;
   this.ty    = ty;
   this.tdegrees   = tdegrees;
   this.lvl    = lvl;
   this.cpass    = 0;
   this.trans_units  = 0;
   this.pr    = RectangleToRect(x, y, bj_DEGTORAD*degrees);
   this.cpass_timeout  = PORTAL_TRANSITION_TIME(lvl);
  
   //test();
  
   return this;
  }
 
  // ----------------------------------
  // * Destructor
  method destroy() {
   if (t != null) {
    PauseTimer(t);
    DDRecycleTim(t);
    t = null;
   }
   StopSound(amb, true, true);
   KillUnit(p);
   RemoveRect(pr);
   p = null;
   pr = null;
   amb = null;
   deallocate();
  }
 
  // ------------------------------------------
  private method PlayPortalEntranceSound() {
   DDGenericSound(PORTAL_ENTRANCE_SOUND_FILE, PORTAL_ENTRANCE_SOUND_VOLUME, x, y, SOUND_MAX_DISTANCE, 1.);
   /*if (GetSoundIsPlaying(SList[SN]))
    StopSound(SList[SN], true, false);
   SList[SN] = CreateSound(PORTAL_ENTRANCE_SOUND_FILE, false, true, true, 10, 10, "DefaultEAXON");
   SetSoundVolume(SList[SN], R2I(PORTAL_ENTRANCE_SOUND_VOLUME*1.27));
   SetSoundPosition(SList[SN], x, y, 0.);
   StartSound(SList[SN]);
   KillSoundWhenDone(SList[SN]);
  
   SN = ModuloInteger(SN + 1, BLIZZARD_MAX_SOUND_INSTANCES);*/
  }
 
  // ------------------------------------------
  public method PlayPortalDeathSound() {
   DDGenericSound(PORTAL_DEATH_SOUND_FILE, PORTAL_DEATH_SOUND_VOLUME, x, y, SOUND_MAX_DISTANCE, 1.);
   //PlayBasicSound.evaluate(PORTAL_DEATH_SOUND_FILE, true, PORTAL_DEATH_SOUND_VOLUME, x, y, null);
  }
 
  // ------------------------------------------
  private method PlayPortalAmbienceSound() {
   if (amb == null) {
    amb = CreateSound(PORTAL_AMBIENCE_SOUND_FILE, true, true, true, 10, 10, "DoodadsEAX");
    SetSoundVolume(amb, R2I(PORTAL_AMBIENCE_SOUND_VOLUME*1.27));
    SetSoundPosition(amb, x, y, 0.);
    SetSoundDistanceCutoff(amb, 3500.);
    StartSound(amb);
   }
  }
 
  // *** Checks if player units are around source portal
  private method IsSrcPortalClear(player p) -> boolean {
   //TempP   = p;
   TempBool  = false;
  
   // *** Check if there are any remaining units
   DDGroupFilterArea(x, y, CAMERA_PAN_AREA_SCAN, GetPlayerId(p), function() -> boolean {
    TempBool = TempBool || (GetUnitAbilityLevel(GetFilterUnit(), 'Aloc') == 0  && PortFilter(GetFilterUnit(), Player(DDGFilterData())));
    return false;
   });
  
   return !TempBool;
  }
 
  // ------------------------------------------
  private method DoSelectJob(unit u, boolean select) {
   player p = GetOwningPlayer(u);
  
   if (!select) {
    DDSet(u, UNIT_SELECTED_KEY, 0);
    if (IsUnitSelected(u, p)) {
     DDSet(u, UNIT_SELECTED_KEY, 1);
     SelectUnit(u, false);
     if (IsUnitInGroup(u, SctG))
      GroupRemoveUnit(SctG, u);
    }
   } else {
    if (DDGet(u, UNIT_SELECTED_KEY) == 1) {
     if (IsSrcPortalClear(p)) {
      if (GetLocalPlayer() == p)
       SelectUnit(u, true);
     } else {
      // *** more units are awaiting to be ported delay the selection
      //BJDebugMsg(GetUnitName(FirstOfGroup(SctG)));
      if (FirstOfGroup(SctG) == null) {
       TimerStart(SctT, SELECTION_CHECK_INTERVAL, true, function() {
        //integer i;

        // ------------------------------------
        // *** reset player portal clear vars
        //for(i=0; i < 0xD; i+=1)
        // SctPClr[i] = 0;
       
        // ------------------------------------
        // *** loop through units ***
        ForGroup(SctG, function() {
         unit u = GetEnumUnit();
         player p = GetOwningPlayer(u);
         portal pt = DDGet(u, UNIT_SELECTION_GROUPING_KEY);
         //integer pid = GetPlayerId(p);
        
         // *** here we do not use just 0 and 1, but 0, 1 and 2, to optimize the code
         // *** requiering only min calls of "IsSrcPortalClear"
         /*
         if (SctPClr[pid] == 0) {
          SctPClr[pid] = 1;
          if (IsSrcPortalClear(p))
           SctPClr[pid] = 2;
         }
         */
        
         // *** if portal is clear, do selection now
         if (pt.IsSrcPortalClear(p) || IsUnitType(pt.p, UNIT_TYPE_DEAD)) {
          if (GetLocalPlayer() == p)
           SelectUnit(u, true);
          GroupRemoveUnit(SctG, u);
         }
        
         u = null;
        });
       
        // *** pause timer???
        if (FirstOfGroup(SctG) == null) {
         PauseTimer(SctT);
        }
       });
      }
      DDSet(u, UNIT_SELECTION_GROUPING_KEY, this);
      GroupAddUnit(SctG, u);
     }
    }
   }
  }
 
  // ------------------------------------------
  private method AttemptCameraPan(player p) {
   p_integer pi;
  
   // *** If no more units remains do pan camera
   if (IsSrcPortalClear(p)) {
    pi = p_integer.create();
    pi[0] = lnk;
    pi[1] = GetPlayerId(p);
   
    DDStartTim(CAMERA_PAN_DELAY, false, pi, function() {
     p_integer pi = DDTimData();
    
     if (GetLocalPlayer() == Player(pi[1]))
      SetCameraPosition(portal(pi[0]).x, portal(pi[0]).y);
    
     pi.destroy();
    });
   }
  }
 
  // --------------------------------------------
  // *** Periodic port timer system
  //
  public method StartPortSys() {
   t = DDStartTim(PORT_CHECK_INTERVAL, true, this, static method()
   {
    thistype   this     = DDTimData();
    real    elapsed    = DDTimTick()*PORT_CHECK_INTERVAL;
    integer   i     = 0;
   
    real    dur     = PORTAL_DURATION(lvl);
    real    trans_time    = PORTAL_TRANSITION_TIME(lvl);
    integer   max_pass    = PORTAL_MAX_PASS(lvl);
   
    p_integer   pi;
   
    // ------------------------------------------------------
    // *** Add the code for minimap ping if wanted
    static if (PORTAL_USE_MINIMAP_PING) {
     if (ModuloInteger(R2I(elapsed*10.), R2I(PORTAL_MINIMAP_PING_INTERVAL*10.)) == 1)
      if (GetLocalPlayer() == GetOwningPlayer(p)) {
       PingMinimapEx(x, y, PORTAL_MINIMAP_PING_DURATION, PORTAL_MINIMAP_PING_RED, PORTAL_MINIMAP_PING_GREEN, PORTAL_MINIMAP_PING_BLUE, false);
       PingMinimapEx(tx, ty, PORTAL_MINIMAP_PING_DURATION, PORTAL_MINIMAP_PING_RED, PORTAL_MINIMAP_PING_GREEN, PORTAL_MINIMAP_PING_BLUE, false);
      }
    }
    // ------------------------------------------------------
   
    // --------------------
    // *** Can port?, elapsed must be between bortal finish birth and from that point + portal duration
    // *** number of passed units between linked portals must be lower than constant specifies depending on level
    if (elapsed > PORTAL_ANIM_BIRTH_DURATION && elapsed < dur+PORTAL_ANIM_BIRTH_DURATION    &&
     cpass+lnk.cpass < max_pass)
    {
     PlayPortalAmbienceSound();
    
     // --------------------------------------------------
     // *** pick all units to be ported ***
     DDGroupFillMemRect(pr, this, static method() -> boolean {
      portal this = DDMemUnitData();
      // *** Unit is in rectangle and whas not ported reacently
      if (IsUnitInRectangle(GetFilterUnit(), x, y, degrees) && DDGet(GetFilterUnit(), UNIT_PORT_KEY) == 0)
       return PortFilter(GetFilterUnit(), GetOwningPlayer(p));
      return false;
     });
    
     // --------------------------------------------------
     // *** Port all units in portal area ***
     while(i < DDMemUnitN()) {
     
      if (!IssueImmediateOrder(DDMemUnit(i), "stop"))
       debug BJDebugMsg("Portal: Fail to stop unit!");
     
      static if (DO_UNIT_SELECTION_JOB)
       DoSelectJob(DDMemUnit(i), false);
      
      static if (USE_UNIT_AFTER_IMAGING)
       unitimage.CreateAfterImage(DDMemUnit(i), GetUnitX(DDMemUnit(i)), GetUnitY(DDMemUnit(i)));

      TranslateUnitFromRectangleToRectangle(DDMemUnit(i), x, y, degrees, tx, ty, tdegrees);
      ShowUnit(DDMemUnit(i), false);
      PlayPortalEntranceSound();
      trans_units += 1;
      lnk.trans_units += 1;
     
      static if (DO_CAMERA_PAN)
       AttemptCameraPan(GetOwningPlayer(DDMemUnit(i)));
     
      DDSet(DDMemUnit(i), UNIT_PORT_KEY, 1);
     
      // ---------------------------------------------------------
      // *** Transition time
      pi = p_integer.create();
      pi[0] = New_pUnit(DDMemUnit(i));
      pi[1] = this.lnk;
      pi[2] = this;
      DDStartTim(trans_time, false, pi, static method() {
       p_integer pi  = DDTimData();
       p_unit   pu   = pi[0];
       real   dist  = GetRandomReal(UNIT_PORT_WALK_OFFSET_MIN, UNIT_PORT_WALK_OFFSET_MAX);
       real   fac  = GetUnitFacing(pu[0]);
      
       portal(pi[1]).trans_units -= 1;
       portal(pi[2]).trans_units -= 1;
       portal(pi[1]).PlayPortalEntranceSound();
       ShowUnit(pu[0], true);
       IssuePointOrder(pu[0], "move",  GetUnitX(pu[0]) + dist * Cos(fac*bj_DEGTORAD),
               GetUnitY(pu[0]) + dist * Sin(fac*bj_DEGTORAD));
      
      
       // *** Make unit to be able to port again ***
       TimerStart(GetExpiredTimer(), UNIT_NEXT_PORT_DELAY, false, static method() {
        p_integer pi  = DDTimData();
        p_unit   pu   = pi[0];
        DDSet(pu[0], UNIT_PORT_KEY, 0);
        pi.destroy();
        pu.destroy();
        DDQuitTim();
       });
      
       static if (DO_UNIT_SELECTION_JOB) {
        thistype(pi[2]).DoSelectJob(p_unit(pi[0])[0], true);
       }
      });
      // ---------------------------------------------------------
     
      cpass += 1;
      i += 1;
     }
    }
   
    // *** Portal end if max pass reached or
    if ((elapsed > PORTAL_ANIM_BIRTH_DURATION + dur /*+ trans_time*/) || (cpass + lnk.cpass >= max_pass)) {
     /*
     if (cpass + lnk.cpass >= max_pass)
      cpass_timeout -= PORT_CHECK_INTERVAL;
     else
      cpass_timeout = 0.;
     */
     //debug BJDebugMsg(I2S(trans_units));
     if (/*cpass_timeout <= 0. ||*/ trans_units == 0) {
      DDStartTim(DESTROY_PORTAL_DELAY, false, this, function() {
       portal this = DDTimData();
      
       PlayPortalDeathSound();
       t = null;
       this.destroy();
       DDQuitTim();
      });
      DDQuitTim();
     }
    }
   
   });
  }
 
  // *** links with other portal
  method operator  link=(portal p)    { lnk = p; }
  method operator  Elapsed() -> real    { return DDCTimTick(t)*PORT_CHECK_INTERVAL; }
 
 }
 
 
 // ================================================================================
 //
 //      *** Extra spell functions ***
 //
 // ================================================================================
 
 
 // ------------------------------------------
 // *** Rounds positive integer
 function Round(real x) -> integer {
  if (x-I2R(R2I(x)) > .5)
   return R2I(x)+1;
  return R2I(x);
 }
 

 function onInit() {
  trigger t = CreateTrigger();
 
  TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CAST);
  TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
  TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST);
  TriggerAddCondition(t, function() -> boolean {
   // ----------------------------------------
   // *** Locals ***
   unit   u;
   sound  s;
   player   p;
   integer  lvl;
   real  ux, uy;
   real  tx, ty;
   real  x, y, rads;
   p_real  fac;
   p_integer pt;
  
   // ----------------------------------------
   // *** Condition ***
   if (GetSpellAbilityId() != SPELL_ID)
    return false;
  
   // ----------------------------------------
   // *** Actions ***
  
  
   // ====================================================================
   // *** Start cast event ***
   // ====================================================================
   if (GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_CAST) {
    u = GetTriggerUnit();
    if (IsUnitInRangeXY(u, GetSpellTargetX(), GetSpellTargetY(), SECOND_PORTAL_MIN_RANGE)) {
     IssuePointOrder(u, "move", GetUnitX(u) + 50. * Cos(GetUnitFacing(u)*bj_DEGTORAD), GetUnitY(u) + 50. * Sin(GetUnitFacing(u)*bj_DEGTORAD));
     DisplayTimedTextFromPlayer(GetOwningPlayer(u), 0., 0., PORTAL_WARNING_MESSAGE_DURATION, "|cffff0020"+PORTAL_WARNING_MESSAGE+"|r");
     if (GetLocalPlayer() == GetOwningPlayer(u)) {
      s = CreateSound(PORTAL_WARNING_SOUND_FILE, false, false, false, 10, 10, "");
      SetSoundVolume(s, R2I(PORTAL_WARNING_SOUND_VOLUME*1.27));
      StartSound(s);
      KillSoundWhenDone(s);
      s = null;
     }
     //PlayBasicSound(PORTAL_WARNING_SOUND_FILE, false, PORTAL_WARNING_SOUND_VOLUME, 0., 0., GetOwningPlayer(u));
     DDSet(u, CASTER_FACING_KEY, 0);
    } else
     DDSet(u, CASTER_FACING_KEY, New_pReal(GetUnitFacing(u)));
   
    u = null;
    return false;
   }
  
  
   // ====================================================================
   // *** End cast event ***
   // ====================================================================
   if (GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_ENDCAST) {
    u = GetTriggerUnit();
   
    // *** Fail to cast spell?
    pt = DDGet(u, CASTER_PORTS_KEY);
    if (pt == 00) {
     u = null;
     return false;
    }
   
   
   
    // *** End spell if portal whas not channeled fully
    // * Use round and increase precission by 10x, meaning that cast times like 7.64 will be 7.6, as real values are unsafe to be compared
    if (Round(portal(pt[0]).Elapsed*10.) < Round(PORTAL_ANIM_BIRTH_DURATION*10.)) {
     portal(pt[0]).PlayPortalDeathSound();
     portal(pt[1]).PlayPortalDeathSound();
     portal(pt[0]).destroy();
     portal(pt[1]).destroy();
    }
    pt.destroy();
   
    u = null;
    return false;
   }
  
   // ====================================================================
  
  
  
   // ====================================================================
   // *** Start cast effect event ***
   // ====================================================================
   u   = GetTriggerUnit();
   p   = GetOwningPlayer(u);
   lvl  = GetUnitAbilityLevel(u, SPELL_ID);
   fac  = DDGet(u, CASTER_FACING_KEY);
  
   ux = GetUnitX(u); uy = GetUnitY(u);
   tx = GetSpellTargetX(); ty = GetSpellTargetY();
   rads = Atan2(-uy+ty, -ux+tx);
   x = ux + FIRST_PORTAL_OFFSET * Cos(rads);
   y = uy + FIRST_PORTAL_OFFSET * Sin(rads);
  
   pt = p_integer.create();
   pt[0] = portal.create(p, lvl, x, y, (rads*bj_RADTODEG)+90., tx, ty, fac[0]-90.);
   pt[1] = portal.create(p, lvl, tx, ty, fac[0]-90., x, y, (rads*bj_RADTODEG)+90.);
  
   portal(pt[0]).link = pt[1];
   portal(pt[1]).link = pt[0];
  
   portal(pt[0]).StartPortSys();
   portal(pt[1]).StartPortSys();
  
   DDSet(u, CASTER_PORTS_KEY, pt);
  
   DDGenericSound(PORTAL_BIRTH_SOUND_FILE, PORTAL_BIRTH_SOUND_VOLUME, x, y, SOUND_MAX_DISTANCE, 1.);
   DDGenericSound(PORTAL_BIRTH_SOUND_FILE, PORTAL_BIRTH_SOUND_VOLUME, tx, ty, SOUND_MAX_DISTANCE, 1.);
  
  
   fac.destroy();
   u = null;
   return false;
  });
 }

}
//! endzinc



// ============================================================================================
// Change me from /* to // and then back to /* after saving and reopening the map
    // |
    // ˇ
      /*
     
// Credits: PurgeandFire for lua tutorial
//! externalblock extension=lua ObjectMerger $FILENAME$
 //! i -- =========================================================
 
 //! i SHIMMERING_PORTAL_ABIL_ID     = "SPrt"
 //! i SHIMMERING_PORTAL_DUMMY_ID    = "port"
 
 //! i -- =========================================================


     //! i -- -----------------------------------------------
     //! i --Ability Create Code
     //! i setobjecttype("abilities")
     //! i createobject("ANto", SHIMMERING_PORTAL_ABIL_ID)
     //! i makechange(current,"anam", "Shimmering Portal")
     //! i makechange(current,"aart", "ReplaceableTextures\\CommandButtons\\BTNPortal.blp")
     //! i makechange(current,"arar", "ReplaceableTextures\\CommandButtons\\BTNPortal.blp")
     //! i makechange(current,"aani", "channel")
     //! i makechange(current,"alev", "3")
     //! i makechange(current,"arlv", "1")
     //! i makechange(current,"ahky", "H")
     //! i makechange(current,"arhk", "H")
     //! i makechange(current,"aret", "Learn S|cff8000ffh|rimmering Portal - [|cffffcc00Level ".. string.char(37) .."d|r]")
     //! i makechange(current,"arut", "|cffb300b3Opens portal allowing any ground unit to pass through.|n|r|n|cff408080[Level 1]|r - Portal lasts 10 seconds, up to 3 units can pass, 5 seconds transition time, cooldown 30 seconds.|n|cff408080[Level 2]|r - Portal lasts 15 seconds, up to 7 units can pass, 4 seconds transition time, cooldown 20 seconds.|n|cff408080[Level 3]|r - Portal lasts 20 seconds, up to 12 units can pass, 2 seconds transition time, cooldown 10 seconds.|n|n|cffb00000Channeling|r")
     //! i makechange(current,"aub1","1","|cffb300b3Portal lasts 10 seconds, up to 3 units can pass, 5 seconds transition time, cooldown 30 seconds.|r")
     //! i makechange(current,"aub1","2","|cffb300b3Portal lasts 15 seconds, up to 7 units can pass, 4 seconds transition time, cooldown 20 seconds.|r")
     //! i makechange(current,"aub1","3","|cffb300b3Portal lasts 20 seconds, up to 12 units can pass, 2 seconds transition time, cooldown 10 seconds.|r")
    
     //! i local i = 0
     //! i for i=1, 3 do
        //! i local si = tostring(i)
        //! i makechange(current,"Ntou",si,"")
        //! i makechange(current,"aran",si,"99999.")
        //! i makechange(current,"acdn",si,tostring(10*(4-i)))
        //! i makechange(current,"adur",si,"7.5")
        //! i makechange(current,"ahdu",si,"7.5")
        //! i makechange(current,"amcs",si,"100")
        //! i makechange(current,"atp1",si,"S|cff8000ffh|rimmering Portal - [|cffffcc00Level ".. si .."|r]")
     //! i end
    
     //! i -- -----------------------------------------------
     //! i --Dummy Create Code
     //! i setobjecttype("units")
     //! i createobject("hpea", SHIMMERING_PORTAL_DUMMY_ID)
     //! i makechange(current, "uabi", "Avul,Aloc")
     //! i makechange(current, "udtm", "5.77")
     //! i makechange(current, "uico", "ReplaceableTextures\\CommandButtons\\BTNPortal.blp")
     //! i makechange(current, "umdl", "Doodads\\Cinematic\\ShimmeringPortal\\ShimmeringPortal.mdl")
     //! i makechange(current, "umxp", "0.")
     //! i makechange(current, "umxr", "0.")
     //! i makechange(current, "ushu", "")
     //! i makechange(current, "ua1t", "")
     //! i makechange(current, "udea", string.char(2))
     //! i makechange(current, "umvt", "")
     //! i makechange(current, "ucol", "0.")
     //! i makechange(current, "ufoo", "0")
     //! i makechange(current, "usid", "1200")
     //! i makechange(current, "usin", "1200")
     //! i makechange(current, "unam", "Portal")
     //! i makechange(current, "unsf", " (Dummy)")
    
//! endexternalblock

       */
    // ^
    // |
// Change me from */ to // and then back to */ after saving and reopening the map
// ============================================================================================

If you need any extra help/info fell free to comment!

Enjoy!
~Dark Dragon

Keywords:
Frozen Throne, campaign, cinematic, portal, teleport, shimmering portal, spell, other
Contents

Spell Shimmering Portal (Map)

Reviews
PurplePoot: Good enough, then, I s'pose. (I'll let you off on the MPI) ~approved~

Moderator

M

Moderator

PurplePoot: Good enough, then, I s'pose. (I'll let you off on the MPI)

~approved~
 
Level 2
Joined
Aug 8, 2007
Messages
3
Good job, only thing i think should be added is a visible timer for the portals, like when an archmage summons his elementals. I love the 'walk-through' feeling of it, instead of the teleportation feeling of the waygate ability that's often been used. 4/5, keep it up!
 
I have seen spell like this in hive. I think that you havent copied it becouse it has been maded long time ago. Still its very nice spell and you have done great job! ^^

copied? ofc i did! its from frozen throne campaign and i give credits to blizzard for that and making the game ofc :D

well there are some other people who tried to make such a spell but in the end i did not found any which can be called realy great... this is one of my first spells and its for some time on hive now and if you checked changelog you will see that i was few times updating it but still i was not so pro at that time. since this was my only spell which was causing HSC i decided to fix it since well i would need to fix it since it would not work for 1.24...

Anyway ty for comment and greets!
~Dark Dragon
 
Looking on the description the spell seems cool, shame that i can't test it, my Warcraft is 1.21. I've created a similar spell that i've used on my map "Ice Elves VS Scourge", but mine isn't in JASS, i must admit that i don't understand JASS xD. Anyway, i think that i will post my version of the Shimering Portal here, but please, don't think i've stollen your idea, even cus mine is a little diferent and simple. =D
 
Looking on the description the spell seems cool, shame that i can't test it, my Warcraft is 1.21. I've created a similar spell that i've used on my map "Ice Elves VS Scourge", but mine isn't in JASS, i must admit that i don't understand JASS xD. Anyway, i think that i will post my version of the Shimering Portal here, but please, don't think i've stollen your idea, even cus mine is a little diferent and simple. =D

i dont mind at all if you upload your version of SP! since this is an blizzards idea not my and i am not sure but you are supposed to be able to test this since if it works for 1.23 it must work for 1.21 as well. however problem might be that you use old JNGP so you need newest one from my description here.

anyway np feel free to post your portal here if its in GUI and if its awesome then we will have two portals! ones for jassers and one for GUI-ers :)

Greets!
~Dark Dragon
 
Level 1
Joined
Apr 22, 2010
Messages
4
I will tell that:
It's very, very good looking and good using spell. Nice for Campaign. If I used it, I will add you to credits of course.
Big Gratz!
 
Level 5
Joined
Dec 26, 2008
Messages
30
I have one problem apropos jass triggers. If I establish jass triggers after check jass helper he gives out me an error. If to check up a map I take off for the main menu Warcraft. At me costs jass new gen pack but why that of for jass does not want to work for me a card.Help me please!
 
Level 1
Joined
Jan 17, 2014
Messages
1
Okay I have opened up the .w3x file using the NG World Editor and copied over the trigger called 'Shimmering Portal v124'. However, when I try to enable it, I get the error 'The trigger Shimmering Portal v124 initialisation function called InitTrig_Shimmering Portal v124'. I then clicked copy, closed the map, opened my desired map, and click paste in the trigger editor, but I can't enable this trigger without receiving the same error message. Can anyone explain why? And yes, I have checked that my Warcraft 3/Frozen Throne version is 1.24, so I'm not sure what the problem is.

Okay it seems to work when I test it, however, I can't make any changes to the map anymore, otherwise it will disable the trigger since it displays that trigger message. Does this mean I will have to copy the trigger every time I make a change to the map?
 
Last edited:
Did you happen to see the issues I posted on your VM wall?

Hi,

yes i checked, if you need to filtrate footman add the below line to "PortFilter" function, 'hfoo' is footmans id.

JASS:
...
!IsUnitType(filter_unit, UNIT_TYPE_FLYING)            &&
GetUnitTypeId(filter_unit) == 'hfoo'                       &&
!DDIsUnitWard(filter_unit)                                    &&
...

JASS:
    // ----------------------------------------
    // **** Unit filter for portal
    function PortFilter(unit filter_unit, player cast_player) -> boolean {
        return     !IsUnitType(filter_unit, UNIT_TYPE_DEAD)             &&
                !IsUnitType(filter_unit, UNIT_TYPE_STRUCTURE)        &&
                !IsUnitType(filter_unit, UNIT_TYPE_ANCIENT)            &&
                !IsUnitType(filter_unit, UNIT_TYPE_POLYMORPHED)        &&
                !IsUnitType(filter_unit, UNIT_TYPE_FLYING)            &&
                !DDIsUnitWard(filter_unit)                            &&
                IsUnitAlly(filter_unit, cast_player);
    }

Still curious ....
someone can show me how to install this !?

Its actaully not appearing in game what shall i do every time I use the spell the portal it is not appearing triggers are copied what do i do

Hi,

from object editor you have to copy "Portal (Dummy)" unit to your map and "Shimmering Portal" ability.
From trigger editor you have to copy triggers to your map and modify constants to match new data in your map. For example that means "Portal (Dummy)" id and "Shimmering Portal" id have to be set to ones in your map. For example in object editor in your map find "Portal (Dummy)", press Ctrl+D and you will see id-s. Modify the "PORTAL_ID" from 'port' to id in your map. Below is the code in "Shimmering Portal" trigger that you should modify to your needs in your map.

JASS:
// =====================================================================================================
//                            * Spell - Shimmering Portal
//                                    by Dark Dragon
//
//
//    Installation:
//
// (1) Install "DD Library" to your map if you have not done so already from my previous spells
// (2) Copy "Shimmering Portal" trigger to your map
// (3) Copy "Shimmering Portal" and "Portal (Dummy)" to your map
// (4) Edit spell to your will in object editor and here below in trigger editor
// (5) Enjoy!
// =====================================================================================================


//! zinc
library ShimmeringPortal requires DDLib
{

 
    // =============================================================
    //            *** Spell editable constants ***
    // =============================================================
 
    // ==========================================================================
    //
    //                     *** Primary Spell Constants ***
    //
    // ==========================================================================
 
    // ----------------------------------------------------------------
    // *** The id of shimmering portal spell
    constant        integer                        SPELL_ID                        = 'SPrt';
 
    // ----------------------------------------------------------------
    // *** The id of shimmering portal dummy
    constant        integer                        PORTAL_ID                        = 'port';
 
    // ----------------------------------------------------------------
    // *** The length of portal, shrinking the value will force units to enter closer to center of portal
    constant        real                        PORTAL_LENGTH                    = 450.;
 
    // ----------------------------------------------------------------
    // *** Width of portal, units will enter the portal more far away if value is increased,
    //     decreassing this value too much can make unit pass portal faster than check interval can scan, thous causing spell to bug
    constant        real                        PORTAL_WIDTH                    = 110.;
 
    // ----------------------------------------------------------------
    // *** Units that are teleported to target portal, will walk away from there position offseted by random value between this (min and max)
    constant        real                        UNIT_PORT_WALK_OFFSET_MIN        = 200.;
    constant        real                        UNIT_PORT_WALK_OFFSET_MAX        = 350.;
 
    // ----------------------------------------------------------------
    // *** The offset of first portal near caster
    constant        real                        FIRST_PORTAL_OFFSET                = 300.;
 
    // ----------------------------------------------------------------
    // *** The minimum casting range for shimmering portal spell
    constant        real                        SECOND_PORTAL_MIN_RANGE            = 1000.;
 
    // ----------------------------------------------------------------
    // *** Time it takes to destroy portal after it expires or last unit exits
    constant        real                        DESTROY_PORTAL_DELAY            = .7;
 
    // ----------------------------------------------------------------
    // *** The warning message, displayed in red color, and sound played with it, volume is in percentage
    constant        string                        PORTAL_WARNING_MESSAGE            = "Warning: Too close cast of Shimmering portal!";
    constant        string                        PORTAL_WARNING_SOUND_FILE        = "Sound\\Interface\\Error.wav";
    constant        real                        PORTAL_WARNING_SOUND_VOLUME        = 90.;
 
    // ----------------------------------------------------------------
    // *** The duration of warning message
    constant        real                        PORTAL_WARNING_MESSAGE_DURATION    = 5.;
 
    // ----------------------------------------------------------------
    // *** This is animation of dummy portal, birth duration must be equeal to spell channel time
    constant        string                        PORTAL_ANIM_BIRTH                = "birth";
    constant        string                        PORTAL_ANIM_DEATH                = "death";
    constant        string                        PORTAL_ANIM_STAND                = "stand";
    constant        real                        PORTAL_ANIM_BIRTH_DURATION        = 7.5;
 
    // ----------------------------------------------------------------
    // *** How often to scan for units entry, greater value slows down CPU, while too low can cause units entered to not be registerd
    constant        real                        PORT_CHECK_INTERVAL                = .1;
 
    // ----------------------------------------------------------------
    // *** Prevents units that are teleported to target portal to be teleported back, causing infinite loop of teleporting,
    //     this timing says that any unit will not be able to use any portals (MUI included)
    constant        real                        UNIT_NEXT_PORT_DELAY            = 2.;
 
    // ----------------------------------------------------------------
    // *** Portal custom sounds files and volume in percentage
    constant        real                        SOUND_MAX_DISTANCE                = 3500.;
    constant        string                        PORTAL_BIRTH_SOUND_FILE            = "Sound\\Ambient\\DoodadEffects\\ShimmeringPortalBirth.wav";
    constant        real                        PORTAL_BIRTH_SOUND_VOLUME        = 95.;
    constant        string                        PORTAL_DEATH_SOUND_FILE            = "Sound\\Ambient\\DoodadEffects\\ShimmeringPortalDeath.wav";
    constant        real                        PORTAL_DEATH_SOUND_VOLUME        = 95.;
    constant        string                        PORTAL_ENTRANCE_SOUND_FILE        = "Sound\\Ambient\\DoodadEffects\\ShimmeringPortalEntrance.wav";
    constant        real                        PORTAL_ENTRANCE_SOUND_VOLUME    = 95.;
    constant        string                        PORTAL_AMBIENCE_SOUND_FILE        = "Sound\\Ambient\\DoodadEffects\\CityScapeMagicRunesLoop1.wav";
    constant        real                        PORTAL_AMBIENCE_SOUND_VOLUME    = 100.;
 
 
    // ==========================================================================
    //
    //                     *** Minimap Ping Constants ***
    //
    // ==========================================================================
 
    // ----------------------------------------------------------------
    // *** Do use minimap ping system for portals?
    constant        boolean                        PORTAL_USE_MINIMAP_PING            = true;
 
    // ----------------------------------------------------------------
    // *** How often to ping minimap
    constant        real                        PORTAL_MINIMAP_PING_INTERVAL    = 4.5;
 
    // ----------------------------------------------------------------
    // *** How long to ping minimap
    constant        real                        PORTAL_MINIMAP_PING_DURATION    = 3.;
 
    // ----------------------------------------------------------------
    // *** The color of minimap ping
    constant        integer                        PORTAL_MINIMAP_PING_RED            = 180;
    constant        integer                        PORTAL_MINIMAP_PING_GREEN        = 23;
    constant        integer                        PORTAL_MINIMAP_PING_BLUE        = 240;
 
    // ==========================================================================
    //
    //                     *** Camera pan Constants ***
    //
    // ==========================================================================
 
 
    // ----------------------------------------------------------------
    // *** Camera pan is triggered when there are no more player units in area around source portal
    constant        boolean                     DO_CAMERA_PAN                    = true;
 
    // ----------------------------------------------------------------
    // *** This is the radius of camera scanning, where it will search for player units
    constant        real                        CAMERA_PAN_AREA_SCAN            = 700.;
 
    // ----------------------------------------------------------------
    // *** The time it takes after last unit enters the portal to pan the camera
    constant        real                        CAMERA_PAN_DELAY                = 1.5;
 
 
    // ==========================================================================
    //
    //                     *** After Image Constants ***
    //
    // ==========================================================================
 
    // ----------------------------------------------------------------
    // *** Do use after imaging, will show transparent unit entering the portal
    constant        boolean                        USE_UNIT_AFTER_IMAGING            = true;
 
    // ----------------------------------------------------------------
    // *** The time image lasts
    constant        real                        UNIT_AFTER_IMAGE_DURATION        = .85;
 
    // ----------------------------------------------------------------
    // *** Animation speed of image, 1. is 100. percent
    constant        real                        UNIT_AFTER_IMAGE_TIMESCALE        = .5;
 
    // ----------------------------------------------------------------
    // *** The color of image
    constant        integer                        UNIT_AFTER_IMAGE_RED            = 128;
    constant        integer                        UNIT_AFTER_IMAGE_GREEN            = 128;
    constant        integer                        UNIT_AFTER_IMAGE_BLUE            = 128;
    // ----------------------------------------------------------------
    // *** The starting transparency in percentage of image, 100. is fully visible
    constant        real                        UNIT_AFTER_IMAGE_TRANSPARENCY_MIN = 60.;
    constant        real                        UNIT_AFTER_IMAGE_TRANSPARENCY_MAX = 70.;
 
    // =================================================================================
    //
    //    *** Enable custom selection, units that are teleported to target portal get
    //        selected by owning player if they where selected when they entered source portal
    constant        boolean                        DO_UNIT_SELECTION_JOB            = true;
 
    // ----------------------------------------------------------------
    // *** How often to check should units be selected, that is did all units enter source portal
    constant        real                        SELECTION_CHECK_INTERVAL        = .5;
 
    // ----------------------------------------------------------------
    // *** non-Editable, do not change ***
    //constant        integer                        BLIZZARD_MAX_SOUND_INSTANCES    = 4;
    constant        key                            CASTER_FACING_KEY;
    constant        key                            UNIT_PORT_KEY;
    constant        key                            CASTER_PORTS_KEY;
    constant        key                            UNIT_SELECTED_KEY;
    constant        key                            UNIT_SELECTION_GROUPING_KEY;
 
 
 
    // -------------------------------------------
    // *** Portal level constants
    // -------------------------------------------
 
    // Portal duration in seconds
    function PORTAL_DURATION(integer level) -> real {
        real dur[];
     
        dur[1] = 10.;
        dur[2] = 15.;
        dur[3] = 20.;
     
        return dur[level];
    }
 
    // Portal units transition time in seconds
    function PORTAL_TRANSITION_TIME(integer level) -> real {
        real trans_tim[];
     
        trans_tim[1] = 5.;
        trans_tim[2] = 4.;
        trans_tim[3] = 2.;
     
        return trans_tim[level];
    }
 
    // Portal max units pass
    function PORTAL_MAX_PASS(integer level) -> integer {
        integer max_pass[];
     
        max_pass[1] = 3;
        max_pass[2] = 7;
        max_pass[3] = 15;
     
        return max_pass[level];
    }
 
    // ----------------------------------------
    // **** Unit filter for portal
    function PortFilter(unit filter_unit, player cast_player) -> boolean {
        return     !IsUnitType(filter_unit, UNIT_TYPE_DEAD)             &&
                !IsUnitType(filter_unit, UNIT_TYPE_STRUCTURE)        &&
                !IsUnitType(filter_unit, UNIT_TYPE_ANCIENT)            &&
                !IsUnitType(filter_unit, UNIT_TYPE_POLYMORPHED)        &&
                !IsUnitType(filter_unit, UNIT_TYPE_FLYING)            &&
                !DDIsUnitWard(filter_unit)                            &&
                IsUnitAlly(filter_unit, cast_player);
    }
 
 
    // ==================================================================================================
    //            --------------                                                --------------
    //                    *** End constant spell modification, start spell code ***
    //            --------------                                                --------------
    // ==================================================================================================

Regards!
~Dark Dragon
 
Last edited:
  • Like
Reactions: Kam
Level 3
Joined
Nov 22, 2018
Messages
73
Hi,

yes i checked, if you need to filtrate footman add the below line to "PortFilter" function, 'hfoo' is footmans id.

JASS:
...
!IsUnitType(filter_unit, UNIT_TYPE_FLYING)            &&
GetUnitTypeId(filter_unit) == 'hfoo'                       &&
!DDIsUnitWard(filter_unit)                                    &&
...

JASS:
    // ----------------------------------------
    // **** Unit filter for portal
    function PortFilter(unit filter_unit, player cast_player) -> boolean {
        return     !IsUnitType(filter_unit, UNIT_TYPE_DEAD)             &&
                !IsUnitType(filter_unit, UNIT_TYPE_STRUCTURE)        &&
                !IsUnitType(filter_unit, UNIT_TYPE_ANCIENT)            &&
                !IsUnitType(filter_unit, UNIT_TYPE_POLYMORPHED)        &&
                !IsUnitType(filter_unit, UNIT_TYPE_FLYING)            &&
                !DDIsUnitWard(filter_unit)                            &&
                IsUnitAlly(filter_unit, cast_player);
    }





Hi,

from object editor you have to copy "Portal (Dummy)" unit to your map and "Shimmering Portal" ability.
From trigger editor you have to copy triggers to your map and modify constants to match new data in your map. For example that means "Portal (Dummy)" id and "Shimmering Portal" id have to be set to ones in your map. For example in object editor in your map find "Portal (Dummy)", press Ctrl+D and you will see id-s. Modify the "PORTAL_ID" from 'port' to id in your map. Below is the code in "Shimmering Portal" trigger that you should modify to your needs in your map.

JASS:
// =====================================================================================================
//                            * Spell - Shimmering Portal
//                                    by Dark Dragon
//
//
//    Installation:
//
// (1) Install "DD Library" to your map if you have not done so already from my previous spells
// (2) Copy "Shimmering Portal" trigger to your map
// (3) Copy "Shimmering Portal" and "Portal (Dummy)" to your map
// (4) Edit spell to your will in object editor and here below in trigger editor
// (5) Enjoy!
// =====================================================================================================


//! zinc
library ShimmeringPortal requires DDLib
{

 
    // =============================================================
    //            *** Spell editable constants ***
    // =============================================================
 
    // ==========================================================================
    //
    //                     *** Primary Spell Constants ***
    //
    // ==========================================================================
 
    // ----------------------------------------------------------------
    // *** The id of shimmering portal spell
    constant        integer                        SPELL_ID                        = 'SPrt';
 
    // ----------------------------------------------------------------
    // *** The id of shimmering portal dummy
    constant        integer                        PORTAL_ID                        = 'port';
 
    // ----------------------------------------------------------------
    // *** The length of portal, shrinking the value will force units to enter closer to center of portal
    constant        real                        PORTAL_LENGTH                    = 450.;
 
    // ----------------------------------------------------------------
    // *** Width of portal, units will enter the portal more far away if value is increased,
    //     decreassing this value too much can make unit pass portal faster than check interval can scan, thous causing spell to bug
    constant        real                        PORTAL_WIDTH                    = 110.;
 
    // ----------------------------------------------------------------
    // *** Units that are teleported to target portal, will walk away from there position offseted by random value between this (min and max)
    constant        real                        UNIT_PORT_WALK_OFFSET_MIN        = 200.;
    constant        real                        UNIT_PORT_WALK_OFFSET_MAX        = 350.;
 
    // ----------------------------------------------------------------
    // *** The offset of first portal near caster
    constant        real                        FIRST_PORTAL_OFFSET                = 300.;
 
    // ----------------------------------------------------------------
    // *** The minimum casting range for shimmering portal spell
    constant        real                        SECOND_PORTAL_MIN_RANGE            = 1000.;
 
    // ----------------------------------------------------------------
    // *** Time it takes to destroy portal after it expires or last unit exits
    constant        real                        DESTROY_PORTAL_DELAY            = .7;
 
    // ----------------------------------------------------------------
    // *** The warning message, displayed in red color, and sound played with it, volume is in percentage
    constant        string                        PORTAL_WARNING_MESSAGE            = "Warning: Too close cast of Shimmering portal!";
    constant        string                        PORTAL_WARNING_SOUND_FILE        = "Sound\\Interface\\Error.wav";
    constant        real                        PORTAL_WARNING_SOUND_VOLUME        = 90.;
 
    // ----------------------------------------------------------------
    // *** The duration of warning message
    constant        real                        PORTAL_WARNING_MESSAGE_DURATION    = 5.;
 
    // ----------------------------------------------------------------
    // *** This is animation of dummy portal, birth duration must be equeal to spell channel time
    constant        string                        PORTAL_ANIM_BIRTH                = "birth";
    constant        string                        PORTAL_ANIM_DEATH                = "death";
    constant        string                        PORTAL_ANIM_STAND                = "stand";
    constant        real                        PORTAL_ANIM_BIRTH_DURATION        = 7.5;
 
    // ----------------------------------------------------------------
    // *** How often to scan for units entry, greater value slows down CPU, while too low can cause units entered to not be registerd
    constant        real                        PORT_CHECK_INTERVAL                = .1;
 
    // ----------------------------------------------------------------
    // *** Prevents units that are teleported to target portal to be teleported back, causing infinite loop of teleporting,
    //     this timing says that any unit will not be able to use any portals (MUI included)
    constant        real                        UNIT_NEXT_PORT_DELAY            = 2.;
 
    // ----------------------------------------------------------------
    // *** Portal custom sounds files and volume in percentage
    constant        real                        SOUND_MAX_DISTANCE                = 3500.;
    constant        string                        PORTAL_BIRTH_SOUND_FILE            = "Sound\\Ambient\\DoodadEffects\\ShimmeringPortalBirth.wav";
    constant        real                        PORTAL_BIRTH_SOUND_VOLUME        = 95.;
    constant        string                        PORTAL_DEATH_SOUND_FILE            = "Sound\\Ambient\\DoodadEffects\\ShimmeringPortalDeath.wav";
    constant        real                        PORTAL_DEATH_SOUND_VOLUME        = 95.;
    constant        string                        PORTAL_ENTRANCE_SOUND_FILE        = "Sound\\Ambient\\DoodadEffects\\ShimmeringPortalEntrance.wav";
    constant        real                        PORTAL_ENTRANCE_SOUND_VOLUME    = 95.;
    constant        string                        PORTAL_AMBIENCE_SOUND_FILE        = "Sound\\Ambient\\DoodadEffects\\CityScapeMagicRunesLoop1.wav";
    constant        real                        PORTAL_AMBIENCE_SOUND_VOLUME    = 100.;
 
 
    // ==========================================================================
    //
    //                     *** Minimap Ping Constants ***
    //
    // ==========================================================================
 
    // ----------------------------------------------------------------
    // *** Do use minimap ping system for portals?
    constant        boolean                        PORTAL_USE_MINIMAP_PING            = true;
 
    // ----------------------------------------------------------------
    // *** How often to ping minimap
    constant        real                        PORTAL_MINIMAP_PING_INTERVAL    = 4.5;
 
    // ----------------------------------------------------------------
    // *** How long to ping minimap
    constant        real                        PORTAL_MINIMAP_PING_DURATION    = 3.;
 
    // ----------------------------------------------------------------
    // *** The color of minimap ping
    constant        integer                        PORTAL_MINIMAP_PING_RED            = 180;
    constant        integer                        PORTAL_MINIMAP_PING_GREEN        = 23;
    constant        integer                        PORTAL_MINIMAP_PING_BLUE        = 240;
 
    // ==========================================================================
    //
    //                     *** Camera pan Constants ***
    //
    // ==========================================================================
 
 
    // ----------------------------------------------------------------
    // *** Camera pan is triggered when there are no more player units in area around source portal
    constant        boolean                     DO_CAMERA_PAN                    = true;
 
    // ----------------------------------------------------------------
    // *** This is the radius of camera scanning, where it will search for player units
    constant        real                        CAMERA_PAN_AREA_SCAN            = 700.;
 
    // ----------------------------------------------------------------
    // *** The time it takes after last unit enters the portal to pan the camera
    constant        real                        CAMERA_PAN_DELAY                = 1.5;
 
 
    // ==========================================================================
    //
    //                     *** After Image Constants ***
    //
    // ==========================================================================
 
    // ----------------------------------------------------------------
    // *** Do use after imaging, will show transparent unit entering the portal
    constant        boolean                        USE_UNIT_AFTER_IMAGING            = true;
 
    // ----------------------------------------------------------------
    // *** The time image lasts
    constant        real                        UNIT_AFTER_IMAGE_DURATION        = .85;
 
    // ----------------------------------------------------------------
    // *** Animation speed of image, 1. is 100. percent
    constant        real                        UNIT_AFTER_IMAGE_TIMESCALE        = .5;
 
    // ----------------------------------------------------------------
    // *** The color of image
    constant        integer                        UNIT_AFTER_IMAGE_RED            = 128;
    constant        integer                        UNIT_AFTER_IMAGE_GREEN            = 128;
    constant        integer                        UNIT_AFTER_IMAGE_BLUE            = 128;
    // ----------------------------------------------------------------
    // *** The starting transparency in percentage of image, 100. is fully visible
    constant        real                        UNIT_AFTER_IMAGE_TRANSPARENCY_MIN = 60.;
    constant        real                        UNIT_AFTER_IMAGE_TRANSPARENCY_MAX = 70.;
 
    // =================================================================================
    //
    //    *** Enable custom selection, units that are teleported to target portal get
    //        selected by owning player if they where selected when they entered source portal
    constant        boolean                        DO_UNIT_SELECTION_JOB            = true;
 
    // ----------------------------------------------------------------
    // *** How often to check should units be selected, that is did all units enter source portal
    constant        real                        SELECTION_CHECK_INTERVAL        = .5;
 
    // ----------------------------------------------------------------
    // *** non-Editable, do not change ***
    //constant        integer                        BLIZZARD_MAX_SOUND_INSTANCES    = 4;
    constant        key                            CASTER_FACING_KEY;
    constant        key                            UNIT_PORT_KEY;
    constant        key                            CASTER_PORTS_KEY;
    constant        key                            UNIT_SELECTED_KEY;
    constant        key                            UNIT_SELECTION_GROUPING_KEY;
 
 
 
    // -------------------------------------------
    // *** Portal level constants
    // -------------------------------------------
 
    // Portal duration in seconds
    function PORTAL_DURATION(integer level) -> real {
        real dur[];
    
        dur[1] = 10.;
        dur[2] = 15.;
        dur[3] = 20.;
    
        return dur[level];
    }
 
    // Portal units transition time in seconds
    function PORTAL_TRANSITION_TIME(integer level) -> real {
        real trans_tim[];
    
        trans_tim[1] = 5.;
        trans_tim[2] = 4.;
        trans_tim[3] = 2.;
    
        return trans_tim[level];
    }
 
    // Portal max units pass
    function PORTAL_MAX_PASS(integer level) -> integer {
        integer max_pass[];
    
        max_pass[1] = 3;
        max_pass[2] = 7;
        max_pass[3] = 15;
    
        return max_pass[level];
    }
 
    // ----------------------------------------
    // **** Unit filter for portal
    function PortFilter(unit filter_unit, player cast_player) -> boolean {
        return     !IsUnitType(filter_unit, UNIT_TYPE_DEAD)             &&
                !IsUnitType(filter_unit, UNIT_TYPE_STRUCTURE)        &&
                !IsUnitType(filter_unit, UNIT_TYPE_ANCIENT)            &&
                !IsUnitType(filter_unit, UNIT_TYPE_POLYMORPHED)        &&
                !IsUnitType(filter_unit, UNIT_TYPE_FLYING)            &&
                !DDIsUnitWard(filter_unit)                            &&
                IsUnitAlly(filter_unit, cast_player);
    }
 
 
    // ==================================================================================================
    //            --------------                                                --------------
    //                    *** End constant spell modification, start spell code ***
    //            --------------                                                --------------
    // ==================================================================================================

Regards!
~Dark Dragon
Thank You It Worked And Incredible Creation
 
Top