• 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.

Custom Chain Lightning

This bundle is marked as awaiting update. A staff member has requested changes to it before it can be approved.
JASS:
//! zinc
library ChainSys /* v1.0.0b
********************************************************************************************
*
*   Chain abilities are great spells and are used by many map-makers that use Chain Lightning
*   and Healing Wave that are built into the object editor. However, that provides minimal
*   control over how the abilitiy functions in-game. With this system, you can create
*   Chain spells with just once function call (GUI-Friendly) or highly customize them through
*   struct variables. All Chains are MUI and leakess.
*
********************************************************************************************
*
*   Usage over default Chain Lightning/Healing Wave in the object editor:
*
*   - Define any lightning type you want.
*   - Apply custom stats to to almost every variable used.
*   - Trigger a custom action to targets hit by a Chain (Such as stun, slow, etc).
*   - Modify the color of the lightning.
*   - Define the Chain instance jump interval.
*   - Control on how much damage/heal is dealt.
*   - Greater ability to increase/decrease how much damage/heal per bounce is modified.
*
********************************************************************************************
*
*/   requires
/*  
*/   TimerUtils,       //http://www.wc3c.net/showthread.php?t=101322   
/*   
*/      optional GroupUtils,   //http://www.wc3c.net/showthread.php?t=104464
/*
*/   optional Damage{   //https://www.thehelper.net/threads/damage.117041/
/*
******************************************************************************************
*==========================================================================================
*===============================ChainSys API===============================================
*==========================================================================================
*******************************************************************************************
*
*   ====================
*   ChainSys methods:
*       ====================
*       These methods are the main methods used by the system to correctly function.
*       
*==========================================================================================
*
*       - Chain.create()
*
*==========================================================================================
*
*       - this.Launch(this)
*
*==========================================================================================
*
*   ====================
*   ChainSys memebers:
*       ====================
*       These members are publically available to users during the creation of a
*       Chain instance.They can be changed and manipulated at any point.
*
*==========================================================================================
*
*       - this.Chain_Caster   - this.Chain_Target   - this.Chain_Source
*
*==========================================================================================
*
*       - this.Chain_Damage_Primary_Effect    - this.Chain_Damage_Secondary_Effect   
*
*       - this.Chain_Damage_Impact_Effect   - this.Chain_Damage_Impact_Attachment
*
*       - this.Chain_Heal_Primary_Effect    - this.Chain_Heal_Effect   
*
*       - this.Chain_Heal_Impact_Effect       - this.Chain_Heal_Impact_Attachment
*
*==========================================================================================
*
*       - this.Chain_Max_Bounce
*
*==========================================================================================
*
*       - this.Chain_Color_Red   - this.Chain_Color_Green   - this.Chain_Color_Blue
*       - this.Chain_Radius   - this.Chain_Cast_Time     - this.Chain_Interval
*       - this.Chain_Duration   - this.Chain_Damage      - this.Chain_Heal
*       - this.Chain_Modify_Damage   - this.Chain_Modify_Heal
*
*==========================================================================================
*
*       - this.Chain_Hits_Friendly   - this.Chain_Hits_Enemy
*       - this.Chain_Hits_Caster       - this.Chain_Modify_Both
*
*==========================================================================================
*
*       - this.Chain_Target_Filter
*
*==========================================================================================
*
*       - this.Chain_Attacktype       - this.Chain_DamageType
*
*==========================================================================================
*
*       - this.Chain_OnImpact   
*
*==========================================================================================
*******************************************************************************************
*==========================================================================================
*===============================ChainSys API End===========================================
*==========================================================================================
*******************************************************************************************
*******************************************************************************************
*==========================================================================================
*===============================ChainSys Globals===========================================
*==========================================================================================
*******************************************************************************************/

   private{
       /*
       =============================================
       * Define the Z-height of a chain.
       ==============================================
       */
       constant real CHAIN_OFFSET    = 65.0;
       /*
       =============================================
       * How often a new target will be aquired.
       * This can be modifed with .Chain_Interval.
       ==============================================
       */
       constant real CHAIN_INTERVAL  = 0.15;
       /*
       =============================================
       * How long before a lightning effect is destroyed.
       * This can be modifed with .Chain_Duration.
       ==============================================
       */
       constant real CHAIN_DURATION  = 0.55;
       /*
       =============================================
       * How long it takes for the primary Chain to be launched.
       * This can be modified with .Chain_Cast_Time.
       ==============================================
       */
       constant real CHAIN_CAST_TIME = 0.01;
   }

   private{
       /*
       =============================================
       * Do not modify this variable! Used to get a units Z height.
       ==============================================
       */
       location LocationZ = Location(0,0);
   }

   public{
       /*
       =============================================
       * All available lightning specialeffects in Warcraft3.
       * Do not modify!
       ==============================================
       */
       constant string LIGHTNING_CHAIN_LIGHTNING_PRIMARY   = "CLPB";
       constant string LIGHTNING_CHAIN_LIGHTNING_SECONDARY = "CLSB";
       constant string LIGHTNING_DRAIN                     = "DRAB";
       constant string LIGHTNING_DRAIN_LIFE                = "DRAL";
       constant string LIGHTNING_DRAIN_MANA                = "DRAM";
       constant string LIGHTNING_FINGER_OF_DEATH           = "AFOD";
       constant string LIGHTNING_FORKED_LIGHTNING          = "FORK";
       constant string LIGHTNING_HEALING_WAVE_PRIMARY      = "HWPB";
       constant string LIGHTNING_HEALING_WAVE_SECONDARY    = "HWSB";
       constant string LIGHTNING_LIGHTNING_ATTACK          = "CHIM";
       constant string LIGHTNING_MAGIC_LEASH               = "LEAS";
       constant string LIGHTNING_MANA_BURN                 = "MBUR";
       constant string LIGHTNING_MANA_FLARE                = "MFPB";
       constant string LIGHTNING_SPIRIT_LINK               = "SPLK";
   }
   
   public{
       /*
       =============================================
       * Returns the last caster unit from a Chain instance.
       ==============================================
       */
       unit Chain_LastCasterUnit;
       /*
       =============================================
       * Returns the last unit damaged/healed from a Chain instance.
       ==============================================
       */
       unit Chain_LastHitUnit;
       /*
       =============================================
       * Returns the last triggered damage from a Chain instance.
       ==============================================
       */
       real Chain_LastDamage;
       /*
       =============================================
       * Returns the last triggered heal from a Chain instance.
       ==============================================
       */
       real Chain_LastHeal;
   }

/******************************************************************************************
*==========================================================================================
*=================================End of ChainSys Globals==================================
*==========================================================================================
*******************************************************************************************/

   function GetLocZ(real x,real y)->real{
       MoveLocation(LocationZ,x,y);
       return GetLocationZ(LocationZ);
   }

   struct ChainEffect{
       lightning Chain_Lightning;
   }

   function onTimer(){
       timer t = GetExpiredTimer();
       ChainEffect this = GetTimerData(t);
       DestroyLightning(this.Chain_Lightning);
       this.destroy();
   }

   /*
   =============================================
   * Provides an easy way to add a timed lightning
   * effect between 2 units.
   ==============================================
   */

   public function AddLightningTimed(string effectId,unit source,unit target,real duration,real red,real green,real blue){
       ChainEffect this = ChainEffect.create();
       timer t = NewTimer();
       real sourceX = GetUnitX(source),sourceY = GetUnitY(source),sourceH = GetUnitFlyHeight(source),sourceZ = GetLocZ(sourceX,sourceY);
       real targetX = GetUnitX(target),targetY = GetUnitY(target),targetH = GetUnitFlyHeight(target),targetZ = GetLocZ(targetX,targetY);
       this.Chain_Lightning=AddLightningEx(effectId,true,
       sourceX,sourceY,sourceZ+CHAIN_OFFSET+sourceH,
       targetX,targetY,targetZ+CHAIN_OFFSET+targetH);
       SetLightningColor(this.Chain_Lightning,red,green,blue,1.0);
       SetTimerData(t,this);
       TimerStart(t,duration,false,function onTimer);
       t = null;
   }

   public struct Chain{
       unit Chain_Caster,Chain_Source,Chain_Target;

       player Chain_Owner;
       
       //Primary effects
       string Chain_Damage_Primary_Effect;
       string Chain_Heal_Primary_Effect;

       //Secondary effects
       string Chain_Damage_Secondary_Effect;
       string Chain_Heal_Secondary_Effect;

       //Impact effects
       string Chain_Damage_Impact_Effect;
       string Chain_Heal_Impact_Effect;

       //Attachment effects
       string Chain_Damage_Impact_Attachment;
       string Chain_Heal_Impact_Attachment;

       integer Chain_Max_Bounce;

       real Chain_Color_Red   = 1.0;
       real Chain_Color_Green = 1.0;
       real Chain_Color_Blue  = 1.0;
       real Chain_Radius;
       real Chain_Cast_Time   = CHAIN_CAST_TIME;
       real Chain_Interval    = CHAIN_INTERVAL;
       real Chain_Duration    = CHAIN_DURATION;
       real Chain_Damage;
       real Chain_Heal;
       real Chain_Modify_Damage;
       real Chain_Modify_Heal;
       private real Chain_RadiusX,Chain_RadiusY;

       boolean Chain_Hits_Friendly  = false;
       boolean Chain_Hits_Enemy     = false;
       boolean Chain_Hits_Caster    = false;       
       boolean Chain_Modify_Both    = false;
       
       boolexpr Chain_Target_Filter = null;

       attacktype Chain_Attacktype = ATTACK_TYPE_MAGIC;
       damagetype Chain_Damagetype = DAMAGE_TYPE_NORMAL;

       private conditionfunc Chain_OnHit;
       
       private{
           group Chain_Hit_Group;
           integer Chain_Tick = -1;
       }

       method operator Chain_OnImpact=(code whichEvent){
           this.Chain_OnHit=Condition(whichEvent);
       }

       method destroy(){
           static if (LIBRARY_GroupUtils){
               ReleaseGroup(this.Chain_Hit_Group);
           }else{
               RemoveGroup(this.Chain_Hit_Group);
           }
           DestroyBoolExpr(this.Chain_Target_Filter);
           this.deallocate();
       }

       method effectString()->string{
           if (IsUnitAlly(this.Chain_Target,this.Chain_Owner)){
               if (this.Chain_Tick==0
               &&this.Chain_Heal_Primary_Effect!=null){
                   return this.Chain_Heal_Primary_Effect;
               }else if (this.Chain_Heal_Secondary_Effect!=null){
                   return this.Chain_Heal_Secondary_Effect;
               }
           }else if (IsUnitEnemy(this.Chain_Target,this.Chain_Owner)){
               if (this.Chain_Tick==0
               && this.Chain_Damage_Primary_Effect!=null){
                   return this.Chain_Damage_Primary_Effect;
               }else if (this.Chain_Damage_Secondary_Effect!=null){
                   return this.Chain_Damage_Secondary_Effect;
               }
           }
           return null;
       }

       method modifyValue(){
           if (this.Chain_Modify_Both){
               this.Chain_Damage+=this.Chain_Damage*this.Chain_Modify_Damage;
               this.Chain_Heal+=this.Chain_Heal*this.Chain_Modify_Heal;
           }else{
               if (IsUnitEnemy(this.Chain_Target,this.Chain_Owner)){
                   this.Chain_Damage+=this.Chain_Damage*this.Chain_Modify_Damage;
               }else{
                   this.Chain_Heal+=this.Chain_Heal*this.Chain_Modify_Heal;
               }
           }
       }           

       static method onTimer(){
           timer t = GetExpiredTimer();
           thistype this = GetTimerData(t);
           group g;
           trigger e=CreateTrigger();
           
           static if (LIBRARY_GroupUtils){
               g = NewGroup();
           }else{
               g = CreateGroup();
           }
           GroupAddUnit(this.Chain_Hit_Group,this.Chain_Target);
           this.Chain_Tick+=1;
           Chain_LastCasterUnit = this.Chain_Caster;
           if (this.Chain_Target==null
           || this.Chain_Tick>this.Chain_Max_Bounce){
               ReleaseTimer(t);
               this.destroy();
           }else{
               AddLightningTimed(this.effectString(),this.Chain_Source,this.Chain_Target,this.Chain_Interval,this.Chain_Color_Red,this.Chain_Color_Green,this.Chain_Color_Blue);
               if (this.Chain_Damage>0.0){
                   if (IsUnitEnemy(this.Chain_Target,this.Chain_Owner)){
                       static if (LIBRARY_Damage){
                           UnitDamageTargetEx(this.Chain_Caster,this.Chain_Target,this.Chain_Damage,false,false,this.Chain_Attacktype,this.Chain_Damagetype,WEAPON_TYPE_WHOKNOWS);
                       }else{
                           UnitDamageTarget(this.Chain_Caster,this.Chain_Target,this.Chain_Damage,false,false,this.Chain_Attacktype,this.Chain_Damagetype,WEAPON_TYPE_WHOKNOWS);   
                       }
                       Chain_LastHitUnit    = this.Chain_Target;
                       Chain_LastDamage = this.Chain_Damage;
                       DestroyEffect(AddSpecialEffectTarget(this.Chain_Damage_Impact_Effect,this.Chain_Target,this.Chain_Damage_Impact_Attachment));
                   }
               }
               if (this.Chain_Heal>0.0){
                   if (IsUnitAlly(this.Chain_Target,this.Chain_Owner)){
                       SetWidgetLife(this.Chain_Target,GetWidgetLife(this.Chain_Target)+this.Chain_Heal);
                       Chain_LastHeal = this.Chain_Heal;
                       Chain_LastHitUnit    = this.Chain_Target;
                       DestroyEffect(AddSpecialEffectTarget(this.Chain_Heal_Impact_Effect,this.Chain_Target,this.Chain_Heal_Impact_Attachment));
                   }
               }
               this.modifyValue();
               if (this.Chain_OnHit!=null){
                   TriggerAddCondition(e,this.Chain_OnHit);
                   TriggerEvaluate(e);
               }
               this.Chain_Source=this.Chain_Target;
               GroupEnumUnitsInRange(g,this.Chain_RadiusX,this.Chain_RadiusY,this.Chain_Radius,this.Chain_Target_Filter);
               if (!this.Chain_Hits_Caster){
                   GroupRemoveUnit(g,this.Chain_Caster);
               }
               this.Chain_Target=FirstOfGroup(g);
               while (this.Chain_Target!=null){
                   if (!IsUnitType(this.Chain_Target,UNIT_TYPE_DEAD)
                   && GetUnitTypeId(this.Chain_Target)>0
                   && !IsUnitInGroup(this.Chain_Target,this.Chain_Hit_Group)){
                       if (this.Chain_Hits_Enemy
                       && IsUnitEnemy(this.Chain_Target,this.Chain_Owner)){
                           break;
                       }else if (this.Chain_Hits_Friendly
                       && IsUnitAlly(this.Chain_Target,this.Chain_Owner)){
                           break;
                       }
                   }
                   GroupRemoveUnit(g,this.Chain_Target);
                   this.Chain_Target=FirstOfGroup(g);
               }   
           }
           static if (LIBRARY_GroupUtils){
               ReleaseGroup(g);
           }else{
               RemoveGroup(g);
           }
           if (this.Chain_Tick==0){
               SetTimerData(t,this);
               TimerStart(t,this.Chain_Interval,true,function thistype.onTimer);
           }
           DestroyTrigger(e);
           t = null;
           g = null;
           e = null;
       }

       static method LaunchChain(thistype this){
           timer t = NewTimer();
           this.Chain_Source = this.Chain_Caster;
           this.Chain_RadiusX = GetUnitX(this.Chain_Target);
           this.Chain_RadiusY = GetUnitY(this.Chain_Target);
           static if (LIBRARY_GroupUtils){
               this.Chain_Hit_Group = NewGroup();
           }else{
               this.Chain_Hit_Group = CreateGroup();
           }
           this.Chain_Tick = -1;
           SetTimerData(t,this);
           TimerStart(t,this.Chain_Cast_Time,false,function thistype.onTimer);
           t = null;
       }
       
       static method create()->thistype{
           Chain this=Chain.allocate();
           return this;
       }
   }

   public function Chain_ChainLightning(unit caster,unit target,real damage,real damageModify,real radius,integer bounces,real intervals,boolexpr groupFilter,code whichEvent){
       Chain this = Chain.create();
       this.Chain_Caster = caster;
       this.Chain_Target = target;
       this.Chain_Owner  = GetOwningPlayer(caster);
       this.Chain_Damage_Primary_Effect    = LIGHTNING_CHAIN_LIGHTNING_PRIMARY;
       this.Chain_Damage_Secondary_Effect  = LIGHTNING_CHAIN_LIGHTNING_SECONDARY;
       this.Chain_Damage_Impact_Effect     = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl";
       this.Chain_Damage_Impact_Attachment = "origin";
       this.Chain_Max_Bounce    = bounces;
       this.Chain_Radius        = radius;
       this.Chain_Damage        = damage;
       this.Chain_Modify_Damage = damageModify;
       this.Chain_Attacktype    = ATTACK_TYPE_MAGIC;
       this.Chain_Damagetype    = DAMAGE_TYPE_LIGHTNING;
       this.Chain_Cast_Time     = 0.01;
       this.Chain_Interval      = intervals;
       this.Chain_Hits_Enemy    = true;
       this.Chain_Target_Filter = groupFilter;
       this.Chain_OnImpact      = whichEvent;
       this.LaunchChain(this);
   }

   
   public function Chain_HealingWave(unit caster,unit target,real heal,real healModify,real radius,integer bounces,real intervals,boolean hitCaster,boolexpr groupFilter,code whichEvent){
       Chain this = Chain.create();
       this.Chain_Caster = caster;
       this.Chain_Target = target;
       this.Chain_Owner  = GetOwningPlayer(caster);
       this.Chain_Heal_Primary_Effect    = LIGHTNING_HEALING_WAVE_PRIMARY;
       this.Chain_Heal_Secondary_Effect  = LIGHTNING_HEALING_WAVE_SECONDARY;
       this.Chain_Heal_Impact_Effect     = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl";
       this.Chain_Heal_Impact_Attachment = "origin";
       this.Chain_Max_Bounce    = bounces;
       this.Chain_Radius        = radius;
       this.Chain_Heal          = heal;
       this.Chain_Modify_Heal   = healModify;
       this.Chain_Cast_Time     = 0.01;
       this.Chain_Interval      = intervals;
       this.Chain_Hits_Friendly = true;
       this.Chain_Hits_Caster   = hitCaster;
       this.Chain_Target_Filter = groupFilter;
       this.Chain_OnImpact      = whichEvent;
       this.LaunchChain(this);
   }
}
//! endzinc

