• 🏆 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!

Reflections of Light [Paladon] v.1.1.2e

-=Reflections of Light=-
by (c) Paladon

The spell "Reflections of Light" is coded in vJass.
It needs the JassNewGenPack in order to be modified/used properly.
You can get the JNGP here: Click
Description:
Creates a shield of holy energy around the caster.
The energy forms orbs flying in an orbital shape around the hero.
Whenever the hero receives damage, an orb is launched to the enemy
countering the damage dealt. The shield lasts 30 seconds.
Level 1 - 3 orbs, each deals 120% of the initial damage.
Level 2 - 6 orbs, each deals 210% of the initial damage.
Level 3 - 9 orbs, each deals 300% of the initial damage.

Import
To import this spell into your very own map, you need to work with the JNGP named above.
For this spell, you need the "Reflections of Light" trigger (below the Readme)
as well as the DUMMY.mdx out of the Import Editor and the Dummy unit out of the Object Editor.
Furthermore, you need the spell "Reflections of Light" which is located in the Object Editor as well.

Import and copy all these things into your map and adjust the settings in the header of the
"Reflections of Light" trigger to your own needs.

Thankswishing
I`d like to thank Eccho and Hanky for their quick feedback and help concerning things i oversaw.
Thanks also to Dr Super Good, he helped me solving problems concerning event removal.
I additionally thank the clans AuKn@Northrend and NgO@Northrend
as well as all the scripters and coders i`ve worked with in my previous times.
Naming them all here would be just a hilarious long list. But i thought of you all.

Other stuff
The usual ownership conditions apply.
~Paladon



v.0.9.9b
- inofficial release ( 2 versions)

v.1.0.5a
- official release

v.1.0.8a
- added changelog
- replaced a real recylce with a better one
- added chooseable minimal damage amount to trigger it
- launched orbs become removed if their target dies before reaching it

v.1.1.2b
- saved space by the removal of the trigger actions
- added dots behind reals to prevent desyncs
- replaced the damage detection update part with a simple condition
- removed one unitgroup by a struct bypass
- the unit launches now just one orbital at a single unit upon strike, no matter the number of recasts. Recasting the spell on the unit now adds the orbitals to the unit without launching more than one.

v.1.1.2c
- got rid of all trigger actions

v.1.1.2d
- fixed a problem with degrees and radians

v.1.1.2e
- fixed a problem concerning triggering



JASS:
scope ReflectionOfLight initializer SetEvent
    globals
        //! general settings
        private constant integer SpellID       = 'A000' // Spell's rawcode
        private constant integer DummyID       = 'n000' // Dummy's rawcode
        private constant real    Timer         = 0.01   // Interval of the timer
       
        private constant integer InitOrbAmount = 3  // Amount of orbs at level 1
        private constant integer IncrOrbAmount = 3  // Amount of orbs added with every further level
        private constant real    InitTime      = 30. // How long the effect lasts on level 1
        private constant real    AddTime       = 0.  // Amount of time added per level
       
        private constant string OrbModel       = "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl" // Model of the orbs
        private constant string OrbCreation    = "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl" // Model of the "death" animation displayed upon creation of the orbs
        private constant string OrbImpact      = "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl" // Model of the "death" animation displayed upon impact on enemied units
        private constant string DummyAttach    = "chest" // Attachment point oof the effects on the "Dummy" model as well as the attachment point used on the enemy
       
        private constant real OrbitalSpeedMin  = 2.05 // Defines the minimal orbital speed
        private constant real OrbitalSpeedMax  = 2.55 // Defines the maximal orbital speed
       
        private constant real OrbitalAngSpMin  = -0.55 // Defines the minimal speed of the spin of the orbital movement
        private constant real OrbitalAngSpMax  = 0.55  // Defines the maximal speed of the spin of the orbital movement
       
        private constant real OrbitalSizeMin   = 110.   // Defines the minimal size of the orbital movement of a single orb
        private constant real OrbitalSizeMax   = 150.   // Defines the maximal size of the orbital movement of a single orb
       
        private constant real OrbitalHeightMin = 45.    // Defines partially the height of the orbital (minimal).
        private constant real OrbitalHeightMax = 60.    // Defines partially the height of the orbital (maximal).
         //!I recommend playing around with this value for ground units to find something fitting. For air units, i recommend simply "0".
       
        private constant real MinDamageCounter = 5. // Returns the minimal damage necessary to release a countering orb
       
        private constant real ReleasedSpeed    = 4.5   // The flying speed of a released orb.
        private constant real PercCounterInit  = 120.   // The percentage of damage returned at level 1
        private constant real PercCounterAdd   = 90.   // The percentage of damage to return added for every further level
       
        private constant attacktype ATT        = ATTACK_TYPE_NORMAL   // The attacktype of the damage dealt
        private constant damagetype DGT        = DAMAGE_TYPE_NORMAL   // The damagetype of the damage dealt
        private constant weapontype WPT        = WEAPON_TYPE_WHOKNOWS // The weapontype of the damage dealt
        
