HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Sacred Circle [JESP]

04-14-2007, 02:33 AM#1
Daxtreme
I've been working on this spell for a while and I decided it would make out something great.

So, here it is!

Description:

"Creates an expanding circle of holy energy, damaging every enemy unit caught."

Simply put, here's a screenshot!



The spell

- It is MUI
- It is in JASS
- Leakless (Should be)
- Very low lag
- It uses Handle vars system, I explain in the implementation details how to implement it.

Steps for implementation are listed. There's also a configuration header.

--------------------------------------

I made this at first for myself but then I realized the whole community could use it, because I feel it is good.

Tell me what you think!

P.S. If by any means this isn't JESP, tell me and I'll fix it instantly. Shouldn't happen. I read like 10 times the JESP standard :P

*Credits to JetFangInferno for the Holy Strike model and KaTTaNa for the Local Handle Vars system!*

EDIT: Update 1:

- No more Divine Revenant.
- Improved the code.
- Gave credits in numerous spots! :P

EDIT: Update 2:

- No more CREDITS for the Divine Revenant. :P
- Improved the code again, a lot.

EDIT: Update 3:

- Improved the code again.

EDIT: Update 4:

Apparently an enemy check bug made it in last version. Updated once again.
Attached Images
File type: jpgSacred Circle Screemshot.JPG (102.8 KB)
Attached Files
File type: w3xSacred Circle.w3x (39.5 KB)
04-14-2007, 01:10 PM#2
Monogrox
I know nothing about JESP,but it looks good
04-14-2007, 01:43 PM#3
WILL THE ALMIGHTY
Looks sweet.
04-14-2007, 01:43 PM#4
Vexorian
I don't think it is good to have spaces in codename.

Also get rid of Trig_ in one function's name.

We don't like models in the spell.
- The models should be yours, are you the author of the models? I would say not. If the models are not yours you need Specific permission for redistribution, that you found a model in a downloads area does not mean the author is releasing it for public domain and does not mean you can redistribute it.
- It is not really a great idea to have non-spell specific models, why the hero model/skin?

The spell itself has issues:
- In Trig_Sacred_Circle_Actions you never initialize the nl location yet you "destroy" it, then the final DestroyTrigger doesn't run. Then it leaks.
- If that cleaning worked, you would be destroying the trig twice. You could pretty much get rid of
Collapse JASS:
    call TriggerSleepAction(Sacred_Circle_Duration(i))
    call FlushHandleLocals(t)
    call PauseTimer(t)

    call RemoveLocation(l)
    call RemoveLocation(nl)
    call DestroyTrigger(trig)
And let the destruction for the timer loop.

Are you sure using "Handle Variables" and "least leaks/Lag as possible" are compatible terms?

in Trig_Sacred_Circle_Actions_Loop you create a Rect yet you never clean it.
04-14-2007, 07:31 PM#5
Daxtreme
> Are you sure using "Handle Variables" and "least leaks/Lag as possible" are compatible terms?

Notice the "as possible" :P

> The models should be yours, are you the author of the models? I would say not. If the models are not yours you need Specific permission for redistribution, that you found a model in a downloads area does not mean the author is releasing it for public domain and does not mean you can redistribute it.

The author of the small holy explosion graphics said so, as long as you give credits. As for the revenant...

> - It is not really a great idea to have non-spell specific models, why the hero model/skin?

Good point. Removing it. The model also disappeared from the net (The author removed it recently) so I think it means he doesn't want people to redistribute it.

Quote:
The spell itself has issues:
- In Trig_Sacred_Circle_Actions you never initialize the nl location yet you "destroy" it, then the final DestroyTrigger doesn't run. Then it leaks.
- If that cleaning worked, you would be destroying the trig twice. You could pretty much get rid of
Collapse JASS:
    call TriggerSleepAction(Sacred_Circle_Duration(i))
    call FlushHandleLocals(t)
    call PauseTimer(t)

    call RemoveLocation(l)
    call RemoveLocation(nl)
    call DestroyTrigger(trig)

And let the destruction for the timer loop.

Done.

There is but one thing I didn't get:

what do you mean by "I don't think it is good to have spaces in codename." ?

