• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Inferior Spellpack v1.5

  • Like
Reactions: Catch_ya
Here is my 2nd Spellpack I upload on Hive. Because of constructive Criticism by Deaod on my 1st Spellpack I tried to improve my coding and now here it goes.

Credits goes to:
- Vexorian
- Rising_Dusk
- Earth-Fury
- grim001
- Anitarf
- TriggerHappy187
for their libraries i use in this pack;
- watermelonman_1234
for creating the 'RoboticArmsHand.mdl'
- DarkT3mpl3r
- Hanky
for teaching me vJass

- v1.0 - Official release
- v1.1 - Added the Spell 'Robot Arms'
- v1.2 - Fixed Some Code related struff
- v1.3 - Fixed Tooltips and recoded Deadly Seduction
- v1.4 - Imported the 'RoboticArmsHand.mdl' to get rid of the stupid sound and
Implemented the TimedHandles library by TriggerHappy187
- v1.5 - Fixed a bug in Robotic Arms


Now the spells:
This pack contains 6 spells.

Polarisation
wc3scrnshot022610184949.jpg
Robot Arms
wc3scrnshot022610185004.jpg
wc3scrnshot022610185019.jpg
Deadly Seduction
The Dread Lord infects an enemy with an deadly virus that makes him to attack the Dread Lords enemies. If the unit gets another order than an attack order to an enemy unit of the Dread Lord, the virus attacks the units organs dealing damage. If the unit dies the virus is released out of the targets body infecting all enemy units nearby with poison that deals damage over time. That damage is amplified if the poisoned unit moves. If a poisoned unit dies it releases a disease cloud that deals damage to its allies.

[jass="Deadly Seduction"]library DeadlySeduction initializer Init needs TimerUtils, Table, GroupUtils, SpellEvent, LastOrder, ABuff
//*******************************************************************************************\\
//*******************************************************************************************\\
//* Deadly Seducetion By Inferior *\\
//* *\\
//* v1.1 *\\
//* *\\
//* ************************* *\\
//* * Requirements: * *\\
//* * - JassNewGenPack * *\\
//* * - TimerUtils * *\\
//* * - Table * *\\
//* * - GroupUtils * *\\
//* * - SpellEvent * *\\
//* * - LastOrder * *\\
//* * - ABuff * *\\
//* ************************* *\\
//* *\\
//* *\\
//* *\\
//* How To Import: *\\
//* - Copy the Spell called 'Deadly Seduction' and create 2 new Buffs, the first for *\\
//* your the Mainability, the second for the DiseaseCloud effect *\\
//* - Remove the exclamation in front of the ObjectMerger execution. *\\
//* Save the map, reopen it and put the exclamations again to remove saving delay. *\\
//* - Copy the whole Triggerscript into your map *\\
//* - Import the 'Dummy.mdx' included in that map to your map *\\
//* - Create a dummy unit and change the model into the 'Dummy.mdx' *\\
//* - Now change the values of the 'ABILID', 'DUMMYID', 'DISEASEID' and 'BUFFID *\\
//* to the Rawcodes of the Spells, Buff and Dummy in your map *\\
//* *\\
//* Spelldescription: *\\
//* The Caster infects the Target Unit with a deadly virus, that makes the Target lose *\\
//* his mind and even attack his allies. Each time the Target gets a new Order from *\\
//* its Owner, the virus destroys the Targets cells and force it to attack his allies. *\\
//* If the Target dies, nearby allied units get infected by the Poison flodding out of *\\
//* the Targts dead body. The Poison deals damage over time. If the Infected moves the *\\
//* Poison destroys the body faster and deals doubled damage. If an Infected unit dies, *\\
//* its dead body releases a deadly Cloud of poison that deals damage to allied units *\\
//* nearby. *\\
//* *\\
//*******************************************************************************************\\
//*******************************************************************************************\\
// Globals Setting

// //! external ObjectMerger w3a ANpi DisC anam DiseaseCloud alev 3 Eim1 1 1.0 Eim1 2 1.0 Eim1 3 1.0 ahdu 1 0.1 ahdu 2 0.1 ahdu 3 0.1 adur 1 0.1 adur 2 0.1 adur 3 0.1 aare 1 250. aare 2 250. aare 3 250. atar 1 ground,enemies,neutral,organic atar 2 ground,friend,neutral,organic atar 3 ground,enemies,friend,neutral,organic

globals
private constant integer ABILID = 'A008'
// The AbilityId of the MainAbility

private constant integer DUMMYID = 'e000'
// The UnitId of the DummyUnit ( Uses imported Dummymodel ) ; you may use your own dummy here

private constant integer DISEASEID = 'DisC'
// The AbilityId of the DiseaseCloud ( Based on 'Permanent Immolation' )

private constant integer BUFFID = 'B000'
// The Rawcode of the Buff

private constant real DURATION = 2.5
// The Base Duration for the MainAbility

private constant real DETECTRADIUS = 500.
// The Radius in which the possible Targets for the MainAbility shall be detected

private constant real INFECTDURATION = 10.
// The Duration of the Infection after the Explosion of the Main Target

private constant real INFECTPERIOD = .02
// The Period of the Infection Timer

private constant real EXPLODEFACTOR = .1
// The Factor of the Targets MAX_HP at which it explodes if it falls below

private constant real HEALTHFACTOR = .01
// The Factor of the Targets CURRENT_HP that is dealt as Damage per INFECTPERIOD

private constant real PAINDAMAGE = .01
// The Factor of the Targets MAX_HP that is dealt as Damage if its AttackOrder gets interrupted

private constant real INSTANTKILL = 10000000.
// The value to instantly kill an Unit. Insert here any value that is big enough to kill any Unit on your map

private constant real EXPLODERADIUS = 350.
// The Radius in which Units get Infected after the Main Target explodes

private constant boolean INFECTENEMIES = false
// The value to check the possible Targets for the Infection

private constant boolean INFECTALLIES = true
// The value to check the possible Targets for the Infection

// The 2 values above are weird, because INFECTENEMIES means the enemies of the Main Target
// and INFECTALLIES means the allies of the Main Target

private constant string INFECTCLOUD = "Abilities\\Spells\\Undead\\PlagueCloud\\PlagueCloudCaster.mdl"
// The Effect for the Dummy

private constant string INFECTTARGET = "Units\\Undead\\PlagueCloud\\PlagueCloudtarget.mdl"
// The Effect attached to the Infected Units

private constant string INFECTATTACH = "head"
// The AttachPoint for the TargetEffect

private constant string PAINEFFECT = "Objects\\Spawnmodels\\Human\\HumanBlood\\HumanBloodRifleman.mdl"
// The Effect attached to the Main Target when it feels Pain ( interrupts AttackOrder )

private constant string PAINATTACH = "origin"
// The AttachPoint for the PAINEFFECT or the DummyEffect

// The following values only refer to all Damage that is dealt in this Spell
private constant boolean DAMAGEDMEELE = true
private constant boolean DAMAGEDRANGED = false
private constant attacktype ATTACKTYPE = ATTACK_TYPE_MAGIC
private constant damagetype DAMAGETYPE = DAMAGE_TYPE_MAGIC
private constant weapontype WEAPONTYPE = WEAPON_TYPE_WHOKNOWS

// DO NOT CHANGE THESE VALUES
private constant integer ATTACKORDER = 851983
// The OrderId for common Attack Order
private constant integer STOPORDER = 851972
// The OrderId for common Stop Order
private constant integer DISARMBUFF = '&ARM'

// Do not change any of the following variables
private unit TEMPUNIT

endglobals


// function that returns the Duration of the Main Ability for each level
private constant function GetRealDuration takes integer level returns real
return DURATION+level
endfunction

// DO NOT TOUCH ANYTHING BELOW THIS COMMENT, ALTHOUGH YOU KNOW WHAT YOU DO.
// HERE BEGINS THE SPELLS SCRIPT.

private keyword Infection // DO NOT CHANGE THIS

// DO NOT CHANGE THIS FUNCTION
// this function returns an important value
private constant function GetDiseaseLevel takes nothing returns integer
if INFECTENEMIES and INFECTALLIES then
return 3
elseif INFECTENEMIES and not INFECTALLIES then
return 2
elseif not INFECTENEMIES and INFECTALLIES then
return 1
else
return 0
endif
endfunction

// the Filter for the Units for the MainAbility
private function GroupFilter takes nothing returns boolean
return IsUnitAlly(GetFilterUnit(),GetOwningPlayer(TEMPUNIT)) and GetWidgetLife(GetFilterUnit())>0.405 and IsUnitType(GetFilterUnit(),UNIT_TYPE_GROUND) and GetFilterUnit()!=TEMPUNIT
endfunction

// DO NOT CHANGE THIS FUNCTION
// you may add more Filters if you want but do not remove any of those
private function InfectionFilter takes nothing returns boolean
if INFECTENEMIES and not INFECTALLIES then
return IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(TEMPUNIT)) and GetWidgetLife(GetFilterUnit())>0.405 and Infection.InfectTable[GetFilterUnit()]==0 and GetFilterUnit()!=TEMPUNIT
elseif not INFECTENEMIES and INFECTALLIES then
return IsUnitAlly(GetFilterUnit(),GetOwningPlayer(TEMPUNIT)) and GetWidgetLife(GetFilterUnit())>0.405 and Infection.InfectTable[GetFilterUnit()]==0 and GetFilterUnit()!=TEMPUNIT
elseif INFECTENEMIES and INFECTALLIES then
return GetWidgetLife(GetFilterUnit())>0.405 and Infection.InfectTable[GetFilterUnit()]==0 and GetFilterUnit()!=TEMPUNIT
else
return false
endif
endfunction

//**********************************************//
// Struct for the Infection //
//**********************************************//

private struct Infection
unit infected
unit caster
real dps
real duration
real x
real y
effect smoke

static integer count = 0
static timer InfectTimer

static boolean array active
static Infection array Data

static HandleTable InfectTable

// Deals Damage over Time and checks if the Infected unit moved.
// If it moved it deals doubled Damage
static method Disease takes nothing returns nothing
local integer int=0
local unit dum
local effect e
loop
exitwhen int>=.count
if .active[int] then
if .Data[int].duration>=0 then

// Checks if the Infected moved. If it did it takes doubled Damage
if GetUnitX(.Data[int].infected)==.Data[int].x and GetUnitY(.Data[int].infected)==.Data[int].y then
call UnitDamageTarget(.Data[int].caster,.Data[int].infected,.Data[int].dps*INFECTPERIOD*2,DAMAGEDMEELE,DAMAGEDRANGED,ATTACKTYPE,DAMAGETYPE,WEAPONTYPE)
else
call UnitDamageTarget(.Data[int].caster,.Data[int].infected,.Data[int].dps*2*INFECTPERIOD*2,DAMAGEDMEELE,DAMAGEDRANGED,ATTACKTYPE,DAMAGETYPE,WEAPONTYPE)
endif

set .Data[int].x=GetUnitX(.Data[int].infected)
set .Data[int].y=GetUnitY(.Data[int].infected)
set .Data[int].duration=.Data[int].duration-INFECTPERIOD

// Checks if the Infected is still alive or not.
// If it is, it creates a Cloud that deals damage to nearby units.
if GetWidgetLife(.Data[int].infected)<0.405 then
set dum=CreateUnit(GetOwningPlayer(.Data[int].caster),DUMMYID,GetUnitX(.Data[int].infected),GetUnitY(.Data[int].infected),0)
call UnitAddAbility(dum,DISEASEID)
call SetUnitAbilityLevel(dum,DISEASEID,GetDiseaseLevel())
call AddSpecialEffectTarget(INFECTCLOUD,dum,PAINATTACH)
call UnitApplyTimedLife(dum,'Bapl',GetRealDuration(GetUnitAbilityLevel(.Data[int].caster,ABILID)))
call DestroyEffect(.Data[int].smoke)
set .active[int]=false
call .Data[int].destroy()
endif
else
set .InfectTable[.Data[int].infected]=0
call DestroyEffect(.Data[int].smoke)
set .active[int]=false
call .Data[int].destroy()
endif
endif
set int=int+1
endloop
endmethod

static method create takes nothing returns Infection
local Infection data=Infection.allocate()
if .count==0 then
set .InfectTimer=NewTimer()
call TimerStart(.InfectTimer,INFECTPERIOD,true,function Infection.Disease)
endif
set .active[.count]=true
set .Data[.count]=data
set .count=.count+1
return data
endmethod

endstruct

//**********************************************//
// Struct for the Main Spell //
//**********************************************//

private struct Spell
unit caster
unit target
unit victim
timer Time
timer Dead
boolean Infection

