| 08-16-2008, 09:04 PM | #1 |
Ok, I already did a lot of updates to this spell, and since no one is giving me suggestions on how to improve the spell and I already waited some time, I think the spell is quite ready for submission. To be honest with myself, I never expected the spell to go this far at all. Using 2D dynamic arrays was a complete new experience for me, I just hope I learned it well. Anyway, Here is the spell. This spell belongs to my project Castle vs Castle Flame Edition and I hope you all enjoy it. Description: Golem Invocation - This spell summons creatures while the caster is channeling the spell, and it has many options for the user. This is my 3rd submit to this community, it is just a way to thank all help and experience you guys have been giving me, and I hope you all like it. Forest Call - Creates a green paradise in the target area. Ancients and trees will spawn to aid the ranger fight her enemies and, while inside the green paradise, all allied units will benefit of special abilities. Affects a 500 AOE. This is also my spell for the Spell Olympics, I hope you all enjoy. Requirements: - Jass NewGen Pack (uses vJASS) - Table - TimerUtils History: Version 1.0 - Innitial release Version 1.1 - Code and comments both improved in performance and readability Version 1.6 - Versions 1.2, 1.3, 1.4, 1.5 were test versions that didn't get released - The core of the spell was drastically improved in performance - The spell now is easier to use and change due the use of dynamic arrays - This version is a culmination of knowledge provided from the other versions before Version 1.6.1 - Updated for patch 1.24 I just hope this can get approved now, since I really wanna enter the olympics and want to add this spell to my project. Please notify me if I am doing something wrong about posting the spell here. JASS://=========================================================================== //A spell that creates an Earthquake. This earthquake creates dust and spawns //golems inside the target area. Each golem has a period of life, and weaker //golems have more chances to be spawned. // //Requires TimerUtils and Table // //@author Flame_Phoenix // //@credits //- the-thingy, Kyrbi0, Pyrogasm, Anitarf //-Rising_Dusk for the dust model //- My first teacher of vJASS: Blue_Jeans //- All other people I forgot or ignored // //@version 1.6 //=========================================================================== scope GolemInvocation initializer Init //This will be needed for spell's core, don't change private keyword golemChances private keyword golemType private keyword GOLEM_POOLS //=========================================================================== //=============================SETUP START=================================== //=========================================================================== globals private constant integer AID = 'A001' //the rawcode of the ability private constant string ANIMATION = "birth" //the animation that will be played when the unit is created private constant integer MAX_TYPES = 3 //The maximum number of unit types of the spell private constant integer MAX_LEVELS = 3 //the maximum number of levels of the spell private constant integer ARRAY_SIZE = 4 //MAX_LEVELS + 1. This will be needed for the spell's core, and is important that is correct. endglobals //=========================================================================== //============================GOLEM SETUP==================================== //=========================================================================== private function GolemCalibration takes nothing returns nothing //here we type all types of units this spell can have //in this case I only use 3 types of units set golemType[1] = 'ngrk' //Mud Golem lv2 set golemType[2] = 'ngst' //Rock Golem lv6 set golemType[3] = 'nggr' //Granite Golem lv9 //Here we type the chances that each unit has to be spawned //Note that the chances start in 0 and go to 1.0 //All chances together must be equal to 1.0, for the spell to work //properly. //level 1 chances set golemChances[1][1] = 0.5 //Mud Golem has 50% chance to be spawned in lv1 set golemChances[1][2] = 0.3 //Rock Golem has 30% chance to be spawned in lv1 set golemChances[1][3] = 0.2 //Granite Golem has 20% chance to be spawned in lv1 //level 2 chances set golemChances[2][1] = 0.4 //Mud Golem has 40% chance to be spawned in lv2 set golemChances[2][2] = 0.4 //Rock Golem has 40% chance to be spawned in lv2 set golemChances[2][3] = 0.2 //Granite Golem has 20% chance to be spawned in lv2 //level 3 chances set golemChances[3][1] = 0.4 //Mud Golem has 40% chance to be spawned in lv3 set golemChances[3][2] = 0.3 //Rock Golem has 30% chance to be spawned in lv3 set golemChances[3][3] = 0.3 //Granite Golem has 30% chance to be spawned in lv3 //PS: note that in all levels, chanceGolem1 + chanceGolem2 + chanceGoem3 = 1 ALWAYS. endfunction private constant function GolemLife takes integer level returns real //the amount of time each golem will have, if ChannelGolems() is false return level * 20. endfunction private constant function ChannelGolems takes integer level returns boolean //if true it will make all spawned golems die when the caster stops the channel //if false, it allows the golems to have GolemLife() seconds of life and they //will not die when the caster stops the channel //in this case my golems have 20, 40 and 60 seconds of life for each level. return false //example of usage // if (level == 3) then // return false // endif // return true //in this example the golems will die when the channel ends in level 1 and 2 //in level 3, the golems will live 60 seconds of life endfunction private constant function GolemsPerCycle takes integer level returns integer //the number of golems that will be created each cycle return 1 + (level * 0) endfunction private constant function Cycle takes integer level returns real //the cycle which will determine when we create golems //this means, GolemsPerCycle() golems are created every cycle //in this case, we create 1 golem per second (in this case a second //is a cycle) return 1. + (level * 0) endfunction //=========================================================================== //============================DUST SETUP===================================== //=========================================================================== private constant function DummyEffectId takes integer level returns integer //the rawcode of the dummy unit that will be dust //this is in a function so you can have different effects in different //levels return 'h000' endfunction private constant function MinRange takes integer level returns real //in this case we don't have a minimum range, which means we can //create golems at the center of the circle //the minimum range is smaller circle, inside the area of the spell //in which golems will not be created return 0. endfunction private constant function MaxRange takes integer level returns real //this ensures golems are not created outside the AOE of the spell //this gives the AOE of the spell. This is the max AOE of the spell. return 200. + (level * 100) endfunction private constant function CircleCount takes integer level returns integer //the number of circles that will be created to give the illusion of the dust //effect. The more circles the more realistic, but more CPU will be needed. return 3 + level endfunction private constant function InnerCurcleUnits takes integer level returns integer //the number of units that the most inner circle will have return level + 5 endfunction private constant function InnerCurcleUnitsIncrease takes integer level returns integer //the increment of units per circle return level + 2 endfunction //=========================================================================== //=============================SETUP END===================================== //=========================================================================== type chancesPerType extends real array[ARRAY_SIZE] globals private group GolemInvocationCasters private HandleTable activeTable private chancesPerType array golemChances private integer array golemType private unitpool array GOLEM_POOLS endglobals private struct MyStruct unit caster integer level timer cycleTimer real spellLocX real spellLocY group dummyEffects group golems static method create takes unit caster, real locX, real locY returns MyStruct local MyStruct data = MyStruct.allocate() //set variables about the caster set data.caster = caster set data.level = GetUnitAbilityLevel(data.caster, AID) //variables about the location set data.spellLocX = locX set data.spellLocY = locY //the timer which will determine when we create golems //creates a golem everytime it expires set data.cycleTimer = NewTimer() //This groups will save the dumy dust effects, so we can kill them //when the caster stops the channeling set data.dummyEffects = CreateGroup() //if we want the golems to die when the caster stops channel //then we create this group so we can add all golems to it //then we kill the golems when the caster stops the channel if (ChannelGolems(data.level)) then set data.golems = CreateGroup() endif return data endmethod method onDestroy takes nothing returns nothing //here we select all units from the dust group and we kill them all ! =P //thus meaning that the dust effects will disappear ! local unit f loop set f = FirstOfGroup(.dummyEffects) exitwhen(f == null) call GroupRemoveUnit(.dummyEffects, f) call KillUnit(f) endloop //if the variable is true, then golems group was created and it has //units, and so now we kill them all. if (ChannelGolems(.level)) then loop set f = FirstOfGroup(.golems) exitwhen(f == null) call GroupRemoveUnit(.golems, f) call KillUnit(f) endloop call DestroyGroup(.golems) 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(GolemInvocationCasters, .caster) //releasing the timer for TimerUitls to use one day later =D call ReleaseTimer(.cycleTimer) call DestroyGroup(.dummyEffects) endmethod endstruct //=========================================================================== private function onStop takes nothing returns boolean local MyStruct data local unit u = GetTriggerUnit() //when a unit stops the channel, we verify if that unit is inside the //catsers group. If so, than it is because it is our caster casting this spell //and so we recover information about him and we make the spell end if(IsUnitInGroup(u, GolemInvocationCasters)) then //recover the data from the caster set data = activeTable[u] //now, time to clean the mess once again call data.destroy() endif set u = null return false endfunction //=========================================================================== //Function made by Themerion and adapted by Flame_Phoenix //Creates circles of dummy units to give the illusion of the dust effect private function createDust takes integer aStruct returns nothing local MyStruct data = aStruct // forPlayer -> Create the units for which player? local player forPlayer = GetOwningPlayer(data.caster) // innerRadius -> The radius of the innermost circle // outerRadius -> The radius of the outmost circle local real innerRadius = MinRange(data.level) local real outerRadius = MaxRange(data.level) // x and y -> Center of circles local real x = data.spellLocX local real y = data.spellLocY local real radiusInc = (outerRadius - innerRadius) / I2R(CircleCount(data.level)-1) //see functions they call local integer fxcount = InnerCurcleUnits(data.level) local integer fxcountinc = InnerCurcleUnitsIncrease(data.level) local real phi = 0 local real phiInc loop exitwhen (innerRadius + 0.001 >= outerRadius) set phi = 0 set phiInc = 2*bj_PI/I2R(fxcount) loop exitwhen (phi + 0.001 >= 2 * bj_PI) //we add the units to a group, so we can kill them when we want ! =P call GroupAddUnit(data.dummyEffects, CreateUnit(forPlayer, DummyEffectId(data.level), x + innerRadius * Cos(phi), y + innerRadius * Sin(phi), 180 + Rad2Deg(phi))) set phi = phi + phiInc endloop set innerRadius = innerRadius + radiusInc set fxcount = fxcount + fxcountinc endloop set forPlayer = null endfunction //=========================================================================== //Function made by Vexorian, it selects a random region in a disk. //All regions have the same chance of beeing choosen private function GetRandomPointInDisk takes real centerx, real centery, real minradius, real maxradius returns location local real d = SquareRoot(GetRandomReal(minradius * minradius, maxradius * maxradius)) local real a = GetRandomReal(0, 2 * bj_PI) return Location(centerx + d * Cos(a), centery + d * Sin(a)) endfunction //=========================================================================== private function createGolems takes nothing returns nothing //we get the structure from the timer local MyStruct data = MyStruct(GetTimerData(GetExpiredTimer())) //variables for us to know where the golems will born local location point local real randomX local real randomY //just to keep count about how many golems we created local integer loopCounter = 0 //variables about the golems local unit golem local real golemFacing = GetUnitFacing(data.caster) //Here we spawn the golems loop exitwhen(loopCounter >= GolemsPerCycle(data.level)) set point = GetRandomPointInDisk(data.spellLocX, data.spellLocY, MinRange(data.level), MaxRange(data.level)) set randomX = GetLocationX(point) set randomY = GetLocationY(point) set golem = PlaceRandomUnit(GOLEM_POOLS[data.level], GetOwningPlayer(data.caster), randomX, randomY, golemFacing) call SetUnitAnimation( golem, ANIMATION ) //if ChannelGolems(data.level) is true we add the golem to it //so we can keep track of it and kill it later in the "onDestroy" //method //else if the variable is false, it means that the group was not //created, and we ant the units to have a LifeTime if (ChannelGolems(data.level)) then call GroupAddUnit(data.golems, golem) else call UnitApplyTimedLife(golem, 'BTLF', GolemLife(data.level)) endif call RemoveLocation(point) set point = null set loopCounter = loopCounter + 1 endloop set golem = null endfunction //=========================================================================== private function Conditions takes nothing returns boolean local MyStruct data local location spellLoc //the location of the spell if (GetSpellAbilityId() == AID) then //setting variables for the struct set spellLoc = GetSpellTargetLoc() set data = MyStruct.create(GetTriggerUnit(), GetLocationX(spellLoc), GetLocationY(spellLoc)) //put the struct in the Table, we just use the caster's handle adress as //the key which tells us where in the Table the struct is stored set activeTable[data.caster] = data // we add the casting unit to some sort of "pool" call GroupAddUnit(GolemInvocationCasters, data.caster) //now we create the dusty effect call createDust(data) //we attach the struct to the timer call SetTimerData(data.cycleTimer, integer(data)) call TimerStart(data.cycleTimer, Cycle(data.level), true, function createGolems) //cleaning up the mess call RemoveLocation(spellLoc) endif set spellLoc = null return false endfunction //========================================================================== private function Init takes nothing returns nothing //these integers are counters for a loop and will be used later local integer i local integer j //here we set up the triggers we will need //Creates the trigger for when the unit start the effect of the spell local trigger GolemInvocationTrigger = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ(GolemInvocationTrigger, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(GolemInvocationTrigger, Condition( function Conditions ) ) //Creates the trigger for when the unit ceases the channel set GolemInvocationTrigger = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(GolemInvocationTrigger, EVENT_PLAYER_UNIT_SPELL_ENDCAST) call TriggerAddCondition(GolemInvocationTrigger, Condition(function onStop)) set GolemInvocationTrigger = null //set our globals set activeTable = HandleTable.create() set GolemInvocationCasters = CreateGroup() //now we will Create UnitPools using loops and the //counters //in this loop we create the 2D dynamic arrays, and create an UnitPool //for each level set i = 1 loop exitwhen(i > MAX_LEVELS) set golemChances[i] = chancesPerType.create() set GOLEM_POOLS[i] = CreateUnitPool() set i = i + 1 endloop //here we fill all our arrays call GolemCalibration() //here we add the units to the UnitPools with a respective weight per level //PS: "weitgh" is the chances a unit has of being spawned. set i = 1 set j = 1 loop exitwhen( i > MAX_LEVELS) loop exitwhen(j > MAX_TYPES) call UnitPoolAddUnitType(GOLEM_POOLS[i], golemType[j], golemChances[i][j]) set j = j + 1 endloop set j = 1 set i = i + 1 endloop endfunction endscope JASS://=========================================================================== //A spell that creates a Green Paradise. This paradise spawns friendly trees //and ancients to aid you. While inside the paradise, friendly units also get //extra abilities. The weaker the tree, the more chances it has to be spawned. // //Requires TimerUtils and Table // //@author Flame_Phoenix // //@credits //- the-thingy, Kyrbi0, Pyrogasm, Anitarf //- My first teacher of vJASS: Blue_Jeans //- All other people I forgot or ignored // //@version 1.6 //=========================================================================== scope ForestCall initializer Init //This will be needed for spell's core, don't change private keyword treeChances private keyword treeType private keyword TREE_POOLS //=========================================================================== //=============================SETUP START=================================== //=========================================================================== globals private constant integer AID = 'A002' //the rawcode of the ability private constant string ANIMATION = "attack" //the animation that will be played when the unit is created private constant integer MAX_TYPES = 9 //The maximum number of unit types of the spell private constant integer MAX_LEVELS = 3 //the maximum number of levels of the spell private constant integer ARRAY_SIZE = 10 //MAX_TYPES + 1. This will be needed for the spell's core, and is important that is correct. private constant integer FOREST_AURA = 'A004' endglobals //=========================================================================== //============================TREE SETUP===================================== //=========================================================================== private function TreeCalibration takes nothing returns nothing //here we type all types of units this spell can have //in this case I use 6 types of units set treeType[1] = 'e000' //Treant set treeType[2] = 'e001' //Ancient Protector set treeType[3] = 'e006' //Tree of Life set treeType[4] = 'e003' //Ancient of Wonders set treeType[5] = 'e004' //Ancient of Lore set treeType[6] = 'e007' //Tree of Ages set treeType[7] = 'e002' //Ancient of War set treeType[8] = 'e005' //Ancient of Wind set treeType[9] = 'e008' //Tree of Eternity //Here we type the chances that each unit has to be spawned //Note that the chances start in 0 and go to 1.0 //All chances together must be equal to 1.0, for the spell to work //properly. //level 1 chances set treeChances[1][1] = 0.5 set treeChances[1][2] = 0.3 set treeChances[1][3] = 0.2 set treeChances[1][4] = 0. set treeChances[1][5] = 0. set treeChances[1][6] = 0. set treeChances[1][7] = 0. set treeChances[1][8] = 0. set treeChances[1][9] = 0. //level 2 chances set treeChances[2][1] = 0. set treeChances[2][2] = 0. set treeChances[2][3] = 0. set treeChances[2][4] = 0.5 set treeChances[2][5] = 0.3 set treeChances[2][6] = 0.2 set treeChances[2][7] = 0. set treeChances[2][8] = 0. set treeChances[2][9] = 0. //level 3 chances set treeChances[3][1] = 0. set treeChances[3][2] = 0. set treeChances[3][3] = 0. set treeChances[3][4] = 0. set treeChances[3][5] = 0. set treeChances[3][6] = 0. set treeChances[3][7] = 0.4 set treeChances[3][8] = 0.3 set treeChances[3][9] = 0.3 //PS: note that in all levels, if we sum the chances, we ALWAYS get 1 endfunction private constant function TreeLife takes integer level returns real //the amount of time each tree will have, if ChannelTrees() is false return level * 10. endfunction private constant function ChannelTrees takes integer level returns boolean //if true it will make all spawned trees die when the caster stops the channel //if false, it allows the trees to have TreeLife() seconds of life and they //will not die when the caster stops the channel //in this case my trees have die when the channel stops in levels 1 and 2, but //in level 3 they get 30 seconds of life. if (level == 3) then return false endif return true endfunction private constant function TreesPerCycle takes integer level returns integer //the number of trees that will be created each cycle return 1 + (level * 0) endfunction private constant function Cycle takes integer level returns real //the cycle which will determine when we create trees //this means, TreesPerCycle() trees are created every cycle //in this case, we create 1 tree per second (in this case a second //is a cycle) return 1. + (level * 0) endfunction //=========================================================================== //============================EFFECT SETUP=================================== //=========================================================================== private constant function DummyEffectId takes integer level returns integer //the rawcode of the dummy unit that will be effect //this is in a function so you can have different effects in different //levels return 'h001' endfunction private constant function MinRange takes integer level returns real //in this case we don't have a minimum range, which means we can //create trees at the center of the circle //the minimum range is smaller circle, inside the area of the spell //in which trees will not be created return 0. endfunction private constant function MaxRange takes integer level returns real //this ensures trees are not created outside the AOE of the spell //this gives the AOE of the spell. This is the max AOE of the spell. return 500. + (level * 0) endfunction private constant function CircleCount takes integer level returns integer //the number of circles that will be created to give the illusion of the effect //effect. The more circles the more realistic, but more CPU will be needed. return 3 + level endfunction private constant function InnerCyrcleUnits takes integer level returns integer //the number of units that the most inner circle will have return level + 5 endfunction private constant function InnerCyrcleUnitsIncrease takes integer level returns integer //the increment of units per circle return level + 2 endfunction //=========================================================================== //=============================SETUP END===================================== //=========================================================================== type chancesPerTreeType extends real array[ARRAY_SIZE] globals private group TreeInvocationCasters private HandleTable activeTable private chancesPerTreeType array treeChances private integer array treeType private unitpool array TREE_POOLS endglobals private struct MyStruct unit caster integer level timer cycleTimer real spellLocX real spellLocY group dummyEffects group trees static method create takes unit caster, real locX, real locY returns MyStruct local MyStruct data = MyStruct.allocate() //set variables about the caster set data.caster = caster set data.level = GetUnitAbilityLevel(data.caster, AID) //variables about the location set data.spellLocX = locX set data.spellLocY = locY //the timer which will determine when we create trees //creates a treeeverytime it expires set data.cycleTimer = NewTimer() //This groups will save the dumy effect effects, so we can kill them //when the caster stops the channeling set data.dummyEffects = CreateGroup() //if we want the trees to die when the caster stops channel //then we create this group so we can add all trees to it //then we kill the trees when the caster stops the channel if (ChannelTrees(data.level)) then set data.trees = CreateGroup() endif return data endmethod method onDestroy takes nothing returns nothing //here we select all units from the effect group and we kill them all ! =P //thus meaning that the effect effects will disappear ! local unit f loop set f = FirstOfGroup(.dummyEffects) exitwhen(f == null) call GroupRemoveUnit(.dummyEffects, f) call KillUnit(f) endloop //if the variable is true, then trees group was created and it has //units, and so now we kill them all. if (ChannelTrees(.level)) then loop set f = FirstOfGroup(.trees) exitwhen(f == null) call GroupRemoveUnit(.trees, f) call KillUnit(f) endloop call DestroyGroup(.trees) 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(TreeInvocationCasters, .caster) //releasing the timer for TimerUitls to use one day later =D call ReleaseTimer(.cycleTimer) call DestroyGroup(.dummyEffects) endmethod endstruct //=========================================================================== private function onStop takes nothing returns boolean local MyStruct data local unit u = GetTriggerUnit() //when a unit stops the channel, we verify if that unit is inside the //catsers group. If so, than it is because it is our caster casting this spell //and so we recover information about him and we make the spell end if(IsUnitInGroup(u, TreeInvocationCasters)) then //recover the data from the caster set data = activeTable[u] //now, time to clean the mess once again call data.destroy() endif set u = null return false endfunction //=========================================================================== //Function made by Themerion and adapted by Flame_Phoenix //Creates circles of dummy units to give the illusion of the effect effect private function createGreenEffect takes integer aStruct returns nothing local MyStruct data = aStruct // forPlayer -> Create the units for which player? local player forPlayer = GetOwningPlayer(data.caster) // innerRadius -> The radius of the innermost circle // outerRadius -> The radius of the outmost circle local real innerRadius = MinRange(data.level) local real outerRadius = MaxRange(data.level) // x and y -> Center of circles local real x = data.spellLocX local real y = data.spellLocY local real radiusInc = (outerRadius - innerRadius) / I2R(CircleCount(data.level)-1) //see functions they call local integer fxcount = InnerCyrcleUnits(data.level) local integer fxcountinc = InnerCyrcleUnitsIncrease(data.level) local unit greenEffect local real phi = 0 local real phiInc loop exitwhen (innerRadius + 0.001 >= outerRadius) set phi = 0 set phiInc = 2*bj_PI/I2R(fxcount) loop exitwhen (phi + 0.001 >= 2 * bj_PI) //we add the units to a group, so we can kill them when we want ! =P set greenEffect = CreateUnit(forPlayer, DummyEffectId(data.level), x + innerRadius * Cos(phi), y + innerRadius * Sin(phi), 180 + Rad2Deg(phi)) call GroupAddUnit(data.dummyEffects, greenEffect) if (data.level == 1) then call SetUnitAbilityLevel(greenEffect, FOREST_AURA, 1) elseif (data.level == 2) then call SetUnitAbilityLevel(greenEffect, FOREST_AURA, 2) elseif (data.level == 3) then call SetUnitAbilityLevel(greenEffect, FOREST_AURA, 3) endif set phi = phi + phiInc endloop set innerRadius = innerRadius + radiusInc set fxcount = fxcount + fxcountinc endloop set forPlayer = null set greenEffect = null endfunction //=========================================================================== //Function made by Vexorian, it selects a random region in a disk. //All regions have the same chance of beeing choosen private function GetRandomPointInDisk takes real centerx, real centery, real minradius, real maxradius returns location local real d = SquareRoot(GetRandomReal(minradius * minradius, maxradius * maxradius)) local real a = GetRandomReal(0, 2 * bj_PI) return Location(centerx + d * Cos(a), centery + d * Sin(a)) endfunction //=========================================================================== private function createTrees takes nothing returns nothing //we get the structure from the timer local MyStruct data = MyStruct(GetTimerData(GetExpiredTimer())) //variables for us to know where the trees will born local location point local real randomX local real randomY //just to keep count about how many trees we created local integer loopCounter = 0 //variables about the trees local unit tree local real treeFacing = GetUnitFacing(data.caster) //Here we spawn the trees loop exitwhen(loopCounter >= TreesPerCycle(data.level)) set point = GetRandomPointInDisk(data.spellLocX, data.spellLocY, MinRange(data.level), MaxRange(data.level)) set randomX = GetLocationX(point) set randomY = GetLocationY(point) set tree = PlaceRandomUnit(TREE_POOLS[data.level], GetOwningPlayer(data.caster), randomX, randomY, treeFacing) call SetUnitAnimation( tree, ANIMATION ) //if ChannelTrees(data.level) is true we add the treeto it //so we can keep track of it and kill it later in the "onDestroy" //method //else if the variable is false, it means that the group was not //created, and we ant the units to have a LifeTime if (ChannelTrees(data.level)) then call GroupAddUnit(data.trees, tree) else call UnitApplyTimedLife(tree, 'BTLF', TreeLife(data.level)) endif call RemoveLocation(point) set point = null set loopCounter = loopCounter + 1 endloop set tree= null endfunction //=========================================================================== private function Conditions takes nothing returns boolean local MyStruct data local location spellLoc //the location of the spell if (GetSpellAbilityId() == AID) then //setting variables for the struct set spellLoc = GetSpellTargetLoc() set data = MyStruct.create(GetTriggerUnit(), GetLocationX(spellLoc), GetLocationY(spellLoc)) //put the struct in the Table, we just use the caster's handle adress as //the key which tells us where in the Table the struct is stored set activeTable[data.caster] = data // we add the casting unit to some sort of "pool" call GroupAddUnit(TreeInvocationCasters, data.caster) //now we create the green effects call createGreenEffect(data) //we attach the struct to the timer call SetTimerData(data.cycleTimer, integer(data)) call TimerStart(data.cycleTimer, Cycle(data.level), true, function createTrees) //cleaning up the mess call RemoveLocation(spellLoc) endif set spellLoc = null return false endfunction //========================================================================== private function Init takes nothing returns nothing //these integers are counters for a loop and will be used later local integer i local integer j //here we set up the triggers we will need //Creates the trigger for when the unit start the effect of the spell local trigger ForestCallTrigger = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ(ForestCallTrigger, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(ForestCallTrigger, Condition( function Conditions ) ) //Creates the trigger for when the unit ceases the channel set ForestCallTrigger = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(ForestCallTrigger, EVENT_PLAYER_UNIT_SPELL_ENDCAST) call TriggerAddCondition(ForestCallTrigger, Condition(function onStop)) set ForestCallTrigger = null //set our globals set activeTable = HandleTable.create() set TreeInvocationCasters = CreateGroup() //now we will Create UnitPools using loops and the //counters //in this loop we create the 2D dynamic arrays, and create an UnitPool //for each level set i = 1 loop exitwhen(i > MAX_LEVELS) set treeChances[i] = chancesPerTreeType.create() set TREE_POOLS[i] = CreateUnitPool() set i = i + 1 endloop //here we fill all our arrays call TreeCalibration() //here we add the units to the UnitPools with a respective weight per level //PS: "weitgh" is the chances a unit has of being spawned. set i = 1 set j = 1 loop exitwhen( i > MAX_LEVELS) loop exitwhen(j > MAX_TYPES) call UnitPoolAddUnitType(TREE_POOLS[i], treeType[j], treeChances[i][j]) set j = j + 1 endloop set j = 1 set i = i + 1 endloop endfunction endscope JASS://=========================================================================== //This is the trigger responsable for giving friendly units the special //abilities. // //@author Flame_Phoenix // //@credits //- Blade.dk (for CopyGroup function) //- All other people I forgot or ignored // //@version 1.0 //=========================================================================== scope ExtraAbility initializer Init globals private constant integer FOREST_AURA_BUFF1_ID = 'B000' private constant integer FOREST_AURA_BUFF2_ID = 'B001' private constant integer FOREST_AURA_BUFF3_ID = 'B002' private constant integer EVASIONBOOK_ID = 'A003' private constant integer CRITICBOOK_ID = 'A007' private constant integer MISCBOOK_ID = 'A008' private constant integer EVASION_ID = 'A000' private constant integer CRITIC_ID = 'A005' private constant integer MISC_ID = 'A006' endglobals globals private group UnitsLv1 private group UnitsLv2 private group UnitsLv3 endglobals //=========================================================================== private function CopyGroup takes group g returns group set bj_groupAddGroupDest = CreateGroup() call ForGroup(g, function GroupAddGroupEnum) return bj_groupAddGroupDest endfunction //=========================================================================== private function Actions takes nothing returns nothing local group map = CreateGroup() local unit f local group g = CreateGroup() //here we pick all units with the aura and add them to the respective groups call GroupEnumUnitsInRect(map, bj_mapInitialPlayableArea, null) loop set f = FirstOfGroup(map) exitwhen (f == null) call GroupRemoveUnit(map, f) if (GetUnitAbilityLevel(f, FOREST_AURA_BUFF1_ID) > 0) then call GroupAddUnit(UnitsLv1, f) call UnitAddAbility(f, EVASIONBOOK_ID) call SetUnitAbilityLevel(f, EVASION_ID, 1) call SetPlayerAbilityAvailable(GetOwningPlayer(f), EVASIONBOOK_ID, false) elseif(GetUnitAbilityLevel(f, FOREST_AURA_BUFF2_ID) > 0) then call GroupAddUnit(UnitsLv2, f) call UnitAddAbility(f, CRITICBOOK_ID) call SetUnitAbilityLevel(f, CRITIC_ID, 1) call SetPlayerAbilityAvailable(GetOwningPlayer(f), CRITICBOOK_ID, false) elseif(GetUnitAbilityLevel(f, FOREST_AURA_BUFF3_ID) > 0) then call GroupAddUnit(UnitsLv3, f) call UnitAddAbility(f, MISCBOOK_ID) call SetUnitAbilityLevel(f, MISC_ID, 1) call SetPlayerAbilityAvailable(GetOwningPlayer(f), MISCBOOK_ID, false) endif endloop //Here we check each unit in the groups, to see if they still have the aura's buff //if not, we remove them the abilities set g = CopyGroup(UnitsLv1) loop set f = FirstOfGroup(g) exitwhen(f == null) call GroupRemoveUnit(g, f) if (GetUnitAbilityLevel(f, FOREST_AURA_BUFF1_ID) <= 0) then call UnitRemoveAbility(f, EVASIONBOOK_ID) call GroupRemoveUnit(UnitsLv1, f) endif endloop set g = CopyGroup(UnitsLv2) loop set f = FirstOfGroup(g) exitwhen(f == null) call GroupRemoveUnit(g, f) if (GetUnitAbilityLevel(f, FOREST_AURA_BUFF2_ID) <= 0) then call UnitRemoveAbility(f, EVASIONBOOK_ID) call GroupRemoveUnit(UnitsLv2, f) endif endloop set g = CopyGroup(UnitsLv3) loop set f = FirstOfGroup(g) exitwhen(f == null) call GroupRemoveUnit(g, f) if (GetUnitAbilityLevel(f, FOREST_AURA_BUFF3_ID) <= 0) then call UnitRemoveAbility(f, EVASIONBOOK_ID) call GroupRemoveUnit(UnitsLv3, f) endif endloop call DestroyGroup(g) set g = null endfunction //=========================================================================== private function Init takes nothing returns nothing local trigger ExtraAbTrg = CreateTrigger() call TriggerRegisterTimerEvent(ExtraAbTrg, 0.1, true) call TriggerAddAction( ExtraAbTrg, function Actions ) set ExtraAbTrg = null //setting globals set UnitsLv1 = CreateGroup() set UnitsLv2 = CreateGroup() set UnitsLv3 = CreateGroup() endfunction endscope This took a lot of my effort, just to have a chance on the contest. I hope you like. |
| 08-17-2008, 03:24 AM | #2 |
You dont' need timer cycleTimer as a struct component, you can use a local timer variable in the Condition function and calling it in the looping function with GetExpiredTimer(). JASS:private function Conditions takes nothing returns boolean local MyStruct data local location spellLoc //the location of the spell local timer t if (GetSpellAbilityId() == AID) then //setting variables for the struct set spellLoc = GetSpellTargetLoc() set data = MyStruct.create(GetTriggerUnit(), GetLocationX(spellLoc), GetLocationY(spellLoc)) //put the struct in the Table, we just use the caster's handle adress as //the key which tells us where in the Table the struct is stored set activeTable[data.caster] = data // we add the casting unit to some sort of "pool" call GroupAddUnit(GolemInvocationCasters, data.caster) //now we create the dusty effect call createDust(data) //we attach the struct to the timer set t = NewTimer() call SetTimerData(t, integer(data)) call TimerStart(t, Cycle(data.level), true, function createGolems) //cleaning up the mess call RemoveLocation(spellLoc) endif set spellLoc = null set t = null return false endfunction |
| 08-17-2008, 09:54 AM | #3 |
Ok, But the reason why I make it part of the struct is so I can stop it on the "onDestroy" method. If I follow your suggestion, how will I stop the timer after that ? |
| 08-17-2008, 02:10 PM | #4 |
ahhh I got it, for a second I thought you used the loop function to control the spell end too. In that case forget what I said. For everything else this spell is fine. Just one advice: try to diversify in spell types, you've done too much channeling spells! :P seriously they're looking too repetitive. Because this spell is part of the Olympic competition, I'll keep it here until the contest finish, then it will be moved where it belongs. |
| 08-17-2008, 03:56 PM | #5 | |||
Quote:
Quote:
Anyway, since that Apocalypse spell I learned so much, I just wanted to use everything I know into making more spells lol. I am now doing another type of spell, a Fire Bomber, but I don't have enough experience to make it, and Anitarf (the only guy helping) is already sick of me asking for his help (I suppose he is, I understand his action though, it is normal, I know he has a life and I can be quite boring some time xD ). So you see, there isn't much I can do for now ... I'll just have to wait until people start paying more attention to the threads I post in the forum. Besides this is only my seconds channeling spell xD Quote:
Just one thing, now that I notice, I can't enter Oly with this spell, I use an imported model (the dust)... Just give me some time, I will change that spell into something else (like spawn trees, per example xD). Thx for approving =) |
| 08-17-2008, 09:40 PM | #6 | ||
Quote:
Quote:
Just so you don't miss that. |
| 08-18-2008, 07:49 AM | #7 |
What is a Pastebin thread ? I just go to Pastebin and upload my file there right ? Oh, I just posted the spell here to know if it had enough quality to compete. I am now kinda creating another spell for section "Nature" and is going to be totally awesome. |
| 08-18-2008, 09:30 AM | #8 | |
Quote:
Yes. |
| 08-18-2008, 07:16 PM | #9 |
Considering that I basically edited this with you and know how it is put together, I approve of it along with Moyack. Also considering how we are supposed to submit our spells for the Spell Olympics, I think it is okay if we move this thread to where it belongs. However, this thread will not be looked at for submissions. If you wish to submit this spell for the Olympics, do as the thread says to do and make your own pastebin thread with all of your spells in one map. Cheers! |
| 08-18-2008, 07:37 PM | #10 |
YEEEEEE !!! xD Thx guys. yes I will submit a modified version of this spell to the Olympics. Already have a name for it "Forest Call" =D |
| 08-18-2008, 08:19 PM | #11 |
Man, what is the model path for that earth dust? I use a lighter earthdust, which is the impale earth dust. |
| 08-19-2008, 08:51 AM | #12 |
I can't use impale because it lack a stand animation. Therefore I am forced to import this dust model. |
| 08-19-2008, 08:57 AM | #13 |
Well you'd better change that before you submit this to the Spell Olympics. There are ways around a model not having a stand animation. I actually didn't realize that that was an imported model until just now, and I must confess that I didn't actually test the spell this time around (thus, I didn't read any in-game messages if there are any), so I'll just have to ask you this: Have you credited the maker of the model in your map. And then the second thing I must say: In order for this to stay approved, you must get written permission (PM, email) from the model's maker to have it in your map. Either that, or simply remove the model and give it as a suggested model to use. |
| 08-19-2008, 09:32 AM | #14 |
Mmmm, I think the creator already left wc3, if so, what do I do ? I will still try to contact him and see what I can do. |
| 08-20-2008, 10:54 PM | #15 |
Send him an email. Contact him somehow or remove the model. |