The spell has been edited according to everything mentionned in your post, except that. Will attach the map once I fix this last little thing :)
04-15-2007, 01:33 AM#6
Hero12341234
Daxtreme your on this site also?

anyways look like a holy nova to me..lol

it's cool

+rep
04-15-2007, 01:35 AM#7
Vexorian
Quote:
Originally Posted by Daxtreme
> Are you sure using "Handle Variables" and "least leaks/Lag as possible" are compatible terms?

Notice the "as possible" :P

> The models should be yours, are you the author of the models? I would say not. If the models are not yours you need Specific permission for redistribution, that you found a model in a downloads area does not mean the author is releasing it for public domain and does not mean you can redistribute it.

The author of the small holy explosion graphics said so, as long as you give credits. As for the revenant...

> - It is not really a great idea to have non-spell specific models, why the hero model/skin?

Good point. Removing it. The model also disappeared from the net (The author removed it recently) so I think it means he doesn't want people to redistribute it.



Done.

There is but one thing I didn't get:

what do you mean by "I don't think it is good to have spaces in codename." ?

The spell has been edited according to everything mentionned in your post, except that. Will attach the map once I fix this last little thing :)
JESP codename should not have "spaces" , name of the trigger should match codename, yet in your case name has spaces and then the actual codename is using underscores ... Don't have spaces there please


Quote:
The author of the small holy explosion graphics said so, as long as you give credits.

Well did he give you permission for using it in an spell map you are gonna put for download or was it something like "use it in your map as long as you give credits" that's more for an actual map.
04-15-2007, 03:44 AM#8
moyack
Quote:
Originally Posted by Daxtreme
There is but one thing I didn't get:

what do you mean by "I don't think it is good to have spaces in codename." ?

Vexorian means to change the spell name from this: Sacred_Circle_RawCode to this: SacredCircle_RawCode
It looks more like a prefix for the spell.

Some other things to improve. In this part in the function "Trig_Sacred_Circle_Actions", you use the local location l to get the x, y coordinates of the caster, it can be avoided perfectly using this:
Collapse JASS:
    local timer t = CreateTimer()
    local trigger trig = CreateTrigger()
    local unit cast = GetTriggerUnit()
    local unit u
    local player p = GetOwningPlayer(cast)
    //local location l = GetUnitLoc(cast) <= it's not necessary anymore
    local real lx = GetUnitX(cast) // <= better :)
    local real ly = GetunitY(cast) 
    local location nl // Where do you use this location???
    local real angle = 0.00
    local integer i = GetUnitAbilityLevel(cast, Sacred_Circle_RawCode())
    local real r = 0.00

With this you improve your code a little bit. Other optimization can be done in the inittrig function. You can set the trigger as a local variable, making easier to port the spell in another map. Other important thing is to preload the effect in order to avoid the first time lag. it can be done in this way:

Collapse JASS:
function InitTrig_SacredCircle takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Trig_SacredCircle_Conditions ) )
    call TriggerAddAction( t, function Trig_SacredCircle_Actions )
    call Preload(SacredCircle_EffectArt())
    set t = null
endfunction

I'll be pending of the new version.
04-15-2007, 05:23 PM#9
blu_da_noob
And in the loop actions, you get your stored X/Y, convert them to a location so you can use the BJ projection on it and then get the X/Y from that location :S. Do it directly. (Ok you use the location too, but rather use coords for those things and skip locations completely)
You also use a rect for doing the damage, any reason? (and it is never destroyed and leaks at that)
04-15-2007, 05:44 PM#10
JetFangInferno
where are the credits for using my model? o.o!!
04-15-2007, 06:31 PM#11
Daxtreme
Quote:
Originally Posted by JetFangInferno
where are the credits for using my model? o.o!!

You have to test (play) the map in order to see them. Want me to put them everywhere? :)

Quote:
Originally Posted by blu_da_noob
And in the loop actions, you get your stored X/Y, convert them to a location so you can use the BJ projection on it and then get the X/Y from that location :S. Do it directly. (Ok you use the location too, but rather use coords for those things and skip locations completely)
You also use a rect for doing the damage, any reason? (and it is never destroyed and leaks at that)