//!*******************************END OF THE SETTINGS*************************!\\
//**Do not modify anything below this line unless you know what you are doing**\\
//!***************************************************************************!\\
        private real array R
        private trigger DmgSys = CreateTrigger()
        private group DSys = CreateGroup()
    endglobals

    private struct S
        private unit    h
        private unit    o
        private unit    t
        private integer l
        private integer s
       
        private real d1
        private real d2
        private real a1
        private real t1
        private real t2
       
        private real OSp
        private real OAS
        private real OSi
        private real OHe
        private real OEx
       
        private effect fx
       
        private static S array indx
        private static integer counter = 0
        private static timer   time    = CreateTimer()
           
            static method ChangeStruct takes nothing returns boolean    
                //! This function interrupts the struct and adjusts the orb's movement type.
                local S d
                local integer i = 0
                local integer ii = 0
                local unit u = GetTriggerUnit()
                set R[0] = GetEventDamage()
                if R[0] >= MinDamageCounter then
                    loop
                        exitwhen i >= S.counter
                        set d = S.indx[i]
                        if d.h == u and d.s == 1 then
                            //! An orb flying in an orbital shape around the attacked hero is detected here.
                            //! d.s = 1 means orbital movement
                            //! d.s = 2 means movement to a target
                            set d.s  = 2
                            set d.t  = GetEventDamageSource()
                            set d.d2 = R[0]*((PercCounterInit+(PercCounterAdd*d.l))/100)
                            set R[0] = 0
                            set i = S.counter
                        endif  
                        set i = i + 1
                    endloop
                endif
                set u = null
                return true
            endmethod
           
            static method Execution takes nothing returns nothing
                local S d
                local integer i = 0
                local unit u
                loop
                    exitwhen i >= S.counter
                    set d = S.indx[i]
                   
                    if d.s == 0 then
                        //! Remove & Recycle
                        call DestroyEffect(d.fx)
                        call RemoveUnit(d.o)
                        set  d.h      = null
                        set  d.o      = null
                        set  d.t      = null
                        call d.destroy()                       
                        set  S.counter = S.counter - 1
                        set  S.indx[i] = d.indx[S.counter]                       
                        set  i = i - 1   

                    elseif d.s == 1 then
                        //! The formula for the orbital movement (by me, i've been asked this already before the official release)
                        set d.t1 = d.t1 + Timer
                        set d.a1 = d.a1 + d.OAS
                        set d.d1 = d.d1 + d.OSp
                        set R[0] = (d.d1/d.OEx)*bj_DEGTORAD
                        set R[1] = d.OSi*Sin(R[0])
                        set R[2] = d.a1*bj_DEGTORAD
                        call SetUnitX(d.o,GetUnitX(d.h)+R[1]*Cos(R[2]))
                        call SetUnitY(d.o,GetUnitY(d.h)+R[1]*Sin(R[2]))
                        call SetUnitFlyHeight(d.o,(d.OSi-d.OHe)*Cos(R[0])+d.OHe+GetUnitFlyHeight(d.h),0)
                        set R[2] = 0
                        //! Since the orbs move with a circular movement, we can reset the angle.
                        if d.a1 >= 360 then
                            set d.a1 = d.a1 - 360
                        endif
                       
                        if d.t1 >= d.t2 or GetUnitState(d.h,UNIT_STATE_LIFE) == 0 then
                            set d.s = 0
                        endif
                    elseif d.s == 2 then
                        //! Launch and move the missile
                       
                        //! X/Y Movement
                        set R[0] = GetUnitX(d.t)
                        set R[1] = GetUnitY(d.t)
                        set R[2] = GetUnitX(d.o)
                        set R[3] = GetUnitY(d.o)
                        set R[4] = Atan2(R[1]-R[3],R[0]-R[2])  
                        call SetUnitX(d.o,R[2] + ReleasedSpeed * Cos(R[4]))
                        call SetUnitY(d.o,R[3] + ReleasedSpeed * Sin(R[4]))
                       
                        //! Z Movement
                        set R[0] = R[2] - R[0]
                        set R[1] = R[3] - R[1]
                        set R[2] = SquareRoot(R[0]*R[0]+R[1]*R[1])
                        set R[0] = GetUnitFlyHeight(d.t)
                        set R[1] = GetUnitFlyHeight(d.o)
                        set R[0] = R[0]-R[1]
                        set R[0] = R[0]/(R[2]/ReleasedSpeed)
                        call SetUnitFlyHeight(d.o,R[1]+R[0],0)

                        //! Exit
                        if R[2] <= ReleasedSpeed then
                            set d.s = 0
                            call DestroyEffect(AddSpecialEffectTarget(OrbImpact,d.t,DummyAttach))
                            call UnitDamageTarget (d.h,d.t,d.d2,true,false,ATT,DGT,WPT)
                        elseif GetUnitState(d.t,UNIT_STATE_LIFE) == 0 then
                            set d.s = 0
                        endif
                    endif
                    set i = i + 1
                endloop
                set u = null
                set i = 0
                if S.counter == 0 then
                    call PauseTimer(S.time)
                endif
            endmethod
           
            static method SetStruct takes unit h, real a1, real a2, real t2, integer l returns nothing
                //! This method mainly inits the orbital movement data and determines the movement shape
                local S d = S.allocate()
                set d.h   = h
                set d.s   = 1
                set d.l   = l
                set d.d1  = 0
                set d.a1  = (360/a1)*a2
                set d.t1  = 0
                set d.t2  = t2
                set d.OSp = GetRandomReal(OrbitalSpeedMin,OrbitalSpeedMax)
                set d.OSi = GetRandomReal(OrbitalSizeMin,OrbitalSizeMax)
                set d.OAS = GetRandomReal(OrbitalAngSpMin,OrbitalAngSpMax)*bj_DEGTORAD
                set d.OHe = GetRandomReal(OrbitalHeightMin,OrbitalHeightMax)
                set d.OEx = d.OSi/90
                set   d.o = CreateUnit(GetOwningPlayer(h),DummyID,GetUnitX(h),GetUnitY(h),0)
                call UnitAddAbility(d.o,'Arav')
                call UnitRemoveAbility(d.o,'Arav') 
                call DestroyEffect(AddSpecialEffectTarget(OrbCreation,d.o,DummyAttach))
                set d.fx = AddSpecialEffectTarget(OrbModel,d.o,DummyAttach)
                if S.counter   == 0 then
                    call TimerStart(S.time,Timer,true,function S.Execution)
                endif
                set S.indx[S.counter] = d
                set S.counter = S.counter + 1
            endmethod
           
            static method Create takes nothing returns nothing
                //! The hero is added to the damage detection
                local unit u = GetTriggerUnit()
                if not IsUnitInGroup(u,DSys) then
                    call GroupAddUnit(DSys,u)
                    call TriggerRegisterUnitEvent(DmgSys,u,EVENT_UNIT_DAMAGED)
                endif
                //! Create the orbs
                set   R[0]   = 0
                set   R[2]   = GetUnitAbilityLevel(u,SpellID)-1
                set   R[1]   = InitOrbAmount+(IncrOrbAmount*R[2])
                loop
                    exitwhen R[0] == R[1]
                    call S.SetStruct(u,R[1],R[0],InitTime+(AddTime*R[2]),R2I(R[2]))
                    set R[0] = R[0] + 1
                endloop
            endmethod
    endstruct
   
    private function condition takes nothing returns boolean
        if GetSpellAbilityId() == SpellID then
            call S.Create()
        endif
        return true
    endfunction
   
    private function SetEvent takes nothing returns nothing
        //! Trigger init
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
        call TriggerAddCondition(t, Condition(function condition))
        call TriggerAddCondition(DmgSys,Filter(function S.ChangeStruct))
        //! Preloading effects
        call Preload(OrbCreation)
        call Preload(OrbImpact)
        call Preload(OrbModel)
        call PreloadStart()
    endfunction
