| 07-08-2008, 02:24 PM | #1 |
Well, now that my Apocalypse spell got approved I am very happy. When making that spell, I also gained some experience, which I am now using to make another spell that many people will like: Stupid Penguin So you guys are now wondering, what is this spell about ? Well, is simple, It is a channeling spell. When the caster starts the channel, he creates a Stupid Penguin that can only move (has no attack). Each second (or cicle you choose) the Penguin grows and gets bigger and bigger until ... BOOMMMM it excplodes and damages enemies !! I like this spell, when I am angry I just pretend the Penguin is some stupid guy I hate, and I just blow him up ! It can be kinda relaxing =P Anyway, I made this spell with dynamic triggers some time ago ... (now you are all cursing me, like, OMG didn't you learn a thing ? Well, but I did, this spell was done before submitting my Apocalypse spell here, so I am now updating it) The mechanic of the spell is again, very simple and intuitive. I bet you will all understand how it works after looking 10 seconds to the code. It is very similar to the Apocalypse spell. JASS:
globals
private group StupidPenguinCasters = CreateGroup()
private HandleTable activeTable //your private Table's global variable
endglobals
private struct MyStruct
unit caster
integer level
unit penguin
real currentScale
timer growPeriod
real grow
real damage
static method create takes unit caster, real spellX, real spellY returns MyStruct
local MyStruct data = MyStruct.allocate()
//set variables about the caster
set data.caster = caster
set data.level = GetUnitAbilityLevel(data.caster, AID)
//set variables about the Stupid Penguin
set data.penguin = CreateUnit(GetOwningPlayer(data.caster), UNITID, spellX, spellY, 0.)
set data.currentScale = 1
set data.growPeriod = NewTimer()
set data.grow = Growth(data.level)
set data.damage = Damage(data.level)
return data
endmethod
method onDestroy takes nothing returns nothing
//since the spell is not active anymore, we clean the Table
call activeTable.flush(.caster)
//the unit is not anymore in the active units group.
call GroupRemoveUnit(StupidPenguinCasters, .caster)
//releasing the timer for CSSafety to use it one day later =D
call ReleaseTimer(.growPeriod)
endmethod
endstruct
//===========================================================================
private function onStop takes nothing returns boolean
local MyStruct data
local unit u = GetTriggerUnit()
//variables for the explosion!
local group g
local unit f
local boolexpr b
local texttag text
local effect explosionPenguin
local effect deathPenguin
local effect blood
//this will save you unnecessary gamecache calls during units' deaths.
//It also prevents conflicts with units getting the same handle id as a ghost,
//however, since you flush when the spell ends that's most likely not an issue.
if(IsUnitInGroup(u, StupidPenguinCasters)) then
set data = activeTable[u] //recover that data (the struct) from the caster
//we blow with everything up !! xD
set g = NewGroup()
//I want to use a boolean expresion, so I can place a function up in the SETUP
//part that will allow the user to choose the targets of the explosion, but I don't
//know how to do that without
// 1. Placing the entire structure before the SETUP (which will be very ugly)
// 2. out of ideas ...
set b = Condition(function Targets)
//we make an explosion and add a death effect to give the ilusion the Penguin exploded
set explosionPenguin = AddSpecialEffect(EXPLOSION_EFFECT, GetUnitX(data.penguin), GetUnitY(data.penguin))
set deathPenguin = AddSpecialEffect(DEATH_EFFECT, GetUnitX(data.penguin), GetUnitY(data.penguin))
//remove the Penguin
call ShowUnit(data.penguin, false)
call KillUnit(data.penguin)
call GroupEnumUnitsInRange(g, GetUnitX(data.penguin), GetUnitY(data.penguin), Radius(data.level), b)
loop
set f = FirstOfGroup(g)
exitwhen (f == null)
call GroupRemoveUnit(g, f)
// the effect when the unit is damaged
set blood = AddSpecialEffect(BLOOD_EFFECT, GetUnitX(f), GetUnitY(f))
call UnitDamageTarget(data.penguin, f, data.damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, null)
//sets the text tag if the flag is true
if(TEXT) then
set text = CreateTextTag()
call SetTextTagText(text, I2S(R2I(data.damage)), .023 )
call SetTextTagPosUnit( text, f, 0 )
call SetTextTagColor( text, RED, GREEN, BLUE, 255 )
call SetTextTagPermanent(text, false)
call SetTextTagVelocity( text, 0, .0277 )
call SetTextTagLifespan(text, 2.0)
endif
call DestroyEffect(blood)
endloop
call DestroyBoolExpr(b)
call DestroyEffect(explosionPenguin)
call DestroyEffect(deathPenguin)
call ReleaseGroup(g)
call data.destroy() //now, time to clean the mess once again
endif
set b = null //PS cant we recycle bolleexpressions ??
set text = null
set explosionPenguin = null
set deathPenguin = null
set g = null
set u = null
return false
endfunction
//===========================================================================
private function Size takes nothing returns nothing
//code to make the penguin grow bigger and bigger !
//we also set the damage variables and all that stuff here
endfunction
//===========================================================================
private function Conditions takes nothing returns boolean
local MyStruct data
//I don't like creating this 2 variables everytime I go for a check =/
local effect ef
local location spellLoc
if (GetSpellAbilityId() == AID) then
//setting variables for the struct
set spellLoc = GetSpellTargetLoc()
set data = MyStruct.create(GetTriggerUnit(), GetLocationX(spellLoc), GetLocationY(spellLoc))
//this is just an eye candy, when the Stupid Penguin borns =)
//If you could find an animation with an egg, that could be cute xD
set ef = AddSpecialEffect(BIRTH_EFFECT, GetUnitX(data.penguin), GetUnitY(data.penguin))
//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 attach the struct to the timer and we start it
call SetCSData(data.growPeriod, integer(data))
call TimerStart(data.growPeriod, GrowTime(data.level), true, function Size)
//we add the casting unit to some sort of "pool" with all other casters
//that are using this spell
call GroupAddUnit(StupidPenguinCasters, data.caster)
//cleaning up the mess
call RemoveLocation(spellLoc)
call DestroyEffect(ef)
endif
set ef = null
set spellLoc = null
return false
endfunction
//===========================================================================
private function Init takes nothing returns nothing
local trigger StupidPenguinTrigger =CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(StupidPenguinTrigger, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition(StupidPenguinTrigger, Condition( function Conditions ) )
set StupidPenguinTrigger = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(StupidPenguinTrigger, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
call TriggerAddCondition(StupidPenguinTrigger, Condition(function onStop))
//Create your spell's private table for the casters
set activeTable = HandleTable.create()
endfunction
endscopeHowever I have some problems, as you can see, mainly on the boolexpression. I'd hate to place the whole struct before the SETUP part and to make it worse, I also have a few problems with the local variables, namely because I don't like some of the stuff I do with them. The Size function is empty because it is not needed for now, I like to clear stuff by sections. Also, one question, every time I create a local variable, is that local variable a pointer, just like when I create triggers ? I hope you people can help me out with this spell as well, I would also like to submit it to this community after finishing it =) Hope one day my submission number can be bigger =) EDIT EDIT Btw, can I (should I) replace this: JASS:
globals
private group StupidPenguinCasters = CreateGroup()
private HandleTable activeTable //your private Table's global variable
endglobalsby this ??? JASS:globals private group StupidPenguinCasters = NewGroup() //maybe using group recycle system ? private HandleTable activeTable //your private Table's global variable endglobals |
| 07-08-2008, 02:35 PM | #2 |
Why not use Tornado? It's already a channeling summon spell. |
| 07-08-2008, 03:18 PM | #3 | |
Quote:
He's right, just setup a trigger that detects the summoned units death and then do AoE damage, and about the growing penguin part you can also trigger it. |
| 07-08-2008, 07:19 PM | #4 |
Mmmmmmm although it wouldn't be a bad idea, the only thing that would change would be the fact that I create the unit in the "create" method. Other things would remain equal or would be little changed. Besides, I am looking for the way to learn more, not the easiest way - the easiest way would be using the spell with the dynamic triggers I made. I also like to make my spells very independent from the WE, if for some reason i want to add this spell to naga hero, they will conflict unless I screw with the ID orders, and there is no need for that. Besides the spell is nearly made, I just need some guide lines and answer from you guys, before submitting the spell =) So, any more suggestions please ? EDIT EDIT EDIT However, I will consider this solution if this get messy. Btw, you should know that the penguin can get attacked and can die =) EDIT EDIT EDIT I can't use tornado, if i do so, the unit will be invulnerable and I don't want that =S |
| 07-08-2008, 07:55 PM | #5 |
Can't the tornado spawn a different unit, which may have or may not have the invulnerability? |
| 07-08-2008, 07:56 PM | #6 |
I haven't reviewed the entire code yet, the only suggestion I could offer you now is to use a global boolexpr to store Condition(Target) so you wouldn't need to create a boolexpr handle each time the function is called. |
| 07-08-2008, 08:21 PM | #7 | ||
Quote:
Quote:
Besides its not in every iteration, it runs 1 time when the Penguin dies ... |
| 07-08-2008, 08:29 PM | #8 |
It's not a new handle every time, boolexpressions work in mysterios ways. |
| 07-08-2008, 08:37 PM | #9 |
Any suggestions on ow to improve the spell ? Any answers to my many questions ??? Some one ? please ? |
| 07-08-2008, 09:51 PM | #10 |
Ok guys, I made some updates, and now I leave code here for more suggestions =P JASS:scope StupidPenguinRmk initializer Init globals private unit tmpCaster = null endglobals //=========================================================================== //=============================SETUP START=================================== //=========================================================================== globals private constant integer AID = 'A000' //The rawcode of the Apocalypse ability private constant integer UNITID = 'npng' //The Penguin's ID private constant string EXPLOSION_EFFECT = "Objects\\Spawnmodels\\NightElf\\NEDeathMedium\\NEDeath.mdl" //explosion effect when penguin dies private constant string DEATH_EFFECT = "Objects\\Spawnmodels\\Orc\\OrcSmallDeathExplode\\OrcSmallDeathExplode.mdl" //death's effect of penguin private constant string BLOOD_EFFECT = "Objects\\Spawnmodels\\Critters\\Albatross\\CritterBloodAlbatross.mdl" //effect shown when units take damage private constant string BIRTH_EFFECT = "Objects\\Spawnmodels\\Other\\ToonBoom\\ToonBoom.mdl" //effect shown when pinguin is created private constant integer RED = 0 //the red RGB color for the text tag private constant integer GREEN = 0 //the green RGB color for the text tag private constant integer BLUE = 255 //the blue RGB color for the text tag private constant boolean TEXT = true //if true, shows text saying damage abve units, if false, it doesn't endglobals private constant function GrowTime takes integer level returns real time return 1. + (level * 0) //time intervail between each time the unit grows (is a cicle) endfunction private constant function Growth takes integer level returns real grow return 0.1 * level //How much the unit will grow. It grows 10% * level in this case endfunction private constant function Radius takes integer level returns integer radius return 200 + (50 * (level - 1)) //the area of damage endfunction private constant function Damage takes integer level returns real damage return 10. * level //Damage increase per cicle endfunction private function Targets takes nothing returns boolean //these are the targets the explosion will affect ! return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(tmpCaster)) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false) and (GetWidgetLife(GetFilterUnit()) > 0.405) endfunction //=========================================================================== //=============================SETUP END===================================== //=========================================================================== globals private group StupidPenguinCasters = CreateGroup() private HandleTable activeTable //your private Table's global variable endglobals private struct MyStruct unit caster integer level unit penguin real currentScale timer growPeriod real grow real damage static method create takes unit caster, real spellX, real spellY returns MyStruct local MyStruct data = MyStruct.allocate() //set variables about the caster set data.caster = caster set data.level = GetUnitAbilityLevel(data.caster, AID) //set variables about the Stupid Penguin set data.penguin = CreateUnit(GetOwningPlayer(data.caster), UNITID, spellX, spellY, 0.) set data.currentScale = 1 set data.growPeriod = NewTimer() set data.grow = Growth(data.level) set data.damage = Damage(data.level) return data endmethod method onDestroy takes nothing returns nothing //since the spell is not active anymore, we clean the Table call activeTable.flush(.caster) //the unit is not anymore in the active units group. call GroupRemoveUnit(StupidPenguinCasters, .caster) //releasing the timer for CSSafety to use it one day later =D call ReleaseTimer(.growPeriod) endmethod endstruct //=========================================================================== private function explosion takes MyStruct structure returns nothing //variables for the explosion! local MyStruct data = structure local group g = NewGroup() local unit f local boolexpr b local texttag text local effect explosionPenguin = AddSpecialEffect(EXPLOSION_EFFECT, GetUnitX(data.penguin), GetUnitY(data.penguin)) local effect deathPenguin = AddSpecialEffect(DEATH_EFFECT, GetUnitX(data.penguin), GetUnitY(data.penguin)) local effect blood //remove the Penguin call ShowUnit(data.penguin, false) call KillUnit(data.penguin) set tmpCaster = data.caster set b = Condition(function Targets) call GroupEnumUnitsInRange(g, GetUnitX(data.penguin), GetUnitY(data.penguin), Radius(data.level), b) loop set f = FirstOfGroup(g) exitwhen (f == null) call GroupRemoveUnit(g, f) // the effect when the unit is damaged and the damage set blood = AddSpecialEffect(BLOOD_EFFECT, GetUnitX(f), GetUnitY(f)) call UnitDamageTarget(data.penguin, f, data.damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, null) //sets the text tag if the flag is true if(TEXT) then set text = CreateTextTag() call SetTextTagText(text, I2S(R2I(data.damage)), .023 ) call SetTextTagPosUnit( text, f, 0 ) call SetTextTagColor( text, RED, GREEN, BLUE, 255 ) call SetTextTagPermanent(text, false) call SetTextTagVelocity( text, 0, .0277 ) call SetTextTagLifespan(text, 2.0) endif call DestroyEffect(blood) endloop call DestroyBoolExpr(b) call DestroyEffect(explosionPenguin) call DestroyEffect(deathPenguin) call ReleaseGroup(g) set b = null //PS cant we recycle bolleexpressions ?? set text = null set explosionPenguin = null set deathPenguin = null set g = null endfunction //=========================================================================== private function onStop takes nothing returns boolean local MyStruct data local unit u = GetTriggerUnit() //this will save you unnecessary gamecache calls during units' deaths. //It also prevents conflicts with units getting the same handle id as a ghost, //however, since you flush when the spell ends that's most likely not an issue. if(IsUnitInGroup(u, StupidPenguinCasters)) then set data = activeTable[u] //recover that data (the struct) from the caster call explosion(data) //here we make things blow up ! call data.destroy() //now, time to clean the mess once again endif set u = null return false endfunction //=========================================================================== private function onDeath takes nothing returns boolean local MyStruct data local unit u = GetTriggerUnit() //PS HELP, HOW DO I KNOW THE PENGUIN THAT DIED ?! //CAN I HAVE 1 TABLE FOR 2 UNITS ? if(IsUnitInGroup(u, StupidPenguinCasters)) then set data = activeTable[u] //recover that data (the struct) from the caster call explosion(data) //here we make things blow up ! call data.destroy() //now, time to clean the mess once again endif set u = null return false endfunction //=========================================================================== private function Size takes nothing returns nothing //code to make the penguin grow bigger and bigger ! //we also set the damage variables and all that stuff here endfunction //=========================================================================== private function Conditions takes nothing returns boolean local MyStruct data local effect ef local location spellLoc if (GetSpellAbilityId() == AID) then //setting variables for the struct set spellLoc = GetSpellTargetLoc() set data = MyStruct.create(GetTriggerUnit(), GetLocationX(spellLoc), GetLocationY(spellLoc)) //this is just an eye candy, when the Stupid Penguin borns =) //If you could find an animation with an egg, that could be cute xD set ef = AddSpecialEffect(BIRTH_EFFECT, GetUnitX(data.penguin), GetUnitY(data.penguin)) //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 attach the struct to the timer and we start it call SetCSData(data.growPeriod, integer(data)) call TimerStart(data.growPeriod, GrowTime(data.level), true, function Size) //we add the casting unit to some sort of "pool" with all other casters //that are using this spell call GroupAddUnit(StupidPenguinCasters, data.caster) //cleaning up the mess call RemoveLocation(spellLoc) call DestroyEffect(ef) endif set ef = null set spellLoc = null return false endfunction //=========================================================================== private function Init takes nothing returns nothing local trigger StupidPenguinTrigger =CreateTrigger() call TriggerRegisterAnyUnitEventBJ(StupidPenguinTrigger, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(StupidPenguinTrigger, Condition( function Conditions ) ) set StupidPenguinTrigger = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(StupidPenguinTrigger, EVENT_PLAYER_UNIT_SPELL_ENDCAST) call TriggerAddCondition(StupidPenguinTrigger, Condition(function onStop)) set StupidPenguinTrigger = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(StupidPenguinTrigger, EVENT_PLAYER_UNIT_DEATH) call TriggerAddCondition(StupidPenguinTrigger, Condition(function onDeath)) //Create your spell's private table for the casters set activeTable = HandleTable.create() endfunction endscope Any suggestions ? |
| 07-09-2008, 03:04 AM | #11 | |
Quote:
|
| 07-09-2008, 03:07 AM | #12 |
. . . Can you use something OTHER than a penguin? |
| 07-09-2008, 03:28 AM | #13 |
I always thought exploding mini abominations were somewhat a sight to behold... |
| 07-09-2008, 09:08 AM | #14 | |||
Quote:
Quote:
And you can choose what kind of unit you can blow in the SETUP part. Quote:
About the code, can any1 tell me how I can solve the Penguin thing ?? when he dies ? I already know a solution, I just need to know how to use table with him =S |
| 07-09-2008, 09:53 AM | #15 | |
Quote:
|
