HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Apocalypse total remake

07-03-2008, 08:30 AM#1
Flame_Phoenix
Quote:
Ok, for you to not be mad at me, I promise I'll make the spell your way, when I finish it this way, just for the lulz =D.

Ok guys, this is a promise I did to Anitarf. To prove I am a man that keeps promises, here I am, asking you all for help to remake this spell.

The main reason I used dynamic triggers is because I probably didn't know how to do this spell without them.
Here I am, any ideas ?
Collapse JASS:
//===========================================================================
//A spell that makes meteors fall randomly in the map, damaging enemy units
//and summoning powerfull infernals
//
// Requires ABC and CSSafety
//
//@author Flame_Phoenix 
//@version 2.2
//===========================================================================
scope Apocalypse
    
//      globals
//          private constant integer AID = 'A000'  
//          ->The rawcode of the Apocalypse hero ability
//        
//          private constant integer INFERNAL = 'A002'  
//          ->Rawcode of the dreadlords dummy infernal spell. 
//          This will be used to spawn an ifernal or any other unit you may like. To change the summoned
//          unit, just go to the dummy spell, and change it
//        
//          private constant string INFERNALORDER = "dreadlordinferno"
//          ->String order of the dreadlords dummy infernal spell.
//
//          private constant integer SPAWN = 'A003'
//          ->Rawcode of the special dummy ability Flame Strike. When an infernal falls we can
//          not show 2 meteors. So this is a special ability, without the meteor falling ability
//          It is only used when the infernal is spawned
//
//          private constant string SPAWNORDER = "flamestrike"
//          -> String order of the dummy ability Flame Strike that spawns the infernal
//
//          private constant integer NO_SPAWN = 'A001' 
//          ->Rawcode of the Flame Strike ability, when no infernal is spwaned. It is an ability
//          with the falling meteor animation.
//
//          private constant string NO_SPAWNORDER = "flamestrike"
//          -> String order of the dummy ability Flame Strike that DOES NOT spawn the infernal
//
//          private constant integer DUMMY_RAW = 'h000'
//          ->The rawcode of the dummy unit that will cast all spells needed for this trigger 
//          to work. For more information, ee the dummy unit i created in object editor.
//
//          private constant real MIN_RANGE = 300.00
//          ->The minimal range. Meteors will not fall behind this number. It was made to protect
//          the caster from being hurt. You can change the minimum range, but it must be ALWAYS 
//          lower the the MAX_RANGE real.
//
//          private constant real MAX_RANGE = 600.00
//          ->The maximal range. Meteors will not fall after the number.This is the area of 
//          effect of the spell. You can change this value, but it must be BIGGER than the
//          MIN_RANGE real.
//
//          private constant integer METEORS = 1
//          -> The number of meteors that fall per cicle
//
//      endglobals

//===========================================================================================
//For more setup, see the functions after the globals
//To change damage and AOE of meteors, change the abilities:
// - Flame Strike NO Infernal
// - Flame Strike Infernal
// - Inferno
// in the ability object editor
//===========================================================================================

    globals
        private constant integer AID = 'A000'   
        private constant integer INFERNAL = 'A002' 
        private constant string INFERNALORDER = "dreadlordinferno"
        private constant integer SPAWN = 'A003'
        private constant string SPAWNORDER = "flamestrike"
        private constant integer NO_SPAWN = 'A001' 
        private constant string NO_SPAWNORDER = "flamestrike"
        private constant integer DUMMY_RAW = 'h000'
        private constant real MIN_RANGE = 300.00
        private constant real MAX_RANGE = 600.00
        private constant integer METEORS = 1
    endglobals

    private constant function meteorFall takes integer level returns integer
        return 6 / level //the time interval that separates each meteor (a cicle)
    endfunction
    
    private constant function meteorEnd takes integer level returns integer
        return 5 * level + 15 //the duration of the spell
    endfunction 
    
    private constant function infernalChance takes integer level returns integer
        //the chances you have to spawn an infernal each time a meteor falls
        // from 0 to 100
        return 3 * level + 1 
    endfunction 
//=============================================================================
    private struct Mystruct
        unit caster
        integer level
        timer repeator
        timer expirer
        trigger end
        triggercondition endCondition
    
    
       static method create takes unit caster returns Mystruct
            local Mystruct this = Mystruct.allocate()
            set .caster = caster
            set .repeator = CreateTimer()
            set .expirer = CreateTimer()
            set .end = CreateTrigger()
            return this
        endmethod
        
        method onDestroy takes nothing returns nothing
            //removes the created trigger in the Actions function
            call TriggerRemoveCondition(.end,.endCondition)
            call DestroyTrigger(.end)
            call ClearTriggerStructB(.end)
    
            //stops the repeator timer and ends the spell once and for all
            call ReleaseTimer(.repeator)
            call ReleaseTimer(.expirer)
        
            call ClearTimerStructA(.repeator)
            call ClearTimerStructB(.expirer)
        endmethod

    endstruct
