Spell Sacred Circle

Sim

Forum Administrator
Staff member
Reaction score
534
I've been working on this spell for a while and I decided it would make out something great.

So, here it is!

What is it? Here's its description:

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

Simply put, here's a screeny:

spells_230_screenshot.jpg


The spell

- It is MUI
- It is in vJASS
- Leakless
- Very low lag

Steps for implementation are listed, although there are several of them :)

Here's the code!

JASS:
//¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
//¤
//¤ ***************** 
//¤ - Sacred Circle - 
//¤ *****************
//¤ 
//¤ By: Daxtreme
//¤ 
//¤ --> How to implement in your map:
//¤     
//¤     1. Copy the game cache variable named "GameCache" in your map.                         
//¤     2. Copy the spell "Sacred Circle" in your map.
//¤     3. Copy everything found in the "Custom script code" section. To do this, click
//¤        on the name of the map in the top-left corner in the trigger editor.
//¤     4. Make a variable called "GameCache".
//¤     5. Copy this trigger into your map.
//¤     6. Import the HolyStrike.mdx model in your map.
//¤
//¤ --> How to customize it:
//¤
//¤     You can configure the spell using the constant functions just below. Change their values.
//¤
//¤ CREDITS:
//¤
//¤     - JetFangInferno's Holy Strike.
//¤     - kenny! for testing, bug-finding, and updating!
//¤
//¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

scope SacredCircle2

    globals
        private constant integer    ABIL_ID    = 'A000'       // Sacred Circle's ability Id
        private constant integer    ORDER_ID   = 852183
        private constant real       INTERVAL   = 0.04         // Period
        private constant string     EFFECT     = "war3mapImported\\HolyStrike.mdx"      // Spell model art
        private constant attacktype A_TYPE     = ATTACK_TYPE_CHAOS
        private constant damagetype D_TYPE     = DAMAGE_TYPE_UNIVERSAL
        private constant weapontype W_TYPE     = WEAPON_TYPE_WHOKNOWS
        private constant boolean    STOP_FIRST = false
    endglobals
    
    private function Damage_dealt takes integer lvl returns real
        return 100.00 * lvl
    endfunction
    
    private function Damage_radius takes integer lvl returns real
        return 175.00 + (0.00 * lvl)
    endfunction
    
    private function Filter_enemies takes unit filter, unit caster returns boolean
        return GetWidgetLife(filter) > 0.406 and IsUnitEnemy(filter,GetOwningPlayer(caster)) == true and IsUnitType(filter,UNIT_TYPE_MAGIC_IMMUNE) == false
    endfunction
    
    private struct Data
        
        unit    cast = null
        real    time = 0.00
        real    dist = 0.00
        real    ang  = 0.00
        integer lvl  = 0
        boolean stop = false
        
        static Data     array D
        static integer  D_total = 0
        static timer    Timer   = null
        static group    Group   = null
        static boolexpr Filt    = null
        
        static method filt takes nothing returns boolean
            return true
        endmethod
        
        method onDestroy takes nothing returns nothing
            set .cast = null
        endmethod
        
        method search takes nothing returns nothing
            local integer i = 1
            
            loop
                exitwhen i > Data.D_total
                if Data.D<i>.cast == .cast then
                    if STOP_FIRST then
                        set Data.D<i>.stop = true
                    else
                        set .stop = true
                    endif
                endif
                set i = i + 1
            endloop
        endmethod
        
        method periodic takes nothing returns boolean
            local real x = GetUnitX(.cast)
            local real y = GetUnitY(.cast)
            local unit u = null
            
            if GetUnitCurrentOrder(.cast) != ORDER_ID or .time &lt;= 0 then
                return true
            else
                set x = x + (50.00 + .dist * 10.00) * Cos(.ang * bj_DEGTORAD)
                set y = y + (50.00 + .dist * 10.00) * Sin(.ang * bj_DEGTORAD)
                
                call DestroyEffect(AddSpecialEffect(EFFECT,x,y))
                call GroupEnumUnitsInRange(Data.Group,x,y,Damage_radius(.lvl),Data.Filt)
                loop
                    set u = FirstOfGroup(Data.Group)
                    exitwhen u == null
                    call GroupRemoveUnit(Data.Group,u)
                    if Filter_enemies(u,.cast) then
                        call UnitDamageTarget(.cast,u,Damage_dealt(.lvl),false,false,A_TYPE,D_TYPE,W_TYPE)
                    endif
                endloop
                
                set .ang  = (.ang  + 24.00 - .dist / 6.00)
                set .dist = (.dist + 1.00)
                set .time = (.time - INTERVAL)
            endif
            
            set u = null
            
            return false
        endmethod            
        
        static method update takes nothing returns nothing
            local integer i = 1
            
            loop
                exitwhen i &gt; Data.D_total
                
                if Data.D<i>.stop or Data.D<i>.periodic() then
                    call Data.D<i>.destroy()
                    set Data.D<i> = Data.D[Data.D_total]
                    set Data.D_total = Data.D_total - 1
                    set i = i - 1
                endif
                
                set i = i + 1
            endloop
            
            if Data.D_total &lt;= 0 then
                call PauseTimer(Data.Timer)
                set Data.D_total = 0
            endif
        endmethod
        
        static method actions takes nothing returns boolean
            local Data d = Data.create()
            
            set d.cast = GetTriggerUnit()
            set d.lvl  = GetUnitAbilityLevel(d.cast,ABIL_ID)
            set d.time = 100.00 * INTERVAL
            call d.search()
            
            set Data.D_total = Data.D_total + 1
            set Data.D[Data.D_total] = d
            if Data.D_total == 1 then
                call TimerStart(Data.Timer,INTERVAL,true,function Data.update)
            endif
            
            return false
        endmethod
        
        static method conditions takes nothing returns boolean
            return GetSpellAbilityId() == ABIL_ID
        endmethod
            
        static method onInit takes nothing returns nothing
            local trigger trig = CreateTrigger()
            local integer i    = 0
            
            set Data.Timer = CreateTimer()
            set Data.Group = CreateGroup()
            set Data.Filt  = Filter(function Data.filt)
            
            loop
                call TriggerRegisterPlayerUnitEvent(trig,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,Data.Filt)
                set i = i + 1
                exitwhen i == bj_MAX_PLAYER_SLOTS
            endloop
            
            call TriggerAddCondition(trig,Condition(function Data.conditions))
            call TriggerAddAction(trig,function Data.actions)
        endmethod
        
    endstruct