static trigger Order
static HandleTable SpellTable

static method Infect takes nothing returns nothing
local Infection data
if Infection.InfectTable[GetEnumUnit()]==0 then
set data=Infection.create()
set data.infected=GetEnumUnit()
set data.caster=TEMPUNIT
set data.x=GetUnitX(data.infected)
set data.y=GetUnitY(data.infected)
set data.dps=GetWidgetLife(data.infected)*HEALTHFACTOR
set data.smoke=AddSpecialEffectTarget(INFECTTARGET,data.infected,INFECTATTACH)
set data.duration=INFECTDURATION
set Infection.InfectTable[data.infected]=data
else
set data.duration=INFECTDURATION
endif
endmethod

// The method for the Spells End. Picks nearby units and infects them.
static method EndSpell takes nothing returns nothing
local Spell data=GetTimerData(GetExpiredTimer())
if data.Infection then
set TEMPUNIT=data.caster
call GroupEnumUnitsInArea(ENUM_GROUP,GetUnitX(data.target),GetUnitY(data.target),EXPLODERADIUS,Condition(function InfectionFilter))
call UnitDamageTarget(data.caster,data.target,INSTANTKILL,DAMAGEDMEELE,DAMAGEDRANGED,ATTACKTYPE,DAMAGETYPE,WEAPONTYPE)
call ForGroup(ENUM_GROUP,function Spell.Infect)
call GroupClear(ENUM_GROUP)
else
call SetUnitExploded(data.target,false)
endif
set Spell.SpellTable[data.target]=0
call IssueImmediateOrderById(data.target,STOPORDER)
call UnitRemoveAbility(data.target,BUFFID)
call ReleaseTimer(data.Dead)
call ReleaseTimer(data.Time)
set data.Dead=null
set data.Time=null
set data.target=null
set data.caster=null
set data.victim=null
set data.Infection=false
call data.destroy()
endmethod

// Checks if the Target Unit is dead or not. Checks if the allied target that is attacked(victim) is dead or not.
// If the victim is dead it searchs for a new one. If there is none the Spell ends and the virus is destroyed.
static method CheckDead takes nothing returns nothing
local Spell data=GetTimerData(GetExpiredTimer())
if GetWidgetLife(data.target)>GetUnitState(data.target,UNIT_STATE_MAX_LIFE)*EXPLODEFACTOR then
if GetWidgetLife(data.victim)<0.405 then
set TEMPUNIT=data.caster
call GroupEnumUnitsInArea(ENUM_GROUP,GetUnitX(data.target),GetUnitY(data.target),DETECTRADIUS,Condition(function GroupFilter))
set data.victim=GroupPickRandomUnit(ENUM_GROUP)
if data.victim==data.target then
call GroupRemoveUnit(ENUM_GROUP,data.victim)
set data.victim=GroupPickRandomUnit(ENUM_GROUP)
endif
call GroupClear(ENUM_GROUP)

if data.victim!=null then
call IssueTargetOrderById(data.target,ATTACKORDER,data.victim)
else
call PauseTimer(data.Time)
call PauseTimer(data.Dead)
call TimerStart(data.Time,0.0,false,function Spell.EndSpell)
endif
endif
else
set data.Infection=true
call PauseTimer(data.Time)
call PauseTimer(data.Dead)
call TimerStart(data.Time,0.0,false,function Spell.EndSpell)
endif
endmethod

// Checks if the target is ordered another order than the Attackorder and if the ordered unit is equal to victim.
// There is one thing to mention. If the target is unable to attack, that means it does not have attack ability
// or it is disarmed, then this spell has no effect and is removed from the Target
// E.g. you cast Deadly Seduction on a target and after that Dark Curse and attack it. Its ability to attack is removed
// and then the Effect of this Spell is removed aswell. That means any disarm abilities have a higher priority than this
// Spell. And I definitly recommend not to remove the 'attack' Ability other than with disarms!!!!
static method CheckOrder takes nothing returns boolean
local unit ordered=GetOrderedUnit()
local Spell data
if GetUnitAbilityLevel(ordered,BUFFID)>0 then
if GetUnitAbilityLevel(ordered,DISARMBUFF)==0 then
set data=Spell.SpellTable[ordered]
if GetWidgetLife(data.target)>GetUnitState(data.target,UNIT_STATE_MAX_LIFE)*EXPLODEFACTOR then
if GetWidgetLife(data.victim)>0.405 then
if GetOrderTargetUnit()!=data.victim or GetLastOrderId(data.target)!=ATTACKORDER then
call IssueTargetOrderById(data.target,ATTACKORDER,data.victim)
call UnitDamageTarget(data.caster,data.target,GetUnitState(data.target,UNIT_STATE_MAX_LIFE)*PAINDAMAGE,DAMAGEDMEELE,DAMAGEDRANGED,ATTACKTYPE,DAMAGETYPE,WEAPONTYPE)
call DestroyEffect(AddSpecialEffectTarget(PAINEFFECT,data.target,PAINATTACH))
endif
else
set TEMPUNIT=data.caster
call GroupEnumUnitsInArea(ENUM_GROUP,GetUnitX(data.target),GetUnitY(data.target),DETECTRADIUS,Condition(function GroupFilter))

set data.victim=GroupPickRandomUnit(ENUM_GROUP)
if data.victim==data.target then
call GroupRemoveUnit(ENUM_GROUP,data.victim)
set data.victim=GroupPickRandomUnit(ENUM_GROUP)
endif
call GroupClear(ENUM_GROUP)

if data.victim!=null then
call IssueTargetOrderById(data.target,ATTACKORDER,data.victim)
else
call PauseTimer(data.Time)
call PauseTimer(data.Dead)
call TimerStart(data.Time,0.0,false,function Spell.EndSpell)
endif
endif
else
set data.Infection=true
call PauseTimer(data.Time)
call PauseTimer(data.Dead)
call TimerStart(data.Time,0.0,false,function Spell.EndSpell)
endif
else
call PauseTimer(data.Time)
call PauseTimer(data.Dead)
call TimerStart(data.Time,0.0,false,function Spell.EndSpell)
endif

endif

return false
endmethod

endstruct

// Buff declaration
globals
private aBuffType buffType = 0
endglobals

// executed when the Buff is removed anyhow, e.g. with a dispel
private function Dispel takes aBuff buffType returns nothing
local Spell data=buffType.data
if GetUnitAbilityLevel(data.target,BUFFID)==0 then
set data.Infection=false
call PauseTimer(data.Time)
call PauseTimer(data.Dead)
call TimerStart(data.Time,0.0,false,function Spell.EndSpell)
call ABuffRemove(buffType)
endif
endfunction

// executed Function on Spellcast. Checks if there is an ally of the target unit.
// If there is none the spell has no effect.
private function SeductionSpell takes nothing returns nothing

local Spell data

if Spell.SpellTable[SpellEvent.TargetUnit]==0 then
set data=Spell.create()
set data.caster=SpellEvent.CastingUnit
set data.target=SpellEvent.TargetUnit
set data.Time=NewTimer()
set data.Dead=NewTimer()
set data.Infection=false
call SetTimerData(data.Time,data)
call SetTimerData(data.Dead,data)

set TEMPUNIT=data.caster
call GroupEnumUnitsInArea(ENUM_GROUP,GetUnitX(data.target),GetUnitY(data.target),DETECTRADIUS,Condition(function GroupFilter))

set data.victim=GroupPickRandomUnit(ENUM_GROUP)
if data.victim==data.target then
call GroupRemoveUnit(ENUM_GROUP,data.victim)
set data.victim=GroupPickRandomUnit(ENUM_GROUP)
endif
call GroupClear(ENUM_GROUP)


set TEMPUNIT=null

if data.victim!=null then

call ABuffApply(buffType,data.target,data.caster,GetRealDuration(GetUnitAbilityLevel(data.caster,ABILID)),GetUnitAbilityLevel(data.caster,ABILID),data)

call SetUnitExploded(data.target,true)
call IssueTargetOrderById(data.target,ATTACKORDER,data.victim)

set Spell.SpellTable[data.target]=data

call TimerStart(data.Time,GetRealDuration(GetUnitAbilityLevel(data.caster,ABILID)),false,function Spell.EndSpell)
call TimerStart(data.Dead,0.01,true,function Spell.CheckDead)

else

call UnitRemoveAbility(data.target,BUFFID)
call ReleaseTimer(data.Time)
call ReleaseTimer(data.Dead)
call data.destroy()

endif
else
set data=Spell.SpellTable[SpellEvent.TargetUnit]
call ABuffApply(buffType,data.target,data.caster,GetRealDuration(GetUnitAbilityLevel(data.caster,ABILID)),GetUnitAbilityLevel(data.caster,ABILID),data)
call PauseTimer(data.Time)
call TimerStart(data.Time,GetRealDuration(GetUnitAbilityLevel(data.caster,ABILID)),false,function Spell.EndSpell)
endif
endfunction

private function Init takes nothing returns nothing
call RegisterSpellEffectResponse(ABILID,SeductionSpell)
set Spell.SpellTable=HandleTable.create()
set Infection.InfectTable=HandleTable.create()
set Spell.Order=CreateTrigger()

set buffType = aBuffType.create()
set buffType.eventPeriodic = Dispel

call Preload(INFECTCLOUD)
call Preload(INFECTTARGET)
call Preload(PAINEFFECT)
call PreloadStart()

