HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Chaotic Raise

02-10-2009, 01:59 PM#1
Flame_Phoenix
Hi guys, I am now working in a spell called Chaotic Raise. This spell will be the counter-part for the Ranger's Golem Invocation and it is also an ultimate spell. I also intend to submit it here, although it summons units it will be slightly different.
It will be divided into a few stages:
1 - Create a circle with a David star in the center
2 - start spamming orc skelies every cycle
3 - when the caster stops everything stops

I don't know if I should make this spell JESP, since 2D array change a lot from last time I checked and it will increase the complexity of the spell considerably.
So far I have no problems, I am working on the Star with Circle effect (which will bring me problems), so I am just creating this thread to get an opinion from the user.

I will try to make this spell in this forum, it is the first time I will try this,maybe I will get some nice suggestions.

Anyway, I would like some opinions in:
1 - When an enemy unit is under a raising skeleton it will be damaged. Do you guys think the damage should differ from skely to skely? (Example, stronger skelies cause more damage)
2 - I use a small fire model for the star and the circle. I think it is pretty simple, anyone has a better idea for a model?

Anyway, here is the spell of the code so far:
Collapse JASS:
//===========================================================================
//A spell that creates a symbol on the casting position and that channels a 
//powerful magic. Every cycle an undead Chaos Orc Skeleton will raise from the
//ground to fight the enemies of the caster. If an enemy unit is under a raising
//skeleton it will take damage.
//
//Requires TimerUtils and Table
//
//@author Flame_Phoenix 
//
//@credits
//-Veev for helping me optimizing the CreateStarCircle function
//
//@version 1.0
//===========================================================================
scope ChaoticRaise initializer Init
//===========================================================================
//=============================SETUP START===================================
//===========================================================================
    globals
        private constant integer AID = 'A000'   //the rawcode of the ability of the caster
        private constant integer FIRE_ID = 'h000'   //the id of teh fire that will create the circle and the star
        private constant boolean CHANNEL_SKELIES = true //if true, all skelies die when the caster stops channel. If false each skelie will have SpawnLife time of life
        private constant real TIMER_CYCLE = 0.1
    endglobals
    
    private constant function SpawnInterval takes integer level returns real
    //the interval that separates each spawn
        return 1. //+ (level * 0)
    endfunction
    
    private constant function SpawnNum takes integer level returns integer
    //the number of units spawned per SpawnInterval
        return 3 //+ (level * 0)
    endfunction
    
    private constant function SpawnLife takes integer level returns real
    //the amount of life each spawn will have. This will only work if variable
    //CHANNEL_SKELIES is set to false
        return 30. * level
    endfunction
//===========================================================================
//=============================SETUP END=====================================
//===========================================================================
    globals
        private group ChaoticRaiseCasters
        private HandleTable activeTable 
    endglobals

    private struct SpellData
        unit caster
        integer level
        group fires //this group will have all fires that make the circle and the star
        group skelies
        timer t
        real spellX
        real spellY
        
        static method create takes unit caster, real locX, real locY returns SpellData
            local SpellData data = SpellData.allocate()
            
            //set variables about the caster
            set data.caster = caster
            set data.level = GetUnitAbilityLevel(data.caster, AID)
            
            //variables about the location
            set data.spellX = locX
            set data.spellY = locY
            
            //the timer which will determine when we create skelies
            //creates a skelie everytime it expires
            set data.t = NewTimer()   
            
            //This groups will save the dumy fires, so we can kill them 
            //when the caster stops the channeling. We also recycle the group
            if data.fires == null then
                set data.fires = CreateGroup()
            endif
            
            //if we want the skelies to die when the caster stops channel
            //then we create this group so we can add all skelies to it
            //then we kill the skelies when the caster stops the channel
            if CHANNEL_SKELIES then
                //again we recycle the skely group!
                if data.skelies == null then
                    set data.skelies = CreateGroup()
                endif
            endif
            
            return data
        endmethod
        
        method onDestroy takes nothing returns nothing
            //here we select all units from the fires group and we kill them all ! =P
            //thus meaning that the fire effects will disappear ! 
            local unit f
            loop
                set f = FirstOfGroup(.fires)
                exitwhen(f == null)
                call GroupRemoveUnit(.fires, f)
                call KillUnit(f)
            endloop
            
            //cleaning the group for us to use one day later =P
            call GroupClear(.fires)
            
            //if the variable is true, then skelies group was created and it has
            //units, and so now we kill them all.
            if CHANNEL_SKELIES then
                loop
                    set f = FirstOfGroup(.skelies)
                    exitwhen(f == null)
                    call GroupRemoveUnit(.skelies, f)
                    call KillUnit(f)
                endloop
                
                //cleaning the group for us to use one day later =P
                call GroupClear(.skelies)
            endif
            
            //since the spell is not active anymore, we clean the Table     
            call activeTable.flush(.caster) 
            
            //the units are not anymore in the active units group.
            call GroupRemoveUnit(ChaoticRaiseCasters, .caster) 
            
            //releasing the timer for TimerUitls to use one day later =D
            call ReleaseTimer(.t)
        endmethod
    endstruct