Below are 3 examples using this system.

JASS:
//! zinc
library ChainLightning requires ChainSys{

   function onCondition()->boolean{
       return GetSpellAbilityId()=='A001';
   }

   function onDamage()->boolean{
       unit u=Chain_LastHitUnit;
       real d=Chain_LastDamage;
       BJDebugMsg(GetUnitName(u)+" took "+R2S(d)+" damage!");
       return false;
   }

   function onFilter()->boolean{
       return !IsUnitType(GetFilterUnit(),UNIT_TYPE_MAGIC_IMMUNE)
       && !IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)
       && !BlzIsUnitInvulnerable(GetFilterUnit());
   }

   function onAction(){
       unit u = GetTriggerUnit(),t = GetSpellTargetUnit();
       integer l=GetUnitAbilityLevel(u,'A001');
       boolexpr b=Filter(function onFilter);
       Chain_ChainLightning(u, //Caster
       t,                      //Target
       (20.0*l)+80.0,          //Damage
       ((5.0*l)-20.0)*0.01,    //Damage Reduction
       500.0,                  //Search Radius
       (2*l)+2,                //Maximim Bounces
       0.33,                   //Bounce intervals
       b,                      //Search filter
       function onDamage);     //Triggered Impact
       ClearTextMessages();
   }

   function onInit(){
       trigger t=CreateTrigger();
           TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT);
       TriggerAddCondition(t,Condition(function onCondition));
           TriggerAddAction(t,function onAction);
       t=null;
   }       
}
//! endzinc