call TriggerRegisterAnyUnitEventBJ(Spell.Order,EVENT_PLAYER_UNIT_ISSUED_ORDER)
call TriggerRegisterAnyUnitEventBJ(Spell.Order,EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
call TriggerRegisterAnyUnitEventBJ(Spell.Order,EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)

call TriggerAddCondition(Spell.Order,Condition(function Spell.CheckOrder))
endfunction

endlibrary[/code]

Dark Curse
The Dread Lord uses mighty dark magic to curse a target unit with a deadly curse, that reduces the targets size. All damage taken by that unit is amplified by 50% and will cause a confusion in the targets head making it unable to attack and to execute its owners orders correctly. To damage the target with physical attacks is harder, because of the lack of size.

[jass="Dark Curse"]library DarkCurse initializer Init needs TimerUtils, LastOrder, UnitStatus, Table, SpellEvent, ABuff, xecast, xepreload
//*******************************************************************************************\\
//*******************************************************************************************\\
//* Dark Curse By Inferior *\\
//* *\\
//* v1.0 *\\
//* *\\
//* ************************* *\\
//* * Requirements: * *\\
//* * - JassNewGenPack * *\\
//* * - TimerUtils * *\\
//* * - LastOrder * *\\
//* * - UnitStatus * *\\
//* * - Table * *\\
//* * - SpellEvent * *\\
//* * - ABuff * *\\
//* * - xecast * *\\
//* * - xepreload * *\\
//* ************************* *\\
//* *\\
//* *\\
//* *\\
//* How To Import: *\\
//* - Copy the Spell called 'Dark Curse' and create a Buff for your Effects *\\
//* - Remove the exclamation in front of the ObjectMerger execution. *\\
//* Save the map, reopen it and put the exclamations again to remove saving delay. *\\
//* - Copy the whole Triggerscript into your map *\\
//* - Import the 'Dummy.mdx' included in that map to your map *\\
//* - Create a dummy unit and change the model into the 'Dummy.mdx' *\\
//* - Now change the values of the 'ABILID', 'DUMMYID', 'SHRINKID' and 'BUFFID *\\
//* to the Rawcodes of the Spells, Buff and Dummy in your map *\\
//* *\\
//* Spelldescription: *\\
//* The Caster curses the Target with Dark Magic reducing its Size. *\\
//* With this Size reduction, the damage taken is amplified and its Movement- and *\\
//* Attackspeed is reduced, but is harder to hit. *\\
//* Each time the Target takes damage its head is shattered, which makes it unable to *\\
//* attack and confuses it. Each time the Target gets an Point Order within that *\\
//* Confusion it will execute that Order in the complete other direction. *\\
//* *\\
//*******************************************************************************************\\
//*******************************************************************************************\\
// Globals Setting

// //! external ObjectMerger w3a Ablo ShrA anam "Shrink/Slow" alev 4 areq null arqa 0 Blo1 1 -0.2 Blo1 2 -0.3 Blo1 3 -0.4 Blo1 4 -0.5 Blo2 1 -0.05 Blo2 2 -0.1 Blo2 3 -0.15 Blo2 4 -0.2 Blo3 1 -0.1 Blo3 2 -0.15 Blo3 3 -0.2 Blo3 4 -0.25 acdn 1 0.0 ahdu 1 4. ahdu 2 6. ahdu 3 8. ahdu 4 10. adur 1 2. adur 2 3. adur 3 4. adur 4 5. amcs 1 0 aran 1 9999.0 aran 2 9999.0 aran 3 9999.0 aran 4 9999.0 abuf 1 Bblo abuf 2 Bblo abuf 3 Bblo abuf 4 Bblo atar 1 "ground,enemies,air,neutral,organic" atar 2 "ground,enemies,air,neutral,organic" atar 3 "ground,enemies,air,neutral,organic" atar 4 "ground,enemies,air,neutral,organic"
// //! external ObjectMerger w3a ANth $Def anam "DefenseFactor" Uts2 1 1.5 Uts1 1 0
// //! external ObjectMerger w3a ANde $Atk anam "DamageFactor" Nde2 1 0.5 Nde3 1 0.5 Nde4 1 0.5 aher 0
// //! external ObjectMerger w3a ACev $Eva anam "EvasionFactor" Eev1 1 0.75
// //! external ObjectMerger w3a Aspb Book anam "CurseEffectBook" spb2 1 0 spb1 1 EvaA,AtkR,DefR aite 0

globals
private constant integer ABILID = 'A001'
// The AbilityId of the Mainability

private constant integer SHRINKID = 'ShrA'
// The AbilityId of the Slow- and Shrinkability

private constant integer BUFFID = 'B001'
// The Buff of the Slow and Shrink

private constant integer EFFECTBOOK = 'Book'
// The Spellbook which contains the Evasion and Damageamplify Abilities

private constant integer DUMMYID = 'e000'
// The Id of your Dummyunit

private constant integer BLOODLUST = 852101
// The casting Order for the Slow and Shrink ( Here based on bloodlust )

private constant integer STOPORDER = 851972
// The common StopOrder

private constant real GROWTHDELAY = 1.
// The time the Target is growing. Will influence the possible duration to confuse the Target. Set this to 0
// if you want it possible until the Target regains its default size

private constant string CONFUSED = "Abilities\\Spells\\Items\\HealingSalve\\HealingSalveTarget.mdl"
private constant string CONFUSEATTACH = "origin"
// The First Confusion Effect

private constant string CONFUSEEFFECT = "Abilities\\Spells\\Orc\\StasisTrap\\StasisTotemTarget.mdl"
private constant string EFFECTATTACH = "overhead"
// The Second Confusion Effect

// For Descriptions for the following Variables see below
private real array DURATION
private real array HERODURATION
private real array CONFUSEDTIME
endglobals

private function SetupSpellVariables takes nothing returns nothing
// The duration for normal targets
set DURATION[1]=4.-GROWTHDELAY
set DURATION[2]=6.-GROWTHDELAY
set DURATION[3]=8.-GROWTHDELAY
set DURATION[4]=10.-GROWTHDELAY

// The duration for Heros
set HERODURATION[1]=2.-GROWTHDELAY
set HERODURATION[2]=3.-GROWTHDELAY
set HERODURATION[3]=4.-GROWTHDELAY
set HERODURATION[4]=5.-GROWTHDELAY

// The duration a unit is confused and disarmed
set CONFUSEDTIME[1]=0.75
set CONFUSEDTIME[2]=1.25
set CONFUSEDTIME[3]=1.75
set CONFUSEDTIME[4]=2.5
endfunction

// DO NOT TOUCH ANYTHING BELOW THIS COMMENT, ALTHOUGH YOU KNOW WHAT YOU DO.
// HERE BEGINS THE SPELLS SCRIPT.

private struct DarkCurse
unit caster
unit target
integer lvl
trigger damage
trigger order
timer end
timer disarm
boolean disarmed
boolean moved
effect confused
effect head

static HandleTable CurseTable

// Ends the Spell
static method EndSpell takes nothing returns nothing
local DarkCurse data=.CurseTable[GetExpiredTimer()]
call UnitRemoveAbility(data.target,EFFECTBOOK)
call ReleaseTimer(data.end)
call ReleaseTimer(data.disarm)
call DestroyTrigger(data.damage)
call DestroyTrigger(data.order)
call DestroyEffect(data.confused)
call DestroyEffect(data.head)
set data.disarmed=false
set data.moved=false
set data.lvl=0
call data.destroy()
endmethod

// Removes the Disarm and Confusion
static method RemoveDisarm takes nothing returns nothing
local DarkCurse data=.CurseTable[GetExpiredTimer()]
call DestroyTrigger(data.order)
set data.disarmed=false
call ReleaseTimer(GetExpiredTimer())
call DestroyEffect(data.confused)
call DestroyEffect(data.head)
endmethod

// Changes the LastOrders Coordinates
static method ConfusedMove takes nothing returns boolean
local DarkCurse data=.CurseTable[GetTriggeringTrigger()]
local real x=GetUnitX(data.target)
local real y=GetUnitY(data.target)
local real dx=x-GetOrderPointX()
local real dy=y-GetOrderPointY()
set x=x+dx
set y=y+dy
if not data.moved then
call AbortOrder(data.target)
set data.moved=true
call IssuePointOrderById(data.target,GetLastOrderId(data.target),x,y)
else
set data.moved=false
endif

return false
endmethod

// Executed when the target is damaged. If the damage taken is higher than 0 it is disabled and confused
static method DisarmTarget takes nothing returns boolean
local DarkCurse data=.CurseTable[GetTriggeringTrigger()]
if GetEventDamage()>0 then
if not data.disarmed then
call DisarmUnitTimed(data.target,CONFUSEDTIME[data.lvl])
set data.disarm=NewTimer()
call TimerStart(data.disarm,CONFUSEDTIME[data.lvl],false,function DarkCurse.RemoveDisarm)
set .CurseTable[data.disarm]=data
set data.order=CreateTrigger()
call TriggerRegisterUnitEvent(data.order,data.target,EVENT_UNIT_ISSUED_POINT_ORDER)
call TriggerAddCondition(data.order,Condition(function DarkCurse.ConfusedMove))
set .CurseTable[data.order]=data
set data.disarmed=true
set data.moved=false
set data.confused=AddSpecialEffectTarget(CONFUSED,data.target,CONFUSEATTACH)
set data.head=AddSpecialEffectTarget(CONFUSEEFFECT,data.target,EFFECTATTACH)
endif
endif

return false
endmethod

static method create takes nothing returns DarkCurse
local DarkCurse data=DarkCurse.allocate()
return data
endmethod

endstruct

// Buff Declaration
globals
private aBuffType buffType = 0
endglobals

// executed when the Buff is removed anyhow, e.g. with a dispel
private function Dispel takes aBuff buffType returns nothing
local DarkCurse data=buffType.data
if GetUnitAbilityLevel(data.target,BUFFID)==0 then
call PauseTimer(data.end)
call TimerStart(data.end,0.0,false,function DarkCurse.EndSpell)
call ABuffRemove(buffType)
endif
endfunction

// Executed on Spellcast
private function CurseSpell takes nothing returns nothing
local unit caster=SpellEvent.CastingUnit
local unit target=SpellEvent.TargetUnit
local integer lvl=GetUnitAbilityLevel(caster,ABILID)
local xecast dummy
local DarkCurse data

if GetUnitAbilityLevel(target,BUFFID)==0 then
call UnitAddAbility(target,EFFECTBOOK)
set dummy=xecast.createBasicA(SHRINKID,BLOODLUST,GetOwningPlayer(caster))
set dummy.level=lvl
call dummy.castOnTarget(target)
set data=DarkCurse.create()
set data.caster=caster
set data.target=target
set data.lvl=lvl
set data.disarmed=false
set data.moved=false
set data.end=NewTimer()
set DarkCurse.CurseTable[target]=data
// Two possibilities: If the target is a hero or not
if IsUnitSpellResistant(target) then
call TimerStart(data.end,HERODURATION[lvl],false,function DarkCurse.EndSpell)
call ABuffApply(buffType,target,caster,HERODURATION[lvl],lvl,data)
else
call TimerStart(data.end,DURATION[lvl],false,function DarkCurse.EndSpell)
call ABuffApply(buffType,target,caster,DURATION[lvl],lvl,data)
endif
set DarkCurse.CurseTable[data.end]=data
set data.damage=CreateTrigger()
call TriggerRegisterUnitEvent(data.damage,target,EVENT_UNIT_DAMAGED)
call TriggerAddCondition(data.damage,Condition(function DarkCurse.DisarmTarget))
set DarkCurse.CurseTable[data.damage]=data
else
set dummy=xecast.createBasicA(SHRINKID,BLOODLUST,GetOwningPlayer(caster))
set dummy.level=lvl
call dummy.castOnTarget(target)
set data=DarkCurse.CurseTable[target]
call PauseTimer(data.end)
if IsUnitSpellResistant(target) then
call TimerStart(data.end,HERODURATION[lvl],false,function DarkCurse.EndSpell)
call ABuffApply(buffType,target,caster,HERODURATION[lvl],lvl,data)
else
call TimerStart(data.end,DURATION[lvl],false,function DarkCurse.EndSpell)
call ABuffApply(buffType,target,caster,DURATION[lvl],lvl,data)
endif
endif

set caster=null
set target=null
endfunction

private function Init takes nothing returns nothing
local integer int=0
loop
exitwhen int>=bj_MAX_PLAYER_SLOTS
call SetPlayerAbilityAvailable(Player(int),EFFECTBOOK,false)
set int=int+1
endloop
call SetupSpellVariables()
call RegisterSpellEffectResponse(ABILID,CurseSpell)
set DarkCurse.CurseTable=HandleTable.create()
call XE_PreloadAbility(SHRINKID)
call XE_PreloadAbility(EFFECTBOOK)

set buffType=aBuffType.create()
set buffType.eventPeriodic=Dispel

call Preload(CONFUSED)
call Preload(CONFUSEEFFECT)

call PreloadStart()
endfunction

endlibrary[/code]

Haunting Ghosts
The Dread Lord owns the souls of all dead units in reange nearby. A dead units soul haunts its killer granting a bonus if the killer is an ally of the Dread Lord or reducing its strength if the killer is an enemy. The killer will regain its common strength step by step after a short duration.
The more units are killed by the same, the higher is the Bonus or Reduction.

[jass="HauntingGhosts"]library HauntingGhosts initializer Init needs TimerUtils, GroupUtils, UnitMaxState, AutoIndex, StatusModifier, Rounding, TimedHandles, SpellEvent
//*******************************************************************************************\\
//*******************************************************************************************\\
//* Haunting Ghosts By Inferior *\\
//* *\\
//* v1.1 *\\
//* *\\
//* ************************* *\\
//* * Requirements: * *\\
//* * - JassNewGenPack * *\\
//* * - TimerUtils * *\\
//* * - GroupUtils * *\\
//* * - UnitMaxState * *\\
//* * - AutoIndex * *\\
//* * - StatusModifier * *\\
//* * - Rounding * *\\
//* * - TimedHandles * *\\
//* * - SpellEvent * *\\
//* ************************* *\\
//* *\\
//* *\\
//* *\\
//* How To Import: *\\
//* - Copy the Spell called 'Haunting Ghosts' into your map *\\
//* - Copy the whole Triggerscript into your map *\\
//* - Now change the values of the 'ABILID' and 'DECAYTIME' to the Values in your map *\\
//* *\\
//* Spelldescription: *\\
//* The Caster owns over the souls of the dead units nearby. For each Unit the Killer *\\
//* of that unit gains a life and damage bonus over a short duration. If the Killer is *\\
//* an enemy of the Caster its MaxLife is reduced and its damage aswell. If the Killer *\\
//* is an ally its MaxLife and damage are increased. The MaxLife of the Killer *\\
//* regenerates to its default after a specific duration. The regeneration has its *\\
//* own duration. The more units were killed by one unit the higher is the bonus the *\\
//* Killer gains. *\\
//* *\\
//*******************************************************************************************\\
//*******************************************************************************************\\
// Globals Setting

globals
private constant integer ABILID = 'A000'
// The AbilityId of the MainSpell

private constant real DECAYTIME = 88.
// The time after which bones vanish. Change this to the value found in Map properties of your map

private constant real TIMERPERIOD = 0.5
// Do not set this value lower than 0.2. Does not really influence performance.
// The matter is that you cannot decrease the Health by less than 1, that means every decreased Health by 0.00 up to 0.9999 will be 1

private constant string SOULEFFECT = "Objects\\Spawnmodels\\Undead\\UndeadDissipate\\UndeadDissipate.mdl"
private constant string HAUNTEFFECT = "Abilities\\Spells\\Orc\\EtherealForm\\SpiritWalkerChange.mdl"
private constant string HAUNTATTACH = "origin"
private constant string LEACHEFFECT = "Abilities\\Spells\\NightElf\\shadowstrike\\shadowstrike.mdl"
private constant string LEACHATTACH = "overhead"

private constant boolean MORETHANONCE = false
// Allows dead units to give bonus or leached Health and Damage more than once.

// For Descriptions for the following Variables see below
private real array HPLEACHED
private integer array DMGLEACHED
private real array ADDLEACH
private real array DURATION
private real array REGAINDURATION
private real array AOE


// Do not touch the following

private unit TEMPUNIT
private timer TEMPTIMER
private unit array KILLERUNIT

// Only used if MORETHANONCE=false
private boolean array ALLREADYUSED
endglobals


private function SetupSpellVariables takes nothing returns nothing
// The Healthpoints leached or added for each killed unit
set HPLEACHED[1]=5.
set HPLEACHED[2]=15.
set HPLEACHED[3]=30.
set HPLEACHED[4]=50.

set DMGLEACHED[1]=2
set DMGLEACHED[2]=3
set DMGLEACHED[3]=4
set DMGLEACHED[4]=5

// A bonus of leached or added Healthpoints, the more units were killed
set ADDLEACH[1]=0.
set ADDLEACH[2]=10.
set ADDLEACH[3]=25.
set ADDLEACH[4]=45.
set ADDLEACH[5]=70.
set ADDLEACH[6]=100.

// The Duration until the Healthpoints fall down
set DURATION[1]=5.
set DURATION[2]=6.
set DURATION[3]=7.
set DURATION[4]=8.

// The Duration after which the origin Health is reached
set REGAINDURATION[1]=3.
set REGAINDURATION[2]=6.
set REGAINDURATION[3]=9.
set REGAINDURATION[4]=12.

// The Spells Area of Effect
set AOE[1]=550.
set AOE[2]=625.
set AOE[3]=700.
set AOE[4]=800.
endfunction


// DO NOT TOUCH ANYTHING BELOW THIS COMMENT, ALTHOUGH YOU KNOW WHAT YOU DO.
// HERE BEGINS THE SPELLS SCRIPT.


// This two functions save the Killer of a dying Unit, because we need to know the Killer.
// So we save the Killer in an Unit array. Now we use the AutoIndex so we can easily say that this unit
// was killed by that unit. We Refresh this value when the unit is removed from the map. That means when its bones vanish
// So you have to Change the DECAYTIME value to the time after which the bones get removed in your map.
private function ClearSavedKiller takes nothing returns nothing
local timer t=GetExpiredTimer()
set KILLERUNIT[GetTimerData(t)]=null
call ReleaseTimer(t)
endfunction

private function SaveKiller takes nothing returns boolean
local unit killer=GetKillingUnit()
local unit dying=GetDyingUnit()
set KILLERUNIT[GetUnitId(dying)]=killer
set TEMPTIMER=NewTimer()
call SetTimerData(TEMPTIMER,GetUnitId(dying))
call TimerStart(TEMPTIMER,DECAYTIME,false,function ClearSavedKiller)
set killer=null
set dying=null
return false
endfunction

private struct LeachedLife
unit leached
real hp
real health
real down
real reg
integer level
integer dmg
real duration
real factor
boolean done
integer counter
boolean active

static integer count=0
static timer Timer
static LeachedLife array Data

method clear takes nothing returns nothing
set .leached=null
set .hp=0
set .health=0
set .reg=0
set .level=0
set .down=0
set .duration=0
set .factor=0
set .done=false
set .counter=0
set .active=false
endmethod


// this method resets the MaxLife of a target over a specific time
static method ResetLife takes nothing returns nothing
local integer int=0
local LeachedLife data
loop
exitwhen int==.count
set data=.Data[int]
if data.active then
if not data.done and data.duration>0 then
set data.duration=data.duration-TIMERPERIOD
elseif not data.done and data.duration<=0 then
set data.done=true
set data.duration=REGAINDURATION[data.level]
set data.reg=data.hp/data.duration * TIMERPERIOD
set data.down=R2I(RealRounding(data.reg+0.5))*data.duration / TIMERPERIOD
set data.down=data.hp-data.down
if data.reg<1 then
set data.reg=1
endif
elseif data.done and data.duration>0 then
call DestroyEffectTimed(AddSpecialEffectTarget(HAUNTEFFECT,data.leached,HAUNTATTACH),1.5)
set data.duration=data.duration-TIMERPERIOD
set data.health=data.health-data.reg
call AddUnitMaxState(data.leached,UNIT_STATE_MAX_LIFE,(data.hp-data.health)*data.factor)
set data.hp=data.health
if AbsReal(data.down)>data.reg then
call AddUnitMaxState(data.leached,UNIT_STATE_MAX_LIFE,data.reg)
if data.down>0 then
set data.down=data.down-R2I(data.reg)
else
set data.down=data.down+R2I(data.reg)
endif
endif
if data.duration==0 then
call AddUnitMaxState(data.leached,UNIT_STATE_MAX_LIFE,-data.down)
endif
elseif data.done and data.duration<=0 then
call UnitAddDamage(data.leached,-data.dmg)
call data.clear()
endif
endif
set int=int+1
endloop
endmethod

static method create takes nothing returns LeachedLife
local LeachedLife data=LeachedLife.allocate()
if .count==0 then
set .Timer=NewTimer()
call TimerStart(.Timer,TIMERPERIOD,true,function LeachedLife.ResetLife)
endif
set .Data[.count]=data
set data.done=false
set data.active=true
set .count=.count+1
return data
endmethod

endstruct

private struct Spell
player caster
unit killer
integer level
integer counter
real factor
effect devil

static Spell array Data

method clear takes nothing returns nothing
call DestroyEffect(.devil)
set .devil=null
set .killer=null
set .caster=null
set .level=0
set .factor=0
set .counter=0
call .destroy()
endmethod

// Executed for each killer on Spellcast
method StartSpell takes nothing returns nothing
local LeachedLife data=LeachedLife.create()
set data.leached=.killer
set data.level=.level
set data.factor=.factor
set data.hp=HPLEACHED[.level]*.counter
set data.dmg=DMGLEACHED[.level]*.counter
call UnitAddDamage(.killer,data.dmg)
if .counter>6 then
set .counter=6
endif
set data.counter=.counter
set data.hp=data.hp+ADDLEACH[.counter]
set data.health=data.hp
set data.down=0
set data.reg=0
set data.duration=DURATION[.level]
call AddUnitMaxState(.killer,UNIT_STATE_MAX_LIFE,data.hp*.factor)
set data.factor=data.factor*-1
call .clear()
endmethod


static method create takes nothing returns Spell
return Spell.allocate()
endmethod

endstruct

// Executed on Spellcast.
// checks how much dead units are in a specific range and checks who is there killer.
// If the Killer is an enemy its MaxLife and damage is reduced otherwise it is increased.
// If the MORETHANONCE variable is set to true, dead units souls can be used more than once by this spell,
// otherwise they will only once give bonuses.
private function HauntingSpell takes nothing returns nothing
local unit caster=SpellEvent.CastingUnit
local real x=GetUnitX(caster)
local real y=GetUnitY(caster)
local unit killer
local integer int=0
local Spell data
call GroupUnitsInArea(ENUM_GROUP,x,y,AOE[GetUnitAbilityLevel(caster,ABILID)])
loop
set TEMPUNIT=FirstOfGroup(ENUM_GROUP)
exitwhen TEMPUNIT==null
if MORETHANONCE then
set ALLREADYUSED[GetUnitId(TEMPUNIT)]=false
endif
if GetWidgetLife(TEMPUNIT)<0.405 and not ALLREADYUSED[GetUnitId(TEMPUNIT)] then
call AddSpecialEffect(HAUNTEFFECT,GetUnitX(TEMPUNIT),GetUnitY(TEMPUNIT))
set killer=KILLERUNIT[GetUnitId(TEMPUNIT)]
if not IsUnitType(killer,UNIT_TYPE_DEAD) and not IsUnitType(killer,UNIT_TYPE_STRUCTURE) and IsUnitAlly(killer,GetOwningPlayer(caster)) then
set data=Spell.Data[GetUnitId(killer)]
set ALLREADYUSED[GetUnitId(TEMPUNIT)]=true
if data.counter==0 then
set data=Spell.create()
set data.caster=GetOwningPlayer(caster)
set data.killer=killer
set data.level=GetUnitAbilityLevel(caster,ABILID)
set data.factor=1
set data.counter=data.counter+1
set Spell.Data[GetUnitId(killer)]=data
call DestroyEffect(AddSpecialEffectTarget(SOULEFFECT,killer,HAUNTATTACH))
else
set data.counter=data.counter+1
endif
elseif not IsUnitType(killer,UNIT_TYPE_DEAD) and not IsUnitType(killer,UNIT_TYPE_STRUCTURE) and IsUnitEnemy(killer,GetOwningPlayer(caster)) then
set data=Spell.Data[GetUnitId(killer)]
set ALLREADYUSED[GetUnitId(TEMPUNIT)]=true
if data.counter==0 then
set data=Spell.create()
set data.killer=killer
set data.devil=AddSpecialEffectTarget(LEACHEFFECT,killer,LEACHATTACH)
set data.level=GetUnitAbilityLevel(caster,ABILID)
set data.factor=-1
set data.counter=data.counter+1
set Spell.Data[GetUnitId(killer)]=data
call DestroyEffect(AddSpecialEffectTarget(SOULEFFECT,killer,HAUNTATTACH))
else
set data.counter=data.counter+1
endif
endif
endif
call GroupRemoveUnit(ENUM_GROUP,TEMPUNIT)
endloop
call GroupClear(ENUM_GROUP)
loop
exitwhen int>8190
if Spell.Data[int].counter!=0 then
call Spell.Data[int].StartSpell()
endif
set int=int+1
endloop

set caster=null
set killer=null
endfunction

private function Init takes nothing returns nothing
local trigger trig=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(trig,Condition(function SaveKiller))
call RegisterSpellEffectResponse(ABILID,HauntingSpell)
call SetupSpellVariables()

call Preload(SOULEFFECT)
call Preload(HAUNTEFFECT)
call Preload(LEACHEFFECT)
call PreloadStart()
endfunction

endlibrary[/code]

Polarisation
The Electrician creates a static link between him and the target unit.The target unit and the Electrician are pulled to the mid between their positions. While they both get pulled the Caster is absorbing the enemies life and converting 40% of it to mana. As long as the target is pulled it is unable to move or attack. After they reached each other the Electrician keeps up the static link, fixing the enemy to his body making the target unable to move out of a specific radius from the Electricians position.

[jass="Polarisation"]library Polarisation initializer Init needs TimerUtils, UnitStatus, SpellEvent
//*******************************************************************************************\\
//*******************************************************************************************\\
//* Polarisation By Inferior *\\
//* *\\
//* v1.0 *\\
//* *\\
//* ************************* *\\
//* * Requirements: * *\\
//* * - JassNewGenPack * *\\
//* * - TimerUtils * *\\
//* * - UnitStatus * *\\
//* * - SpellEvent * *\\
//* ************************* *\\
//* *\\
//* *\\
//* *\\
//* How To Import: *\\
//* - Copy the Spell called 'Polarisation' into your map *\\
//* - Copy the whole Triggerscript into your map *\\
//* - Now change the values of the 'ABILID' to the Rawcode of the Mainability in your map *\\
//* *\\
//* Spelldescription: *\\
//* The Caster creates a static link between the target and himself absorbing the *\\
//* Targets life and converting it to Mana. The Target and the caster are pulled *\\
//* to the middle between their positions, while the target is disabled. The Health is *\\
//* absorbed during the pullphase. After they reached each other the Caster keeps up the *\\
//* static link between him and the target. The target is now unable to move farther *\\
//* away than the link allows him. *\\
//* *\\
//*******************************************************************************************\\
//*******************************************************************************************\\
// Globals Setting

globals
private constant integer ABILID = 'A009'
// The AbilityId of your Mainability

private constant string LIGHTNING = "CHIM"
// The Lightning Effect of the static link

private constant real DEFAULTHEIGHT = 50.
// The Height of the Lightning

private constant real DEFAULTDIST = 150.
// The Minimum Distance which has to be reached until the Pullphase is stopped

private constant real HEALTHTOMANA = 0.4
// The absorbed Health to Mana Factor

private constant real PULLPERIOD = 0.025
// The Timer Period of this Spell

// The 3 following variables are just important for the damage dealt in this spell
private constant attacktype ATKTYPE = ATTACK_TYPE_NORMAL
private constant damagetype DMGTYPE = DAMAGE_TYPE_UNIVERSAL
private constant weapontype WPNTYPE = WEAPON_TYPE_WHOKNOWS

private constant boolean PULLDOWNAIR = true
// Set this to true weather Air units shall be pulled down to the ground.

private constant boolean CASTERACTION = true
// Set this to false weather the caster should be disabled aswell duration the Pullphase

// For Descriptions for the following Variables see below
private real array PULLDURATION
private real array FIXDURATION
private real array MAXRANGE
private real array DAMAGEDEALT
endglobals



private function SetupSpellVariables takes nothing returns nothing
// The time in which the target is pulled in the direction of the caster
set PULLDURATION[1]=1.25
set PULLDURATION[2]=1.75
set PULLDURATION[3]=2.25
set PULLDURATION[4]=3.

// The time how long the target is fixed to the caster by the static link
set FIXDURATION[1]=1.5
set FIXDURATION[2]=2.
set FIXDURATION[3]=2.5
set FIXDURATION[4]=3.

// The Maximum range the target can move away from the caster with the static link
set MAXRANGE[1]=600.
set MAXRANGE[2]=500.
set MAXRANGE[3]=400.
set MAXRANGE[4]=300.

// The Damage dealt during the Pullphase
set DAMAGEDEALT[1]=60.
set DAMAGEDEALT[2]=125.
set DAMAGEDEALT[3]=185.
set DAMAGEDEALT[4]=250.
endfunction

// DO NOT TOUCH ANYTHING BELOW THIS COMMENT, ALTHOUGH YOU KNOW WHAT YOU DO.
// HERE BEGINS THE SPELLS SCRIPT.

private struct MagnetPull
unit caster
unit target
real castX
real castY
real targX
real targY
real duration
integer level
lightning light

static integer count=0
static timer MagnetTimer
static MagnetPull array Data
static boolean array done

// checks if the target is to far away from the caster
// stops the spell when the static link time expires
static method callback takes nothing returns nothing
local integer int=0
local MagnetPull data
local real dx
local real dy
loop
exitwhen int==.count
set data=.Data[int]
if data.duration>0 and not .done[int] and GetWidgetLife(data.target)>0.405 then
set dx=data.castX-data.targX
set dy=data.castY-data.targY
if SquareRoot(dx*dx+dy*dy)>MAXRANGE[data.level] then
set data.targX=data.castX-MAXRANGE[data.level]*Cos(Atan2(dy,dx))
set data.targY=data.castY-MAXRANGE[data.level]*Sin(Atan2(dy,dx))
call SetUnitX(data.target,data.targX)
call SetUnitY(data.target,data.targY)
else
set data.targX=GetUnitX(data.target)
set data.targY=GetUnitY(data.target)
endif
set data.castX=GetUnitX(data.caster)
set data.castY=GetUnitY(data.caster)
set data.duration=data.duration-PULLPERIOD
call MoveLightningEx(data.light,true,data.castX,data.castY,DEFAULTHEIGHT,data.targX,data.targY,DEFAULTHEIGHT)
elseif data.light!=null and not .done[int] or GetWidgetLife(data.target)<0.405 and not .done[int] then
call DestroyLightning(data.light)
if PULLDOWNAIR and IsUnitType(data.target,UNIT_TYPE_FLYING) then
call SetUnitFlyHeight(data.target,GetUnitDefaultFlyHeight(data.target),100)
endif
set .done[int]=true
call data.destroy()
endif
set int=int+1
endloop
endmethod


static method create takes nothing returns MagnetPull
local MagnetPull data=MagnetPull.allocate()
if .count==0 then
set .MagnetTimer=NewTimer()
call TimerStart(.MagnetTimer,PULLPERIOD,true,function MagnetPull.callback)
endif
set .Data[.count]=data
set .count=.count+1
return data
endmethod

endstruct

private struct PolarPull
unit caster
unit target
real castX
real castY
real targX
real targY
real velZ
real angle
real distance
real speed
integer level
lightning light

static integer count=0
static timer PullTimer
static PolarPull array Data
static boolean array done

// pulls the target and the caster to they position between each other
// Flying Units are pulled down if PULLDOWNAIR=true
// When the Pullphase is over Flying units regain their default height
static method callback takes nothing returns nothing
local integer int=0
local PolarPull data
local MagnetPull dat
loop
exitwhen int==.count
set data=.Data[int]
if data.distance>DEFAULTDIST and not .done[int] and GetWidgetLife(data.target)>0.405 then
set data.castX=data.castX-data.speed*Cos(data.angle)
set data.castY=data.castY-data.speed*Sin(data.angle)
call SetUnitX(data.caster,data.castX)
call SetUnitY(data.caster,data.castY)
set data.targX=data.targX+data.speed*Cos(data.angle)
set data.targY=data.targY+data.speed*Sin(data.angle)
call SetUnitX(data.target,data.targX)
call SetUnitY(data.target,data.targY)
call UnitDamageTarget(data.caster,data.target,(DAMAGEDEALT[data.level]/PULLDURATION[data.level])*PULLPERIOD,true,false,ATKTYPE,DMGTYPE,WPNTYPE)
call SetUnitState(data.caster,UNIT_STATE_MANA,GetUnitState(data.caster,UNIT_STATE_MANA)+(DAMAGEDEALT[data.level]*HEALTHTOMANA/PULLDURATION[data.level])*PULLPERIOD)
if PULLDOWNAIR and IsUnitType(data.target,UNIT_TYPE_FLYING) then
call SetUnitFlyHeight(data.target,GetUnitFlyHeight(data.target)-data.velZ,0)
call MoveLightningEx(data.light,true,data.castX,data.castY,DEFAULTHEIGHT,data.targX,data.targY,GetUnitFlyHeight(data.target)+DEFAULTHEIGHT)
else
call MoveLightningEx(data.light,true,data.castX,data.castY,DEFAULTHEIGHT,data.targX,data.targY,DEFAULTHEIGHT)
endif
set data.distance=data.distance-2*data.speed
elseif data.distance<=DEFAULTDIST and not .done[int] and GetWidgetLife(data.target)>0.405 then
set dat=MagnetPull.create()
set dat.caster=data.caster
set dat.target=data.target
set dat.castX=data.castX
set dat.castY=data.castY
set dat.targX=data.targX
set dat.targY=data.targY
set dat.light=data.light
set dat.level=data.level
set dat.duration=FIXDURATION[data.level]
if not CASTERACTION then
call DisableUnit(data.caster,false)
endif
call DisableUnit(data.target,false)
set .done[int]=true
call data.destroy()
elseif GetWidgetLife(data.target)<0.405 and not .done[int] then
call DestroyLightning(data.light)
if not CASTERACTION then
call DisableUnit(data.caster,false)
endif
call DisableUnit(data.target,false)
set .done[int]=true
call data.destroy()
endif
set int=int+1
endloop
endmethod

static method create takes nothing returns PolarPull
local PolarPull data=PolarPull.allocate()
if .count==0 then
set .PullTimer=NewTimer()
call TimerStart(.PullTimer,PULLPERIOD,true,function PolarPull.callback)
endif
set .Data[.count]=data
set .count=.count+1
return data
endmethod

endstruct

// Executed on Spellcast
// Disables the caster when CASTERACTION=false
private function PolarisationSpell takes nothing returns nothing
local unit caster=SpellEvent.CastingUnit
local unit target=SpellEvent.TargetUnit
local PolarPull data=PolarPull.create()
local real dx=0.
local real dy=0.
set data.caster=caster
set data.target=target
set data.castX=GetUnitX(caster)
set data.castY=GetUnitY(caster)
set data.targX=GetUnitX(target)
set data.targY=GetUnitY(target)
set dx=data.castX-data.targX
set dy=data.castY-data.targY
set data.angle=Atan2(dy,dx)
set data.distance=SquareRoot(dx*dx+dy*dy)
set data.level=GetUnitAbilityLevel(caster,ABILID)
set data.speed=(data.distance/PULLDURATION[data.level])*PULLPERIOD*0.5
call DisableUnit(data.target,true)
if not CASTERACTION then
call DisableUnit(data.caster,true)
endif
call SetUnitAnimation(data.caster,"spell channel")
if PULLDOWNAIR and IsUnitType(target,UNIT_TYPE_FLYING) then
set data.velZ=(GetUnitFlyHeight(target)/PULLDURATION[data.level])*PULLPERIOD
set data.light=AddLightningEx(LIGHTNING,true,data.castX,data.castY,DEFAULTHEIGHT,data.targX,data.targY,GetUnitFlyHeight(target)+DEFAULTHEIGHT)
else
set data.light=AddLightningEx(LIGHTNING,true,data.castX,data.castY,DEFAULTHEIGHT,data.targX,data.targY,DEFAULTHEIGHT)
set data.velZ=0
endif

set caster=null
set target=null
endfunction

private function Init takes nothing returns nothing
call RegisterSpellEffectResponse(ABILID,PolarisationSpell)
call SetupSpellVariables()
endfunction

endlibrary[/code]

Magnetic Field
The Electrician releases the electric Energy in his body and creates a strong Magnetic Field arround him. All enemy units nearby are slowed and slightly pulled to the source of the Field. If an enemy unit contacts with the Core of the Field an discharge will shock it. The Shock is reducing its armor by 50%, dealing 5% of the units current Health and pushing it away from the Core. To keep up the Magnetic Field the caster is forced to use his mana and for each discharge the Electrician is losing some mana.

[jass="Magnetic Field"]library MagneticField initializer Init needs TimerUtils, GroupUtils, LastOrder, ArmorUtils, StatusModifier, Rounding, BoundSentinel, UnitStatus, IsUnitSpellResistant, TimedHandles, xepreload, xedamage
//********************************************************************************************\\
//********************************************************************************************\\
//* Magnetic Field By Inferior *\\
//* *\\
//* v1.1 *\\
//* *\\
//* ************************** *\\
//* * Requirements: * *\\
//* * - JassNewGenPack * *\\
//* * - TimerUtils * *\\
//* * - GroupUtils * *\\
//* * - ArmorUtils * *\\
//* * - StatusModifier * *\\
//* * - Rounding * *\\
//* * - BoundSentinel * *\\
//* * - UnitStatus * *\\
//* * - IsUnitSpellResistant * *\\
//* * - TimedHandles * *\\
//* * - xepreload * *\\
//* * - xedamage * *\\
//* ************************** *\\
//* *\\
//* *\\
//* *\\
//* How To Import: *\\
//* - Copy the Spell called 'Magnetic Field' and 'Magnet Aura(Slow)' into your map and *\\
//* create 2 buffs like those in the Object Editor with 'Magnetic Field' in its name *\\
//* - Copy the whole Triggerscript into your map *\\
//* - Now change the values of the 'ABILID' and 'SLOWID' to the RawCodes in your map *\\
//* *\\
//* Spelldescription: *\\
//* The Caster fills the air arround him with strong static energy, creating a strong *\\
//* magnetic Field arround his body that slows every enemy unit and slightly pulls them *\\
//* to the caster. If an enemy unit comes in contact with the caster, the unit gets *\\
//* shocked, losing an specific amount of its current Life and half of its armor, and *\\
//* pushed away. For each contact the caster loses Mana and passively burns Mana over *\\
//* Time as long as the Magnetic Field is active. *\\
//* *\\
//********************************************************************************************\\
//********************************************************************************************\\
// Globals Setting

globals
private constant integer ABILID = 'A00A'
// The AbilityId of your Mainability

private constant integer SLOWID = 'A00B'
// The AbilityId of your Slowing Aura

private constant integer ORDERID = 852177
// The OrderId to activate the Spell

private constant integer UNORDERID = 852178
// The OrderId to deactive the Spell

private constant real MAGNETPERIOD = .025
// The Timerperiod of this Spell

private constant real MINDISTANCE = 120.
// The Minimum Distance between the caster and an enemy until it gets pushed away

private constant real SHOCKDAMAGE = 0.05
// The percentage of Life an enemy loses on contact

private constant real ARMORDOWNTIME = 5.
// The Time the enemy loses half of its armor

private constant string IMPACTEFFECT = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl"
private constant string IMPACTATTACH = "origin"
// The Effect on impact

// For Descriptions for the following Variables see below
private real array IMPACTMANABURN
private real array BUFFERMANA
private real array PUSHDISTANCE
private real array PULLSPEED
private real array PULLRADIUS

// Do not change any of the following variables
private unit TEMPUNIT
private xedamage dmgOptions
endglobals

private function MagnetFilter takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(TEMPUNIT)) and not IsUnitSpellResistant(GetFilterUnit()) and not IsUnitSpellImmune(GetFilterUnit()) and GetWidgetLife(GetFilterUnit())>0.405
endfunction

