• 🏆 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!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

[JASS] How do I make the orbs to only drain life from organic units??

Status
Not open for further replies.
Level 14
Joined
Jul 19, 2007
Messages
772
I currently got a problem with this JASS-triggered ability that I've imported from this ability http://www.hiveworkshop.com/forums/...2/?prev=search=touch%20of%20light&d=list&r=20 but I've edited it alot. I'm very lost and noobish at JASS-triggering so I could only edit the simplest things in it.. I got a problem I cannot solve, the orbs are supposed to drain life from enemy units in the targeted area but it also seems to drain life from buildings, wards and spell immunity units and I just want the orbs to drain life from organic units that is not immune to spells or invulnerable.. How can I change that?
The JASS-trigger of the ability.
JASS:
//TESH.scrollpos=0
//TESH.alwaysfold=0
library ElvenOrbs initializer InitTrig_Elven_Orbs
// Spell Touch of Light by The_Witcher
//
// Creates orbs that patrol in a area, draining life from enemies. 
// When their time has come they return and heal the caster and his allies!
//
// THE SETUP PART:

globals
    // the rawcode of the spell
    private constant integer SPELL_ID = 'A0D3'
    
    // the rawcode of the dummy used as orb
    private constant integer ORB_ID = 'h01Z'
    
    // the range in which the orbs can drain
    private constant integer ORB_DRAIN_RANGE = 150
    
    // the timer interval (increase if laggy)
    private constant real INTERVAL = 0.02
    
    // the flying height of the orbs
    private constant real ORB_HEIGHT = 80
    
    // the time it takes a orb to drain life
    private constant real DRAIN_TIME = 0.5
    
    // false = only caster is healed
    // true = caster and near allies are healed
    private constant boolean AOE_HEAL = true
    
    // the sfx created on units while healing
    private constant string HEAL_SFX = "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl"  
    
    // the attachement point for HEAL_SFX
    private constant string HEAL_SFX_TARGET = "origin"
    
    // the sfx created on units while draining
    private constant string DRAIN_SFX = "Abilities\\Spells\\Human\\Slow\\SlowCaster.mdl"  
    
    // the sfx created on a orb when it returns to the caster
    private constant string EXPLODE_SFX = "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl"   
    
    // the lightning created while draining
    private constant string LIGHTNING = "HWPB"
    
    // false = lightning is normal
    // true = lightnings endings are switched
    private constant boolean LIGHTNING_TURNED = true
endglobals

private function GetOrbAmount takes integer lvl returns integer
    return 1 + 2 * lvl                     // 2 orbs more each level !!CAN NEVER BE HIGHER THAN 50!!
endfunction

private function GetOrbDrainCooldown takes integer lvl returns real
    return 1.1 - 0.1 * lvl                     // 1sec/0.9sec/0.8sec/0.7sec
endfunction

private function GetOrbAreaSize takes integer lvl returns real
    return 200. + 50 * lvl                     // 250/300/350/400 radius for the area where the orbs do their work
endfunction

private function GetDrainPercentage takes integer lvl returns real
    return 0.01 + 0.02 * lvl                      //draines 2% more each level
endfunction

private function GetDuration takes integer lvl returns real
    return 4. + 1 * lvl                      //lasts for 5/6/7/8... seconds
endfunction

private function GetHealPercentage takes integer lvl returns real
    return 0.02 + 0.02 * lvl                      //heals 2% more each level
endfunction

private function GetHealRange takes integer lvl returns real
    return 100. + 50 * lvl                      //heals in a AOE of 150/200/250/300...
endfunction

private function GetOrbSpeed takes integer lvl returns real
    return 5. + 1. * lvl                      //  6/7/8/9 per interval...
endfunction

//---------------------------------------------------------
//-----------Don't modify anything below this line---------
//---------------------------------------------------------

private struct data
    unit               u
    real               x
    real               y
    integer            lvl
    unit      array    orb       [50]
    unit      array    target    [50]
    integer   array    phase     [50]
    real      array    drained   [50]
    real      array    delay     [50]
    lightning array    light     [50]
    timer              tim       = CreateTimer()
    integer            orbs      = 0
    real               temp      = 19
    real               t         = 0
endstruct

globals
    private hashtable h = InitHashtable()
    private data DATA
    private group g = CreateGroup()
    private location loc = Location(0,0)
endglobals

private function AngleBetweenCoords takes real x, real y, real xx, real yy returns real
    return Atan2(yy - y, xx - x)
endfunction

private function DistanceBetweenCoords takes real x , real y , real xx , real yy returns real
    return SquareRoot((x-xx)*(x-xx)+(y-yy)*(y-yy))
endfunction

private function DistanceBetweenUnits takes unit a, unit b returns real
    local real dx = GetUnitX(b) - GetUnitX(a)
    local real dy = GetUnitY(b) - GetUnitY(a)
    return SquareRoot(dx * dx + dy * dy)
endfunction

private function AngleBetweenUnits takes unit a, unit b returns real
    return Atan2(GetUnitY(b) - GetUnitY(a), GetUnitX(b) - GetUnitX(a))
endfunction

private function EnemiesOnly takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(DATA.u)) and not (IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD) or GetUnitTypeId(GetFilterUnit()) == 0 )
endfunction

private function AlliesOnly takes nothing returns boolean
    return IsUnitAlly(GetFilterUnit(),GetOwningPlayer(DATA.u)) and not (IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD) or GetUnitTypeId(GetFilterUnit()) == 0 )
endfunction

private function AOEheal takes nothing returns nothing
    call SetWidgetLife(GetEnumUnit(),GetWidgetLife(GetEnumUnit()) + DATA.temp * GetHealPercentage(DATA.lvl))
    call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX,GetEnumUnit(),HEAL_SFX_TARGET))
endfunction

function RandomEnemyUnit takes real x, real y, real range returns unit
    local unit a=null
    local unit i
    local integer b=0
    call GroupClear(g)
    call GroupEnumUnitsInRange(g,x,y,range,Condition(function EnemiesOnly))
    loop
        set i=FirstOfGroup(g)
        exitwhen i==null
        set b=b+1
        if (GetRandomInt(1,b) == 1) then
            set a = i
        endif
        call GroupRemoveUnit(g,i)
    endloop
    set bj_groupRandomCurrentPick=a
    set a=null
    return bj_groupRandomCurrentPick
endfunction

