//TESH.scrollpos=384
//TESH.alwaysfold=0
//=====================================================================================================
//
// ===== = = ===== ===== = =
// == = = = = = == =
// == ===== ===== = = = =
// == = = = = = = ==
// ===== = = = = ===== = =
//
// = = ===== ===== = ====
// = = = = = =
// = = = = = ===
// = = = = = =
// ===== = ===== ===== ====
// By Raven0 V2.0
//
//
// --Description--
// Allows for easy creation and modification of chain effects
//
//
// --Capabilities--
// You can:
//
// - Create a chain of any lightning type
// - Set the number of bounces, unit detection radius, damage, and damage reduction of the chain
// - Set the lightning color of the chain
// - Create a "handler function" for the chain (which runs whenever a unit is hit by a chain effect)
// - Display damage text
// - Modify the creation interval, duration, and z-offset of the lightning bolts
// - Cause the chain to heal instead of damage
//
//
// --Functions--
// Can be found easily at the bottom of the library
//
//
// --Lightning ID's--
//
// (thanks to PurgeandFire111):
//
// LIGHTNING_CHAIN_LIGHTNING_PRIMARY = "CLPB";
// LIGHTNING_CHAIN_LIGHTNING_SECONDARY = "CLSB";
// LIGHTNING_DRAIN = "DRAB";
// LIGHTNING_DRAIN_LIFE = "DRAL";
// LIGHTNING_DRAIN_MANA = "DRAM";
// LIGHTNING_FINGER_OF_DEATH = "AFOD";
// LIGHTNING_FORKED_LIGHTNING = "FORK";
// LIGHTNING_HEALING_WAVE_PRIMARY = "HWPB";
// LIGHTNING_HEALING_WAVE_SECONDARY = "HWSB";
// LIGHTNING_LIGHTNING_ATTACK = "CHIM";
// LIGHTNING_MAGIC_LEASH = "LEAS";
// LIGHTNING_MANA_BURN = "MBUR";
// LIGHTNING_MANA_FLARE = "MFPB";
// LIGHTNING_SPIRIT_LINK = "SPLK";
//
//=====================================================================================================
//! zinc
library ChainSystem requires GroupUtils
{
private
{
constant real Bolt_CreationInterval = .2; //How often do 'bounces' occur
constant real Bolt_MoveInterval = .05; //How often do bolts compensate for unit movement
constant real Bolt_Lifespan = .8; //After how long are bolts destroyed
constant real Bolt_ZOffset = 65; //At what height off the ground are bolts created
}
//============================================================================================//
//============================================================================================//
//==========================Do Not Modify Any of the Code Below===============================//
//============================================================================================//
//============================================================================================//
private
{
//==========================================//
constant timer CHAIN_TIMER = CreateTimer();
constant timer BOLT_TIMER = CreateTimer();
group POTENTIAL_TARGETS = CreateGroup();
//==========================================//
integer MaxChainIndex = 0;
Chain Chains[];
boolean ChainIndexUsed[];
Chain LastCreatedChain;
//==========================================//
integer MaxBoltIndex = 0;
Bolt Bolts[];
boolean BoltIndexUsed[];
//==========================================//
location Loc1 = Location(0, 0);
location Loc2 = Location(0, 0);
//==========================================//
}
public
{
unit Chain_LastHitUnit;
}
function DestroyChain(integer index)
{
ReleaseGroup(Chains[index].UnitsHit);
ChainIndexUsed[index] = false;
Chains[index].destroy();
}
function GetNextAvailableChainIndex() -> integer
{
integer count = 0;
while (ChainIndexUsed[count] == true)
{
count = count + 1;
}
if (count > MaxChainIndex) {MaxChainIndex = count;}
ChainIndexUsed[count] = true;
return count;
}
function GetNextAvailableBoltIndex() -> integer
{
integer count = 0;
while (BoltIndexUsed[count] == true)
{
count = count + 1;
}
if (count > MaxBoltIndex) {MaxBoltIndex = count;}
BoltIndexUsed[count] = true;
return count;
}
struct Bolt
{
unit Origin,Target;
real CurrentLife,DecayPoint;
lightning Lightning;
integer Index;
method Initialize(integer index, unit origin, unit target, real duration, lightning whichlightning)
{
this.Origin = origin;
this.Target = target;
this.Lightning = whichlightning;
this.Index = index;
this.DecayPoint = duration;
this.CurrentLife = 0;
}
method Tick()
{
real Origin_X = GetUnitX(this.Origin);
real Origin_Y = GetUnitY(this.Origin);
real Target_X = GetUnitX(this.Target);
real Target_Y = GetUnitY(this.Target);
MoveLocation(Loc1, Origin_X, Origin_Y);
MoveLocation(Loc2, Target_X, Target_Y);
if (this.Target != null)
{
MoveLightningEx(this.Lightning, true, Origin_X, Origin_Y, GetLocationZ(Loc1) + Bolt_ZOffset, Target_X, Target_Y, GetLocationZ(Loc2) + Bolt_ZOffset);
}
this.CurrentLife = this.CurrentLife + Bolt_MoveInterval;
if (this.CurrentLife > this.DecayPoint)
{
DestroyLightning(this.Lightning);
BoltIndexUsed[this.Index] = false;
this.destroy();
}
}
}
struct Chain
{
unit Caster,Target;
string PrimaryLightning,SecondaryLightning,HitEffect,AttachmentPoint;
integer MaxJumps,CurrentJumps;
real Radius,CurrentDamage,Reduction,X,Y,BoltLife,Red,Green,Blue;
boolean ShowText,HitsFriendly,HitsEnemy,HitsStructure;
attacktype AtkType;
damagetype DmgType;
string UserDefinedFunction;
group UnitsHit;
method GetNextAvailableTarget() -> unit
{
player CastingPlayer = GetOwningPlayer(this.Caster);
unit picked;
GroupEnumUnitsInRange(POTENTIAL_TARGETS, this.X, this.Y, this.Radius, null);
picked = FirstOfGroup(POTENTIAL_TARGETS);
while (picked != null)
{
if ((GetUnitState(picked, UNIT_STATE_LIFE) > 0) && (IsUnitInGroup(picked, this.UnitsHit) == false) && ((this.HitsStructure) || (IsUnitType(picked, UNIT_TYPE_STRUCTURE) == false)))
{
if (((this.HitsFriendly) && (IsUnitAlly(picked, CastingPlayer))) || ((this.HitsEnemy) && (IsUnitEnemy(picked, CastingPlayer))))
{
break;
}
}
GroupRemoveUnit(POTENTIAL_TARGETS, picked);
picked = FirstOfGroup(POTENTIAL_TARGETS);
}
return picked;
}
static method DamageUnit(unit caster, unit target, real damage, boolean Text, attacktype AtkType, damagetype DmgType)
{
texttag dmgtxt;
string txtoutput;
if (damage > 0)
{
UnitDamageTarget(caster, target, damage, true, false, AtkType, DmgType, WEAPON_TYPE_WHOKNOWS);
txtoutput = ("|cffCD0000" + I2S(R2I(damage)) + "|r");
}
else
{
SetWidgetLife(target, (GetWidgetLife(target) + damage));
txtoutput = ("|cff00CD00" + I2S(R2I(damage)) + "|r");
}
if (Text)
{
dmgtxt = CreateTextTag();
SetTextTagPermanent(dmgtxt, false);
SetTextTagVisibility(dmgtxt, true);
SetTextTagPosUnit(dmgtxt, target, 0);
SetTextTagText(dmgtxt, txtoutput, .023);
SetTextTagFadepoint(dmgtxt, 1);
SetTextTagLifespan(dmgtxt, 1.5);
SetTextTagVelocity(dmgtxt, 0, .05);
SetTextTagColor(dmgtxt, 255, 255, 255, 255);
}
}
method SpawnBolt(unit origin, unit target, string whichkind)
{
integer x = GetNextAvailableBoltIndex();
real BoltLifespan = this.BoltLife;
lightning Lightning;
if (BoltLifespan == 0)
{
BoltLifespan = Bolt_Lifespan;
}
MoveLocation(Loc1, GetUnitX(origin), GetUnitY(origin));
MoveLocation(Loc2, GetUnitX(target), GetUnitY(target));
Bolts[x] = Bolt.create();
if (whichkind == "primary")
{
Lightning = AddLightningEx(this.PrimaryLightning, true, GetUnitX(origin), GetUnitY(origin), (GetLocationZ(Loc1) + Bolt_ZOffset), GetUnitX(target), GetUnitY(target), (GetLocationZ(Loc2) + Bolt_ZOffset));
}
else
{
Lightning = AddLightningEx(this.SecondaryLightning, true, GetUnitX(origin), GetUnitY(origin), (GetLocationZ(Loc1) + Bolt_ZOffset), GetUnitX(target), GetUnitY(target), (GetLocationZ(Loc2) + Bolt_ZOffset));
}
SetLightningColor(Lightning, this.Red, this.Green, this.Blue, 1);
Bolts[x].Initialize(x, origin, target, BoltLifespan, Lightning);
DestroyEffect(AddSpecialEffectTarget(this.HitEffect, target, this.AttachmentPoint));
this.DamageUnit(this.Caster, target, this.CurrentDamage, this.ShowText, this.AtkType, this.DmgType);
this.CurrentDamage = this.CurrentDamage * this.Reduction;
GroupAddUnit(this.UnitsHit, target);
if (this.UserDefinedFunction != null)
{
Chain_LastHitUnit = target;
ExecuteFunc(this.UserDefinedFunction);
}
}
method Jump()
{
unit picked = this.GetNextAvailableTarget();
if (picked == null)
{
this.destroy();
}
else
{
this.CurrentJumps = this.CurrentJumps + 1;
this.SpawnBolt(this.Target, picked, "secondary");
this.Target = picked;
}
picked = null;
}
method Initialize(unit caster, unit target, string p_lightning, string s_lightning, string hiteffect, string attachmentpoint, integer jumps, real radius, real dmg, real reduct, boolean showtext, boolean affects_allies, boolean affects_enemies, boolean affects_structures, attacktype atktype, damagetype dmgtype) -> thistype
{
this.Caster = caster;
this.Target = target;
this.X = GetUnitX(target);
this.Y = GetUnitY(target);
this.PrimaryLightning = p_lightning;
this.SecondaryLightning = s_lightning;
this.HitEffect = hiteffect;
this.AttachmentPoint = attachmentpoint;
this.CurrentJumps = 0;
this.Red = 1;
this.Green = 1;
this.Blue = 1;
this.MaxJumps = jumps;
this.Radius = radius;
this.CurrentDamage = dmg;
this.BoltLife = 0;
this.Reduction = (100 - reduct) / 100;
this.ShowText = showtext;
this.HitsFriendly = affects_allies;
this.HitsEnemy= affects_enemies;
this.HitsStructure = affects_structures;
this.AtkType = atktype;
this.DmgType = dmgtype;
this.UnitsHit = NewGroup();
this.SpawnBolt(caster, target, "primary");
return this;
}
}
function ChainPeriodic()
{
integer x;
for (0 <= x <= MaxChainIndex)
{
if (ChainIndexUsed[x] == true)
{
if (Chains[x].CurrentJumps <= Chains[x].MaxJumps)
{
Chains[x].Jump();
}
else
{
DestroyChain(x);
}
}
}
}
function BoltPeriodic()
{
integer x;
for (0 <= x <= MaxBoltIndex)
{
if (BoltIndexUsed[x] == true)
{
Bolts[x].Tick();
}
}
}
function onInit()
{
TimerStart(CHAIN_TIMER, Bolt_CreationInterval, true, function ChainPeriodic);
TimerStart(BOLT_TIMER, Bolt_MoveInterval, true, function BoltPeriodic);
}
//********************************************************************************************//
//********************************************************************************************//
////========================================================================================////
//////====================================================================================//////
////////===============================CALL FUNCTIONS===================================////////
//////====================================================================================//////
////========================================================================================////
//********************************************************************************************//
//********************************************************************************************//
//===============================
//=====Primary Call Function=====
//===============================
public function CreateChain(unit Caster, unit Target, string PrimaryLightningId, string SecondaryLightningId, string HitEffect, string HitEffectAttachmentPoint, integer Jumps, real Radius, real Damage, real PercentReduction, boolean ShowText, boolean AffectsAllies, boolean AffectsEnemies, boolean AffectsStructures, attacktype WhichAttackType, damagetype WhichDamageType)
{
integer x = GetNextAvailableChainIndex();
Chains[x] = Chain.create();
LastCreatedChain = Chains[x];
Chains[x].Initialize(Caster, Target, PrimaryLightningId, SecondaryLightningId, HitEffect, HitEffectAttachmentPoint, Jumps, Radius, Damage, PercentReduction, ShowText, AffectsAllies, AffectsEnemies, AffectsStructures, WhichAttackType, WhichDamageType);
}
//===============================
//======Advanced Modifiables=====
//===============================
public function SetLastCreatedChain_CustomBoltDuration(real BoltDuration)
{
//Bolts will disappear after this whatever duration you set
LastCreatedChain.BoltLife = BoltDuration;
}
public function SetLastCreatedChain_HandlerFunction(string HandlerFunctionName)
{
//This function will be called whenever a unit is hit with a bolt
//Useful for making chain spells that stun or cripple enemies
//Use "Chain_LastHitUnit" to refer to the unit being hit by the bolt
LastCreatedChain.UserDefinedFunction = HandlerFunctionName;
}
public function SetLastCreatedChain_Color(real Red, real Green, real Blue)
{
//Bolts of this chain object will be set to these RGB values
//Setting custom RGB values may effect visibility of the lighting
if ((Red < 0) || (Red > 1)) {LastCreatedChain.Red = 1;} else {LastCreatedChain.Red = Red;}
if ((Green < 0) || (Green > 1)) {LastCreatedChain.Green = 1;} else {LastCreatedChain.Green = Green;}
if ((Blue < 0) || (Blue > 1)) {LastCreatedChain.Blue = 1;} else {LastCreatedChain.Blue = Blue;}
}
//===============================
//=========Simple Chains=========
//===============================
public function CreateChainLightning(unit Caster, unit Target, integer Jumps, real Radius, real Damage, real PercentReduction, boolean ShowText)
{
//Creates a basic chain lightning spell (similar to object editor one)
integer x = GetNextAvailableChainIndex();
Chains[x] = Chain.create();
LastCreatedChain = Chains[x];
Chains[x].Initialize(Caster, Target, "CLPB", "CLSB", "Abilities\\Weapons\\Bolt\\BoltImpact.mdl", "Origin", Jumps, Radius, Damage, PercentReduction, ShowText, false, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL);
}
public function CreateHealingWave(unit Caster, unit Target, integer Jumps, real Radius, real HealAmount, real PercentReduction, boolean ShowText)
{
//Creates a basic healing wave spell (similar to object editor one)
integer x = GetNextAvailableChainIndex();
Chains[x] = Chain.create();
LastCreatedChain = Chains[x];
Chains[x].Initialize(Caster, Target, "HWPB", "HWSB", "Abilities\\Spells\\Orc\\HealingWave\\HealingWaveTarget.mdl", "Origin", Jumps, Radius, (HealAmount * -1), PercentReduction, ShowText, true, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL);
}
}
//! endzinc
Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
function Trig_Untitled_Trigger_003_Conditions takes nothing returns boolean
if ( not ( GetSpellAbilityId() == 'AUdc' ) ) then
return false
endif
return true
endfunction
function Trig_Untitled_Trigger_003_Actions takes nothing returns nothing
call CreateChain(GetTriggerUnit(), GetSpellTargetUnit(), "CLPB", "CLSB", "Abilities\\Weapons\\Bolt\\BoltImpact.mdl", "origin", 5, 500, 100, 15, true, false, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL)
endfunction
//===========================================================================
function InitTrig_Chain_Lightning_Cast takes nothing returns nothing
local trigger gg_trg_Chain_Lightning_Cast = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( gg_trg_Chain_Lightning_Cast, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( gg_trg_Chain_Lightning_Cast, Condition( function Trig_Untitled_Trigger_003_Conditions ) )
call TriggerAddAction( gg_trg_Chain_Lightning_Cast, function Trig_Untitled_Trigger_003_Actions )
endfunction
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//*
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it. It is only worth doing this on
//* groups that you plan to have around for awhile.
//*
//* Constants that can be used from the library:
//* [group] ENUM_GROUP As you might expect, this group is good for
//* when you need a group just for enumeration.
//* [boolexpr] BOOLEXPR_TRUE This is a true boolexpr, which is important
//* because a 'null' boolexpr in enumeration
//* calls results in a leak. Use this instead.
//* [boolexpr] BOOLEXPR_FALSE This exists mostly for completeness.
//*
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//*
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//*
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//*
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//*
//* Example usage:
//* local group MyGroup = NewGroup()
//* call GroupRefresh(MyGroup)
//* call ReleaseGroup(MyGroup)
//* call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//* call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//*
globals
//If you don't have xebasic in your map, this value will be used instead.
//This value corresponds to the max collision size of a unit in your map.
private constant real MAX_COLLISION_SIZE = 197.
//If you are insane and don't care about any of the protection involved in
//this library, but want this script to be really fast, set this to true.
private constant boolean LESS_SAFETY = false
endglobals
globals
//* Constants that are available to the user
group ENUM_GROUP = CreateGroup()
boolexpr BOOLEXPR_TRUE = null
boolexpr BOOLEXPR_FALSE = null
endglobals
globals
//* Hashtable for debug purposes
private hashtable ht = InitHashtable()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Arrays and counter for the group stack
private group array Groups
private integer Count = 0
//* Variables for use with the GroupUnitsInArea function
private real X = 0.
private real Y = 0.
private real R = 0.
private hashtable H = InitHashtable()
endglobals
private function HookDestroyGroup takes group g returns nothing
if g == ENUM_GROUP then
call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
endif
endfunction
debug hook DestroyGroup HookDestroyGroup
private function AddEx takes nothing returns nothing
if Flag then
call GroupClear(Refr)
set Flag = false
endif
call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
set Flag = true
set Refr = g
call ForGroup(Refr, function AddEx)
if Flag then
call GroupClear(g)
endif
endfunction
function NewGroup takes nothing returns group
if Count == 0 then
set Groups[0] = CreateGroup()
else
set Count = Count - 1
endif
static if not LESS_SAFETY then
call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
endif
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer id = GetHandleId(g)
static if LESS_SAFETY then
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
else
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif not HaveSavedInteger(ht, 0, id) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
return false
elseif LoadInteger(ht, 0, id) == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
call SaveInteger(ht, 0, id, 2)
endif
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
return true
endfunction
private function Filter takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction
private function HookDestroyBoolExpr takes boolexpr b returns nothing
local integer bid = GetHandleId(b)
if HaveSavedHandle(H, 0, bid) then
//Clear the saved boolexpr
call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
call RemoveSavedHandle(H, 0, bid)
endif
endfunction
hook DestroyBoolExpr HookDestroyBoolExpr
private constant function GetRadius takes real radius returns real
static if LIBRARY_xebasic then
return radius+XE_MAX_COLLISION_SIZE
else
return radius+MAX_COLLISION_SIZE
endif
endfunction
function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
local integer bid = 0
//Set variables to new values
set X = x
set Y = y
set R = radius
if filter == null then
//Adjusts for null boolexprs passed to the function
set filter = Condition(function Filter)
else
//Check for a saved boolexpr
set bid = GetHandleId(filter)
if HaveSavedHandle(H, 0, bid) then
//Set the filter to use to the saved one
set filter = LoadBooleanExprHandle(H, 0, bid)
else
//Create a new And() boolexpr for this filter
set filter = And(Condition(function Filter), filter)
call SaveBooleanExprHandle(H, 0, bid, filter)
endif
endif
//Enumerate, if they want to use the boolexpr, this lets them
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
//Set variables to new values
set X = x
set Y = y
set R = radius
//Enumerate
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
private function True takes nothing returns boolean
return true
endfunction
private function False takes nothing returns boolean
return false
endfunction
private function Init takes nothing returns nothing
set BOOLEXPR_TRUE = Condition(function True)
set BOOLEXPR_FALSE = Condition(function False)
endfunction
endlibrary
//TESH.scrollpos=146
//TESH.alwaysfold=0
//================================================================================================//
//== ==//
//== ========== == == == ============ == == ==//
//== ========== == == ==== ==== ==== == ==//
//== == == == == == ==== == == == ==//
//== == == == == == ==== == == == ==//
//== == ========== ============ ==== == == == ==//
//== == == == == == ==== == == == ==//
//== == == == == == ==== == ==== ==//
//== ========== == == == == ==== == === ==//
//== ========== == == == == ============ == == ==//
//== ==//
//== --Description-- ==//
//== Creates a lighting chain that bounces between units ==//
//== ==//
//== --Functions-- ==//
//== call CreateChain takes unit Caster, unit Target, string PrimaryLightningId, ==//
//== string SecondaryLightningId, string HitEffect, integer Jumps, real Radius, real Damage, ==//
//== real PercentReduction, boolean ShowText returns nothing ==//
//== ==//
//== --Example Usage-- ==//
//== call CreateChain(GetTriggerUnit(), GetSpellTargetUnit(), "CLPB", "CLSB", ==//
//== "Abilities\\Weapons\\Bolt\\BoltImpact.mdl", 6, 500, 150, 15, true) ==//
//== ==//
//== The above will create a chain lightning effect using the blue primary and secondary ==//
//== lightning effects. It will bounce 6 times, deal 150 damage to the initial target, ==//
//== lose 15% of its initial damage every bounce, and display damage text. ==//
//== ==//
//== --GUI Users-- ==//
//== You can use this system without any knowledge of VJass. Simple type the above ==//
//== statement into a custom script action. Use GetTriggerUnit() to refer to the ==//
//== triggering unit and GetSpellTargetUnit() to refer to the target unit of ability being ==//
//== cast. You can type "udg_" before any variables you have defined in the variable window ==//
//== ==//
//== --Parameters-- ==//
//== 1st parameter: casting unit ==//
//== 2nd parameter: target unit ==//
//== 3rd parameter: the string ID of whatever primary lightning effect you want ==//
//== 4th parameter: the string ID of whatever secondary lightning effect you want ==//
//== 5th parameter: the path of whatever effect you want to be displayed on each hit unit ==//
//== 6th parameter: how many times do you want the chain to bounce ==//
//== 7th parameter: what is the maximum distance units can be from the original target in ==//
//== order to be effected by the chain spell. ==//
//== 8th parameter: how much damage do you want to be dealt initially. ==//
//== 9th parameter: how much damage reduction do you want on each bounce (example: 15%) ==//
//== 10th parameter: do you want to display damage/healing texttags? ==//
//== ==//
//== --Authors Note-- ==//
//== I know the call statement is hideously long, however it is necessary in order to make ==//
//== this system as dynamic as I want it to be. If you send a negative damage amount, it ==//
//== will heal affected units instead of damaging them. ==//
//== ==//
//================================================================================================//
//! zinc
library ChainSystem2
{
private
{
constant real Periodic = .2; //Represents how often (in seconds) chains will "jump" to another target
constant real BoltLifespan = .5; //The lifespan of bolts (reccomend to keep this at .4)
constant real BoltReset = .03; //The periodic instance in which bolts will reset their location
constant real Z_Offset = 65; //The height above the ground at which bolts are created
//==========================================================================================
constant integer InstanceLimit = 200; //The maximum number of chains that can be active at once
constant integer BoltLimit = 100; //The maximum number of "jumps" per chain that can occur
}
//=================================================
//Do Not Modify Any of the Code Below
//=================================================
private
{
constant timer CS_TIMER = CreateTimer();
constant timer CSB_TIMER = CreateTimer();
group CS_GROUP = CreateGroup();
integer MaxIndex = 0;
Chain Chains[InstanceLimit];
boolean ChainIndex[InstanceLimit];
location Loc1 = Location(0, 0);
location Loc2 = Location(0, 0);
group GroupPool[InstanceLimit];
boolean GroupPoolFree[InstanceLimit];
}
function GetGroup() -> integer
{
integer x = 0;
while (GroupPoolFree[x] == false)
{
x = x + 1;
}
GroupPoolFree[x] = true;
if (GroupPool[x] == null)
{
GroupPool[x] = CreateGroup();
}
return x;
}
struct Lightning
{
real CurrentLife;
unit Origin,Target;
lightning Bolt;
method Initialize(unit start, unit end, lightning whichlightning) -> thistype
{
BJDebugMsg("lightning initialized");
this.Origin = start;
this.Target = end;
this.CurrentLife = 0;
this.Bolt = whichlightning;
return this;
}
method Tick()
{
real OriginX = GetUnitX(this.Origin);
real OriginY = GetUnitY(this.Origin);
real TargetX = GetUnitX(this.Target);
real TargetY = GetUnitY(this.Target);
BJDebugMsg("Running Tick");
if (this.Target != null)
{
MoveLocation(Loc1, OriginX, OriginY);
MoveLocation(Loc2, TargetX, TargetY);
MoveLightningEx(this.Bolt, true, OriginX, OriginY, GetLocationZ(Loc1), TargetX, TargetY, GetLocationZ(Loc2));
}
this.CurrentLife = this.CurrentLife + BoltReset;
if (this.CurrentLife >= BoltLifespan)
{
this.destroy();
}
}
}
struct Chain
{
unit Caster,Target;
string PrimaryLightning,SecondaryLightning,HitEffect;
integer Jumps,CurrentJumps,GroupIndex;
real Radius,CurrentDamage,Reduction,X,Y;
boolean Text;
Lightning Bolts[BoltLimit];
attacktype AtkType;
damagetype DmgType;
static method DamageUnit(unit caster, unit target, real damage, boolean Text, attacktype AtkType, damagetype DmgType)
{
texttag dmgtxt;
string txtoutput;
if (damage > 0)
{
UnitDamageTarget(caster, target, damage, true, false, AtkType, DmgType, WEAPON_TYPE_WHOKNOWS);
txtoutput = ("|cffCD0000" + I2S(R2I(damage)) + "|r");
}
else
{
SetWidgetLife(target, (GetWidgetLife(target) + damage));
txtoutput = ("|cff00CD00" + I2S(R2I(damage)) + "|r");
}
if (Text)
{
dmgtxt = CreateTextTag();
SetTextTagPermanent(dmgtxt, false);
SetTextTagVisibility(dmgtxt, true);
SetTextTagPosUnit(dmgtxt, target, 0);
SetTextTagText(dmgtxt, txtoutput, .023);
SetTextTagFadepoint(dmgtxt, 1);
SetTextTagLifespan(dmgtxt, 1.5);
SetTextTagVelocity(dmgtxt, 0, .05);
SetTextTagColor(dmgtxt, 255, 255, 255, 255);
}
}
method CreateLightning(unit origin, unit target, string whichlightning)
{
MoveLocation(Loc1, GetUnitX(origin), GetUnitY(origin));
MoveLocation(Loc2, GetUnitX(target), GetUnitY(target));
Bolts[this.CurrentJumps] = Lightning.create();
if (whichlightning == "Primary")
{
Bolts[this.CurrentJumps].Initialize(origin, target, AddLightningEx(this.PrimaryLightning, true, GetUnitX(origin), GetUnitY(origin), (GetLocationZ(Loc1) + Z_Offset), GetUnitX(target), GetUnitY(target), (GetLocationZ(Loc2) + Z_Offset)));
}
else
{
Bolts[this.CurrentJumps].Initialize(origin, target, AddLightningEx(this.SecondaryLightning, true, GetUnitX(origin), GetUnitY(origin), (GetLocationZ(Loc1) + Z_Offset), GetUnitX(target), GetUnitY(target), (GetLocationZ(Loc2) + Z_Offset)));
}
DestroyEffect(AddSpecialEffect(this.HitEffect, GetUnitX(target), GetUnitY(target)));
this.DamageUnit(this.Caster, target, this.CurrentDamage, this.Text, this.AtkType, this.DmgType);
this.CurrentDamage = this.CurrentDamage * this.Reduction;
}
method Initialize(unit caster, unit target, string lightning1, string lightning2, string hiteffect, integer jumps, real radius, real dmg, real reduct, boolean showtext, attacktype atktype, damagetype dmgtype) -> thistype
{
this.Caster = caster;
this.Target = target;
this.X = GetUnitX(target);
this.Y = GetUnitY(target);
this.PrimaryLightning = lightning1;
this.SecondaryLightning = lightning2;
this.HitEffect = hiteffect;
this.CurrentJumps = 0;
this.Jumps = jumps;
this.Radius = radius;
this.CurrentDamage = dmg;
this.Reduction = (100 - reduct) / 100;
this.Text = showtext;
this.AtkType = atktype;
this.DmgType = dmgtype;
this.GroupIndex = GetGroup();
GroupAddUnit(GroupPool[this.GroupIndex], target);
CreateLightning(caster, target, "primary");
return this;
}
}
function DestroyChain(integer index)
{
ChainIndex[index] = false;
GroupClear(GroupPool[Chains[index].GroupIndex]);
GroupPoolFree[Chains[index].GroupIndex] = true;
Chains[index].destroy();
}
function GetNextAvailableIndex() -> integer
{
integer count = 0;
while (ChainIndex[count] == true)
{
count = count + 1;
}
if ((count + 1) > MaxIndex) {MaxIndex = (count + 1);}
ChainIndex[count] = true;
return count;
}
function GetNextAvailableTarget(integer x) -> unit
{
player CastingPlayer = GetOwningPlayer(Chains[x].Caster);
unit picked;
GroupEnumUnitsInRange(CS_GROUP, Chains[x].X, Chains[x].Y, Chains[x].Radius, null);
picked = FirstOfGroup(CS_GROUP);
while (picked != null)
{
if (IsUnitEnemy(picked, CastingPlayer) && (GetUnitState(picked, UNIT_STATE_LIFE) > 0) && (IsUnitInGroup(picked, GroupPool[Chains[x].GroupIndex]) == false) && (IsUnitType(picked, UNIT_TYPE_STRUCTURE) == false))
{
break;
}
GroupRemoveUnit(CS_GROUP, picked);
picked = FirstOfGroup(CS_GROUP);
}
return picked;
}
function ChainSystem_Periodic()
{
integer x;
unit picked;
for (0 <= x < MaxIndex)
{
if (ChainIndex[x] == true)
{
if (Chains[x].CurrentJumps < Chains[x].Jumps)
{
picked = GetNextAvailableTarget(x);
if (picked == null)
{
Chains[x].CurrentJumps = Chains[x].Jumps;
}
else
{
GroupAddUnit(GroupPool[Chains[x].GroupIndex], picked);
Chains[x].CurrentJumps = Chains[x].CurrentJumps + 1;
Chains[x].CreateLightning(Chains[x].Target, picked, "Secondary");
Chains[x].Target = picked;
}
}
else
{
if (Chains[x].CurrentJumps > (Chains[x].Jumps + (BoltLifespan / Periodic)))
{
DestroyChain(x);
}
}
//if (Chains[x].TickCount >= BoltLifespan)
//{
//DestroyLightning(Chains[x].Bolts[Chains[x].TickCount - BoltLifespan]);
//}
//if (Chains[x].TickCount >= Chains[x].Jumps)
//{
//DestroyChain(x);
//}
//Chains[x].TickCount = Chains[x].TickCount + 1;
}
}
}
function Bolt_Periodic()
{
integer x;
integer k;
for (0 <= x < MaxIndex)
{
if (ChainIndex[x] == true)
{
for (0 <= k <= Chains[x].CurrentJumps)
{
Chains[x].Bolts[k].Tick();
}
}
}
}
function onInit()
{
//TimerStart(CS_TIMER, Periodic, true, function ChainPeriodic);
//TimerStart(CSB_TIMER, BoltReset, true, function BoltPeriodic);
}
//===========================================================================
//===========================================================================
public function CreateChain2(unit caster, unit target, string PrimaryLightningId, string SecondaryLightningId, string HitEffect, integer Jumps, real Radius, real Damage, real PercentReduction, boolean ShowText)
{
//integer x = GetNextAvailableIndex();
//Chains[x] = Chain.create();
//Chains[x].Initialize(caster, target, PrimaryLightningId, SecondaryLightningId, HitEffect, Jumps, Radius, Damage, PercentReduction, ShowText, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL);
}
public function CreateChainEx(unit caster, unit target, string PrimaryLightningId, string SecondaryLightningId, string HitEffect, integer Jumps, real Radius, real Damage, real PercentReduction, boolean ShowText, attacktype WhichAttackType, damagetype WhichDamageType)
{
//integer x = GetNextAvailableIndex();
///Chains[x] = Chain.create();
//Chains[x].Initialize(caster, target, PrimaryLightningId, SecondaryLightningId, HitEffect, Jumps, Radius, Damage, PercentReduction, ShowText, WhichAttackType, WhichDamageType);
}
//===========================================================================
//===========================================================================
}
//! endzinc