private function SetupSpellVariables takes nothing returns nothing
// The Mana burnt for each Contact with an enemy
set IMPACTMANABURN[1]=25.
set IMPACTMANABURN[2]=20.
set IMPACTMANABURN[3]=15.
set IMPACTMANABURN[4]=10.

// The Speed with which the enemies are pulled to the caster
set PULLSPEED[1]=.2
set PULLSPEED[2]=.3
set PULLSPEED[3]=.4
set PULLSPEED[4]=.5

// The Area of Effect of the Magnetic Field
set PULLRADIUS[1]=400.
set PULLRADIUS[2]=550.
set PULLRADIUS[3]=650.
set PULLRADIUS[4]=800.

// The Distance the enemies are pushed away after contact
set PUSHDISTANCE[1]=300.
set PUSHDISTANCE[2]=350.
set PUSHDISTANCE[3]=400.
set PUSHDISTANCE[4]=450.
endfunction

private function ConfigureDamageOptions takes nothing returns nothing
set dmgOptions=xedamage.create()
set dmgOptions.atype=ATTACK_TYPE_CHAOS
set dmgOptions.dtype=DAMAGE_TYPE_UNIVERSAL
set dmgOptions.wtype=WEAPON_TYPE_WHOKNOWS
endfunction

// DO NOT TOUCH ANYTHING BELOW THIS COMMENT, ALTHOUGH YOU KNOW WHAT YOU DO.
// HERE BEGINS THE SPELLS SCRIPT.