private function Execute takes nothing returns nothing
    local data dat = LoadInteger(h,GetHandleId(GetExpiredTimer()),0)
    local real X 
    local real Y
    local real a
    local integer i = 0
    //****check whether caster is alive****
    if (IsUnitType(dat.u,UNIT_TYPE_DEAD) or GetUnitTypeId(dat.u) == 0 ) then
        loop
            exitwhen i >= dat.orbs
            call RemoveUnit(dat.orb[i])
            call DestroyLightning(dat.light[i])
            set i = i + 1
        endloop
        set dat.orbs = 0
    else
        //****increase time****
        set dat.t = dat.t + INTERVAL
        //****new orbs****
        if dat.orbs < IMinBJ(GetOrbAmount(dat.lvl),50) and dat.t <= GetDuration(dat.lvl) then
            set dat.temp = dat.temp + 1
            if dat.temp == 20 then 
                set dat.orb[dat.orbs] = CreateUnit(GetOwningPlayer(dat.u),ORB_ID,GetUnitX(dat.u),GetUnitY(dat.u),0)
                call UnitAddAbility(dat.orb[dat.orbs], 'Aloc')
                call UnitAddAbility(dat.orb[dat.orbs], 'Amrf')
                call SetUnitFlyHeight(dat.orb[dat.orbs],ORB_HEIGHT,0)
                set dat.temp = 0
                set DATA = dat
                set dat.target[dat.orbs] = RandomEnemyUnit(dat.x,dat.y,GetOrbAreaSize(dat.lvl))       
                set dat.orbs = dat.orbs + 1
            endif
        endif              
        //****process all active orbs****
        loop
            exitwhen i >= dat.orbs
            if dat.phase[i] == 0 then    
            //****search a target and drain****
                //****reduce cooldown****
                if dat.delay[i] > 0 then
                    set dat.delay[i] = dat.delay[i] - INTERVAL
                    if dat.delay[i] < 0 then
                        set dat.delay[i] = 0
                    endif
                    if dat.delay[i] <= GetOrbDrainCooldown(dat.lvl) then
                        if IsUnitPaused(dat.orb[i]) then
                            call DestroyLightning(dat.light[i])
                            call PauseUnit(dat.orb[i],false)
                            //****new target****
                            set DATA = dat
                            set dat.target[i] = RandomEnemyUnit(dat.x,dat.y,GetOrbAreaSize(dat.lvl))    
                        endif
                    else
                        call MoveLocation(loc,GetUnitX(dat.orb[i]),GetUnitY(dat.orb[i]))
                        set a = GetLocationZ(loc)
                        call MoveLocation(loc,GetUnitX(dat.target[i]),GetUnitY(dat.target[i]))
                        if LIGHTNING_TURNED then
                            call MoveLightningEx(dat.light[i],true,GetUnitX(dat.target[i]),GetUnitY(dat.target[i]),GetLocationZ(loc)+50,GetUnitX(dat.orb[i]),GetUnitY(dat.orb[i]),a+ORB_HEIGHT)
                        else
                            call MoveLightningEx(dat.light[i],true,GetUnitX(dat.orb[i]),GetUnitY(dat.orb[i]),a+ORB_HEIGHT,GetUnitX(dat.target[i]),GetUnitY(dat.target[i]),GetLocationZ(loc)+50)
                        endif
                    endif
                endif
                //****if time is up and the orb isn't draining, start returning to the caster****
                if dat.delay[i] <= GetOrbDrainCooldown(dat.lvl) and dat.t > GetDuration(dat.lvl) then
                    set dat.phase[i] = 1
                elseif dat.target[i] != null then
                //****distance to target check*****
                    if DistanceBetweenUnits(dat.target[i],dat.orb[i]) < ORB_DRAIN_RANGE then
                        //****no cooldown left?****         
                        if dat.delay[i] == 0 then
                            //****drain****
                            set X = GetUnitX(dat.target[i])
                            set Y = GetUnitY(dat.target[i])
                            set a = GetWidgetLife(dat.target[i]) * GetDrainPercentage(dat.lvl)
                            set dat.drained[i] = dat.drained[i] + a
                            call SetWidgetLife(dat.target[i], GetWidgetLife(dat.target[i]) - a)
                            //****effects****
                            call DestroyEffect(AddSpecialEffect(DRAIN_SFX,X,Y)) 
                            if LIGHTNING_TURNED then
                                set dat.light[i] = AddLightningEx(LIGHTNING,true,X,Y,GetUnitFlyHeight(dat.target[i])+50,GetUnitX(dat.orb[i]),GetUnitY(dat.orb[i]),ORB_HEIGHT)
                            else
                                set dat.light[i] = AddLightningEx(LIGHTNING,true,GetUnitX(dat.orb[i]),GetUnitY(dat.orb[i]),ORB_HEIGHT,X,Y,GetUnitFlyHeight(dat.target[i])+50) 
                            endif
                            //****cooldown****
                            call PauseUnit(dat.orb[i],true)
                            set dat.delay[i] = GetOrbDrainCooldown(dat.lvl) + DRAIN_TIME
                        endif 
                    //****distance too huge so go nearer****
                    elseif not IsUnitPaused(dat.orb[i]) then
                        set a = AngleBetweenUnits(dat.orb[i],dat.target[i])
                        set X = GetUnitX(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Cos(a)
                        set Y = GetUnitY(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Sin(a) 
                        call SetUnitX(dat.orb[i],X)
                        call SetUnitY(dat.orb[i],Y) 
                    endif 
                else
                    //****new target****
                    set DATA = dat
                    set dat.target[i] = RandomEnemyUnit(dat.x,dat.y,GetOrbAreaSize(dat.lvl))     
                endif
            else 
            //****Time is up so return to caster****
                set a = AngleBetweenCoords(GetUnitX(dat.orb[i]),GetUnitY(dat.orb[i]),GetUnitX(dat.u),GetUnitY(dat.u))
                set X = GetUnitX(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Cos(a)
                set Y = GetUnitY(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Sin(a) 
                call SetUnitX(dat.orb[i],X)
                call SetUnitY(dat.orb[i],Y)
                //****caster reached so heal****
                if DistanceBetweenCoords(X,Y,GetUnitX(dat.u),GetUnitY(dat.u)) < 10 then
                    call RemoveUnit(dat.orb[i])
                    call DestroyEffect(AddSpecialEffect(EXPLODE_SFX,X,Y))
                    if AOE_HEAL then
                        set DATA = dat
                        set dat.temp = dat.drained[i]
                        call GroupEnumUnitsInRange(g,X,Y,GetHealRange(dat.lvl),Condition(function AlliesOnly))
                        call ForGroup(g, function AOEheal)
                    else
                        call SetWidgetLife(dat.u,GetWidgetLife(dat.u) + dat.drained[i] * GetHealPercentage(dat.lvl))
                        call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX,dat.u,HEAL_SFX_TARGET))
                    endif
                    set dat.orbs = dat.orbs - 1
                    set dat.orb[i] = dat.orb[dat.orbs]
                    set i = i - 1
                endif  
            endif
            set i = i + 1 
        endloop
    endif
    //****no orbs left ==> end spell****
    if dat.orbs == 0 then
        call FlushChildHashtable(h,GetHandleId(dat.tim))
        call DestroyTimer(dat.tim)
        call dat.destroy()
    endif
endfunction

private function Cast takes nothing returns boolean
    local data dat
    local integer i = 0
    if GetSpellAbilityId() == SPELL_ID then
        set dat = data.create()
        loop
            exitwhen i == 50                                                                    
            set dat.phase[i] = 0
            set dat.drained[i] = 0
            set dat.delay[i] = 0
            set i = i + 1
        endloop
        set dat.x = GetSpellTargetX()
        set dat.y = GetSpellTargetY()
        set dat.u = GetTriggerUnit()
        set dat.lvl = GetUnitAbilityLevel(dat.u,SPELL_ID)
        call SaveInteger(h,GetHandleId(dat.tim),0,dat)
        call TimerStart(dat.tim,INTERVAL,true,function Execute)
    endif
    return false
endfunction

private function InitTrig_Elven_Orbs takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerAddCondition(t,Condition(function Cast))
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
endfunction

endlibrary
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
didnt read the code, but did you try if IsUnitType(someUnitVariable, UNIT_TYPE_ORGANIC) then
?

edit: turns out there is nothing like that.

You can still do if not(IsUnitType(someUnit, UNIT_TYPE_UNDEAD) or IsUnitType(someUnit, UNIT_TYPE_MECHANICAL) or IsUnitType(someUnit, UNIT_TYPE_MAGIC_IMMUNE) then

Forgot how to check if unit is invulnerable, but eh someone will help you out with that. And btw sorry I dont know where to type this exactly to the script, I dont have time to go through it right now
 
Level 12
Joined
Oct 16, 2010
Messages
680
JASS:
private function EnemiesOnly takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(DATA.u)) and not (IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD) or GetUnitTypeId(GetFilterUnit()) == 0 )
endfunction

private function AlliesOnly takes nothing returns boolean
    return IsUnitAlly(GetFilterUnit(),GetOwningPlayer(DATA.u)) and not (IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD) or GetUnitTypeId(GetFilterUnit()) == 0 )
endfunction
this
edit these a bit and add what edo said
to
JASS:
private function EnemiesOnly takes nothing returns boolean
    local unit u = GetFilterUnit()
    local unittype ut = GetUnitType(u)
    if IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(DATA.u)) and ut!=UNIT_TYPE_STRUCTURE and ut!=UNIT_TYPE_MECHANICAL and GetUnitState(u,UNIT_STATE_LIFE)>0.405 and u!=null then
        set u = null
        set ut = null
        return true
    endif
    return false
endfunction

private function AlliesOnly takes nothing returns boolean
    local unit u = GetFilterUnit()
    local unittype ut = GetUnitType(u)
    if IsUnitAlly(u,GetOwningPlayer(DATA.u)) and ut!=UNIT_TYPE_STRUCTURE and ut!=UNIT_TYPE_MECHANICAL and GetUnitState(u,UNIT_STATE_LIFE)>0.405 and u!=null then
        set u = null
        set ut = null
        return true
    endif
    return false
endfunction
 
Level 14
Joined
Jul 19, 2007
Messages
772
JASS:
private function EnemiesOnly takes nothing returns boolean
    return IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(DATA.u)) and not (IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD) or GetUnitTypeId(GetFilterUnit()) == 0 )
endfunction

private function AlliesOnly takes nothing returns boolean
    return IsUnitAlly(GetFilterUnit(),GetOwningPlayer(DATA.u)) and not (IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD) or GetUnitTypeId(GetFilterUnit()) == 0 )
endfunction
this
edit these a bit and add what edo said
to
JASS:
private function EnemiesOnly takes nothing returns boolean
    local unit u = GetFilterUnit()
    local unittype ut = GetUnitType(u)
    if IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(DATA.u)) and ut!=UNIT_TYPE_STRUCTURE and ut!=UNIT_TYPE_MECHANICAL and GetUnitState(u,UNIT_STATE_LIFE)>0.405 and u!=null then
        set u = null
        set ut = null
        return true
    endif
    return false
endfunction

private function AlliesOnly takes nothing returns boolean
    local unit u = GetFilterUnit()
    local unittype ut = GetUnitType(u)
    if IsUnitAlly(u,GetOwningPlayer(DATA.u)) and ut!=UNIT_TYPE_STRUCTURE and ut!=UNIT_TYPE_MECHANICAL and GetUnitState(u,UNIT_STATE_LIFE)>0.405 and u!=null then
        set u = null
        set ut = null
        return true
    endif
    return false
endfunction
Thanks but didn't u forget the magic immune and invulnerable check?
 
Level 14
Joined
Jul 19, 2007
Messages
772
ah yeah

for magic immune add ut != UNIT_TYPE_MAGIC_IMMUNE

and for invulnerability i think you should check for buff as there no such type that represents invulnerability:/
and I don't have WE atm.
Ok, that's sick they didn't make an option to check for units who is invulnerable.. Btw the JASS-trigger now got errors and I can't get it to work and I'm using NewGen WE and Normal World Editor..
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
there only exists GetUnitTypeId, no GetUnitType.

You can however combine it with ConvertUnitType to achieve this.

So you do

JASS:
local boolean isMagicImmune = ConvertUnitType(GetUnitTypeId(u)) == UNIT_TYPE_MAGIC_IMMUNE

and you instantly get the boolean value
 
Level 14
Joined
Jul 19, 2007
Messages
772
replace local unittype ut = GetUnitType(u) with local unittype ut = ConvertUnitType(GetUnitTypeId(u))
I tried it and it successed to save the map without any error notices but instead when I'm trying to use this ability when playing the map, the game crashes with a fatal error.. Did I do the right or is something missing?
JASS:
private function EnemiesOnly takes nothing returns boolean
    local unit u = GetFilterUnit()
    local unittype ut = ConvertUnitType(GetUnitTypeId(u))
    if IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(DATA.u)) and ut!=UNIT_TYPE_STRUCTURE and ut!=UNIT_TYPE_MECHANICAL and ut!=UNIT_TYPE_MAGIC_IMMUNE and GetUnitState(u,UNIT_STATE_LIFE)>0.405 and u!=null then
        set u = null
        set ut = null
        return true
    endif
    return false
endfunction

private function AlliesOnly takes nothing returns boolean
    local unit u = GetFilterUnit()
    local unittype ut = ConvertUnitType(GetUnitTypeId(u))
    if IsUnitAlly(u,GetOwningPlayer(DATA.u)) and ut!=UNIT_TYPE_STRUCTURE and ut!=UNIT_TYPE_MECHANICAL and ut!=UNIT_TYPE_MAGIC_IMMUNE and GetUnitState(u,UNIT_STATE_LIFE)>0.405 and u!=null then
        set u = null
        set ut = null
        return true
    endif
    return false
endfunction
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
could you please instead of calling ConvertUnitType print the value of GetUnitTypeId? My guess is that for some reason it is out of range, and therefore when you call ConvertUnitType it will crash the game(Convert functions dont do bound checks, so you can try to read any memory from computer, but OS will not allow that and will make your program fatal crash)
 
Level 14
Joined
Jul 19, 2007
Messages
772
could you please instead of calling ConvertUnitType print the value of GetUnitTypeId? My guess is that for some reason it is out of range, and therefore when you call ConvertUnitType it will crash the game(Convert functions dont do bound checks, so you can try to read any memory from computer, but OS will not allow that and will make your program fatal crash)
Sry I don't really get what u mean.. U told me earlier to change the script to this :-/
 