endscope


Keywords:
Light, Holy, Magic, Shield, Absorb, Counter, Hero, Might, Power, Unleash, launch, Orbit, Missile, Orb, Movement, Formula, Paladin, Church, Priest, Dam
Contents

Reflections of Light [Paladon] (Map)

Reviews
12:02, 12th Sep 2009 Hanky: Good job Paladon. In my opinion the spell looks really amazing and the coding is just nice. And also the use of special effects is nicely done. So I have no reason why I shouldn't approve this spell. I just can...

Moderator

M

Moderator

12:02, 12th Sep 2009
Hanky:
Good job Paladon. In my opinion the spell looks really amazing and the coding is just nice. And also the use of special effects is nicely done.

So I have no reason why I shouldn't approve this spell. I just can highly recommend to use this spell to all users who are searching for something like this.
 
Level 17
Joined
Mar 17, 2009
Messages
1,349
=D checking...

EDIT:
Nicely done :)

Here are somethings you could do:
JASS:
                        if d.a1 >= 360 then
                            set d.a1 = 0
                        endif

Should be one of the following two:
JASS:
                        if d.a1 >= 360 then
                            set d.a1 = d.a1 - 360
                        endif
Or instead of if/then when adding the value to d.a1:
JASS:
set d.a1 = ModuloReal(d.a1 + d.OAS, 360.)