I used x,y because GetHandleLocation returned (0.00, 0.00) no matter what I did. I posted that on the wc3jass forums too, the fact that I couldn't pass locations using the handles system : /

Never understood why, so I pass x,y coordinates now.

The rect is currently being removed in my version of the map, I just waited to upload it again because I didn't know what Vex meant by spaces in codenames. Now I know, thanks guys :)

Quote:
Originally Posted by moyack
Vexorian means to change the spell name from this: Sacred_Circle_RawCode to this: SacredCircle_RawCode
It looks more like a prefix for the spell.

Some other things to improve. In this part in the function "Trig_Sacred_Circle_Actions", you use the local location l to get the x, y coordinates of the caster, it can be avoided perfectly using this:
Collapse JASS:
    local timer t = CreateTimer()
    local trigger trig = CreateTrigger()
    local unit cast = GetTriggerUnit()
    local unit u
    local player p = GetOwningPlayer(cast)
    //local location l = GetUnitLoc(cast) <= it's not necessary anymore
    local real lx = GetUnitX(cast) // <= better :)
    local real ly = GetunitY(cast) 
    local location nl // Where do you use this location???
    local real angle = 0.00
    local integer i = GetUnitAbilityLevel(cast, Sacred_Circle_RawCode())
    local real r = 0.00

With this you improve your code a little bit. Other optimization can be done in the inittrig function. You can set the trigger as a local variable, making easier to port the spell in another map. Other important thing is to preload the effect in order to avoid the first time lag. it can be done in this way:

Collapse JASS:
function InitTrig_SacredCircle takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function Trig_SacredCircle_Conditions ) )
    call TriggerAddAction( t, function Trig_SacredCircle_Actions )
    call Preload(SacredCircle_EffectArt())
    set t = null
endfunction

I'll be pending of the new version.

Thx for your inputs. It is well appreciated :) Changes are being made according to your comments.

JetFangInferno, knowing now would be cool! Can I use your model in my spell? :) Don't forget I gave you credits, but one needs to test the spell in order to see them >_< I can give credits in the loading screen also, and on this site! :)

EDIT:

> local location nl // Where do you use this location???

Nowhere actually :P Removed. I think I used it before, and forgot to remove it when It wasn't needed anymore.

EDIT#2: Updated.
04-19-2007, 01:09 AM#12
JetFangInferno
sure lol xD u can mention me here too =)
04-21-2007, 03:03 AM#13
moyack
Checked your new version.
  • You haven't changed the GetLocationX/Y for GetUnitX/Y
  • still you have the unnecessary location nl in the SacredCircle_Actions function.
  • I'm checking this part of the code:
    Collapse JASS:
    function SacredCircle_Actions_Loop takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local trigger trig = GetHandleTrigger(t, "trig")
        local unit cast = GetHandleUnit(t, "cast")
        local real lx = GetHandleReal(t, "lx")
        local real ly = GetHandleReal(t, "ly")
        local location l = Location(lx, ly)
        local real angle = GetHandleReal(t, "angle")
        local real r = GetHandleReal(t, "r")
        local player p = GetOwningPlayer(cast)
        local unit targ
        local effect e
        local location nl = PolarProjectionBJ(l, 50.00 + r * 10.00, angle)
        local integer i = GetHandleInt(t, "i")
        local group g = CreateGroup()
    //    local rect targetrect = Rect(GetLocationX(nl) - 100.00, GetLocationY(nl) - 100.00, GetLocationX(nl) + 100.00, GetLocationY(nl) + 100.00)
        local rect targetrect = Rect(0.00, 0.00, 200.00, 200.00)
        
        if GetTriggerEvalCount(trig)>0 then
            call RemoveLocation(l)
            call RemoveLocation(nl)
            call DestroyTrigger(trig)
            call DestroyGroup(g)
            call RemoveRect(targetrect)
            set targetrect = null
            set trig = null
            set cast = null
            set l = null
            set p = null
            set nl = null
            set g = null
            call FlushHandleLocals(t)
            call PauseTimer(t)
            return
        endif            
        set e = AddSpecialEffectLoc(SacredCircle_EffectArt(), nl)
        call MoveRectToLoc(targetrect, nl)
        call GroupEnumUnitsInRect(g, targetrect, null)
        loop
            set targ = FirstOfGroup(g)
            exitwhen targ == null
            if targ != cast and SacredCircle_CheckAlly(targ, p) and SacredCircle_CheckMagicImmune(targ) and not IsUnitDeadBJ(targ) then
            call UnitDamageTarget(cast, targ, SacredCircle_StrikeDmg(i), true, false, SacredCircle_AttackType(), SacredCircle_DamageType(), SacredCircle_WeaponType())
            endif
            call GroupRemoveUnit(g, targ)
        endloop  
        set angle = angle + 24.00 - r / 6.00       // Helps preventing distance vs. angle mitigation.
        set r = r + 1.00 
        call SetHandleReal(t, "angle", angle)
        call SetHandleReal(t, "r", r)
        call DestroyGroup(g)
        call DestroyEffect(e)
        call RemoveLocation(l)
        call RemoveLocation(nl)
        call RemoveRect(targetrect)
        set targetrect = null
        set l = null
        set targ = null
        set e = null
        set g = null
        set p = null
        set cast = null
    endfunction

    In order to avoid locations and the rect var, I suggest to use this functions instead:
    • For the FX: native AddSpecialEffect takes string modelName, real x, real y returns effect
    • Avoid the local rect by using this grouper function: native GroupEnumUnitsInRange takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing This function detects units in a defined range, and it's faster.