Level 12
Joined
Oct 16, 2010
Messages
680
ah my bad sory for the bad code:D

try this
JASS:
if IsUnitAlly(u,GetOwningPlayer(DATA.u)) and not(IsUnitType(u,UNIT_TYPE_STRUCTURE)) and not(IsUnitType(u,UNIT_TYPE_MECHANICAL)) and not(IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)) and GetUnitState(u,UNIT_STATE_LIFE)>0.405 and u!=null then
    set u = null
    return true
endif
set u = null
return false
 
Level 14
Joined
Jul 19, 2007
Messages
772
ah my bad sory for the bad code:D

try this
JASS:
if IsUnitAlly(u,GetOwningPlayer(DATA.u)) and not(IsUnitType(u,UNIT_TYPE_STRUCTURE)) and not(IsUnitType(u,UNIT_TYPE_MECHANICAL)) and not(IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)) and GetUnitState(u,UNIT_STATE_LIFE)>0.405 and u!=null then
    set u = null
    return true
endif
set u = null
return false
It still doesn't work :-/ This is how it currently looks like
JASS:
rivate function EnemiesOnly takes nothing returns boolean
    local unit u = GetFilterUnit()
    local unittype ut = GetUnitTypeId(u)
    if IsUnitEnemy(u,GetOwningPlayer(DATA.u)) and not(IsUnitType(u,UNIT_TYPE_STRUCTURE)) and not(IsUnitType(u,UNIT_TYPE_MECHANICAL)) and not(IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)) and GetUnitState(u,UNIT_STATE_LIFE)>0.405 and u!=null then
    set u = null
    return true
    endif
    set u = null
    return false
endfunction

private function AlliesOnly takes nothing returns boolean
    local unit u = GetFilterUnit()
    local unittype ut = GetUnitTypeId(u)
    if IsUnitAlly(u,GetOwningPlayer(DATA.u)) and not(IsUnitType(u,UNIT_TYPE_STRUCTURE)) and not(IsUnitType(u,UNIT_TYPE_MECHANICAL)) and not(IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)) and GetUnitState(u,UNIT_STATE_LIFE)>0.405 and u!=null then
    set u = null
    return true
    endif
    set u = null
    return false
endfunction
What is wrong!?
 
Level 14
Joined
Jul 19, 2007
Messages
772
U can remove the local unittype ut but I can't see anything else. Maybe the problem is with DATA.u:/
Oh that solved the problem, now it works like it should with no crashing :) I just wish I could make the orbs unable to drain life from invulnerable units.. Also I think it would be nice if units who gets killed by the life drain is counted as a kill but it isn't :( Is there any way to fix that?
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
you have to pass in correct arguments.

The signature is:

native UnitDamageTarget takes unit whichUnit, widget target, real amount, boolean attack, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns boolean
 
Level 14
Joined
Jul 19, 2007
Messages
772
you have to pass in correct arguments.

The signature is:

native UnitDamageTarget takes unit whichUnit, widget target, real amount, boolean attack, boolean ranged, attacktype attackType, damagetype damageType, weapontype weaponType returns boolean
I don't get what u mean sry.. Maybe someone could fix it to me cause I'm so lost at JASS-triggering.. Also I do not really get how the healing thing works in this spell. The orbs drains health from enemies in the targeted area and then returns to the caster after a period of time and then heals him and all friendly units around it within a small AOE, so it means the healing is equal to the amount of life draining each orb made to the enemies?
 
Level 19
Joined
Jul 14, 2011
Messages
875
Try it like this: call UnitDamageTarget(damageDealer, target, damageAmount, false, false, null, null, null)

WEAPON_TYPE_... is the sound it makes when it deals the damage, ATTACK_TYPE_... is the attack type of the unit (chaos, hero, piers etc.), and DAMAGE_TYPE_... was something a bit more tricky, I think they were used in some vanilla spells, but a few of them rarely make a difference, afaik.

Some combinations of the attack and ranged booleans may cause the damage not to be dealt, although I dont remember what each of them does. I think attack had something to do with the unit being able to dodge the attack.

The function above should work in any case, just change the values according to your needs.
 
Level 14
Joined
Jul 19, 2007
Messages
772
Try it like this: call UnitDamageTarget(damageDealer, target, damageAmount, false, false, null, null, null)

WEAPON_TYPE_... is the sound it makes when it deals the damage, ATTACK_TYPE_... is the attack type of the unit (chaos, hero, piers etc.), and DAMAGE_TYPE_... was something a bit more tricky, I think they were used in some vanilla spells, but a few of them rarely make a difference, afaik.

Some combinations of the attack and ranged booleans may cause the damage not to be dealt, although I dont remember what each of them does. I think attack had something to do with the unit being able to dodge the attack.

The function above should work in any case, just change the values according to your needs.
Well can't u maybe post a longer Jass-trigger of it so I know where to place it or maybe u could fix it if I let u edit my map? I hope the ability will work the same way if it is counted as damage instead of lifedraining :/ Attack Type should be Spells and Damage Type Magic.
 
Level 19
Joined
Jul 14, 2011
Messages
875
Instead of:
JASS:
call SetWidgetLife(dat.target[i], GetWidgetLife(dat.target[i]) - a)
write this:
JASS:
call UnitDamageTarget(dat.orb[i], dat.target[i], a, false, false, null, null, null)
This will work the exact same way, but now it will give experience if it gets a kill.
although I would suggest saving the caster and using him instead of the orb.

Also instead of using ForGroup use the FirstOfGroup method.
For example instead of call ForGroup(g, function AOEheal) use:
JASS:
loop
    set u = FirstOfGroup(g)
    // If the group is empty 'u' is null, so it ends the loop
    exitwhen u == null
    // Basically the actions you do in the function
    call SetWidgetLife(u,GetWidgetLife(u) + DATA.temp * GetHealPercentage(DATA.lvl))
    call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX, u, HEAL_SFX_TARGET))
    // We remove the unit from the group, so it doesnt get picked again
    call GroupRemoveUnit(g, u)
endloop

Its much faster. You need to use 'u' instead of GetEnumUnit(), so replace it with another variable if they collide. Also the unit group gets emptied at the end of the loop.
 
Last edited:
Level 14
Joined
Jul 19, 2007
Messages
772
Instead of:
JASS:
call SetWidgetLife(dat.target[i], GetWidgetLife(dat.target[i]) - a)
write this:
JASS:
call DamageTarget(dat.orb[i], dat.target[i], a, false, false, null, null, null)
This will work the exact same way, but now it will give experience if it gets a kill.
although I would suggest saving the caster and using him instead of the orb.

Also instead of using ForGroup use the FirstOfGroup method.
For example instead of call ForGroup(g, function AOEheal) use:
JASS:
loop
    set u = FirstOfGroup(g)
    // If the group is empty 'u' is null, so it ends the loop
    exitwhen u == null
    // Basically the actions you do in the function
    call SetWidgetLife(u,GetWidgetLife(u) + DATA.temp * GetHealPercentage(DATA.lvl))
    call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX, u, HEAL_SFX_TARGET))
    // We remove the unit from the group, so it doesnt get picked again
    call GroupRemoveUnit(g, u)
endloop

Its much faster. You need to use 'u' instead of GetEnumUnit(), so replace it with another variable if they collide. Also the unit group gets emptied at the end of the loop.
I'm sorry but this sound kinda messed up :/ and I still don't get where exactly to place these things.. and u sure it wont fuck up the spell?
 
Level 19
Joined
Jul 14, 2011
Messages
875
Basically wherever you want to damage a unit, you use call UnitDamageTarget(...) instead of call SetWidgetLife(...), and wherever you have call ForGroup(group, function something) you use the method I described - just replace 'u' with your unit variable(e.g. tempUnit), 'g' with the name of the group and the actions with the actions in the function, and use your unit variable (in this case 'u') instead of GetEnumUnit().

Dinodin said:
... and u sure it wont fuck up the spell?
No, it wont.

I cant explain it any better, you just have to replace a few things and youre done.
 
Level 14
Joined
Jul 19, 2007
Messages
772
Basically wherever you want to damage a unit, you use call UnitDamageTarget(...) instead of call SetWidgetLife(...), and wherever you have call ForGroup(group, function something) you use the method I described - just replace 'u' with your unit variable(e.g. tempUnit), 'g' with the name of the group and the actions with the actions in the function, and use your unit variable (in this case 'u') instead of GetEnumUnit().


No, it wont.

I cant explain it any better, you just have to replace a few things and youre done.
Well I can't get it to work I'm sorry and I'm not sure I've done this right.. Can't u please fix the JASS-trigger to me then? I'm so lost and noobish at JASS-triggering and can only edit the most simply things in it.. The JASS-trigger of the spell currently looks like this
JASS:
//TESH.scrollpos=0
//TESH.alwaysfold=0
library ElvenOrbs initializer InitTrig_Elven_Orbs
// Spell Touch of Light by The_Witcher
//
// Creates orbs that patrol in a area, draining life from enemies. 
// When their time has come they return and heal the caster and his allies!
//
// THE SETUP PART:

globals
    // the rawcode of the spell
    private constant integer SPELL_ID = 'A0D3'
    
    // the rawcode of the dummy used as orb
    private constant integer ORB_ID = 'h01Z'
    
    // the range in which the orbs can drain
    private constant integer ORB_DRAIN_RANGE = 150
    
    // the timer interval (increase if laggy)
    private constant real INTERVAL = 0.02
    
    // the flying height of the orbs
    private constant real ORB_HEIGHT = 80
    
    // the time it takes a orb to drain life
    private constant real DRAIN_TIME = 0.5
    
    // false = only caster is healed
    // true = caster and near allies are healed
    private constant boolean AOE_HEAL = true
    
    // the sfx created on units while healing
    private constant string HEAL_SFX = "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl"  
    
    // the attachement point for HEAL_SFX
    private constant string HEAL_SFX_TARGET = "origin"
    
    // the sfx created on units while draining
    private constant string DRAIN_SFX = "Abilities\\Spells\\Human\\Slow\\SlowCaster.mdl"  
    
    // the sfx created on a orb when it returns to the caster
    private constant string EXPLODE_SFX = "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl"   
    
    // the lightning created while draining
    private constant string LIGHTNING = "HWPB"
    
    // false = lightning is normal
    // true = lightnings endings are switched
    private constant boolean LIGHTNING_TURNED = true