//===========================================================================
    private function CreateStarCircle  takes integer aStruct returns nothing
        
    endfunction
//===========================================================================
    private function onStop takes nothing returns boolean
        return false
    endfunction
//===========================================================================
    private function Conditions takes nothing returns boolean
        return true
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        //Creates the trigger for when the unit starts casting the spell
        local trigger ChaoticRaiseTrg = CreateTrigger(  )
        call TriggerRegisterAnyUnitEventBJ(ChaoticRaiseTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition(ChaoticRaiseTrg, Condition( function Conditions ) )
        
        //Creates the trigger for when the unit ceases the channel
        set ChaoticRaiseTrg = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(ChaoticRaiseTrg, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
        call TriggerAddCondition(ChaoticRaiseTrg,  Condition(function onStop))
        
        //setting out globals
        set ChaoticRaiseCasters = CreateGroup()
        set activeTable = HandleTable.create() 
    endfunction
endscope

If you guys read the header, you will see the credits section is empty. Well, if you help me, you will get +rep and your name will be there =P
02-11-2009, 07:37 AM#2
Pyrogasm
There is no JESP anymore. Just common sense and good coding practices.
02-11-2009, 12:48 PM#3
Flame_Phoenix
Quote:
There is no JESP anymore. Just common sense and good coding practices.
So Vexorian graved the standard at last... it is the end of an age. JESP will never be over to me =)

EDIT EDIT EDIT

Ok guys, I finished up the cleaning. If anyone has ideas on how to optimize the creation of the star within the circle please be my guest.
I decided to use bj_variables, this way instead of have 1000 variables I have 999 (you get the point).
Enjoy and despair =)
Collapse JASS:
scope StarWithCircle initializer Init
    globals 
        private constant real RADIUS = 400.
        private constant integer FIRE_ID = 'h000'
        private constant real FIRE_SIZE = 10.
    endglobals