//==========================================================================
    private function endConds takes nothing returns boolean
        local Mystruct data = GetTriggerStructB(GetTriggeringTrigger())
        local boolean result = false
        
        if (GetTriggerEventId() == EVENT_GAME_TIMER_EXPIRED) then
            call data.destroy()
            set result = true
        elseif (GetTriggerUnit() == data.caster) then
                call data.destroy()
                set result = true
        endif
        
        return result
    endfunction
//==========================================================================
    private function Effect takes nothing returns nothing
        //catches the expired timer and runs this function
        local Mystruct data = GetTimerStructA(GetExpiredTimer())
        
        //local variables 
        local integer infernal
        local unit dummy1
        local unit dummy2
        local integer counter = 0
        
        //This uses polar projection. Formula: Center + r * trigfunction(angle teta); 
        //where Center is the center coordinate X or Y
        //r is the distance (in the case rabdom between 300 and 600)
        //trigfunction is Sin if using Y and Cos if using X
        //angle teta is the angle formed between r and the axis (in this case random, can be all circle)
        local real angle 
        local real distance 
        local real randomX 
        local real randomY 
        
        
        loop
            exitwhen(counter >= METEORS)
            
            set angle = GetRandomReal(0., 360.) * bj_DEGTORAD
            set distance = GetRandomReal(MIN_RANGE, MAX_RANGE)
            set randomX = GetUnitX(data.caster) + distance * Cos(angle)
            set randomY = GetUnitY(data.caster) + distance * Sin(angle)
            
            set infernal = GetRandomInt(0, 100)
            
            if (infernal <= infernalChance(data.level)) then
                set dummy1 = CreateUnit(GetOwningPlayer(data.caster), DUMMY_RAW, GetUnitX(data.caster), GetUnitY(data.caster), 0)
                call UnitAddAbility(dummy1, INFERNAL)
                call SetUnitAbilityLevel(dummy1, INFERNAL, data.level)
            
                call IssuePointOrder(dummy1, INFERNALORDER, randomX, randomY)
            
                call UnitApplyTimedLife(dummy1, 'BTLF', 3)
                set dummy2 = CreateUnit(GetOwningPlayer(data.caster), DUMMY_RAW, GetUnitX(data.caster), GetUnitY(data.caster), 0)
                call UnitAddAbility(dummy2, SPAWN)
                call SetUnitAbilityLevel(dummy2, SPAWN, data.level)
            
                call IssuePointOrder(dummy2, SPAWNORDER, randomX, randomY)
            
                call UnitApplyTimedLife(dummy2, 'BTLF', 3)
            else 
                set dummy1 = CreateUnit(GetOwningPlayer(data.caster), DUMMY_RAW, GetUnitX(data.caster), GetUnitY(data.caster), 0)
                call UnitAddAbility(dummy1, NO_SPAWN)
                call SetUnitAbilityLevel(dummy1, NO_SPAWN, data.level)
            
                call IssuePointOrder(dummy1, NO_SPAWNORDER, randomX, randomY)
            
                call UnitApplyTimedLife(dummy1, 'BTLF', 3)
            endif
        
            set dummy1 = null
            set dummy2 = null
            
            set counter = counter + 1
        endloop
        
    endfunction
//==========================================================================
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == AID
    endfunction
//==========================================================================
    private function Actions takes nothing returns nothing
        local Mystruct data = Mystruct.create(GetTriggerUnit())
        set data.level = GetUnitAbilityLevel(data.caster, AID)
        
        //starts the effect of the spell, meteors start falling from sky
        call SetTimerStructA(data.repeator, data)
        call TimerStart(data.repeator, meteorFall(data.level), true, function Effect)
    
        //creates the timer that ends the spell when expired
        call SetTimerStructB(data.expirer, data)
        call TimerStart(data.expirer, meteorEnd(data.level), false, null)
    
        //creates the trigger that will end the spell when the timer expires or the hero dies
        call SetTriggerStructB(data.end, data)
        call TriggerRegisterTimerExpireEvent( data.end, data.expirer )
        call TriggerRegisterAnyUnitEventBJ(data.end, EVENT_PLAYER_UNIT_DEATH )
        set data.endCondition = TriggerAddCondition( data.end, Condition( function endConds ) )
    endfunction