endglobals

private function GetOrbAmount takes integer lvl returns integer
    return 1 + 2 * lvl                     // 2 orbs more each level !!CAN NEVER BE HIGHER THAN 50!!
endfunction

private function GetOrbDrainCooldown takes integer lvl returns real
    return 1.1 - 0.1 * lvl                     // 1sec/0.9sec/0.8sec/0.7sec
endfunction

private function GetOrbAreaSize takes integer lvl returns real
    return 200. + 50 * lvl                     // 250/300/350/400 radius for the area where the orbs do their work
endfunction

private function GetDrainPercentage takes integer lvl returns real
    return 0.01 + 0.02 * lvl                      //draines 2% more each level
endfunction

private function GetDuration takes integer lvl returns real
    return 4. + 1 * lvl                      //lasts for 5/6/7/8... seconds
endfunction

private function GetHealPercentage takes integer lvl returns real
    return 0.02 + 0.02 * lvl                      //heals 2% more each level
endfunction

private function GetHealRange takes integer lvl returns real
    return 100. + 50 * lvl                      //heals in a AOE of 150/200/250/300...
endfunction

private function GetOrbSpeed takes integer lvl returns real
    return 5. + 1. * lvl                      //  6/7/8/9 per interval...
endfunction

//---------------------------------------------------------
//-----------Don't modify anything below this line---------
//---------------------------------------------------------

private struct data
    unit               u
    real               x
    real               y
    integer            lvl
    unit      array    orb       [50]
    unit      array    target    [50]
    integer   array    phase     [50]
    real      array    drained   [50]
    real      array    delay     [50]
    lightning array    light     [50]
    timer              tim       = CreateTimer()
    integer            orbs      = 0
    real               temp      = 19
    real               t         = 0
endstruct

globals
    private hashtable h = InitHashtable()
    private data DATA
    private group g = CreateGroup()
    private location loc = Location(0,0)
endglobals

private function AngleBetweenCoords takes real x, real y, real xx, real yy returns real
    return Atan2(yy - y, xx - x)
endfunction

private function DistanceBetweenCoords takes real x , real y , real xx , real yy returns real
    return SquareRoot((x-xx)*(x-xx)+(y-yy)*(y-yy))
endfunction

private function DistanceBetweenUnits takes unit a, unit b returns real
    local real dx = GetUnitX(b) - GetUnitX(a)
    local real dy = GetUnitY(b) - GetUnitY(a)
    return SquareRoot(dx * dx + dy * dy)
endfunction

private function AngleBetweenUnits takes unit a, unit b returns real
    return Atan2(GetUnitY(b) - GetUnitY(a), GetUnitX(b) - GetUnitX(a))
endfunction

private function EnemiesOnly takes nothing returns boolean
    local unit u = GetFilterUnit()
    if IsUnitEnemy(u,GetOwningPlayer(DATA.u)) and not(IsUnitType(u,UNIT_TYPE_STRUCTURE)) and not(IsUnitType(u,UNIT_TYPE_MECHANICAL)) and not(IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)) and GetUnitState(u,UNIT_STATE_LIFE)>0.405 and u!=null then
    set u = null
    return true
    endif
    set u = null
    return false
endfunction

private function AlliesOnly takes nothing returns boolean
    local unit u = GetFilterUnit()
    if IsUnitAlly(u,GetOwningPlayer(DATA.u)) and not(IsUnitType(u,UNIT_TYPE_STRUCTURE)) and not(IsUnitType(u,UNIT_TYPE_MECHANICAL)) and not(IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)) and GetUnitState(u,UNIT_STATE_LIFE)>0.405 and u!=null then
    set u = null
    return true
    endif
    set u = null
    return false
endfunction

private function AOEheal takes nothing returns nothing
    call UnitDamageTarget(GetEnumUnit(),UnitDamageTarget(GetEnumUnit()) + DATA.temp * GetHealPercentage(DATA.lvl))
    call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX,GetEnumUnit(),HEAL_SFX_TARGET))
endfunction

function RandomEnemyUnit takes real x, real y, real range returns unit
    local unit a=null
    local unit i
    local integer b=0
    call GroupClear(g)
    call GroupEnumUnitsInRange(g,x,y,range,Condition(function EnemiesOnly))
    loop
        set i=FirstOfGroup(g)
        exitwhen i==null
        set b=b+1
        if (GetRandomInt(1,b) == 1) then
            set a = i
        endif
        call GroupRemoveUnit(g,i)
    endloop
    set bj_groupRandomCurrentPick=a
    set a=null
    return bj_groupRandomCurrentPick
endfunction

private function Execute takes nothing returns nothing
    local data dat = LoadInteger(h,GetHandleId(GetExpiredTimer()),0)
    local real X 
    local real Y
    local real a
    local integer i = 0
    //****check whether caster is alive****
    if (IsUnitType(dat.u,UNIT_TYPE_DEAD) or GetUnitTypeId(dat.u) == 0 ) then
        loop
            exitwhen i >= dat.orbs
            call RemoveUnit(dat.orb[i])
            call DestroyLightning(dat.light[i])
            set i = i + 1
        endloop
        set dat.orbs = 0
    else
        //****increase time****
        set dat.t = dat.t + INTERVAL
        //****new orbs****
        if dat.orbs < IMinBJ(GetOrbAmount(dat.lvl),50) and dat.t <= GetDuration(dat.lvl) then
            set dat.temp = dat.temp + 1
            if dat.temp == 20 then 
                set dat.orb[dat.orbs] = CreateUnit(GetOwningPlayer(dat.u),ORB_ID,GetUnitX(dat.u),GetUnitY(dat.u),0)
                call UnitAddAbility(dat.orb[dat.orbs], 'Aloc')
                call UnitAddAbility(dat.orb[dat.orbs], 'Amrf')
                call SetUnitFlyHeight(dat.orb[dat.orbs],ORB_HEIGHT,0)
                set dat.temp = 0
                set DATA = dat
                set dat.target[dat.orbs] = RandomEnemyUnit(dat.x,dat.y,GetOrbAreaSize(dat.lvl))       
                set dat.orbs = dat.orbs + 1
            endif
        endif              
        //****process all active orbs****
        loop
            exitwhen i >= dat.orbs
            if dat.phase[i] == 0 then    
            //****search a target and drain****
                //****reduce cooldown****
                if dat.delay[i] > 0 then
                    set dat.delay[i] = dat.delay[i] - INTERVAL
                    if dat.delay[i] < 0 then
                        set dat.delay[i] = 0
                    endif
                    if dat.delay[i] <= GetOrbDrainCooldown(dat.lvl) then
                        if IsUnitPaused(dat.orb[i]) then
                            call DestroyLightning(dat.light[i])
                            call PauseUnit(dat.orb[i],false)
                            //****new target****
                            set DATA = dat
                            set dat.target[i] = RandomEnemyUnit(dat.x,dat.y,GetOrbAreaSize(dat.lvl))    
                        endif
                    else
                        call MoveLocation(loc,GetUnitX(dat.orb[i]),GetUnitY(dat.orb[i]))
                        set a = GetLocationZ(loc)
                        call MoveLocation(loc,GetUnitX(dat.target[i]),GetUnitY(dat.target[i]))
                        if LIGHTNING_TURNED then
                            call MoveLightningEx(dat.light[i],true,GetUnitX(dat.target[i]),GetUnitY(dat.target[i]),GetLocationZ(loc)+50,GetUnitX(dat.orb[i]),GetUnitY(dat.orb[i]),a+ORB_HEIGHT)
                        else
                            call MoveLightningEx(dat.light[i],true,GetUnitX(dat.orb[i]),GetUnitY(dat.orb[i]),a+ORB_HEIGHT,GetUnitX(dat.target[i]),GetUnitY(dat.target[i]),GetLocationZ(loc)+50)
                        endif
                    endif
                endif
                //****if time is up and the orb isn't draining, start returning to the caster****
                if dat.delay[i] <= GetOrbDrainCooldown(dat.lvl) and dat.t > GetDuration(dat.lvl) then
                    set dat.phase[i] = 1
                elseif dat.target[i] != null then
                //****distance to target check*****
                    if DistanceBetweenUnits(dat.target[i],dat.orb[i]) < ORB_DRAIN_RANGE then
                        //****no cooldown left?****         
                        if dat.delay[i] == 0 then
                            //****drain****
                            set X = GetUnitX(dat.target[i])
                            set Y = GetUnitY(dat.target[i])
                            set a = UnitDamageTarget(dat.target[i]) * GetDrainPercentage(dat.lvl)
                            set dat.drained[i] = dat.drained[i] + a
                            call UnitDamageTarget(dat.target[i], UnitDamageTarget(dat.target[i]) - a)
                            //****effects****
                            call DestroyEffect(AddSpecialEffect(DRAIN_SFX,X,Y)) 
                            if LIGHTNING_TURNED then
                                set dat.light[i] = AddLightningEx(LIGHTNING,true,X,Y,GetUnitFlyHeight(dat.target[i])+50,GetUnitX(dat.orb[i]),GetUnitY(dat.orb[i]),ORB_HEIGHT)
                            else
                                set dat.light[i] = AddLightningEx(LIGHTNING,true,GetUnitX(dat.orb[i]),GetUnitY(dat.orb[i]),ORB_HEIGHT,X,Y,GetUnitFlyHeight(dat.target[i])+50) 
                            endif
                            //****cooldown****
                            call PauseUnit(dat.orb[i],true)
                            set dat.delay[i] = GetOrbDrainCooldown(dat.lvl) + DRAIN_TIME
                        endif 
                    //****distance too huge so go nearer****
                    elseif not IsUnitPaused(dat.orb[i]) then
                        set a = AngleBetweenUnits(dat.orb[i],dat.target[i])
                        set X = GetUnitX(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Cos(a)
                        set Y = GetUnitY(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Sin(a) 
                        call SetUnitX(dat.orb[i],X)
                        call SetUnitY(dat.orb[i],Y) 
                    endif 
                else
                    //****new target****
                    set DATA = dat
                    set dat.target[i] = RandomEnemyUnit(dat.x,dat.y,GetOrbAreaSize(dat.lvl))     
                endif
            else 
            //****Time is up so return to caster****
                set a = AngleBetweenCoords(GetUnitX(dat.orb[i]),GetUnitY(dat.orb[i]),GetUnitX(dat.u),GetUnitY(dat.u))
                set X = GetUnitX(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Cos(a)
                set Y = GetUnitY(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Sin(a) 
                call SetUnitX(dat.orb[i],X)
                call SetUnitY(dat.orb[i],Y)
                //****caster reached so heal****
                if DistanceBetweenCoords(X,Y,GetUnitX(dat.u),GetUnitY(dat.u)) < 10 then
                    call RemoveUnit(dat.orb[i])
                    call DestroyEffect(AddSpecialEffect(EXPLODE_SFX,X,Y))
                    if AOE_HEAL then
                        set DATA = dat
                        set dat.temp = dat.drained[i]
                        call GroupEnumUnitsInRange(g,X,Y,GetHealRange(dat.lvl),Condition(function AlliesOnly))
                        call ForGroup(g, function AOEheal)
                    else
                        call UnitDamageTarget(dat.u,UnitDamageTarget(dat.u) + dat.drained[i] * GetHealPercentage(dat.lvl))
                        call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX,dat.u,HEAL_SFX_TARGET))
                    endif
                    set dat.orbs = dat.orbs - 1
                    set dat.orb[i] = dat.orb[dat.orbs]
                    set i = i - 1
                endif  
            endif
            set i = i + 1 
        endloop
    endif
    //****no orbs left ==> end spell****
    if dat.orbs == 0 then
        call FlushChildHashtable(h,GetHandleId(dat.tim))
        call DestroyTimer(dat.tim)
        call dat.destroy()
    endif
endfunction

private function Cast takes nothing returns boolean
    local data dat
    local integer i = 0
    if GetSpellAbilityId() == SPELL_ID then
        set dat = data.create()
        loop
            exitwhen i == 50                                                                    
            set dat.phase[i] = 0
            set dat.drained[i] = 0
            set dat.delay[i] = 0
            set i = i + 1
        endloop
        set dat.x = GetSpellTargetX()
        set dat.y = GetSpellTargetY()
        set dat.u = GetTriggerUnit()
        set dat.lvl = GetUnitAbilityLevel(dat.u,SPELL_ID)
        call SaveInteger(h,GetHandleId(dat.tim),0,dat)
        call TimerStart(dat.tim,INTERVAL,true,function Execute)
    endif
    return false
endfunction

private function InitTrig_Elven_Orbs takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerAddCondition(t,Condition(function Cast))
    call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
endfunction

endlibrary
 
Level 12
Joined
Oct 16, 2010
Messages
680
JASS:
private function AOEheal takes nothing returns nothing
    call UnitDamageTarget(GetEnumUnit(),UnitDamageTarget(GetEnumUnit()) + DATA.temp * GetHealPercentage(DATA.lvl))
    call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX,GetEnumUnit(),HEAL_SFX_TARGET))