Instead of:
JASS:
GetUnitState(d.h,UNIT_STATE_LIFE) == 0
Use IsUnitType(d.h, UNIT_TYPE_DEAD) (this is safer and bugless).


That's all I could locate through my skim... impressive work and wow, very good coding there =)
 
Level 15
Joined
Jul 6, 2009
Messages
889
One phrase. Maths. GO AWAY :p

SPELL:
-Nice. I thought the models you used were imports, but they weren't.... they look really good!
-Maybe you should make a texttag displaying how much damage the orb dealt.
-Also, what will happen with dummy units that cast a damaging spell? What will happen to the orb? Will it shoot at empty air?

CODING:
I don't have anything to say about the coding, looks fine.
 
Level 15
Joined
Jan 31, 2007
Messages
502
Instead of:
JASS:
GetUnitState(d.h,UNIT_STATE_LIFE) == 0
Use IsUnitType(d.h, UNIT_TYPE_DEAD) (this is safer and bugless).

I always use GetUnitState for checking if its dead and i never ever had any problems with it, but as far as i know IsUnitType has some disadvantages too

but youre right that this useage should be improved
1. Do not use ==
2. Do not use "0.00" due a unit is dies when reaching below 0.405 health !

GetUnitState(d.h,UNIT_STATE_LIFE) <= 0.405

second thing i saw is your use of the Damage System
didnt really take a deep look into it but as i unterstood it you got a global trigger and group for that, and due you cannot "remove" events you remove the unit from the group and update the trigger with recrating it...
i doupt that this really is the most efficient way

dunno but i would have added the unit to a global event when learning the spell and adding/removing it from the group and checking in the actions if its in the group ( but it would just stack up the missiles and stop triggering it multiple times per single hit with casting it more than once at the same time)

i would do so because i think there wont be a huge amount of units using this epic spell ingame and if they do so they will use it more than once i guess
but i guess your way is also good

anyway - good idea - great coding - awesome Paladon x)

Edit :
One phrase. Maths. GO AWAY :p

SPELL:
-Nice. I thought the models you used were imports, but they weren't.... they look really good!
-Maybe you should make a texttag displaying how much damage the orb dealt.
-Also, what will happen with dummy units that cast a damaging spell? What will happen to the orb? Will it shoot at empty air?

CODING:
I don't have anything to say about the coding, looks fine.

lol, imported models? i didnt see any
there just a Dummy

But your third thig is really something to think about