//===========================================================================
    public function InitTrig takes nothing returns nothing
        set gg_trg_Apocalypse = CreateTrigger( )
        call TriggerRegisterAnyUnitEventBJ( gg_trg_Apocalypse, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( gg_trg_Apocalypse, Condition( function Conditions ) )
        call TriggerAddAction( gg_trg_Apocalypse, function Actions )
    endfunction
endscope
07-03-2008, 08:06 PM#2
chobibo
can you state how the spell works?
07-03-2008, 08:20 PM#3
Flame_Phoenix
Well, the code is pretty simple.
I am trying to make the spell JESP too.
First, this is a spell like locust from the spider hero. When the hero uses the spell, meteors will start falling from the sky every X seconds. Each meteor that falls has a chance to spawn an Infernal =)

By looking at the code you will have a very clear image of what I want.
- the hero starts the spell
- I start 2 timers: a timer that will say us the duration of the spell and the other we call the "effect" function every X seconds
- Each time the effect function is called, it counts how many meteors we want to make fall, the chances for them to spawn an infernal and the place where they fall. To spawn an infernal is quite tricky. If we want to spawn an Infernal we use two dummies with two spells: Inferno and Flame Strike. The Inferno will make the Infernal arise and stun the bad guys, the Flame Strike is a different Flame Strike, and it damages normally.
The place where the meteor fall must be chosen carefully, we don't want a meteor falling to much near the caster - imagine if a user decides that all meteors damage all units, damaging the caster would be stupid (well at first the original idea was to kill everything, but then people started whining because the spell was killing their allies, so I had to change it xD).
- There are however 2 cases in which the spell will end: the timer of duration reaches the end, or the spell caster dies because we are too much newbs to control him xD

This is mainly the mechanic of the spell.
Other things like damage cause, AOE and units spawned can be changed in the abilities.

I post map for you all to see.
Also, Vexorian, if you manage to make spell, please don't steal my credits, I was its creator and had all this effort to create it ...
Attached Files
File type: w3xApocalypse 2.2.w3x (54.0 KB)
07-04-2008, 01:25 AM#4
Anitarf
Personaly, I'd use just one timer, the periodic one, and have an integer counter for the duration; after the periodic timer would run a certain number of times the spell would end.
07-04-2008, 04:52 AM#5
Vexorian
Oh, so that's the dynamic trigger stuff? It is a little trivial to get rid of the dynamic trigger: Just have a global trigger to detect a unit death event, also have a unit group to store units that cast the spell, if a unit that's in the group dies. Get the attached struct from it (yes, in the SPELL_EFFECT event you need to attach the struct to the unit) and end the instance. Of course, you also need a single timer that does the same.


Quote:
Also, Vexorian, if you manage to make spell, please don't steal my credits, I was its creator and had all this effort to create it ...
If I made your spell idea with my own code I wouldn't credit you, because you cannot really own spell ideas, you only own the code, that's where your effort went. Just like people making spells I have already made don't credit me at all.
07-04-2008, 08:53 AM#6
Flame_Phoenix
Quote:
Personaly, I'd use just one timer, the periodic one, and have an integer counter for the duration; after the periodic timer would run a certain number of times the spell would end.
MMMmm, well, I dunno ... i find it easier this way. But that is a good idea, I may do that.
Quote:

Oh, so that's the dynamic trigger stuff? It is a little trivial to get rid of the dynamic trigger: Just have a global trigger to detect a unit death event, also have a unit group to store units that cast the spell, if a unit that's in the group dies. Get the attached struct from it (yes, in the SPELL_EFFECT event you need to attach the struct to the unit) and end the instance. Of course, you also need a single timer that does the same.
I make no idea what you are talking about xD. I know I would have to use a global variable, but wouldn't that make my spell not MUI ?
I am really lost here... =S