endscope
</i></i></i></i></i></i>


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

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!

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

EDIT: First update.

EDIT: 2nd update.

EDIT: 3rd update.

EDIT: 4th update.

EDIT: 5th update.

EDIT: (After a while) Update! Credits to kenny! for the whole changes. :)
 

Attachments

  • Sacred Circle.w3x
    39.8 KB · Views: 565

waaaks!

Zinctified
Reaction score
255
wow, real great....

its made in jass
leakless

did you wrote to your trigger that how to import this spell?
its for people who do not know jass

well its great...+rep
 

SFilip

Gone but not forgotten
Reaction score
633
SC_RawCode, SC_StrikeDmg, SC_CheckMagicImmune and SC_CheckAlly can (and should) all be constants.
IsUnitType tends to cause an unusual bug if not compared to something...so only in this case you should use "return IsUnitType(targ, UNIT_TYPE_MAGIC_IMMUNE) == false" instead of "return not IsUnitType(targ, UNIT_TYPE_MAGIC_IMMUNE)".

Code:
    loop
        exitwhen GetTriggerEvalCount(trig)>0
        call TriggerSleepAction( 0.10 )
    endloop
Bad idea.
You can check the exec/eval count in the trigger itself (from Trig_Sacred_Circle_Actions_Loop) and disable/clean it there.

Oh and you might wanna use the native instead of AddSpecialEffectLocBJ, this seems to be the only BJ you used (except for PolarProjection).
 
I

IKilledKEnny

Guest
Looks great, those are imported models, right (or am I wrong? Screeny bit small)? Anyway if there are imported any chance you could attach them?
 

Sim

Forum Administrator
Staff member
Reaction score
534
You can export those models once you open the map.

> Bad idea.
> You can check the exec/eval count in the trigger itself (from Trig_Sacred_Circle_Actions_Loop) and disable/clean it there.

Will do.

> Oh and you might wanna use the native instead of AddSpecialEffectLocBJ, this seems to be the only BJ you used (except for PolarProjection).

Well, the effect thingy bugged when I used the native, don't know why -_- I will try again though.

The condition functions will be constants.

Thanks guys :)

EDIT:

>did you wrote to your trigger that how to import this spell?
>its for people who do not know jass

Look for yourself! :p
 

Dismembered

New Member
Reaction score
1
Wow Very very nice spell man

I might use it so i give u all credit for this spell ! Very nice again
 

Sim

Forum Administrator
Staff member
Reaction score
534
The spell has been updated according to SFilip's comments.
 

emjlr3

Change can be a good thing
Reaction score
395
have not looked myself, however if you fixed what he said I will move this over, gj
 

SFilip