i guess you should add a minimum amount of damage to grigger it
else each Fire Rain oder whatever will drain all your spheres in seconds due the dps it deals :(
i guess the damage will always have a "dealing" unit so e.g. Shawo Strik wont bug due its delayed damage
but i would want to know what happens if the caster of a spell is dead and it triggers it afterwards, ill test it ;D
edit² , yeah - You do not cheeck if the "target" is alive when shooting the missiles
this looks strange if its a delayed damage and the caster is already dead and the missiles are flying into nowhere

I also tested it with thorn aura and its imba D; shoots all spheres after one attack to the target due retriggering
 
Level 17
Joined
Mar 17, 2009
Messages
1,349
-JonNny said:
but youre right that this useage should be improved
1. Do not use ==
2. Do not use "0.00" due a unit is dies when reaching below 0.405 health !
This time I get to correct you :p when the health reaches 0.405 it is automatically dropped to 0 by the game, so what he did works too :p

-JonNny said:
I always use GetUnitState for checking if its dead and i never ever had any problems with it, but as far as i know IsUnitType has some disadvantages too
There's one issue with GetUnitState if Morphing root ability is used on the picked unit... but I can't remember quite well what it is, something with life increasing slightly...
 
Level 9
Joined
Nov 25, 2008
Messages
194
Meh just a dmg return^^

No seriously good job.

i guess you should add a minimum amount of damage to grigger it

I totally agree to this. If you have for example a DoT running every .01 sec you're orbs are gone quite fast ;)

A kinda strange thing happens, if you have two enemy casters and both cast it, and then one attacks. They sort of get healed and damaged then Oo

About the dead stuff:
Wouldn't work
JASS:
GetWidgetLife(d.h) <= .405
just as fine as GetUnitState?
 
Level 30
Joined
Dec 6, 2007
Messages
2,228
Should be one of the following two:
JASS:
                        if d.a1 >= 360 then
                            set d.a1 = d.a1 - 360
                        endif
Or instead of if/then when adding the value to d.a1:
JASS:
set d.a1 = ModuloReal(d.a1 + d.OAS, 360.)

Great idea. Thanks.

One phrase. Maths. GO AWAY :p
After all, it took me a week to figure out a proper formula. I guess a couple of people here are better and faster at this stuff than me.

1. Do not use ==
And what instead?
second thing i saw is your use of the Damage System
didnt really take a deep look into it but as i unterstood it you got a global trigger and group for that, and due you cannot "remove" events you remove the unit from the group and update the trigger with recrating it...
i doupt that this really is the most efficient way

I discussed the whole problem with dsg yesterday. We agreed that it is probably the best possible way (still a bad one) to do this. I could simply leave the event as well, just checking whether the hero has orbs, but it's generally safer this way. Even if the removal of the event wouldn't work, the spell can't bug since i check for available orbs every time.

i guess you should add a minimum amount of damage to grigger it

Okay, that would also rmeove the thorns aura and other problems. Thanks :)

You do not cheeck if the "target" is alive when shooting the missiles
this looks strange if its a delayed damage and the caster is already dead and the missiles are flying into nowhere

Simple thing to change, thx.

Spell stays as it is only that once particle hits the enemy it returns to the caster and it heals him for small amount of life. Dunno if you like the idea =)

Cool idea.

Thank you for the feedback :D




Edit: Updated to version 1.0.8a, added changelog.
 
Last edited:
Level 8
Joined
Aug 4, 2006
Messages
357
Well I was planning on testing it, but there's a thread on that which is pretty convincing so i didn't :)

IsUnitDead
:grin:

Paladon, this is a good spell. Nice to see you working in vJASS. I can only think of a few constructive comments for you.
1) You should precede comments with "//" not "//!". Sometimes "//!" is used to do commands like //! runtextmacro.
2) I hate one (or two or three) letter variable names, especially when you don't leave comments telling us what they do. Having shorter code does not make up for the benefits of having readable code. It benefits the creator just as much as the user. It would be nice if you could replace these with real variable names.
3) In my opinion, the orbitting looks a little sloppy. I haven't bothered to look at the math behind it yet, but if I see something that can be improved, I'll let you know.
 
Level 16
Joined
Oct 12, 2008
Messages
1,570
Do not use ==
And what instead?