endfunction

why do u damage allies? or what ever you do...
you shouldn't change that

call UnitDamageTarget(dat.u,UnitDamageTarget(dat.u) + dat.drained[i] * GetHealPercentage(dat.lvl))
u don't have any clue how many parameters you should pass to a function
look up the native in the function list

it looks like this
JASS:
function UnitDamageTarget takes unit WhichUnit, real Damage, unit TargetUnit, boolean attack, boolean ranged, attacktype WhichAttackType, damagetype WhichDamageType, weapontype WhichWeaponType returns nothing

U really shouldn't start with editing a complex spell like this

read tutorials, learn and don't just whine about " Oh Im suck at JASS please make it for me.."
practice makes perfect
 
Level 14
Joined
Jul 19, 2007
Messages
772
JASS:
private function AOEheal takes nothing returns nothing
    call UnitDamageTarget(GetEnumUnit(),UnitDamageTarget(GetEnumUnit()) + DATA.temp * GetHealPercentage(DATA.lvl))
    call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX,GetEnumUnit(),HEAL_SFX_TARGET))
endfunction

why do u damage allies? or what ever you do...
you shouldn't change that

call UnitDamageTarget(dat.u,UnitDamageTarget(dat.u) + dat.drained[i] * GetHealPercentage(dat.lvl))
u don't have any clue how many parameters you should pass to a function
look up the native in the function list

it looks like this
JASS:
function UnitDamageTarget takes unit WhichUnit, real Damage, unit TargetUnit, boolean attack, boolean ranged, attacktype WhichAttackType, damagetype WhichDamageType, weapontype WhichWeaponType returns nothing

U really shouldn't start with editing a complex spell like this

read tutorials, learn and don't just whine about " Oh Im suck at JASS please make it for me.."
practice makes perfect
Well I've just changed the things on it that the users told me to :-/ but still the spell doesn't work, I keep getting errors.. The other things I changed is just the simply things like how many orbs that should spawn, duration, amount of life drained and more... and these simply things I changed on it works like it should but not the things that should make the life-draining to be counted as damage..
 
Level 14
Joined
Jul 19, 2007
Messages
772
Are you using JassNewGenPack?

Edit: Your trigger header is showing the Tesh config, so probably you're using the native editor, and this probably is the problem here.

He said at some point that it compiled correctly, so I guess he is using JNGP.(E:He said it in an above post, just checked.)

What kind of errors?E: And where?

post again the whole trigger or update in first post and post a screenshot of the error u got
Yes I'm using JassNewGenPack but then after the map is saved with it, I return to normal WE.. I can't see what kind of errors with JassNewGenPack because it's just stuck at loading when it comes to errors :-/
 