private struct MagnetPush
unit caster
unit target
real speed
real x
real y
real angle
real distance
integer dec
integer level

static integer count=0
static timer PushTimer
static MagnetPush array Data
static boolean array done

// pushes the enemies away after contact
// Disarms them during the Pushphase
static method callback takes nothing returns nothing
local integer int=0
local MagnetPush data
loop
exitwhen int==.count
set data=.Data[int]
if not .done[int] then
if data.distance>0 then
set data.x=data.x-data.speed*Cos(data.angle)
set data.y=data.y-data.speed*Sin(data.angle)
call SetUnitX(data.target,data.x)
call SetUnitY(data.target,data.y)
set data.distance=data.distance-data.speed
else
set .done[int]=true
call DisarmUnit(data.target,false)
call data.destroy()
endif
endif
set int=int+1
endloop
endmethod

static method create takes nothing returns MagnetPush
local MagnetPush data=MagnetPush.allocate()
if .count==0 then
set .PushTimer=NewTimer()
call TimerStart(.PushTimer,MAGNETPERIOD,true,function MagnetPush.callback)
endif
set .done[.count]=false
set .Data[.count]=data
set .count=.count+1
return data
endmethod

endstruct

private struct MagnetField
unit caster
real speed
integer level

static integer count=0
static integer array Index
static timer MagnetTimer
static MagnetField array Data
static boolean array done