JASS:
//! zinc
library HealingWave requires ChainSys{

   function onCondition()->boolean{
       return GetSpellAbilityId()=='A002';
   }
   
   function onHeal()->boolean{
       unit u=Chain_LastHitUnit;
       real d=Chain_LastHeal;
       BJDebugMsg(GetUnitName(u)+" was healed for "+R2S(d)+"!");
       return false;
   }

   function onFilter()->boolean{
       return !IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE);
   }

   function onAction(){
       unit u = GetTriggerUnit(),t = GetSpellTargetUnit();
       integer l=GetUnitAbilityLevel(u,'A002');
       boolexpr b=Filter(function onFilter);
       Chain_HealingWave(u,  //Caster
       t,              //Target
       (50.0*l)+50.0,        //Heal
       ((5.0*l)-30.0)*0.01,  //Heal Reduction
       500.0,                //Radius
       (1*l)+2,              //Max Bounces
       0.33,                 //Bounce Intervals
       false,                //Doesn't Hit Caster
       b,                    //Target Filter
       function onHeal);     //Triggered Impact
       ClearTextMessages();
   }

   function onInit(){
       trigger t=CreateTrigger();
           TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT);
       TriggerAddCondition(t,Condition(function onCondition));
           TriggerAddAction(t,function onAction);
       t=null;
   }       
}
//! endzinc