//===========================================================================
 private function Actions takes nothing returns nothing
        local unit caster = GetTriggerUnit()
        local player owner = GetOwningPlayer(caster)
        local real casterX = GetUnitX(caster)
        local real casterY = GetUnitY(caster)
        local real x1
        local real y1
        local real x2
        local real y2
        local real x3
        local real y3
        local real dx
        local real dy
        local real angle
        local real dist
        local integer fireNum
        
        //here we create the circle
        set bj_forLoopAIndex = 1
        set bj_forLoopAIndexEnd = R2I(RADIUS / FIRE_SIZE)
        loop
            exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
            //polar projection stuff
            set x1 = casterX + RADIUS * Cos(bj_forLoopAIndex * ( 360.00 / (RADIUS / FIRE_SIZE )) * bj_DEGTORAD) 
            set y1 = casterY + RADIUS * Sin(bj_forLoopAIndex * ( 360.00 / (RADIUS / FIRE_SIZE )) * bj_DEGTORAD) 
            
            set bj_lastCreatedUnit = CreateUnit(owner, FIRE_ID, x1, y1, 0)
            call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', GetRandomReal(8.00, 10.00))
            set bj_forLoopAIndex = bj_forLoopAIndex + 1
        endloop
        
        //all this stuff creates the first line of the start ...
        set x1 = casterX + RADIUS * Cos(234.0 * bj_DEGTORAD)
        set y1 = casterY + RADIUS * Sin(234.0 * bj_DEGTORAD)
        set x2 = casterX + RADIUS * Cos(90. * bj_DEGTORAD)
        set y2 = casterY + RADIUS * Sin(90. * bj_DEGTORAD)
        set dx = x2 - x1
        set dy = y2 - y1
        set dist = SquareRoot(dx * dx + dy * dy)
        set angle = Atan2(y2-y1, x2-x1)
        set fireNum = R2I(dist) / 40
    
        set bj_forLoopAIndex = 1
        set bj_forLoopAIndexEnd = fireNum
        loop
            exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
            set x3 = x1 + (bj_forLoopAIndex * (dist / fireNum)) * Cos(angle)
            set y3 = y1 + (bj_forLoopAIndex * (dist / fireNum)) * Sin(angle)
            
            set bj_lastCreatedUnit = CreateUnit(owner, FIRE_ID, x3, y3, 0)
            call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', GetRandomReal(8.00, 10.00))
            set bj_forLoopAIndex = bj_forLoopAIndex + 1
        endloop
        
        //now prepare for the second line of the start lol... don't worry, it 
        //only has 5 lines xD
        set x1 = casterX + RADIUS * Cos(90. * bj_DEGTORAD)
        set y1 = casterY + RADIUS * Sin(90. * bj_DEGTORAD)
        set x2 = casterX + RADIUS * Cos(306. * bj_DEGTORAD)
        set y2 = casterY + RADIUS * Sin(306. * bj_DEGTORAD)
        set dx = x2 - x1
        set dy = y2 - y1
        set dist = SquareRoot(dx * dx + dy * dy)
        set angle = Atan2(y2-y1, x2-x1)
        set fireNum = R2I(dist) / 40
    
        set bj_forLoopAIndex = 1
        set bj_forLoopAIndexEnd = fireNum
        loop
            exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
            set x3 = x1 + (bj_forLoopAIndex * (dist / fireNum)) * Cos(angle)
            set y3 = y1 + (bj_forLoopAIndex * (dist / fireNum)) * Sin(angle)
            
            set bj_lastCreatedUnit = CreateUnit(owner, FIRE_ID, x3, y3, 0)
            call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', GetRandomReal(8.00, 10.00))
            set bj_forLoopAIndex = bj_forLoopAIndex + 1
        endloop
        
        //now for the 3rd line
        set x1 = casterX + RADIUS * Cos(306. * bj_DEGTORAD)
        set y1 = casterY + RADIUS * Sin(306. * bj_DEGTORAD)
        set x2 = casterX + RADIUS * Cos(162. * bj_DEGTORAD)
        set y2 = casterY + RADIUS * Sin(162. * bj_DEGTORAD)
        set dx = x2 - x1
        set dy = y2 - y1
        set dist = SquareRoot(dx * dx + dy * dy)
        set angle = Atan2(y2-y1, x2-x1)
        set fireNum = R2I(dist) / 40
    
        set bj_forLoopAIndex = 1
        set bj_forLoopAIndexEnd = fireNum
        loop
            exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
            set x3 = x1 + (bj_forLoopAIndex * (dist / fireNum)) * Cos(angle)
            set y3 = y1 + (bj_forLoopAIndex * (dist / fireNum)) * Sin(angle)
            
            set bj_lastCreatedUnit = CreateUnit(owner, FIRE_ID, x3, y3, 0)
            call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', GetRandomReal(8.00, 10.00))
            set bj_forLoopAIndex = bj_forLoopAIndex + 1
        endloop
        
        //now for the 4th line, don't worry, we are almost there
        set x1 = casterX + RADIUS * Cos(162. * bj_DEGTORAD)
        set y1 = casterY + RADIUS * Sin(162. * bj_DEGTORAD)
        set x2 = casterX + RADIUS * Cos(19. * bj_DEGTORAD)
        set y2 = casterY + RADIUS * Sin(19. * bj_DEGTORAD)
        set dx = x2 - x1
        set dy = y2 - y1
        set dist = SquareRoot(dx * dx + dy * dy)
        set angle = Atan2(y2-y1, x2-x1)
        set fireNum = R2I(dist) / 40
    
        set bj_forLoopAIndex = 1
        set bj_forLoopAIndexEnd = fireNum
        loop
            exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
            set x3 = x1 + (bj_forLoopAIndex * (dist / fireNum)) * Cos(angle)
            set y3 = y1 + (bj_forLoopAIndex * (dist / fireNum)) * Sin(angle)
            
            set bj_lastCreatedUnit = CreateUnit(owner, FIRE_ID, x3, y3, 0)
            call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', GetRandomReal(8.00, 10.00))
            set bj_forLoopAIndex = bj_forLoopAIndex + 1
        endloop
        
        //at last the last line of our David star!
        set x1 = casterX + RADIUS * Cos(19.0 * bj_DEGTORAD)
        set y1 = casterY + RADIUS * Sin(19.0 * bj_DEGTORAD)
        set x2 = casterX + RADIUS * Cos(234. * bj_DEGTORAD)
        set y2 = casterY + RADIUS * Sin(234. * bj_DEGTORAD)
        set dx = x2 - x1
        set dy = y2 - y1
        set dist = SquareRoot(dx * dx + dy * dy)
        set angle = Atan2(y2-y1, x2-x1)
        set fireNum = R2I(dist) / 40
    
        set bj_forLoopAIndex = 1
        set bj_forLoopAIndexEnd = fireNum
        loop
            exitwhen bj_forLoopAIndex > bj_forLoopAIndexEnd
            set x3 = x1 + (bj_forLoopAIndex * (dist / fireNum)) * Cos(angle)
            set y3 = y1 + (bj_forLoopAIndex * (dist / fireNum)) * Sin(angle)
            
            set bj_lastCreatedUnit = CreateUnit(owner, FIRE_ID, x3, y3, 0)
            call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', GetRandomReal(8.00, 10.00))
            set bj_forLoopAIndex = bj_forLoopAIndex + 1
        endloop
        
        //cleaning the mess
        set caster = null
        set owner = null
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        local trigger StarWithCircleTrg = CreateTrigger(  )
        call TriggerRegisterAnyUnitEventBJ( StarWithCircleTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddAction( StarWithCircleTrg, function Actions )
    endfunction
endscope

02-11-2009, 02:45 PM#4
Veev
There are a couple calculations that get repeated in the loop but never change... SquareRoot(dx * dx + dy * dy) appears a lot of times. Although it changes in each section you can still create a variable and only calculate it once per section (by section I mean "first line," "second line," and so on). You can do the same for the Atan2 calculations. Also, due to the GUI, you're multiplaying bj_DEGTORAD and bj_RADTODEG together so... Remove that, that's really unnecessary.

So...
Expand JASS:
02-11-2009, 03:33 PM#5
Flame_Phoenix
Mmm I will try your code in this precise instant! btw, if "dist" is a real, then there is no need to make I2R(fireNum) because the result of an equation between an integer and a real, is a real. I2R can be removed I think.
If it works, You will definitely get credits. I was so focused in that GUI thing I couldn't see the real loop behind it.

EDIT EDIT EDIT

Ok, about the systems, I want the spell to be channeling, so I will use Table for this. Also, I want to created a sequence of events, timed events, so I will also use TimerUtils.
I don't have much ideas for the effects, but if the opportunity arises to use a cool effect with long animation time I will use this system too:
http://www.wc3campaigns.net/showthre...11#post1063811

I think this is be all I will need. If any one has suggestions or cool ideas, go ahead and shoot them =P

EDIT EDIT EDIT

Ok the Circle with the david start on the center was updated. I would post the initial code if people want to make a comparison =P
Veev was added to credits.