Level 14
Joined
Jul 19, 2007
Messages
772
if the game stops at loading screen then I doubt the problem is with this spell
I'm not stuck at the loading screen when u want to play/test the map. I'm stuck when trying to save the map because the JassHelper finds errors but never loading it so I can see what the errors is about :(
stuck.jpg
 
Level 19
Joined
Jul 14, 2011
Messages
875
Go to the JASSHelper tab and click show previous errors. Also, what jngp are you using? Try switching from 5d/e to 2.0 or vice versa - sometimes one of them doesnt give the error while the other does.
Yes I'm using JassNewGenPack but then after the map is saved with it, I return to normal WE

You do realise that the normal we doesnt have vjass support, right? Unless you have jasshelper installed on it too, youll get errors when trying to save (if you even manage to open it). Either way, there is no reason to open it in the normal we.
 
Level 14
Joined
Jul 19, 2007
Messages
772
Go to the JASSHelper tab and click show previous errors. Also, what jngp are you using? Try switching from 5d/e to 2.0 or vice versa - sometimes one of them doesnt give the error while the other does.


You do realise that the normal we doesnt have vjass support, right? Unless you have jasshelper installed on it too, youll get errors when trying to save (if you even manage to open it). Either way, there is no reason to open it in the normal we.
Oh I didn't see that tab.. Now I could check the errors and it was found 10 errors :eek: Earlier Lender told me to change instead of
JASS:
call SetWidgetLife()
to
JASS:
call UnitDamageTarget()
and I did that change but it seems to have got errors on all these scripts I changed to that :-/ It says "Not enough agruments passed to function", "Bad types for binary operator" and "Cannot convert real to widget"..
 
Level 19
Joined
Jul 14, 2011
Messages
875
This
call UnitDamageTarget(GetEnumUnit(),UnitDamageTarget(GetEnumUnit()) + DATA.temp * GetHealPercentage(DATA.lvl))
to
call SetWidgetLife(GetEnumUnit(),GetWidgetLife(GetEnumUnit()) + DATA.temp * GetHealPercentage(DATA.lvl))
this
set a = UnitDamageTarget(dat.target[i]) * GetDrainPercentage(dat.lvl)
to
set a = GetWidgetLife(dat.target[i]) * GetDrainPercentage(dat.lvl)
this
call UnitDamageTarget(dat.target[i], UnitDamageTarget(dat.target[i]) - a)
to
call UnitDamageTarget(dat.orb[i], dat.target[i], a, false, false, null, null, null)
this
call UnitDamageTarget(dat.u,UnitDamageTarget(dat.u) + dat.drained[i] * GetHealPercentage(dat.lvl))
to
call SetWidgetLife(dat.u,GetWidgetLife(dat.u) + dat.drained[i] * GetHealPercentage(dat.lvl))
That should remove all errors.

Read carefully next time:
(...)
Instead of:
JASS:
call SetWidgetLife(dat.target[i], GetWidgetLife(dat.target[i]) - a)
write this:
JASS:
call UnitDamageTarget(dat.orb[i], dat.target[i], a, false, false, null, null, null)
(...)
Basically wherever you want to damage a unit, you use call UnitDamageTarget(...) instead of call SetWidgetLife(...) ...

S!=G
Edit: Did the jass tags always come up as if the code is 2-3 lines longer?
 
Level 14
Joined
Jul 19, 2007
Messages
772
This
call UnitDamageTarget(GetEnumUnit(),UnitDamageTarget(GetEnumUnit()) + DATA.temp * GetHealPercentage(DATA.lvl))
to
call SetWidgetLife(GetEnumUnit(),GetWidgetLife(GetEnumUnit()) + DATA.temp * GetHealPercentage(DATA.lvl))
this
set a = UnitDamageTarget(dat.target[i]) * GetDrainPercentage(dat.lvl)
to
set a = GetWidgetLife(dat.target[i]) * GetDrainPercentage(dat.lvl)
this
call UnitDamageTarget(dat.target[i], UnitDamageTarget(dat.target[i]) - a)
to
call UnitDamageTarget(dat.orb[i], dat.target[i], a, false, false, null, null, null)
this
call UnitDamageTarget(dat.u,UnitDamageTarget(dat.u) + dat.drained[i] * GetHealPercentage(dat.lvl))
to
call SetWidgetLife(dat.u,GetWidgetLife(dat.u) + dat.drained[i] * GetHealPercentage(dat.lvl))
That should remove all errors.

Read carefully next time:



S!=G
Edit: Did the jass tags always come up as if the code is 2-3 lines longer?
Oh sorry my bad.. I thought he meant me to change all the things that contained "GetWidgetLife" to "UnitDamageTarget" :-/ Well I've changed these things now but still I get 1 error with
JASS:
call SetWidgetLife(dat.u,GetWidgetLife(dat.u) + dat.drained[i] * ElvenOrbs___GetHealPercentage(dat.lvl))
It says "dat is not of a type that allows . syntax" :-/
 
Level 19
Joined
Jul 14, 2011
Messages
875
I just tried replacing and it gave no errors(7 hours of installing warcraft finally paid off).
JASS:
library ElvenOrbs initializer InitTrig_Elven_Orbs
// Spell Touch of Light by The_Witcher
//
// Creates orbs that patrol in a area, draining life from enemies. 
// When their time has come they return and heal the caster and his allies!
//
// THE SETUP PART:

    globals
    // the rawcode of the spell
        private constant integer SPELL_ID = 'A0D3'
    
    // the rawcode of the dummy used as orb
        private constant integer ORB_ID = 'h01Z'
    
    // the range in which the orbs can drain
        private constant integer ORB_DRAIN_RANGE = 150
    
    // the timer interval (increase if laggy)
        private constant real INTERVAL = 0.02
    
    // the flying height of the orbs
        private constant real ORB_HEIGHT = 80
    
    // the time it takes a orb to drain life
        private constant real DRAIN_TIME = 0.5
    
    // false = only caster is healed
    // true = caster and near allies are healed
        private constant boolean AOE_HEAL = true
    
    // the sfx created on units while healing
        private constant string HEAL_SFX = "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl"
    
    // the attachement point for HEAL_SFX
        private constant string HEAL_SFX_TARGET = "origin"
    
    // the sfx created on units while draining
        private constant string DRAIN_SFX = "Abilities\\Spells\\Human\\Slow\\SlowCaster.mdl"
    
    // the sfx created on a orb when it returns to the caster
        private constant string EXPLODE_SFX = "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl"
    
    // the lightning created while draining
        private constant string LIGHTNING = "HWPB"
    
    // false = lightning is normal
    // true = lightnings endings are switched
        private constant boolean LIGHTNING_TURNED = true
    endglobals

    private function GetOrbAmount takes integer lvl returns integer
        return 1 + 2 * lvl // 2 orbs more each level !!CAN NEVER BE HIGHER THAN 50!!
    endfunction

    private function GetOrbDrainCooldown takes integer lvl returns real
        return 1.1 - 0.1 * lvl // 1sec / 0.9sec / 0.8sec / 0.7sec
    endfunction

    private function GetOrbAreaSize takes integer lvl returns real
        return 200. + 50 * lvl // 250 / 300 / 350 / 400 radius for the area where the orbs do their work
    endfunction

    private function GetDrainPercentage takes integer lvl returns real
        return 0.01 + 0.02 * lvl //draines 2% more each level
    endfunction

    private function GetDuration takes integer lvl returns real
        return 4. + 1 * lvl //lasts for 5 / 6 / 7 / 8... seconds
    endfunction

    private function GetHealPercentage takes integer lvl returns real
        return 0.02 + 0.02 * lvl //heals 2% more each level
    endfunction

    private function GetHealRange takes integer lvl returns real
        return 100. + 50 * lvl //heals in a AOE of 150 / 200 / 250 / 300...
    endfunction

    private function GetOrbSpeed takes integer lvl returns real
        return 5. + 1. * lvl // 6 / 7 / 8 / 9 per interval...
    endfunction

//---------------------------------------------------------
//-----------Don't modify anything below this line---------
//---------------------------------------------------------

    private struct data
        unit u
        real x
        real y
        integer lvl
        unit array orb [50]
        unit array target [50]
        integer array phase [50]
        real array drained [50]
        real array delay [50]
        lightning array light [50]
        timer tim = CreateTimer()
        integer orbs = 0
        real temp = 19
        real t = 0
    endstruct

    globals
        private hashtable h = InitHashtable()
        private data DATA
        private group g = CreateGroup()
        private location loc = Location(0, 0)
    endglobals

    private function AngleBetweenCoords takes real x, real y, real xx, real yy returns real
        return Atan2(yy - y, xx - x)
    endfunction

    private function DistanceBetweenCoords takes real x , real y , real xx , real yy returns real
        return SquareRoot((x - xx) * (x - xx) + (y - yy) * (y - yy))
    endfunction

    private function DistanceBetweenUnits takes unit a, unit b returns real
        local real dx = GetUnitX(b) - GetUnitX(a)
        local real dy = GetUnitY(b) - GetUnitY(a)
        return SquareRoot(dx * dx + dy * dy)
    endfunction

    private function AngleBetweenUnits takes unit a, unit b returns real
        return Atan2(GetUnitY(b) - GetUnitY(a), GetUnitX(b) - GetUnitX(a))
    endfunction

    private function EnemiesOnly takes nothing returns boolean
        local unit u = GetFilterUnit()
        if IsUnitEnemy(u, GetOwningPlayer(DATA.u)) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE)) and not(IsUnitType(u, UNIT_TYPE_MECHANICAL)) and not(IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)) and GetUnitState(u, UNIT_STATE_LIFE) > 0.405 and u != null then
            set u = null
            return true
        endif
        set u = null
        return false
    endfunction

    private function AlliesOnly takes nothing returns boolean
        local unit u = GetFilterUnit()
        if IsUnitAlly(u, GetOwningPlayer(DATA.u)) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE)) and not(IsUnitType(u, UNIT_TYPE_MECHANICAL)) and not(IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)) and GetUnitState(u, UNIT_STATE_LIFE) > 0.405 and u != null then
            set u = null
            return true
        endif
        set u = null
        return false
    endfunction

    private function AOEheal takes nothing returns nothing
        call SetWidgetLife(GetEnumUnit(), GetWidgetLife(GetEnumUnit()) + DATA.temp * GetHealPercentage(DATA.lvl))
        call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX, GetEnumUnit(), HEAL_SFX_TARGET))
    endfunction

    function RandomEnemyUnit takes real x, real y, real range returns unit
        local unit a = null
        local unit i
        local integer b = 0
        call GroupClear(g)
        call GroupEnumUnitsInRange(g, x, y, range, Condition(function EnemiesOnly))
        loop
            set i = FirstOfGroup(g)
            exitwhen i == null
            set b = b + 1
            if (GetRandomInt(1, b) == 1) then
                set a = i
            endif
            call GroupRemoveUnit(g, i)
        endloop
        set bj_groupRandomCurrentPick = a
        set a = null
        return bj_groupRandomCurrentPick
    endfunction

    private function Execute takes nothing returns nothing
        local data dat = LoadInteger(h, GetHandleId(GetExpiredTimer()), 0)
        local real X
        local real Y
        local real a
        local integer i = 0
    //****check whether caster is alive****
        if (IsUnitType(dat.u, UNIT_TYPE_DEAD) or GetUnitTypeId(dat.u) == 0 ) then
            loop
                exitwhen i >= dat.orbs
                call RemoveUnit(dat.orb[i])
                call DestroyLightning(dat.light[i])
                set i = i + 1
            endloop
            set dat.orbs = 0
        else
        //****increase time****
            set dat.t = dat.t + INTERVAL
        //****new orbs****
            if dat.orbs < IMinBJ(GetOrbAmount(dat.lvl), 50) and dat.t <= GetDuration(dat.lvl) then
                set dat.temp = dat.temp + 1
                if dat.temp == 20 then
                    set dat.orb[dat.orbs] = CreateUnit(GetOwningPlayer(dat.u), ORB_ID, GetUnitX(dat.u), GetUnitY(dat.u), 0)
                    call UnitAddAbility(dat.orb[dat.orbs], 'Aloc')
                    call UnitAddAbility(dat.orb[dat.orbs], 'Amrf')
                    call SetUnitFlyHeight(dat.orb[dat.orbs], ORB_HEIGHT, 0)
                    set dat.temp = 0
                    set DATA = dat
                    set dat.target[dat.orbs] = RandomEnemyUnit(dat.x, dat.y, GetOrbAreaSize(dat.lvl))
                    set dat.orbs = dat.orbs + 1
                endif
            endif
        //****process all active orbs****
            loop
                exitwhen i >= dat.orbs
                if dat.phase[i] == 0 then
            //****search a target and drain****
                //****reduce cooldown****
                    if dat.delay[i] > 0 then
                        set dat.delay[i] = dat.delay[i] - INTERVAL
                        if dat.delay[i] < 0 then
                            set dat.delay[i] = 0
                        endif
                        if dat.delay[i] <= GetOrbDrainCooldown(dat.lvl) then
                            if IsUnitPaused(dat.orb[i]) then
                                call DestroyLightning(dat.light[i])
                                call PauseUnit(dat.orb[i], false)
                            //****new target****
                                set DATA = dat
                                set dat.target[i] = RandomEnemyUnit(dat.x, dat.y, GetOrbAreaSize(dat.lvl))
                            endif
                        else
                            call MoveLocation(loc, GetUnitX(dat.orb[i]), GetUnitY(dat.orb[i]))
                            set a = GetLocationZ(loc)
                            call MoveLocation(loc, GetUnitX(dat.target[i]), GetUnitY(dat.target[i]))
                            if LIGHTNING_TURNED then
                                call MoveLightningEx(dat.light[i], true, GetUnitX(dat.target[i]), GetUnitY(dat.target[i]), GetLocationZ(loc) + 50, GetUnitX(dat.orb[i]), GetUnitY(dat.orb[i]), a + ORB_HEIGHT)
                            else
                                call MoveLightningEx(dat.light[i], true, GetUnitX(dat.orb[i]), GetUnitY(dat.orb[i]), a + ORB_HEIGHT, GetUnitX(dat.target[i]), GetUnitY(dat.target[i]), GetLocationZ(loc) + 50)
                            endif
                        endif
                    endif
                //****if time is up and the orb isn't draining, start returning to the caster****
                    if dat.delay[i] <= GetOrbDrainCooldown(dat.lvl) and dat.t > GetDuration(dat.lvl) then
                        set dat.phase[i] = 1
                    elseif dat.target[i] != null then
                //****distance to target check*****
                        if DistanceBetweenUnits(dat.target[i], dat.orb[i]) < ORB_DRAIN_RANGE then
                        //****no cooldown left?****         
                            if dat.delay[i] == 0 then
                            //****drain****
                                set X = GetUnitX(dat.target[i])
                                set Y = GetUnitY(dat.target[i])
                                set a = GetWidgetLife(dat.target[i]) * GetDrainPercentage(dat.lvl)
                                set dat.drained[i] = dat.drained[i] + a
                                call UnitDamageTarget(dat.orb[i], dat.target[i], a, false, false, null, null, null)
                            //****effects****
                                call DestroyEffect(AddSpecialEffect(DRAIN_SFX, X, Y))
                                if LIGHTNING_TURNED then
                                    set dat.light[i] = AddLightningEx(LIGHTNING, true, X, Y, GetUnitFlyHeight(dat.target[i]) + 50, GetUnitX(dat.orb[i]), GetUnitY(dat.orb[i]), ORB_HEIGHT)
                                else
                                    set dat.light[i] = AddLightningEx(LIGHTNING, true, GetUnitX(dat.orb[i]), GetUnitY(dat.orb[i]), ORB_HEIGHT, X, Y, GetUnitFlyHeight(dat.target[i]) + 50)
                                endif
                            //****cooldown****
                                call PauseUnit(dat.orb[i], true)
                                set dat.delay[i] = GetOrbDrainCooldown(dat.lvl) + DRAIN_TIME
                            endif
                    //****distance too huge so go nearer****
                        elseif not IsUnitPaused(dat.orb[i]) then
                            set a = AngleBetweenUnits(dat.orb[i], dat.target[i])
                            set X = GetUnitX(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Cos(a)
                            set Y = GetUnitY(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Sin(a)
                            call SetUnitX(dat.orb[i], X)
                            call SetUnitY(dat.orb[i], Y)
                        endif
                    else
                    //****new target****
                        set DATA = dat
                        set dat.target[i] = RandomEnemyUnit(dat.x, dat.y, GetOrbAreaSize(dat.lvl))
                    endif
                else
            //****Time is up so return to caster****
                    set a = AngleBetweenCoords(GetUnitX(dat.orb[i]), GetUnitY(dat.orb[i]), GetUnitX(dat.u), GetUnitY(dat.u))
                    set X = GetUnitX(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Cos(a)
                    set Y = GetUnitY(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Sin(a)
                    call SetUnitX(dat.orb[i], X)
                    call SetUnitY(dat.orb[i], Y)
                //****caster reached so heal****
                    if DistanceBetweenCoords(X, Y, GetUnitX(dat.u), GetUnitY(dat.u)) < 10 then
                        call RemoveUnit(dat.orb[i])
                        call DestroyEffect(AddSpecialEffect(EXPLODE_SFX, X, Y))
                        if AOE_HEAL then
                            set DATA = dat
                            set dat.temp = dat.drained[i]
                            call GroupEnumUnitsInRange(g, X, Y, GetHealRange(dat.lvl), Condition(function AlliesOnly))
                            call ForGroup(g, function AOEheal)
                        else
                            call SetWidgetLife(dat.u, GetWidgetLife(dat.u) + dat.drained[i] * GetHealPercentage(dat.lvl))
                            call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX, dat.u, HEAL_SFX_TARGET))
                        endif
                        set dat.orbs = dat.orbs - 1
                        set dat.orb[i] = dat.orb[dat.orbs]
                        set i = i - 1
                    endif
                endif
                set i = i + 1
            endloop
        endif
    //****no orbs left ==> end spell****
        if dat.orbs == 0 then
            call FlushChildHashtable(h, GetHandleId(dat.tim))
            call DestroyTimer(dat.tim)
            call dat.destroy()
        endif
    endfunction

    private function Cast takes nothing returns boolean
        local data dat
        local integer i = 0
        if GetSpellAbilityId() == SPELL_ID then
            set dat = data.create()
            loop
                exitwhen i == 50
                set dat.phase[i] = 0
                set dat.drained[i] = 0
                set dat.delay[i] = 0
                set i = i + 1
            endloop
            set dat.x = GetSpellTargetX()
            set dat.y = GetSpellTargetY()
            set dat.u = GetTriggerUnit()
            set dat.lvl = GetUnitAbilityLevel(dat.u, SPELL_ID)
            call SaveInteger(h, GetHandleId(dat.tim), 0, dat)
            call TimerStart(dat.tim, INTERVAL, true, function Execute)
        endif
        return false
    endfunction

    private function InitTrig_Elven_Orbs takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerAddCondition(t, Condition(function Cast))
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    endfunction

endlibrary


//Code indented using The_Witcher's Script Language Aligner
//Download the newest version and report bugs at [url]www.hiveworkshop.com[/url]
I ran the code through the script language aligner so it looks better(imo).
 
Level 14
Joined
Jul 19, 2007
Messages
772
I just tried replacing and it gave no errors(7 hours of installing warcraft finally paid off).
JASS:
library ElvenOrbs initializer InitTrig_Elven_Orbs
// Spell Touch of Light by The_Witcher
//
// Creates orbs that patrol in a area, draining life from enemies. 
// When their time has come they return and heal the caster and his allies!
//
// THE SETUP PART:

    globals
    // the rawcode of the spell
        private constant integer SPELL_ID = 'A0D3'
    
    // the rawcode of the dummy used as orb
        private constant integer ORB_ID = 'h01Z'
    
    // the range in which the orbs can drain
        private constant integer ORB_DRAIN_RANGE = 150
    
    // the timer interval (increase if laggy)
        private constant real INTERVAL = 0.02
    
    // the flying height of the orbs
        private constant real ORB_HEIGHT = 80
    
    // the time it takes a orb to drain life
        private constant real DRAIN_TIME = 0.5
    
    // false = only caster is healed
    // true = caster and near allies are healed
        private constant boolean AOE_HEAL = true
    
    // the sfx created on units while healing
        private constant string HEAL_SFX = "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl"
    
    // the attachement point for HEAL_SFX
        private constant string HEAL_SFX_TARGET = "origin"
    
    // the sfx created on units while draining
        private constant string DRAIN_SFX = "Abilities\\Spells\\Human\\Slow\\SlowCaster.mdl"
    
    // the sfx created on a orb when it returns to the caster
        private constant string EXPLODE_SFX = "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl"
    
    // the lightning created while draining
        private constant string LIGHTNING = "HWPB"
    
    // false = lightning is normal
    // true = lightnings endings are switched
        private constant boolean LIGHTNING_TURNED = true
    endglobals

    private function GetOrbAmount takes integer lvl returns integer
        return 1 + 2 * lvl // 2 orbs more each level !!CAN NEVER BE HIGHER THAN 50!!
    endfunction

    private function GetOrbDrainCooldown takes integer lvl returns real
        return 1.1 - 0.1 * lvl // 1sec / 0.9sec / 0.8sec / 0.7sec
    endfunction

    private function GetOrbAreaSize takes integer lvl returns real
        return 200. + 50 * lvl // 250 / 300 / 350 / 400 radius for the area where the orbs do their work
    endfunction

    private function GetDrainPercentage takes integer lvl returns real
        return 0.01 + 0.02 * lvl //draines 2% more each level
    endfunction

    private function GetDuration takes integer lvl returns real
        return 4. + 1 * lvl //lasts for 5 / 6 / 7 / 8... seconds
    endfunction

    private function GetHealPercentage takes integer lvl returns real
        return 0.02 + 0.02 * lvl //heals 2% more each level
    endfunction

    private function GetHealRange takes integer lvl returns real
        return 100. + 50 * lvl //heals in a AOE of 150 / 200 / 250 / 300...
    endfunction

    private function GetOrbSpeed takes integer lvl returns real
        return 5. + 1. * lvl // 6 / 7 / 8 / 9 per interval...
    endfunction

//---------------------------------------------------------
//-----------Don't modify anything below this line---------
//---------------------------------------------------------

    private struct data
        unit u
        real x
        real y
        integer lvl
        unit array orb [50]
        unit array target [50]
        integer array phase [50]
        real array drained [50]
        real array delay [50]
        lightning array light [50]
        timer tim = CreateTimer()
        integer orbs = 0
        real temp = 19
        real t = 0
    endstruct

    globals
        private hashtable h = InitHashtable()
        private data DATA
        private group g = CreateGroup()
        private location loc = Location(0, 0)
    endglobals

    private function AngleBetweenCoords takes real x, real y, real xx, real yy returns real
        return Atan2(yy - y, xx - x)
    endfunction

    private function DistanceBetweenCoords takes real x , real y , real xx , real yy returns real
        return SquareRoot((x - xx) * (x - xx) + (y - yy) * (y - yy))
    endfunction

    private function DistanceBetweenUnits takes unit a, unit b returns real
        local real dx = GetUnitX(b) - GetUnitX(a)
        local real dy = GetUnitY(b) - GetUnitY(a)
        return SquareRoot(dx * dx + dy * dy)
    endfunction

    private function AngleBetweenUnits takes unit a, unit b returns real
        return Atan2(GetUnitY(b) - GetUnitY(a), GetUnitX(b) - GetUnitX(a))
    endfunction

    private function EnemiesOnly takes nothing returns boolean
        local unit u = GetFilterUnit()
        if IsUnitEnemy(u, GetOwningPlayer(DATA.u)) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE)) and not(IsUnitType(u, UNIT_TYPE_MECHANICAL)) and not(IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)) and GetUnitState(u, UNIT_STATE_LIFE) > 0.405 and u != null then
            set u = null
            return true
        endif
        set u = null
        return false
    endfunction

    private function AlliesOnly takes nothing returns boolean
        local unit u = GetFilterUnit()
        if IsUnitAlly(u, GetOwningPlayer(DATA.u)) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE)) and not(IsUnitType(u, UNIT_TYPE_MECHANICAL)) and not(IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)) and GetUnitState(u, UNIT_STATE_LIFE) > 0.405 and u != null then
            set u = null
            return true
        endif
        set u = null
        return false
    endfunction

    private function AOEheal takes nothing returns nothing
        call SetWidgetLife(GetEnumUnit(), GetWidgetLife(GetEnumUnit()) + DATA.temp * GetHealPercentage(DATA.lvl))
        call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX, GetEnumUnit(), HEAL_SFX_TARGET))
    endfunction

    function RandomEnemyUnit takes real x, real y, real range returns unit
        local unit a = null
        local unit i
        local integer b = 0
        call GroupClear(g)
        call GroupEnumUnitsInRange(g, x, y, range, Condition(function EnemiesOnly))
        loop
            set i = FirstOfGroup(g)
            exitwhen i == null
            set b = b + 1
            if (GetRandomInt(1, b) == 1) then
                set a = i
            endif
            call GroupRemoveUnit(g, i)
        endloop
        set bj_groupRandomCurrentPick = a
        set a = null
        return bj_groupRandomCurrentPick
    endfunction

    private function Execute takes nothing returns nothing
        local data dat = LoadInteger(h, GetHandleId(GetExpiredTimer()), 0)
        local real X
        local real Y
        local real a
        local integer i = 0
    //****check whether caster is alive****
        if (IsUnitType(dat.u, UNIT_TYPE_DEAD) or GetUnitTypeId(dat.u) == 0 ) then
            loop
                exitwhen i >= dat.orbs
                call RemoveUnit(dat.orb[i])
                call DestroyLightning(dat.light[i])
                set i = i + 1
            endloop
            set dat.orbs = 0
        else
        //****increase time****
            set dat.t = dat.t + INTERVAL
        //****new orbs****
            if dat.orbs < IMinBJ(GetOrbAmount(dat.lvl), 50) and dat.t <= GetDuration(dat.lvl) then
                set dat.temp = dat.temp + 1
                if dat.temp == 20 then
                    set dat.orb[dat.orbs] = CreateUnit(GetOwningPlayer(dat.u), ORB_ID, GetUnitX(dat.u), GetUnitY(dat.u), 0)
                    call UnitAddAbility(dat.orb[dat.orbs], 'Aloc')
                    call UnitAddAbility(dat.orb[dat.orbs], 'Amrf')
                    call SetUnitFlyHeight(dat.orb[dat.orbs], ORB_HEIGHT, 0)
                    set dat.temp = 0
                    set DATA = dat
                    set dat.target[dat.orbs] = RandomEnemyUnit(dat.x, dat.y, GetOrbAreaSize(dat.lvl))
                    set dat.orbs = dat.orbs + 1
                endif
            endif
        //****process all active orbs****
            loop
                exitwhen i >= dat.orbs
                if dat.phase[i] == 0 then
            //****search a target and drain****
                //****reduce cooldown****
                    if dat.delay[i] > 0 then
                        set dat.delay[i] = dat.delay[i] - INTERVAL
                        if dat.delay[i] < 0 then
                            set dat.delay[i] = 0
                        endif
                        if dat.delay[i] <= GetOrbDrainCooldown(dat.lvl) then
                            if IsUnitPaused(dat.orb[i]) then
                                call DestroyLightning(dat.light[i])
                                call PauseUnit(dat.orb[i], false)
                            //****new target****
                                set DATA = dat
                                set dat.target[i] = RandomEnemyUnit(dat.x, dat.y, GetOrbAreaSize(dat.lvl))
                            endif
                        else
                            call MoveLocation(loc, GetUnitX(dat.orb[i]), GetUnitY(dat.orb[i]))
                            set a = GetLocationZ(loc)
                            call MoveLocation(loc, GetUnitX(dat.target[i]), GetUnitY(dat.target[i]))
                            if LIGHTNING_TURNED then
                                call MoveLightningEx(dat.light[i], true, GetUnitX(dat.target[i]), GetUnitY(dat.target[i]), GetLocationZ(loc) + 50, GetUnitX(dat.orb[i]), GetUnitY(dat.orb[i]), a + ORB_HEIGHT)
                            else
                                call MoveLightningEx(dat.light[i], true, GetUnitX(dat.orb[i]), GetUnitY(dat.orb[i]), a + ORB_HEIGHT, GetUnitX(dat.target[i]), GetUnitY(dat.target[i]), GetLocationZ(loc) + 50)
                            endif
                        endif
                    endif
                //****if time is up and the orb isn't draining, start returning to the caster****
                    if dat.delay[i] <= GetOrbDrainCooldown(dat.lvl) and dat.t > GetDuration(dat.lvl) then
                        set dat.phase[i] = 1
                    elseif dat.target[i] != null then
                //****distance to target check*****
                        if DistanceBetweenUnits(dat.target[i], dat.orb[i]) < ORB_DRAIN_RANGE then
                        //****no cooldown left?****         
                            if dat.delay[i] == 0 then
                            //****drain****
                                set X = GetUnitX(dat.target[i])
                                set Y = GetUnitY(dat.target[i])
                                set a = GetWidgetLife(dat.target[i]) * GetDrainPercentage(dat.lvl)
                                set dat.drained[i] = dat.drained[i] + a
                                call UnitDamageTarget(dat.orb[i], dat.target[i], a, false, false, null, null, null)
                            //****effects****
                                call DestroyEffect(AddSpecialEffect(DRAIN_SFX, X, Y))
                                if LIGHTNING_TURNED then
                                    set dat.light[i] = AddLightningEx(LIGHTNING, true, X, Y, GetUnitFlyHeight(dat.target[i]) + 50, GetUnitX(dat.orb[i]), GetUnitY(dat.orb[i]), ORB_HEIGHT)
                                else
                                    set dat.light[i] = AddLightningEx(LIGHTNING, true, GetUnitX(dat.orb[i]), GetUnitY(dat.orb[i]), ORB_HEIGHT, X, Y, GetUnitFlyHeight(dat.target[i]) + 50)
                                endif
                            //****cooldown****
                                call PauseUnit(dat.orb[i], true)
                                set dat.delay[i] = GetOrbDrainCooldown(dat.lvl) + DRAIN_TIME
                            endif
                    //****distance too huge so go nearer****
                        elseif not IsUnitPaused(dat.orb[i]) then
                            set a = AngleBetweenUnits(dat.orb[i], dat.target[i])
                            set X = GetUnitX(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Cos(a)
                            set Y = GetUnitY(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Sin(a)
                            call SetUnitX(dat.orb[i], X)
                            call SetUnitY(dat.orb[i], Y)
                        endif
                    else
                    //****new target****
                        set DATA = dat
                        set dat.target[i] = RandomEnemyUnit(dat.x, dat.y, GetOrbAreaSize(dat.lvl))
                    endif
                else
            //****Time is up so return to caster****
                    set a = AngleBetweenCoords(GetUnitX(dat.orb[i]), GetUnitY(dat.orb[i]), GetUnitX(dat.u), GetUnitY(dat.u))
                    set X = GetUnitX(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Cos(a)
                    set Y = GetUnitY(dat.orb[i]) + GetOrbSpeed(dat.lvl) * Sin(a)
                    call SetUnitX(dat.orb[i], X)
                    call SetUnitY(dat.orb[i], Y)
                //****caster reached so heal****
                    if DistanceBetweenCoords(X, Y, GetUnitX(dat.u), GetUnitY(dat.u)) < 10 then
                        call RemoveUnit(dat.orb[i])
                        call DestroyEffect(AddSpecialEffect(EXPLODE_SFX, X, Y))
                        if AOE_HEAL then
                            set DATA = dat
                            set dat.temp = dat.drained[i]
                            call GroupEnumUnitsInRange(g, X, Y, GetHealRange(dat.lvl), Condition(function AlliesOnly))
                            call ForGroup(g, function AOEheal)
                        else
                            call SetWidgetLife(dat.u, GetWidgetLife(dat.u) + dat.drained[i] * GetHealPercentage(dat.lvl))
                            call DestroyEffect(AddSpecialEffectTarget(HEAL_SFX, dat.u, HEAL_SFX_TARGET))
                        endif
                        set dat.orbs = dat.orbs - 1
                        set dat.orb[i] = dat.orb[dat.orbs]
                        set i = i - 1
                    endif
                endif
                set i = i + 1
            endloop
        endif
    //****no orbs left ==> end spell****
        if dat.orbs == 0 then
            call FlushChildHashtable(h, GetHandleId(dat.tim))
            call DestroyTimer(dat.tim)
            call dat.destroy()
        endif
    endfunction

    private function Cast takes nothing returns boolean
        local data dat
        local integer i = 0
        if GetSpellAbilityId() == SPELL_ID then
            set dat = data.create()
            loop
                exitwhen i == 50
                set dat.phase[i] = 0
                set dat.drained[i] = 0
                set dat.delay[i] = 0
                set i = i + 1
            endloop
            set dat.x = GetSpellTargetX()
            set dat.y = GetSpellTargetY()
            set dat.u = GetTriggerUnit()
            set dat.lvl = GetUnitAbilityLevel(dat.u, SPELL_ID)
            call SaveInteger(h, GetHandleId(dat.tim), 0, dat)
            call TimerStart(dat.tim, INTERVAL, true, function Execute)
        endif
        return false
    endfunction

    private function InitTrig_Elven_Orbs takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerAddCondition(t, Condition(function Cast))
        call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    endfunction

endlibrary


//Code indented using The_Witcher's Script Language Aligner
//Download the newest version and report bugs at [url]www.hiveworkshop.com[/url]
I ran the code through the script language aligner so it looks better(imo).
Oh thank you so much man for taking your time to fix it.. The spell seems to work like it should now but I can still see a small problem with it. Since the life-draining is counted as damage, it doesn't seem to drain the same amount of life from your enemies because if your enemies got high armor the amount of life drained will not be the same when it is counted as damage because their high armor reduces damage taken. Also I've been trying to kill enemies with the orbs life drain but they are not able to kill units completely just get them to 1 hit points.. Btw one good thing with the life drain counted as damage is that units who is invulnerable will not lose any hit points at all when the orbs strikes them because they are unable to take damage as invulnerable :) So if I want the amount of life drain to be the same to all units I have to set the damage type to Chaos, right?
 
Level 19
Joined
Jul 14, 2011
Messages
875
Hmm, I was convinced that null, null, null competely ignores armor.
Try call UnitDamageTarget(dat.orb[i], dat.target[i], a, false, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_DIVINE, null).
If you havent changed the gameplay constants for chaos damage, it should ignore both the values and the percentages. The last null is the weapon type, afaik it only changes the sound that is made(I think I mentioned this already, too lazy to search:p) so you could change it to add a bit more detail to the spell.
 
Level 14
Joined
Jul 19, 2007
Messages
772
call UnitDamageTarget(dat.orb[i], dat.target[i], a, false, false, null, null, null)
change to
call UnitDamageTarget(dat.orb[i], dat.target[i], a, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)

damage type universal ignores armor

null == DAMAGE_TYPE_UNIVERSAL
Thank you! Now the draining seems to be like it should but still it will never kill anything completely, just turn enemies Hit Points to 1.. Well I don't think it matters that much. If the life drain works like before, then I'm happy. Atleast one good thing about changing the life draining to count as damage is when enemies are invulnerable because they wont get any life drained when the orbs strikes them but they did get life drained when it wasn't counted as damage but no longer :) Well what about the "false, false, null" who is before the damage type? Also I do not really get to 100% how the healing thing works like :-/
 
Status
Not open for further replies.
Top