JASS:
//! zinc
library ChainHealDamage requires ChainSys{

   function onCondition()->boolean{
       return GetSpellAbilityId()=='A003';
   }

   function onHealDamage()->boolean{
       unit u = Chain_LastCasterUnit, t = Chain_LastHitUnit;
       player p = GetOwningPlayer(u);
       if (IsUnitEnemy(t,p)){
           BJDebugMsg(GetUnitName(t)+" took "+R2S(Chain_LastDamage)+" damage!");
       }else if (IsUnitAlly(t,p)){
           BJDebugMsg(GetUnitName(t)+" was healed for "+R2S(Chain_LastHeal)+"!");
       }
       return false;
       return false;
   }

   function onFilter()->boolean{
       return !IsUnitType(GetFilterUnit(),UNIT_TYPE_MAGIC_IMMUNE)
       && !IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE)
       && !BlzIsUnitInvulnerable(GetFilterUnit());
   }

   function onAction(){
       Chain this = Chain.create();
       integer l;
       this.Chain_Caster = GetTriggerUnit();
       l=GetUnitAbilityLevel(this.Chain_Caster,'A003');
       this.Chain_Target = GetSpellTargetUnit();
       this.Chain_Owner = GetOwningPlayer(this.Chain_Caster);
       this.Chain_Damage_Primary_Effect = LIGHTNING_CHAIN_LIGHTNING_PRIMARY;
       this.Chain_Damage_Secondary_Effect = LIGHTNING_CHAIN_LIGHTNING_SECONDARY;
       this.Chain_Damage_Impact_Effect = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl";
       this.Chain_Damage_Impact_Attachment = "origin";
       //\\
       this.Chain_Heal_Primary_Effect = LIGHTNING_HEALING_WAVE_PRIMARY;
       this.Chain_Heal_Secondary_Effect = LIGHTNING_HEALING_WAVE_SECONDARY;
       this.Chain_Heal_Impact_Effect = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl";
       this.Chain_Heal_Impact_Attachment = "origin";
       this.Chain_Max_Bounce = (2*l)+2;
       this.Chain_Radius = 500.0;
       this.Chain_Damage = (20.0*l)+80.0;
       this.Chain_Heal = (20.0*l)+80.0;
       this.Chain_Modify_Damage = ((5.0*l)-20.0)*0.01;
       this.Chain_Modify_Heal   = ((5.0*l)-20.0)*0.01;   
       this.Chain_Modify_Both = true;
       this.Chain_Attacktype = ATTACK_TYPE_MAGIC;
       this.Chain_Damagetype = DAMAGE_TYPE_LIGHTNING;
       this.Chain_Cast_Time = 0.01;
       this.Chain_Interval = 0.33;
       this.Chain_Hits_Friendly = true;
       this.Chain_Hits_Enemy    = true;
       this.Chain_Hits_Caster   = false;
       this.Chain_Target_Filter=Filter(function onFilter);
       this.Chain_OnImpact = function onHealDamage;
       this.LaunchChain(this);
       ClearTextMessages();
   }

   function onInit(){
       trigger t=CreateTrigger();
           TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT);
       TriggerAddCondition(t,Condition(function onCondition));
           TriggerAddAction(t,function onAction);
       t=null;
   }       
}
//! endzinc
Contents