Please update your file with these recomendations.
04-22-2007, 04:56 AM#14
Daxtreme
It will be done in the near future. Thanks :)

Also, I was sure I removed that nl location! Well...

***EDIT*** :

Updated! Everything you pointed out has been implemented Moyack.

Changelog:

- No more credits for the Divine Revenant.
- Improved the code a lot.*
- No more rect.

------------------------------------

*: Here's a list of the improvements.

- Use of OrderId now in order to stop the spell. No more trigger needed.
- IsUnitAlly --> IsUnitEnemy.
- WEAPON_TYPE_WHOKNOWS changed to null. No more constant to edit it.
- No more targetrect commented line.
- No more useless TriggerSleepAction and double nulling at the end of the Action function.
- Some optimization has been done.
- Removed the variables:

p
nl
u
l

They were useless.

- Using x,y coordinates now, instead of locations.
- No more timer leak.
- not IsUnitDeadBJ(targ) --> GetWidgetLife(targ) > 0
- local effect e - use - destroy --> DestroyEffect( AddSpecialEffect( (params) ) )

Thanks for the inputs!

***EDIT#1***

God, another update!

Improved the code once again!
04-30-2007, 11:58 PM#15
moyack
Sorry for my late response :P

Ok, checked and it looks much better, no useless locations = better spell :)

As a minor thing you should indicate that your spell doesn't depend on the FX made by JetFangInferno, this should be clarified.

This comment is optional

If you want to make your spell more efficient, you can use a timer stack based on CSSafety. Here's the code:

Collapse JASS:
//******************************************************************************************
//*
//* CSSafety 14.1
//* ¯¯¯¯¯¯¯¯
//*
//*  Utilities to make things safer. Currently this simply includes a timer recycling
//* Stack. Once you replace CreateTimer with NewTimer and DestroyTimer with ReleaseTimer
//* you no longer have to care about setting timers to null nor about timer related issues
//* with the handle index stack.
//*
//******************************************************************************************

//==========================================================================================
globals
    private timer array T
    private integer N = 0
endglobals

 //==========================================================================================
function NewTimer takes nothing returns timer
    if (N==0) then
        return CreateTimer()
    endif
 set N=N-1
 return T[N]
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
    call PauseTimer(t)
    if (N==8191) then
        debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
        //stack is full, the map already has much more troubles than the chance of bug
        call DestroyTimer(t)
    else
        set T[N]=t
        set N=N+1
    endif    
endfunction

With this, you replace the function CreateTimer() with NewTimer() and DestroyTimer() with RealeaseTimer() and you don't need to worry about timers anymore.



*Approved*