Gone but not forgotten
Reaction score
633
Something I forgot last time...
Code:
function LocalVars takes nothing returns gamecache  
    return InitGameCache("jasslocalvars.w3v") 
endfunction   

function GameCache takes nothing returns gamecache
    return udg_GameCache
endfunction
This isn't right, should be something like this
Code:
function LocalVars takes nothing returns gamecache  
    if udg_GameCache == null then
        set udg_GameCache = InitGameCache("jasslocalvars.w3v")
    endif
    return udg_GameCache
endfunction
 

Tom Jones

N/A
Reaction score
437
It does look really great ingame, and it certainly deals some serious damage :)

For future consideration, there are somethings that seems unnecessary. At first I simply couldn't figure out what in the world you were using the local trigger for, when I suddenly realized it was a channeling ability. You could have added the END_CAST event to the main trigger, and then had a if statement in your actions trigger like this:
Code:
if GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_EFFECT then
*Turn on timer, store handle locals*
elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_ENDCAST then
*turn of timer, flush handle locals, destroy the timer*
endif
The condition will work for both events. Another thing that seemed abit strange, was that your using a region. You could acheive the same result with xypoints.
 

Sim

Forum Administrator
Staff member
Reaction score
534
It does look really great ingame, and it certainly deals some serious damage :)

For future consideration, there are somethings that seems unnecessary. At first I simply couldn't figure out what in the world you were using the local trigger for, when I suddenly realized it was a channeling ability. You could have added the END_CAST event to the main trigger, and then had a if statement in your actions trigger like this:
Code:
if GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_EFFECT then
*Turn on timer, store handle locals*
elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_ENDCAST then
*turn of timer, flush handle locals, destroy the timer*
endif
The condition will work for both events. Another thing that seemed abit strange, was that your using a region. You could acheive the same result with xypoints.

Well, I had other ideas in mind for this spell, and it needed Handle vars. Once I decided to change it, the handle vars still worked good and everything was already settled so I kept them. I noticed they were only half useful ;)

But I'm lazy.

Also, regions are easy to handle :p

Something I forgot last time...
Code:
function LocalVars takes nothing returns gamecache  
    return InitGameCache("jasslocalvars.w3v") 
endfunction   

function GameCache takes nothing returns gamecache
    return udg_GameCache
endfunction
This isn't right, should be something like this
Code:
function LocalVars takes nothing returns gamecache  
    if udg_GameCache == null then
        set udg_GameCache = InitGameCache("jasslocalvars.w3v")
    endif
    return udg_GameCache
endfunction

Are you sure? In both my maps I use the 2 above functions instead of what you said and... it always worked. Maybe I got a false version of the Handle Vars? :eek:
 

SFilip

Gone but not forgotten
Reaction score
633
> it always worked
The original works as well without any changes...
Though it causes a memory leak and is much slower as the game cache gets reloaded every time you use it.
 

Tom Jones

N/A
Reaction score
437
The original should be:
Code:
function LocalVars takes nothing returns gameache
    return udg_*VariableName*
endfunction
Or SFilips method, which is safer, since it'll automatically create the gamecache if it isn't created by the user.

>Also, regions are easy to handle

Then create a local region and move it around with handle locals. Implementation of a jass spell using a handle vars system should only include copying of triggers, abilities, custom script, and the game cache variable in my opinion
 

emjlr3

Change can be a good thing
Reaction score
395
alternatively, you could create your gc variable at map init, and always just return it, therefore saving some lines of code every time you get it, or not use a function call in the first place, and wherever you need it just use the gc variable, like I do, which would save even more time
 

Sim

Forum Administrator
Staff member
Reaction score
534
I'll use local region good idea *thumbs up*

EDIT: *rect

This isn't right, should be something like this
Code:
function LocalVars takes nothing returns gamecache  
    if udg_GameCache == null then
        set udg_GameCache = InitGameCache("jasslocalvars.w3v")
    endif
    return udg_GameCache
endfunction

Is it the exact code? :p

Anyways, I'll test it as it is.

EDIT: I'd need to change all the handle vars functions because they call the function "GameCache" >_<

I don't really know if my changes would work... Anyone here knows exactly how those functions work and can mess around with them?

Or do I just need to change every "GameCache" call to a "LocalVars" call?

EDIT again (omg): It worked by changing every "GameCache" call to a "LocalVars" call.

pretty cool man...the divine revenant also looks so nice

It isn't made by me though! :p
 

Tom Jones

N/A
Reaction score
437
You could add the code from the LocalVars function to your GameCache function. You could also copy paste the LocalVars function, change the function name to GameCache, and then delete the old GameCache function.

Changing all instances of GameCache is a bit complex way to do it :)
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top