Custom Chain Lightning (Map)

Reviews
MyPad
Spell Review: System: The library GroupUtils, although declared as optional, will cause the library ChainSys to not compile correctly when not found. Error type is misnaming of a destructor function, which is RemoveGroup. It should be DestroyGroup...
Level 3
Joined
Aug 12, 2010
Messages
29
When I get back to my desktop I'll create an example of how to use it via GUI. It does not require the use of multiple custom scripts, only one! I'll also add better documentation on how to use it with GUI without the pre-made functions and possibly add some extra functions specifically designed for GUI users so they can better customize the chains. Sorry for the confusion! I'll get it updated soon.

The chainsys is mainly intended for advance usage, but I'll add more simplified ways of creating and modifying the chains.
 

Spell Review:

System:

  • The library GroupUtils, although declared as optional, will cause the library ChainSys to not compile correctly when not found.
    • Error type is misnaming of a destructor function, which is RemoveGroup. It should be DestroyGroup
      It is found within these methods:
      • static method onTimer()
      • method destroy()
  • In static method onTimer(), a local trigger is created and destroyed upon execution. A suggested optimization is to save that trigger instead as a private member of the struct Chain and only destroy the trigger upon invocation of destroy(), or changing of the callback function, creating a new one with the new callback.

  • The library Damage does not appear to have any impact on the system. A recommended course of action is to omit that library from the system.

GUI-Friendliness:


As it stands, the system is still quite far from being GUI-friendly, due to numerous function parameters which may confuse users.
Such are the following examples (Two of which are included, despite being preset functions):
  • public function AddLightningTimed(string effectId,unit source,unit target,real duration,real red,real green,real blue)
  • public function Chain_ChainLightning(unit caster,unit target,real damage,real damageModify,real radius,integer bounces,real intervals,boolexpr groupFilter,code whichEvent) (A preset function)
  • public function Chain_HealingWave(unit caster,unit target,real heal,real healModify,real radius,integer bounces,real intervals,boolean hitCaster,boolexpr groupFilter,code whichEvent) (A preset function)
In addition, if a GUI user wants to cast a truly custom Chain Lightning (different lightning models), the user must have to declare at least 2 lines of Custom Scripts.

In order to resolve this, a recommendation is to create a helper trigger which interfaces nicely with the system, as the system is already in place. The helper trigger will enable GUI users in creating custom Chain Lightning(s) of any sort without having to look into the function parameters above. Additionally, a customizing helper trigger would help them a lot more by directly assigning the members of a desired Chain instance to arbitrary GUI variables that you would have created by then.

Status

  • Awaiting Update
 
Top