==true is useless, as you can just put the boolean or comparison there. and ==false is also useless as we have got not.

examples:
JASS:
local boolean b = true
if b == true then // Useles!
    // do stuff
endif
if b then // does the exact same!
    // do stuff
endif

//

local boolean b = false
if b == false then // Useless!
    //do stuff
endif
if not b then // does, again, the exact same
    // do stuff
endif
ONLY use ==true and ==false when you use IsUnitType() that is NOT in a boolexpr. Because it somehow bugs when it is used outside Boolexpr, ask Poot for full explanation.
 
Level 14
Joined
Jan 15, 2007
Messages
349
Somehow I like it :) good job Paladon, I'll try to check the code as soon as possible...

Ok I reviewed the code and there are some things that are disturbing me:
  • First off instead of the TriggerAction just use a condition since if you use the condition it will be faster. If you don't understand what I meant just ask me.
  • Well you could replace the BJ function ("TriggerRegisterAnyUnitEventBJ") with a loop, but since it's just executed once at the init it isn't that bad to keep it.
  • Just as hint you should use dots behind numbers of the type real. Since I heart that there could appear errors if you don't but those just appear very rarely.
  • Your "damage detection system" or whatever you want to call it seems to me abit ineffective. By ineffective I mean the "update" part. Maybe using an external "damage detection system" would be better.
  • Also I was wondering why you set the Loop group to the DmgG group since that's pretty senseless. If you think by making the action "set Loop = DmgG" that you can "clone" the DmgG group for the Loop group then you are wrong. You have to use the function "GroupAddGroup".

That's all for now. If you fixed the above just PM me. ;)
 
Level 17
Joined
Mar 17, 2009
Messages
1,349
Hanky said:
Just as hint you should use dots behind numbers of the type real. Since I heart that there could appear errors if you don't but those just appear very rarely.
They appear in occasions when you want to return a real.
JASS:
function Whatev takes nothing returns real
     return 5
endfunction
This would return an error.

JASS:
function Whatev takes nothing returns real
     return 5.
endfunction
This works.

JASS:
function Whatev takes nothing returns real
     return 2 + 3.
endfunction
This works.

I can't recall any other case, however.


Hanky said:
Maybe using an external "damage detection system" would be better.
I agree on that :)
 
Level 30
Joined
Dec 6, 2007
Messages
2,228
Ok I reviewed the code and there are some things that are disturbing me:
  • First off instead of the TriggerAction just use a condition since if you use the condition it will be faster. If you don't understand what I meant just ask me.

  • PM me that please in German :p
    [*]Well you could replace the BJ function ("TriggerRegisterAnyUnitEventBJ") with a loop, but since it's just executed once at the init it isn't that bad to keep it.
    I leave it as it is, i have been notified by Purple Poot that the replacement is senseless :p
    I used a loop in my earlier jass spells as well.

    [*]Just as hint you should use dots behind numbers of the type real. Since I heart that there could appear errors if you don't but those just appear very rarely.
    I guess this bug is cleared (Deut's post).

    [*]Your "damage detection system" or whatever you want to call it seems to me abit ineffective. By ineffective I mean the "update" part. Maybe using an external "damage detection system" would be better.
    A general damage detection system is senseless for this use. The update part is indeed not a good looking way but probably the one and only possibility to prevent the system running all the time.
    The other possibility would be a system running all the time which is another effectivity decreasement. I talked a while with dsg about the problem and it's probably the only solution next to an all-time system.

    [*]Also I was wondering why you set the Loop group to the DmgG group since that's pretty senseless. If you think by making the action "set Loop = DmgG" that you can "clone" the DmgG group for the Loop group then you are wrong. You have to use the function "GroupAddGroup".

Weird. If the set group1 = group2 doesnt work, the spell would somehow not work since the clone is filtered for the units. But i will change that nonetheless.

Thank you :)
 
Last edited:
Level 14
Joined
Jan 15, 2007
Messages
349
Yes I don't know much about this one, but Im pretty sure that I heard it somewhere at wc3c. But Im not 100% sure, since I never tried if there really could appear desyncs (also this one just appear very rarely as far I heard it).
 
Level 30
Joined
Dec 6, 2007
Messages
2,228
Updated, i made a couple of changes and improvements. Update mainly includes:

