| 07-03-2008, 08:30 AM | #1 | |
Quote:
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 ? 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 |
can you state how the spell works? |
| 07-03-2008, 08:20 PM | #3 |
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 ... |
| 07-04-2008, 01:25 AM | #4 |
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 | |
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:
|
| 07-04-2008, 08:53 AM | #6 | |||
Quote:
Quote:
I am really lost here... =S Quote:
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 |
JASS:globals private group castingUnits = CreateGroup() endglobals ...and in the onDestroy method: call GroupRemoveUnit(castingUnits, caster) Then, you need the following function: 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 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 | |
Quote:
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 | ||
Quote:
Quote:
|
| 07-04-2008, 02:38 PM | #10 | ||
Quote:
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... Quote:
|
| 07-04-2008, 02:43 PM | #11 |
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: 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 |
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 | |
Quote:
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 |
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 | |
Quote:
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 =) |