// Slightly pulls enemies in range to the caster
// if they are to near to the caster they get pushed away, disarmed, and lose half of their Armor
method operator pos= takes unit a returns nothing
local real x=GetUnitX(a)
local real y=GetUnitY(a)
local real dx=GetUnitX(.caster)-x
local real dy=GetUnitY(.caster)-y
local real angle=Atan2(dy,dx)
local MagnetPush data
set x=x+.speed*Cos(angle)
set y=y+.speed*Sin(angle)
call SetUnitX(a,x)
call SetUnitY(a,y)
if SquareRoot(dx*dx+dy*dy)<MINDISTANCE then
set data=MagnetPush.create()
set data.caster=.caster
set data.target=a
set data.level=.level
set data.x=x
set data.y=y
set data.angle=angle
set data.distance=PUSHDISTANCE[data.level]
set data.speed=25
call DisarmUnit(a,true)
call dmgOptions.damageTarget(data.caster,data.target,GetUnitState(data.target,UNIT_STATE_LIFE)*SHOCKDAMAGE)
call UnitAddArmorTimed(data.target,-RealRounding(GetUnitArmor(data.target)/2),ARMORDOWNTIME)
call DestroyEffectTimed(AddSpecialEffectTarget(IMPACTEFFECT,a,IMPACTATTACH),1.5)
call SetUnitState(data.caster,UNIT_STATE_MANA,GetUnitState(data.caster,UNIT_STATE_MANA)-IMPACTMANABURN[data.level])
endif
endmethod

static method callback takes nothing returns nothing
local integer int=0
local unit a=null
local MagnetField data
loop
exitwhen int==.count
set data=.Data[int]
if not .done[int] then
set TEMPUNIT=data.caster
call GroupEnumUnitsInArea(ENUM_GROUP,GetUnitX(data.caster),GetUnitY(data.caster),PULLRADIUS[data.level],Condition(function MagnetFilter))
loop
set a=FirstOfGroup(ENUM_GROUP)
exitwhen a==null
set data.pos=a
call GroupRemoveUnit(ENUM_GROUP,a)
endloop
call GroupClear(ENUM_GROUP)
set TEMPUNIT=null
endif
set int=int+1
if GetUnitState(data.caster,UNIT_STATE_MANA)<=0 then
set .done[.Index[GetUnitId(data.caster)]]=true
call UnitRemoveAbility(data.caster,SLOWID)
call data.destroy()
endif
endloop
set a=null
endmethod

static method create takes unit u returns MagnetField
local MagnetField data=MagnetField.allocate()
if .count==0 then
set .MagnetTimer=NewTimer()
call TimerStart(.MagnetTimer,MAGNETPERIOD,true,function MagnetField.callback)
endif
set .Data[.count]=data
set .Index[GetUnitId(u)]=.count
set .done[.count]=false
set .count=.count+1
return data
endmethod

endstruct

// Executed on Spellcast
private function MagnetSpell takes nothing returns boolean
local unit caster=GetOrderedUnit()
local MagnetField data
if GetLastOrderId(caster)==ORDERID then
set data=MagnetField.create(caster)
set data.caster=caster
set data.level=GetUnitAbilityLevel(caster,ABILID)
set data.speed=PULLSPEED[data.level]
call UnitAddAbility(data.caster,SLOWID)
call SetUnitAbilityLevel(data.caster,SLOWID,data.level)
call UnitAddAbility(data.caster,'A00C')
call SetUnitAbilityLevel(data.caster,'A00C',2)
elseif GetLastOrderId(caster)==UNORDERID then
set data=MagnetField.Data[MagnetField.Index[GetUnitId(caster)]]
set MagnetField.done[MagnetField.Index[GetUnitId(caster)]]=true
call UnitRemoveAbility(data.caster,SLOWID)
call data.destroy()
endif

set caster=null

return false
endfunction

private function Init takes nothing returns nothing
local trigger trig=CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(trig,EVENT_PLAYER_UNIT_ISSUED_ORDER)
call TriggerAddCondition(trig,Condition(function MagnetSpell))

call ConfigureDamageOptions()
call SetupSpellVariables()
call XE_PreloadAbility(SLOWID)
call Preload(IMPACTEFFECT)
call PreloadStart()

set trig=null
endfunction

endlibrary[/code]

Robot Arms
The Electrician uses his technical knowlegdes to create 3 Robot Arms that act on their own. If the Arm detects an enemy, it moves to that enemy and knocks it out on contact, dealing damage and absorbing the enemies Life. If the Electrician moves out of the enemies Range the Arm moves back. After every action, the Arms have to move back into their default Position.

[jass="Robot Arms"]library RobotArms initializer Init needs Table, GroupUtils, TimerUtils, ABuff, ABuffHeroSkill, BoundSentinel, UnitStatus, IsUnitSpellResistant, xebasic, xefx, xedamage, Rounding
//********************************************************************************************\\
//********************************************************************************************\\
//* Robot Arms By Inferior *\\
//* *\\
//* v1.3 *\\
//* *\\
//* ************************** *\\
//* * Requirements: * *\\
//* * - JassNewGenPack * *\\
//* * - Table * *\\
//* * - GroupUtils * *\\
//* * - TimerUtils * *\\
//* * - ABuff * *\\
//* * - ABuffHeroSkill * *\\
//* * - BoundSentinel * *\\
//* * - UnitStatus * *\\
//* * - IsUnitSpellResistant * *\\
//* * - xebasic * *\\
//* * - xefx * *\\
//* * - xedamage * *\\
//* * - Rounding * *\\
//* ************************** *\\
//* *\\
//* *\\
//* *\\
//* How To Import: *\\
//* - Copy the Spell called 'Robot Arms', the dummy called facingdummy into your map *\\
//* and import the "fdummy.mdl" and "RoboticArmsHand" included in this map into yours *\\
//* - Copy the whole Triggerscript into your map *\\
//* - Change the model of your new Dummymodel and change the RawCodes of the ABILID *\\
//* and CHAINID to the Rawcodes in your map *\\
//* *\\
//* Spelldescription: *\\
//* The Electrician uses his technical knowlegdes to create 3 Robot Arms that act on *\\
//* their own. If the Arm detects an enemy, it moves to that enemy and knocks it out on *\\
//* contact, dealing absorbing the enemies Life. If the Electrician moves out of the *\\
//* enemies Range the Arm moves back. After every action, the Arms have to move back into *\\
//* their default Position. *\\
//* *\\
//********************************************************************************************\\
//********************************************************************************************\\
// Globals Setting

globals
private constant integer ABILID = 'A003'
// The AbilityId of your Main Ability

private constant integer CHAINID = 'e001'
// The Rawcode for your dummy. This dummy needs a specific model included in that map. You need this model if you want the full eyecandy

private constant integer MAXARMS = 3
// The Number of Arms. The more arms you use, the higher is the performance lack.

private constant integer CHAINPARTS = 16
// The Parts of Dummies for the Chain of the arm. Do not forget to change the .chain array in the struct.

private constant real CHAINSIZE = 1.3
// The Size of the Chain parts

private constant real ARMSPEED = 12.
// The Speed an arm is moving back or to a target

private constant real DMGTOHEALTH = 1.
// Conversionfactor from damage dealt to absorbed Health. This value is only active if USEABSORB = true

private constant real HEALTHFACTOR = 0.05
// The Factor of the targets MaxHealth that is absorbed. This value is only active if USEABSORB = true

private constant real DEALTDAMAGE = 1.5
// Damage dealt per Period: 0.025*40=1 -> 40*1.5= 60 damage dealt per second. This value is only active if USEABSORB = false

private constant real DETECTIONRADIUS = 500.
// The Radius in which an Arm detects an enemy

private constant real DEFAULTOFFSET = 250.
// The Default offset from the Electricians Position. Also defines the DefaultPosition where the arms move back to

private constant real DEFAULTHEIGHT = 75.
// The Default Height of the Chains

private constant real MAXHEIGHT = 350.
// The Maximum Height that can be reached by the arm

private constant real MINHEIGHT = 150.
// The Minimum Height that can be reached by the arm

private constant real MAXOFFSET = 750.
// The Maximum Range an arm can bent

private constant string CHAINMDL = "RoboticArmsHand.mdx"
// The Chains Model effect

private constant string CHAINATTACH = "origin"
// DO NOT CHANGE THIS (the dummy model has no other attachement)

private constant string CHAINHAND = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
// The End of the Arms effect

private constant string ABSORBMDL = "Abilities\\Spells\\Undead\\AbsorbMana\\AbsorbManaBirthMissile.mdl"
// The Effect that is used if USEABSORB = true

private constant boolean USEABSORB = true
// This value defines the type of damage that is dealt. if USEABSORB = false then the damage is dealt periodicly.
// For more informations check the .OnAbsorb method.

private real array SHOCKTIME
// The Value that defines the time an arm deals damage to a target.

// DO NOT CHANGE THIS. Defines the Damage types.
private xedamage dmgOptions
// DO NOT CHANGE THIS.
private unit TEMPUNIT
private constant integer StunCode = 'BPSE'
endglobals

private keyword Arm


private function SetupSpellVariables takes nothing returns nothing
set SHOCKTIME[1]=1.5
set SHOCKTIME[2]=2.
set SHOCKTIME[3]=2.75
set SHOCKTIME[4]=3.5
endfunction

private function ConfigureDamageOptions takes nothing returns nothing
set dmgOptions=xedamage.create()
set dmgOptions.atype=ATTACK_TYPE_CHAOS
set dmgOptions.dtype=DAMAGE_TYPE_UNIVERSAL
set dmgOptions.wtype=WEAPON_TYPE_WHOKNOWS
endfunction

private function UnitFilter takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(TEMPUNIT)) and IsUnitType(GetFilterUnit(),UNIT_TYPE_GROUND) and not IsUnitSpellImmune(GetFilterUnit()) and not Arm.stunned[GetUnitId(GetFilterUnit())] and GetWidgetLife(GetFilterUnit())>0.405 and GetUnitTypeId(GetFilterUnit())!=CHAINID
endfunction