Quote:
If I made your spell idea with my own code I wouldn't credit you, because you cannot really own spell ideas, you only own the code, that's where your effort went. Just like people making spells I have already made don't credit me at all.
Than despiting your great offer, I must ask you for not to do it please. I see my spells and ideas as my children. I gave special effort to this one, I hope you understand I want to finish it by myself =(.

So logic ??
- Unit starts the effect
- I start 1 timer that counts the meteors that fall
- Each timer a meteors falls i dunno what to do with a counter
- I make no idea about if the hero dies ... (i really didn't get the point sorry =S )
- I make no idea about how many globals I will need
- Make no idea how to use the gloabls I may need
07-04-2008, 10:09 AM#7
Anitarf
Collapse JASS:
globals
    private group castingUnits = CreateGroup()
endglobals
...then, in the create method: call GroupAddUnit(castingUnits, caster) and attach the struct to the unit somehow.
...and in the onDestroy method: call GroupRemoveUnit(castingUnits, caster)
Then, you need the following function:
Collapse JASS:
private function UnitDeath takes nothing returns boolean
    if IsUnitInGroup(GetTriggerUnit(), castingUnits) then
        //get the attached struct from the unit and destroy it here
    endif
    return false
endfunction
...and this in your init function:
Collapse JASS:
    local trigger t= CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
    call TriggerAddCondition(t, Condition(function UnitDeath))
07-04-2008, 02:01 PM#8
Flame_Phoenix
Quote:
...then, in the create method: call GroupAddUnit(castingUnits, caster) and attach the struct to the unit somehow.
Well Unit groups can do it, but I was thinking of an Unit array. An array of units, like a mini system. I add a unit to an empty spot and increase the counter, counter + 1. When I remove a unit decrease the counter ... But this would give me many troubles, the array would eventually become fill with empty slots and every time I removed an unit from the array i would have to move all others ... it would be to complicated so a Group of Units would be good. However you are telling me the problem I always had and never could fix: how do I attach the sruct to the unit somehow ? I could do it if I could identify the unit... I have to identify it so I can remove it on the OnDestroy group of the other trigger right ?
Jesus ... I am so lost !

About your second function, shouldn't it be an event ?
Nvm, it is an event that calls that function, my fault xD

Also, why a local trigger ? PurplePoot and other say that normal Triggers are faster =S
07-04-2008, 02:36 PM#9
Vexorian
Quote:
Well Unit groups can do it, but I was thinking of an Unit array. An array of units, like a mini system. I add a unit to an empty spot and increase the counter, counter + 1. When I remove a unit decrease the counter ... But this would give me many troubles, the array would eventually become fill with empty slots and every time I removed an unit from the array i would have to move all others ... it would be to complicated so a Group of Units would be good. However you are telling me the problem I always had and never could fix: how do I attach the sruct to the unit somehow ?
I would use Table and let the spell have its own private Table object where I attach things to units. Since you are already using Cohadar's stuff and you don't mind preventing your spell's users from using UserData on their own stuff, you can use PUI. You can also take grim's data system thing and make a new instance of the data system for your unit.
Quote:
PurplePoot and other say that normal Triggers are faster =S
PurplePoot is being ridiculous.
07-04-2008, 02:38 PM#10
Anitarf
Quote:
Originally Posted by Flame_Phoenix
Well Unit groups can do it, but I was thinking of an Unit array. An array of units, like a mini system. I add a unit to an empty spot and increase the counter, counter + 1. When I remove a unit decrease the counter ... But this would give me many troubles, the array would eventually become fill with empty slots and every time I removed an unit from the array i would have to move all others ... it would be to complicated so a Group of Units would be good. However you are telling me the problem I always had and never could fix: how do I attach the sruct to the unit somehow ? I could do it if I could identify the unit... I have to identify it so I can remove it on the OnDestroy group of the other trigger right ?
If you have an array, you don't need to move all of the indexes when you remove an item from it, you just need to move the last index to the point where you removed an item. Like this:
Code:
Array:
A C D E B X 0 0 0 0 0...
Add Y:
A C D E B X Y 0 0 0 0...
Remove D:
A C Y E B X 0 0 0 0 0...
The good thing about using an array like this is that with a parallel array, you can also attach things like structs to items in the first array, so you then don't need an attachment system.

Quote:
Also, why a local trigger ? PurplePoot and other say that normal Triggers are faster =S
There's no such thing as a local trigger. It's just a local variable pointing to a trigger, you could have used a global variable just as well, I don't care. You just need to set it to some variable so oyu can add events and conditions to it; since you don't need to modify it after that, you might as well use a local variable, there's no need to have a global variable pointing to your trigger if you're not going to do anything with it for the rest of the game.
07-04-2008, 02:43 PM#11
Flame_Phoenix
Hey guys I talked to HD and he told me the magic of "UserData" which was complete unknown to me.
This is what I made so far:
Collapse JASS:
scope ApocalypseRmk initializer Init
//SETUP START, THIS USES CSSafety and ABC
    globals
        private constant integer AID = 'A000'   //rawcode of the ability Apocalypse
        private constant integer INFERNAL = 'A002' //rw of dummy dreadlord Infernal 
        private constant string INFERNALORDER = "dreadlordinferno" //order for dl Infernal
        private constant integer SPAWN = 'A003' //rw of FlameStrike spwan form
        private constant string SPAWNORDER = "flamestrike" //order for FS spwan form
        private constant integer NO_SPAWN = 'A001' //rw of FlameStrike no spawn form
        private constant string NO_SPAWNORDER = "flamestrike" //order for no spawn FS
        private constant integer DUMMY_RAW = 'h000' //rw of the dummy caster
        private constant real MIN_RANGE = 300.00 //minimun range in which meteors wont fall
        private constant real MAX_RANGE = 600.00 //maximum range that meteors can have when falling
        private constant integer METEORS = 1 //number of meteors that falls per cicle
    endglobals

    private constant function meteorFall takes integer level returns integer
        return 6 / level //the time interval that separates each meteor (a cicle)
    endfunction
    
    private constant function meteorEnd takes integer level returns integer
        return 5 * level + 15 //the duration of the spell
    endfunction 
    
    private constant function infernalChance takes integer level returns integer
        //the chances you have to spawn an infernal each time a meteor falls
        // from 0 to 100
        return 3 * level + 1 
    endfunction 
   
// SETUP END
//=============================================================================
//=============================================================================
    globals 
        group ApocalypseCasters = CreateGroup()
    endglobals
   
   private struct MyStruct
        unit caster
        integer level
        timer meteorCounter
    
       static method create takes unit caster returns MyStruct
            local MyStruct this = MyStruct.allocate()
            
            //set stuff
            set .caster = caster
            set .meteorCounter = NewTimer()
            
            // we kinda "link" this unit with this struct and then we add it to some 
            //sort of "poo"
            call SetUnitUserData(.caster, integer(this))
            call GroupAddUnit(ApocalypseCasters, .caster)
            
            return this
        endmethod
        
        method onDestroy takes nothing returns nothing
            //this will clean all mess
            //PS I am not sure if thise should be here ...
            call ReleaseTimer(.meteorCounter)
        endmethod

    endstruct
//==========================================================================
    private function Effect takes nothing returns nothing
        //this will make meteors fall
    endfunction
//==========================================================================
    private function Conditions takes nothing returns boolean
        return GetSpellAbilityId() == AID
    endfunction
//==========================================================================
    private function Actions takes nothing returns nothing
        local MyStruct data = MyStruct.create(GetTriggerUnit())
        set data.level = GetUnitAbilityLevel(data.caster, AID)
        
    endfunction
//===========================================================================
    private function Init takes nothing returns nothing
        set gg_trg_Apocalypse = CreateTrigger( )
        call TriggerRegisterAnyUnitEventBJ( gg_trg_Apocalypse, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( gg_trg_Apocalypse, Condition( function Conditions ) )
        call TriggerAddAction( gg_trg_Apocalypse, function Actions )
    endfunction
endscope

Any suggestions now ?
Oh and yes HD also explained me how array would work. However i prefer to use unit groups for now, because they are easier, and then when polishing the code, change it into array.
07-04-2008, 02:47 PM#12
Vexorian
Wasting UserData on a single spell is murder.

If you used an array instead of a group, your lookup would be O(n), that's not good.
07-04-2008, 02:49 PM#13
Flame_Phoenix
Quote:
Wasting UserData on a single spell is murder.

If you used an array instead of a group, your lookup would be O(n), that's not good.
hã !?
Xinese xD

So, if user data is also evil, than how do I attach the whole struct to the caster ??? I will need that so when I pull the caster out of the pool I can change the state of the spell and end it =S
07-04-2008, 03:50 PM#14
spiwn
User data is not evil, it is great(if used correctly).
The problem is that if you use used data for only one spell(the way you did), you will not be able to use it for others.
I do not know if HSAS causes problems, but it is a way to attack structs to handles.
07-04-2008, 07:03 PM#15
Flame_Phoenix
Quote:
User data is not evil, it is great(if used correctly).
The problem is that if you use used data for only one spell(the way you did), you will not be able to use it for others.
I do not know if HSAS causes problems, but it is a way to attack structs to handles.
So is ABC ... (as far as I know).
Besides, If I use UserData in this spell, does it mean that my other spells using UserData will conflict or not work at all ??
Vexorian says I shouldn't use UserData ... what should I use then ?? Anyone can help me ?
Btw, ABC has a lot more support than HSAS, therefore one more reason for me to use it xD
ALso, good to see you spiwn mate =)