v.1.1.2b
- saved space by the removal of the trigger actions
- added dots behind reals to prevent desyncs
- replaced the damage detection update part with a simple condition
- removed one unitgroup by a struct bypass
- the unit launches now just one orbital at a single unit upon strike, no matter the number of recasts. Recasting the spell on the unit now adds the orbitals to the unit without launching more than one.
v.1.1.2c
- got rid of all trigger actions
 
Last edited:
Level 30
Joined
Dec 6, 2007
Messages
2,228
It would fly into empty air. And i don't mind changing it. Why? Simple:
Whenever you have a dummy unit casting a spell, your hero never gets the full experience and the credits for the kill. Always add damage by triggers if you use own spells.
Besides, there's no possibility since it's a dummy and not the unit.

By the way, if the damage is dealt by a trigger, the orb directly flies to the caster.
 
Level 16
Joined
Oct 12, 2008
Messages
1,570
Ok, i finally had the time to look through the whole code.
The spell is really well-coded and good, but i found a flaw, which doesnt seem to affect the performance, but is maths-wise a big flaw.


In the SetStruct method, you have this line:
JASS:
set d.a1  = (360/a1)*a2
This is an angle, and as there is a 360, i assume that you use degrees.
This also made me assume you used degrees, method Execution: (Under elseif d.s == 1 then)
JASS:
if d.a1 >= 360 then
    set d.a1 = d.a1 - 360
endif
Now we know you use degrees, you need to know that most natives (not all) take radians, whereunder Cos,Sin and Tan.
Now here we see you use some Cos and Sin functions.
JASS:
set R[1] = d.OSi*Sin(R[0])
call SetUnitX(d.o,GetUnitX(d.h)+R[1]*Cos(d.a1))
call SetUnitY(d.o,GetUnitY(d.h)+R[1]*Sin(d.a1))
call SetUnitFlyHeight(d.o,(d.OSi-d.OHe)*Cos(R[0])+d.OHe+GetUnitFlyHeight(d.h),0)
Since Cos and Sin take radians, and d.a1 is in degrees, this is where the flaw is made.

As i said, it didnt affect performance, but it is a major flaw if you think logically.


I dont know if it is intentional, but i saw this when i read the code.

Good job on the spell!!!
 
Level 17
Joined
Sep 8, 2007
Messages
994
The spell is PURE SEX. The coding style ... meh, matter of taste :p
If you wanna make it professional you'd better use CAPSLOCK for constants and give the variables proper names instead of "h" and "o".
But whatever ... as I said, the spell is PURE SEX so 5/5 and rep from me!! :)
 
Level 19
Joined
Feb 4, 2009
Messages
1,313
it looks weird on cliff edges so adding + GetLocZ somewhere would be useful
and short variable names are very nice cause such a long spell script needs kb too (not very much but it adds up)
however some comments describing what they are used for would be suitable (though I know as being a mapper myself that comments and descriptions and so on can be a pain in the ass)

just wrote that so I can vote it :) 5/5
 
Level 11
Joined
May 13, 2010
Messages
167
Awesome spell, 5/5 + rep

I got a problem, though. The spell effect (i mean the orbs) are not appearing ingame, but the effect on impact with the enemy is. What am i doing wrong? I noticed this also happend with a couple of other imported vJASS spells.. I smell incompatibility issues? Cheers!


EDIT: Don't even bother, i worked it out.
 
Last edited:
Level 12
Joined
Feb 27, 2019
Messages
399
Hello,

I tried with version 1.30.4 and 1.31, I cannot launch your test map. In game, when you select the map from your maps folder it shows a pop-up that says it couldn't read the map file. From editor, when you launch the map an empty error pop-up shows up.

Is it only compatible with older versions ? :)
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
Hello,

I tried with version 1.30.4 and 1.31, I cannot launch your test map. In game, when you select the map from your maps folder it shows a pop-up that says it couldn't read the map file. From editor, when you launch the map an empty error pop-up shows up.

Is it only compatible with older versions ? :)
Make sure your JassHelper is enabled when saving his map. I don't think it should cause an error otherwise. Also, that your map script is compiled with JASS and not Lua.
 
Top