// DO NOT TOUCH ANYTHING BELOW THIS COMMENT, ALTHOUGH YOU KNOW WHAT YOU DO.
// HERE BEGINS THE SPELLS SCRIPT.

private function AngleConv takes real angle returns real
local integer factor=0
if angle>=360 then
set factor=R2I(angle/360)
return angle-360*factor
elseif angle<0 then
set factor=R2I(AbsReal(angle)/360)
return angle+360*(factor+1)
endif
return angle
endfunction

private function HeightCalc takes real d returns real
return ((MINHEIGHT-MAXHEIGHT+DEFAULTHEIGHT)/MAXOFFSET)*d+MAXHEIGHT-DEFAULTHEIGHT
endfunction

// This function defines the Arm parts height
private function ArmHeight takes real d, integer z returns real
return HeightCalc(d)*Sin( bj_PI/CHAINPARTS * z ) + DEFAULTHEIGHT
endfunction

// Defines the Arm parts Z-Facing.
// THIS FUNCTION STILL NEEDS PERFECTION. IM STILL SEARCHING FOR A BETTER FORMULA
private function UnitZFacing takes unit u, real d, integer z returns nothing
local real s=(MAXHEIGHT-(MAXHEIGHT-MINHEIGHT)/2)/HeightCalc(d)
local real a=s*HeightCalc(d) * bj_PI/CHAINPARTS * Cos(bj_PI/CHAINPARTS * z)
local boolean b=a<0
local integer v=RealRounding(AbsReal(a))
if not b then
call SetUnitAnimationByIndex(u,v)
else
call SetUnitAnimationByIndex(u,252-v)
endif
endfunction


// Defines an arms Defaultpositions Facing
private constant function Angles takes integer int returns real
return 360./MAXARMS * int
endfunction


private struct Arm
unit caster
unit array chain[17] // Add here the value of the CHAINPARTS + 1
unit hand
unit targ
integer pos=CHAINPARTS
integer phase=0
// 0=search after an enemy
// 1=haunt an enemy
// 2=shock an enemy
// 3=out of range
real angle
real startangle
real offset
real timed

xefx absorb
// this effect is only used if USEABSORB = true

readonly integer index

static integer count=0
static boolean array done
static boolean array stunned
static timer ArmTimer
static Arm array Data
static Table array ArmTable
static Table array HTable

// You may change this function if you want.
method OnAbsorb takes nothing returns nothing
if USEABSORB then
// the time between these too actions is proportional to the number of Chainparts
if .pos==CHAINPARTS then
// this is executed when the Absorption effect runs off the target.
call dmgOptions.damageTarget(.caster,.targ,GetUnitState(.targ,UNIT_STATE_MAX_LIFE)*HEALTHFACTOR)
elseif .pos<0 then
// this is executed when the Absorption effect reaches the caster
call SetWidgetLife(.caster,GetWidgetLife(.caster)+GetUnitState(.targ,UNIT_STATE_MAX_LIFE)*HEALTHFACTOR*DMGTOHEALTH)
endif
else
// this is executed when USEABSORB=false
// this is executed once per Timerperiod: every 0.025 seconds
call dmgOptions.damageTarget(.caster,.targ,DEALTDAMAGE)
endif
endmethod

// destroys the Arm on Casters death or Unlearning of the MainAbility
method KillArm takes nothing returns boolean
local integer int=0
loop
exitwhen int>CHAINPARTS
call KillUnit(.chain[int])
set int=int+1
endloop
call KillUnit(.hand)
return true
endmethod

// sets an Arms Position and the parts Height and facing
method PosNormal takes real offset returns nothing
local real dx=GetUnitX(.hand)-GetUnitX(.caster)
local real dy=GetUnitY(.hand)-GetUnitY(.caster)
local integer i=0
local real step=0.
set .offset=offset
set .angle=Atan2(dy,dx)
if .offset>MAXOFFSET then
set .offset=MAXOFFSET
endif
set step=.offset/CHAINPARTS
loop
exitwhen i>CHAINPARTS
call SetUnitX(.chain,GetUnitX(.caster)+step*i*Cos(.angle))
call SetUnitY(.chain,GetUnitY(.caster)+step*i*Sin(.angle))
call SetUnitFacing(.chain,.angle*180/bj_PI)
call SetUnitFlyHeight(.chain,ArmHeight(.offset,i),0)
set i=i+1
endloop
call SetUnitX(.hand,GetUnitX(.chain[CHAINPARTS]))
call SetUnitY(.hand,GetUnitY(.chain[CHAINPARTS]))
call SetUnitFlyHeight(.hand,GetUnitFlyHeight(.chain[CHAINPARTS]),0)
endmethod

// Creates an arm on Casters revive or Learning of the Ability
method createChain takes nothing returns nothing
local real x=GetUnitX(.caster)
local real y=GetUnitY(.caster)
local integer count=CHAINPARTS
local real dx=.offset/count
local integer int=0
loop
exitwhen int>count
set .chain[int]=CreateUnit(GetOwningPlayer(.caster),CHAINID,x,y,.angle)
call SetUnitScale(.chain[int],CHAINSIZE,CHAINSIZE,CHAINSIZE)
call SetUnitTimeScale(.chain[int],0.)
call UnitZFacing(.chain[int],.offset,int)
call UnitAddAbility(.chain[int],XE_HEIGHT_ENABLER)
call UnitRemoveAbility(.chain[int],XE_HEIGHT_ENABLER)
call SetUnitX(.chain[int],x+dx*Cos(.angle*bj_PI/180)*int)
call SetUnitY(.chain[int],y+dx*Sin(.angle*bj_PI/180)*int)
call SetUnitFlyHeight(.chain[int],ArmHeight(.offset,int),0)
call AddSpecialEffectTarget(CHAINMDL,.chain[int],CHAINATTACH)
set int=int+1
endloop
set .hand=CreateUnit(GetOwningPlayer(.caster),CHAINID,GetUnitX(.chain[CHAINPARTS]),GetUnitY(.chain[CHAINPARTS]),.angle)
call UnitAddAbility(.hand,XE_HEIGHT_ENABLER)
call UnitRemoveAbility(.hand,XE_HEIGHT_ENABLER)
call SetUnitFlyHeight(.hand,GetUnitFlyHeight(.chain[CHAINPARTS]),0)
call AddSpecialEffectTarget(CHAINHAND,.hand,CHAINATTACH)
endmethod

static method callback takes nothing returns nothing
local integer int=0
local Arm data
local group g
local unit a
local real dx
local real dy
loop
exitwhen int==Arm.count
if not Arm.done[int] then
set data=Arm.Data[int]

// Searchs possible targets for the Arm
if data.phase==0 then

set TEMPUNIT=data.caster
call GroupEnumUnitsInArea(ENUM_GROUP,GetUnitX(data.hand),GetUnitY(data.hand),DETECTIONRADIUS,Condition(function UnitFilter))
set data.targ=GroupPickRandomUnit(ENUM_GROUP)
set TEMPUNIT=null

call GroupClear(ENUM_GROUP)

if data.targ!=null then
set data.phase=1
else
set data.angle=GetUnitFacing(data.caster)+data.startangle
call SetUnitX(data.hand,GetUnitX(data.caster)+DEFAULTOFFSET*Cos(data.angle*bj_DEGTORAD))
call SetUnitY(data.hand,GetUnitY(data.caster)+DEFAULTOFFSET*Sin(data.angle*bj_DEGTORAD))
call data.PosNormal(DEFAULTOFFSET)
endif

// Moves the Arm to an target. If the target is out of range the arm moves back into DefaultPosition
elseif data.phase==1 then

set dx=GetUnitX(data.targ)-GetUnitX(data.hand)
set dy=GetUnitY(data.targ)-GetUnitY(data.hand)
set data.angle=Atan2(dy,dx)
call SetUnitX(data.hand,GetUnitX(data.hand)+ARMSPEED*Cos(data.angle))
call SetUnitY(data.hand,GetUnitY(data.hand)+ARMSPEED*Sin(data.angle))
set dx=GetUnitX(data.hand)-GetUnitX(data.caster)
set dy=GetUnitY(data.hand)-GetUnitY(data.caster)
set data.angle=Atan2(dy,dx)
set data.offset=SquareRoot(dx*dx+dy*dy)
call data.PosNormal(data.offset)

if data.offset>=MAXOFFSET then
set data.phase=3
endif

if Arm.stunned[GetUnitId(data.targ)] then
set data.phase=3
else
if IsUnitInRange(data.hand,data.targ,5.0) then
call SetUnitX(data.hand,GetUnitX(data.targ))
call SetUnitY(data.hand,GetUnitY(data.targ))
set dx=GetUnitX(data.hand)-GetUnitX(data.caster)
set dy=GetUnitY(data.hand)-GetUnitY(data.caster)
set data.angle=Atan2(dy,dx)
set data.offset=SquareRoot(dx*dx+dy*dy)
if data.offset>MAXOFFSET then
set data.phase=3
else
call data.PosNormal(data.offset)
call StunUnit(data.targ,true)
set Arm.stunned[GetUnitId(data.targ)]=true
set data.phase=2
if USEABSORB then
set data.absorb=xefx.create(GetUnitX(data.chain[.chain.size]),GetUnitY(data.chain[.chain.size]),GetUnitFacing(data.chain[.chain.size]))
set data.absorb.fxpath=ABSORBMDL
endif
endif
endif
endif

// If the arm reached an enemy, the enemy is stunned and is sucked hp or damaged based on the USEABSORB value.
// The Arm moves back to default Position if the target moves out of range, or the Buff on the target is removed.
elseif data.phase==2 then
set dx=GetUnitX(data.targ)-GetUnitX(data.caster)
set dy=GetUnitY(data.targ)-GetUnitY(data.caster)
set data.angle=Atan2(dy,dx)
set data.offset=SquareRoot(dx*dx+dy*dy)

if data.offset>=MAXOFFSET then
set data.phase=3
call StunUnit(data.targ,false)
set Arm.stunned[GetUnitId(data.targ)]=false
set data.timed=0
set data.pos=CHAINPARTS
set data.targ=null
if USEABSORB then
call data.absorb.hiddenDestroy()
endif
else
call SetUnitX(data.hand,GetUnitX(data.targ))
call SetUnitY(data.hand,GetUnitY(data.targ))
call data.PosNormal(data.offset)
set data.timed=data.timed+0.025
if USEABSORB then
set data.absorb.x=GetUnitX(data.chain[data.pos])
set data.absorb.y=GetUnitY(data.chain[data.pos])
set data.absorb.z=GetUnitFlyHeight(data.chain[data.pos])
if data.pos<0 then
call data.absorb.hiddenDestroy()
call data.OnAbsorb()
set data.pos=CHAINPARTS
set data.absorb=xefx.create(GetUnitX(data.chain[.chain.size]),GetUnitY(data.chain[.chain.size]),GetUnitFacing(data.chain[.chain.size]))
set data.absorb.fxpath=ABSORBMDL
endif
if data.pos==CHAINPARTS then
call data.OnAbsorb()
endif
else
call data.OnAbsorb()
endif
set data.pos=data.pos-1
if data.timed>SHOCKTIME[GetUnitAbilityLevel(data.caster,ABILID)] or GetWidgetLife(data.targ)<0.405 then
set data.phase=3
call StunUnit(data.targ,false)
set Arm.stunned[GetUnitId(data.targ)]=false
set data.timed=0
set data.pos=CHAINPARTS
if USEABSORB then
call data.absorb.hiddenDestroy()
endif
else
call data.PosNormal(data.offset)
endif
endif

// Moves the Arm back to DefaultPosition
elseif data.phase==3 then

set dx=GetUnitX(data.hand)-GetUnitX(data.caster)-DEFAULTOFFSET*Cos((GetUnitFacing(data.caster)+data.startangle)*bj_DEGTORAD)
set dy=GetUnitY(data.hand)-GetUnitY(data.caster)-DEFAULTOFFSET*Sin((GetUnitFacing(data.caster)+data.startangle)*bj_DEGTORAD)
set data.angle=Atan2(dy,dx)
call SetUnitX(data.hand,GetUnitX(data.hand)-ARMSPEED*Cos(data.angle))
call SetUnitY(data.hand,GetUnitY(data.hand)-ARMSPEED*Sin(data.angle))
set dx=GetUnitX(data.caster)+DEFAULTOFFSET*Cos((GetUnitFacing(data.caster)+data.startangle)*bj_DEGTORAD)
set dy=GetUnitY(data.caster)+DEFAULTOFFSET*Sin((GetUnitFacing(data.caster)+data.startangle)*bj_DEGTORAD)
if IsUnitInRangeXY(data.hand,dx,dy,10) then
set data.phase=0
set data.targ=null
set data.offset=DEFAULTOFFSET
else
set dx=GetUnitX(data.hand)-GetUnitX(data.caster)
set dy=GetUnitY(data.hand)-GetUnitY(data.caster)
set data.angle=Atan2(dy,dx)
set data.offset=SquareRoot(dx*dx+dy*dy)
call data.PosNormal(data.offset)
endif
endif
endif
set int=int+1
endloop
endmethod

