//! 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