static method create takes nothing returns Arm
local Arm data=Arm.allocate()
if Arm.count==0 then
set Arm.ArmTimer=NewTimer()
call TimerStart(Arm.ArmTimer,0.025,true,function Arm.callback)
endif
set data.index=Arm.count
set Arm.Data[Arm.count]=data
set Arm.done[Arm.count]=false
set Arm.count=Arm.count+1
return data
endmethod
endstruct

globals
private aBuffType buffType = 0
endglobals

private function AbilLearn takes aBuff buffType returns nothing
local integer int=0
local Arm data
if Arm.HTable[GetHandleId(buffType.caster)][int]==0 then
if Arm.ArmTable[buffType]==0 then
set Arm.ArmTable[buffType]=Table.create()
endif
loop
exitwhen int==MAXARMS
set Arm.HTable[GetHandleId(buffType.caster)][int]=Table.create()
set data=Arm.create()
set Arm.ArmTable[buffType][int]=data
set data.caster=buffType.caster
set data.angle=GetUnitFacing(data.caster)+Angles(int)
set data.startangle=Angles(int)
set data.offset=DEFAULTOFFSET
set Arm.HTable[GetHandleId(buffType.caster)][int]=data
call data.createChain()
set int=int+1
endloop
else
loop
exitwhen int==MAXARMS
set data=Arm.HTable[GetHandleId(buffType.caster)][int]
set Arm.done[data.index]=false
set data.angle=GetUnitFacing(data.caster)+Angles(int)
set data.startangle=Angles(int)
set data.offset=DEFAULTOFFSET
call data.createChain()
set int=int+1
endloop
endif

endfunction

private function CleanArms takes aBuff buffType returns nothing
local integer int=0
local Arm data
loop
exitwhen int==MAXARMS
set data=Arm.ArmTable[buffType][int]
call data.KillArm()
set Arm.done[data.index]=true
set int=int+1
endloop
endfunction

private function Init takes nothing returns nothing
set buffType=aBuffType.create()
set buffType.eventCreate=AbilLearn
set buffType.eventCleanup=CleanArms
set buffType.countsAsBuff=false
call NewABuffHeroSkill(ABILID,buffType)
call SetupSpellVariables()
call ConfigureDamageOptions()

call Preload(CHAINMDL)
call Preload(CHAINHAND)
call Preload(ABSORBMDL)
call PreloadStart()
endfunction

endlibrary[/code]

I know i should upload a screenshot and i'll do it fast as i can, but i hope you understand that its hard to make a sreenshot of 5 spells.

Anyway i hope you enjoy that pack.

~Inferior

PS: I know my english is bad :p

Keywords:
Spell, Pack, Inferior, Dark, Curse, Deadly, Seduction, Haunting, Ghosts, Magnetic Field, Polarisation, Dread Lord, Electrician, Robot, Arms, vJASS
Contents

Inferiors Spellpack v1.4 (Map)

Reviews
09:31, 3rd Jun 2010 The_Reborn_Devil: The coding looks decent, and the bug seems to be fixed. Status: Approved Rating: Recommended Damn lag

Moderator

M

Moderator

09:31, 3rd Jun 2010
The_Reborn_Devil:

The coding looks decent, and the bug seems to be fixed.


Status: Approved
Rating: Recommended


Damn lag
 
Level 12
Joined
May 21, 2009
Messages
994
Good job! Alot of changeable globals. Also im glad this ain't GUI. At the moment im still learning vJass so I wont comment the code. But it looks clean and I did not find any BJ (other than the event of course).
By the way can someone tell me what the difference is between "library needs blahblah" and "library requires blahblah"?

5/5
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
Very Quick Comments on Coding:
Update GroupUtils if you haven't already. You can substitute GroupEnumUnitsInRange with GroupEnumUnitsInArea.

You should null the local handle variables such as unit dum and effect e.

Why not use xecast for all of your dummy targeting spells? (You used it in Dark Curse but not Deadly Seduction)

Using only
JASS:
IsUnitType(target,UNIT_TYPE_HERO)
won't set the correct duration for units that have Resistant Skin or belong to the Creep race and have a level higher than 5. Why don't you just use IsUnitSpellResistant? (This is about Black Curse)

I don't think it's wise to use TriggerRegisterUnitEvent for every unit that gets affected by the spell as it will be called on units that may already have been registered to the trigger.

I think you should have a configuration function for the damage (When you're using xedamage) so that people who use the spells find it easier to change the spell's damagetype/attacktype/weapontype.

FYI:
Trigger conditions get executed faster than trigger actions.

FirstOfGroup loops are slower than using the filter directly.

I haven't downloaded the spell pack yet; I've only looked at the spell code you posted online.
 
Level 6
Joined
Nov 3, 2008
Messages
117
Update GroupUtils if you haven't already. You can substitute GroupEnumUnitsInRange with GroupEnumUnitsInArea.

Fixed

You should null the local handle variables such as unit dum and effect e.

Fixed

Why not use xecast for all of your dummy targeting spells? (You used it in Dark Curse but not Deadly Seduction)

I dont see the reason to use xecast for Deadly Seduction since i dont use any targeting dummy spell. If you tell me where I'll fix it ASAP.

Using only
JASS:
IsUnitType(target,UNIT_TYPE_HERO)
won't set the correct duration for units that have Resistant Skin or belong to the Creep race and have a level higher than 5. Why don't you just use IsUnitSpellResistant? (This is about Black Curse)

Fixed


I don't think it's wise to use TriggerRegisterUnitEvent for every unit that gets affected by the spell as it will be called on units that may already have been registered to the trigger.

No problem with it since the triggers are destroyed and there is also a checking wheather a unit allready is under effect of the spell (if you mean Dark Curse :p)

I think you should have a configuration function for the damage (When you're using xedamage) so that people who use the spells find it easier to change the spell's damagetype/attacktype/weapontype.

Fixed

FYI:
Trigger conditions get executed faster than trigger actions.

Fixed

FirstOfGroup loops are slower than using the filter directly.

Fixed

Thx for your Feedback watermelon
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
I dont see the reason to use xecast for Deadly Seduction since i dont use any targeting dummy spell. If you tell me where I'll fix it ASAP.
Sorry for that, I didn't look carefully enough to see that the ability was passive. You could use xefx for this, but it's not really necessary.

For group enumerations, you could use the ENUM_GROUP variable that is provided by GroupUtils instead of having a private global one or using a local one. You also don't need to use GroupClear.

Am I mistaken, or is there something wrong with the debug messages in UnitStatus library? (No call after the debug.)
It seems that the test map is bugged. (Errors occur as soon as I save.)

Deadly Seduction can crash the map. It happens when I try to order my units under the buff to do something else.
Edit; It doesn't happen all the time though.
To be honest, I don't really understand what it does. (Why does it make allied units attack each other while nothing happens for enemy units?)
Edit: Oh, I'm guessing it can only affect Player units.

The tooltip for Haunting Ghosts is messed up.

You should use TimedHandles (http://www.wc3c.net/showthread.php?t=105456) or TimedEffects (http://www.wc3c.net/showthread.php?t=104216) for destroying effects.

I will edit this post if I test the other spells.

Edit with more comments:

Dark Curse will play the Dizzy SFX endlessly if the unit gets killed. (I'm not sure how to reproduce it so this just have been my testing.) You should avoid playing the SFX if the damage ends up killing the unit. I suggest making a timer that expires after 0 seconds to check if the unit is alive or dead which will then play the sfx. (I don't recommend checking the damage against the unit's life as the damage might get reduced by something else.)

You should update the version number for the test map.

I'm impressed with the Electrician's spells, mainly Robotic Arms.

I'm not sure if this affects anything, but you may want to return false instead of true in your functions that act like conditions (Like MagnetSpell).

You could use xefx instead of a unit for the hand variable in Robot Arms but that's up to your decision.
 
Last edited:
Level 6
Joined
Nov 3, 2008
Messages
117
For group enumerations, you could use the ENUM_GROUP variable that is provided by GroupUtils instead of having a private global one or using a local one. You also don't need to use GroupClear.

Fixed

Am I mistaken, or is there something wrong with the debug messages in UnitStatus library? (No call after the debug.)
It seems that the test map is bugged. (Errors occur as soon as I save.)

Fixed, only occurs when debug mode is active. Not my fault since i copied it as it is posted on wc3c.net :p

Deadly Seduction can crash the map. It happens when I try to order my units under the buff to do something else.
Edit; It doesn't happen all the time though.
To be honest, I don't really understand what it does. (Why does it make allied units attack each other while nothing happens for enemy units?)
Edit: Oh, I'm guessing it can only affect Player units.

Fixed the crash bug. And you got it. Since this spell only affects unit which get orders, this won't have any effect on AI units. Thats the reason why i made it possible to be casted on you own units.


The tooltip for Haunting Ghosts is messed up.

Fixed

You should use TimedHandles (http://www.wc3c.net/showthread.php?t=105456) or TimedEffects (http://www.wc3c.net/showthread.php?t=104216) for destroying effects.

May add this later, but i'm pretty busy atm.

You should update the version number for the test map.

Fixed

I'm impressed with the Electrician's spells, mainly Robotic Arms.

Glad you like it ^^. Was pretty hard to get it work.

I'm not sure if this affects anything, but you may want to return false instead of true in your functions that act like conditions (Like MagnetSpell).

Fixed

You could use xefx instead of a unit for the hand variable in Robot Arms but that's up to your decision.

I think that xefx won't make any difference o_O


@Black Rose

Thanks for your reply. What do you mean with reimport? Editing with Magos won't be possible since i don't get it work on ******* Windows 7.
 
Level 14
Joined
Nov 18, 2007
Messages
1,084
I suggested using TimedHandles/TimedEffects because it did the same thing as EffectUtils but was coded better.

By reimport, xBlackRose means to import a modified effect for it.
Since you can't use Magos, I attached the SFX for the Robotic Hands with the sounds removed.
 

Attachments

  • RoboticArmsHand.mdx
    3.4 KB · Views: 96
Level 6
Joined
Nov 3, 2008
Messages
117
Weird thing. I just downloaded it and it shows me no error. The Rounding Library should be normally found in the MathFuncs trigger. I has to look like this:
[jass="Code"]library Logarithm

globals
private constant integer ITERATIONS=20
endglobals

function Log takes real x returns real
local real min=-88.0
local real max= 88.0
local real mid
local integer i=ITERATIONS

loop
set mid=(min+max)/2
exitwhen(i<=0)
set i=i-1
if (Pow(bj_E,mid)>=x) then
set max=mid
else
set min=mid
endif
endloop
return mid
endfunction

function Logarithm takes real base, real x returns real
local real min=-88.0
local real max= 88.0
local real mid
local integer i=ITERATIONS

loop
set mid=(min+max)/2
exitwhen(i<=0)
set i=i-1
if (Pow(base,mid)>=x) then
set max=mid
else
set min=mid
endif
endloop
return mid
endfunction

endlibrary


library Rounding
//*************************************************************
//* Rounding by Inferior
//*
//* The purpose of this library is to correct the Rounding.
//* Normally if we have e.g. 1.5 and we round it we should get 2,
//* but Blizzard is rounding down to 1. Even if we have 1.9999....
//* it will be rounded down to 1.
//* If you want a value to be rounded correctly use the following:
//*
//* function RealRounding takes real r returns nothing
//*
//***************************************************************

// returns the Absolute value of a real
function AbsReal takes real a returns real
return SquareRoot(a*a)
endfunction

// returns the Absolute value of an integer
function AbsInt takes integer i returns integer
return R2I(SquareRoot(i*i))
endfunction

function RealRounding takes real r returns integer
return R2I(r+0.5)
endfunction

endlibrary[/code]
 
Level 2
Joined
Dec 20, 2009
Messages
7
thanks that was wat was wrong, but now im getting another problem my jasshelper keeps saying "Unable to find textmacro: "optional"" and it highlights one of the endifs in the autoindex trigger, sorry if i sound extremely nubish im trying to understand vjass spells
 
Top