Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
I'm not an originality inspector here, I just checked your entry and gave my honest opinion. If it's your idea, well, it's OK then. But I thought maybe you need to know about it before you can modify it. Just trying to help here and no offense intended.
Contestants should not review other's work, that gives them advantage
as for Raynor's ship and Flux's Spell, it's up to the eye of the judges
For me, the spell's originality is not a bit unique ( I have seen so many spells/scenes of that spell from many games) but how the spell is made that what makes it unique (come on, just look at that awesome range indicator)
I did not review his entry. But I find leaving other contestants in the dark, in order to increase one's own chance of winning is a bit unfair. And also notifying others of the existing pitfalls, increases the quality of the contest. I'd like to compete against the best, not slip through by the mistakes that others make (Of course, if there are any mistakes, at all)
You need to reconfigure the names of your variables; look at the other entries to find out.
And by the way I don't think there is a need for Aghanim mode in this contest. Aghanim is only for DotA, and if it changes your spell's nature I think it is counted as 2 versions of your spell, which I think, completely from a personal viewpoint, will have negative score for you.
There's a difference between "reviewing" and "commenting", though. If the point is to discourage discussion between Contestants, well then, why even bother with a Contest thread, or WIP posts or anything? All of that is to get people talking, communicating, sharing, and exciting one another with the thrill of a good competition.
Some of my favorite Contests have been ones where the entrants weren't afraid/felt free to offer comments, tips, pointers, etc to the other entrants; in fact, they were sharing whole sections of code & discussing the pros/cons of various design elements. It was a real positive, collaborative environment, and extra-fun for it.
The actual nitty-gritty design & implementation, of course, must come from the individual, but there's no harm in talking & sharing & helping; no one likes to lose because of (the equivalent of) a spelling error, right?
I'm sorry you feel that way, man. I dream of a better world; one where cooperation is better than competition. I may not always live that to the max, but I believe we are all enriched when both we & our "opponents" are at our best. Who wants to win against a cripple (figuratively speaking)?
I'm sorry you feel that way, man. I dream of a better world; one where cooperation is better than competition. I may not always live that to the max, but I believe we are all enriched when both we & our "opponents" are at our best. Who wants to win against a cripple (figuratively speaking)?
I support your way of thinking by 100%! Feels like "Fremantle" spirit - good ol' home
I thought, I was not allowed to comment other people's spells.
Anyway, if you help someone, this does not matter much in this kind of contest, since you got to be creative and hope, that the judges love your idea more than others. I dont think you will lose only because of minor coding, if your idea is pretty neat. And anyway so what? Its better to create something awesome!
I know this is OT, but I want to add, that this is the reason why I think that protecting maps is not helpful for anybody.
Seems silly to me.
That's like saying "I want to lose so I'll give them a handicap" (Or, "I am much better than them, they need all the help they can get to stand a chance.")
Saying that it looks nice, or the concept is interesting etc is fine. But offering code advise.. nope nope.
Not until the results are in anyway.
I am all for helping, I do that a lot. But not in a contest.
Makes sense too, but giving the same tools to everybody makes it more interesting, I think. Lets say for example, someone is not able to figure out how to calculate a jumping motion, then I would rather help him finding the formula rather than leaving him and knowing he will lose a 100% just because he can't do the math. Maybe he would have created a hell of a jump if I helped him. Not saying how exactly he should do, I think just providing the basic stuff would be fair ... Like that you would still win most probably, but the other contestant would have a chance to develop something at least ..
Depends on the type of contest and what you are helping with, of course. If the contest was about finding the best jumping motion, it would be rather pointless, I agree. But still, the other one would lose in this case because of copying your stuff, so ...
This is a contest and contest is all about competition. If one is not ready for a competition then he shouldn't join. Why does one want to participate in a contest? Because he wants a challenge. To overcome a challenge he must know his own ability whether he could do that or not. That is the reason why a preparation is needed. Prepare everything you need to defeat the other contestants before the contest started. Once it has started, you may only rely on the ability you've prepared. If it's not good enough, you'll lose. That's just how competitions are.
Unit - Create 1 HO_DummyType[4] for (Owner of HO_Caster[HO_Cur_Index]) at Point[2] facing (Angle from Point[1] to Point[2]) degrees
Set HO_Dummy5[HO_Cur_Index] = (Last created unit)
Unit Group - Add HO_Dummy5[HO_Cur_Index] to HO_DumGroup[HO_Cur_Index]
Unit - Add a (((HO_MaxDist[HO_Level[HO_Cur_Index]] x 2.00) / HO_Speed3[HO_Level[HO_Cur_Index]]) / 100.00) second Generic expiration timer to HO_Dummy5[HO_Cur_Index]
Unit Group - Pick every unit in (Units within HO_AoE2[HO_Level[HO_Cur_Index]] of Point[1]) and do (Actions)
Loop - Actions
Set TempUnit = (Picked unit)
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(TempUnit belongs to an enemy of (Owner of HO_Caster[HO_Cur_Index])) Equal to True
(TempUnit is alive) Equal to True
(TempUnit is A structure) Equal to False
(TempUnit is in HO_DU[HO_Cur_Index]) Equal to False
Then - Actions
Unit - Cause HO_Caster[HO_Cur_Index] to damage TempUnit, dealing HO_Damage2[HO_Level[HO_Cur_Index]] damage of attack type Spells and damage type Normal
Set Point[1] = (Position of HO_Dummy5[HO_Cur_Index])
Set Point[2] = (Position of HO_Dummy2[HO_Cur_Index])
Set Point[3] = (Point[2] offset by ((Distance between Point[1] and Point[2]) - HO_Speed3[HO_Level[HO_Cur_Index]]) towards ((Angle from Point[2] to Point[1]) + 5.00) degrees)
How to Import:
Go to World Editor => file => Preferences => Check the "Automatically create unknown Variable while pasting trigger" => ok
then copy all triggers inside the Hellfire Category into your map. Copy the dummies and the ability itself into your map as well.
I would like to enter this contest as well with a WIP of the spell "Chicken Rage".
Tooltipp
Comments
This spell can be combined with another spell, that lets you type in a message, that the target player has to type in order to summon the chicken slayer. This unit instantly kills all chickens and throws them at the casting player, dealing explosive damage. I am not sure about that combination thing yet.
well, it is work in progress
Picutres
Sry for the ugly WIP pictures, I do not have much time atm.
"]
Credits
Models will be credited in the maps Quest Log and via a message on the screen.
This is a contest and contest is all about competition. If one is not ready for a competition then he shouldn't join. Why does one want to participate in a contest? Because he wants a challenge. To overcome a challenge he must know his own ability whether he could do that or not. That is the reason why a preparation is needed. Prepare everything you need to defeat the other contestants before the contest started. Once it has started, you may only rely on the ability you've prepared. If it's not good enough, you'll lose. That's just how competitions are.
Your comment reminds me of the times when I used to play DotA and saw people who would feast upon the noobs to prove that they are skilled players and chat shit about everyone and when faced with pro opponents you could see how they would fail and start crying.
If you compete against the best you need to raise your game, too. Otherwise you can simply rely on the weak and never improve your skills. That's what contests are about. Everyone wins even if they lose the contest.
Your comment reminds me of the times when I used to play DotA and saw people who would feast upon the noobs to prove that they are skilled players and chat shit about everyone and when faced with pro opponents you could see how they would fail and start crying.
If you compete against the best you need to raise your game, too. Otherwise you can simply rely on the weak and never improve your skills. That's what contests are about. Everyone wins even if they lose the contest.
It's not that much of an experience really. I could understand that argument if you went to the world cup in football or something because it's completely different and it brings much more than just matches.
But a contest in the hive.. it's exactly like uploading something to the spell section.
You code something on your own and you upload it, and get it graded.
The only difference is the competitive aspect. (due prizes)
Your comment reminds me of the times when I used to play DotA and saw people who would feast upon the noobs to prove that they are skilled players and chat shit about everyone and when faced with pro opponents you could see how they would fail and start crying.
If you compete against the best you need to raise your game, too. Otherwise you can simply rely on the weak and never improve your skills. That's what contests are about. Everyone wins even if they lose the contest.
Except that this isn't DotA and I doubt there are noobs in DotA tournament. This is a contest, not a game for practicing. Even DotA players practiced a lot of times against AI and other players until they feel they can compete in tournament.
Indeed, that's the reason I also joined. But what is the good of contest if you don't have a competitive aspect? The main goal of course is to win and the experience to get there is what worth.
Indeed, that's the reason I also joined. But what is the good of contest if you don't have a competitive aspect? The main goal of course is to win and the experience to get there is what worth.
I didn't mean to say that you shouldn't have a competitive aspect, you totally should.
What I meant is that the experience of participating in a contest and creating a new spell is worth more than the 5 rep points for participating (in case you don't score higher), as you will learn more by creating a new spell, seeing where there's room for improvement and getting skilled people's reactions.
Except that this isn't DotA and I doubt there are noobs in DotA tournament. This is a contest, not a game for practicing. Even DotA players practiced a lot of times against AI and other players until they feel they can compete in tournament.
I didn't compare DotA (especially tournaments) to this contest. I compared the behavior of average players in ordinary DotA games who prey on the weak to the attitudes found here to take advantage of others' weaknesses.
Let me tell you something: If you think that letting others know a major mistake in their work, will affect your winning odds, you probably lack enough confidence on your own skills/ideas and you need to reassess them, or as a hero once said "time to man up" Dare to face the betters of you.
I have nothing against letting others know a major mistake in their work. I'm just saying if one is not ready for a contest, he shouldn't participate. At least not until he's ready. That is what my comment was all about.
library ElementalChaos initializer Init /* version 1.0.1
*======================================Elemental Chaos by DD_LegionTN======================================================*
*
* Spell Description :
* An unstable sphere is generated for 3/2.5/2 seconds using the elemental power of the caster.
*
* The unstabilized sphere will emits out 3/4/5 packs of energy around the sphere,
* which will explode and creating 1~2/1~3/2~3 missiles.
* Once the missile landed on the ground, 10/20/30 damage within 50/80/110 AoE is dealt to nearby enemy units
* as well as summons an unstable elemental unit with 10/12/14 seconds life time.
*
* The unstable elemental unit will get reabsorb back to the sphere once it life time is expired.
* Maximum reabsorb 6/8/10 units. Maximum absorb range is 1000.
*
* The sphere will slowly lands on the ground after all summons expired,
* dealing 100/200/300 damage within 500/600/700 AoE to nearby enermy units
* and summons a stabilized elemental unit with life time 30/40/50 seconds.
* The summons hit points is dependent on the amount of the unstable elemental unit reabsorbed.
*
*==========================================================================================================================*
*
* Requirement :
* */requires /*
* - Elemental Chaos requires no nessasary library
*
*
* */optional DummyRecycler /*
* - Performance gain, recommended
*
* */optional SpellEffectEvent /*
* - Minor performance gain, recommended
*
* */optional Table /*
* - Not nessasary needed, in case people have Table present in their map
* so that there would not be generated an extra hashtable
*
* */optional ZLibrary /*
* - Not nessasary needed, in case people have ZLibrary present in their map
* so that no unnessasary location handle and function are generated
*==========================================================================================================================*
* Import Instruction :
* 1. Copy abilities into your map :
* a) Required - Elemental Chaos (A000)
* b) Optional - dummy unit (e000) if you don't have dummy unit in your map (using vexorian's dummy unit)
* The dummy unit MUST have locust and crow form abilties (Aloc and Amrf)
* c) Optional - Energy Absorbption (A004, A005, A006) and Regeneration (A001, A002, A003) if you want the
* passive regeneration of the SECOND_SUMMON
* d) Optional - Energy Loss (A00A, A00B, A00C) and Degeneration (A007, A008, A009) if you want the
* passive degeneration of the FIRST_SUMMON
* e) Optional - Energy Explode (A00D) if you want the active skill of the FIRST_SUMMON
*
* 2. Copy units into your map :
* a) Optional - Unstabilized Element (h001, h002, h003) into your map (or create new units and use your own)
* b) Optional - Stabilized Element (h004, h005, h006) into your map (or create new units and use your own)
*
* 3. Copy spell scripts into your map :
* a) Required - Elemental Chaos
* b) Optional - Only copy Energy Explosion if you want the active skill of the FIRST_SUMMON
*
* 4. Copy libraries into your map :
* a) Optional - DummyRecycler
* b) Optional - SpellEffectEvent (Requires RegisterPlayerUnitEvent)
* c) Optional - Table
* d) Optional - ZLibrary
*
* 5. Modify the copied abilities data to suit your need
*
* 6. Modify the copied units data to suit your need
*
* 7. Configures the spell of Elemental Chaos
*
* 8. Configures the spell of Energy Explosion (if you did copy)
*
*==========================================================================================================================*
* API :
* Yes, there is 1 API available in this spell, it only used for defining whether FIRST_SUMMON is enable to get
* reabsorbed back to the sphere once it expired (remember to configure the function EC_AbsorbFilter)
* Use this API to create custom ablities for summons if needed (Add abilities via function SummonAddAbilities)
*
* struct ECData
* static method absorbable takes unit u, boolean b returns nothing
* unit u - the summon unit (ONLY for FIRST_SUMMON type)
* boolean b - true for enable reabsorb, false for disable reabsorb
*/
//================================================================================
//============================Configurable Part===================================
//================================================================================
static if not LIBRARY_DummyRecycler then
globals
//Only need to set dummy unit id only if there is no DummyRecycler present
//Dummy unit id
private constant integer DUMMY_UNIT_ID = 'e000'
endglobals
endif
globals
/*
GENERAL Configuration
*/
//The id of the spell
private constant integer SPELL_ID = 'A000'
//Interval time between energy packs to release the next energy pack
private constant real SPHERE_PACK_RELEASE_INTERVAL = 1.
//Timer updating time constant
private constant real TIMER_TIMEOUT = 0.03125
//How long does the dummy unit get removed so that the attached effect could be fully played
private constant real DUMMY_DEATH_TIME = 2.
//The attachement path of the effect on the dummy units
private constant string ATTACHMENT_PATH = "origin"
//Attack type of the damage
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
//Damage type of the damage
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
//Weapon type of the damage
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
//Gravitational force determines how strong the verticle force that pulling the missile to the ground
private constant real GRAVITATIONAL_FORCE = 150.
/*
SPHERE Configuration (the generating sphere at start)
*/
//Sphere effect model path
private constant string SPHERE_EFFECT_PATH = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
//Color adjustment - red color component of sphere
private constant integer SPHERE_COLOR_RED = 70
//Color adjustment - green color component of sphere
private constant integer SPHERE_COLOR_GREEN = 200
//Color adjustment - blue color component of sphere
private constant integer SPHERE_COLOR_BLUE = 255
//Transparency adjustment - transparency of sphere
private constant integer SPHERE_TRANSPARENCY = 255
//Initial scale of the sphere before generating start
private constant real SPHERE_INITIAL_SCALE = 0.
/*
PARTICLE_EFFECT Configuration (those little particles moving toward the sphere)
*/
//Sphere particle effect stop spawn timing before sphere fully generated
private constant real PARTICLE_EFFECT_STOP_TIMING = 1.
//Sphere particle maximum scale
private constant real PARTICLE_EFFECT_MAX_SCALE = 2.5
//Sphere particle minimum scale
private constant real PARTICLE_EFFECT_MIN_SCALE = 1.0
//Sphere particle maximum distance from sphere
private constant real PARTICLE_EFFECT_MAX_DIST = 250.
//Sphere particle minimum distance from sphere
private constant real PARTICLE_EFFECT_MIN_DIST = 150.
//Sphere particle maximum height distance from shpere
private constant real PARTICLE_EFFECT_MAX_HEIGHT = 200.
//Sphere particle minimum height distance from sphere
private constant real PARTICLE_EFFECT_MIN_HEIGHT = 100.
//Sphere particle maximum speed moving toward sphere (horizontal)
//(verticle) speed is dependent on the (horizontal) speed and height (distance) between particle and sphere
private constant real PARTICLE_EFFECT_MAX_SPEED = 32.
//Sphere particle minimum speed moving toward sphere (horizontal)
private constant real PARTICLE_EFFECT_MIN_SPEED = 16.
//The distance between sphere and sphere particle must within PARTICLE_EFFECT_END_DIST for the sphere effect to remove
private constant real PARTICLE_EFFECT_END_DIST = 25.
//Color adjustment - red color component of sphere particle
private constant integer PARTICLE_EFFECT_COLOR_RED = 255
//Color adjustment - green color component of sphere particle
private constant integer PARTICLE_EFFECT_COLOR_GREEN = 255
//Color adjustment - blue color component of sphere particle
private constant integer PARTICLE_EFFECT_COLOR_BLUE = 255
//Transparency adjustment - transparency of sphere particle
private constant integer PARTICLE_EFFECT_TRANSPARENCY = 255
//Sphere particle effect model path
private constant string PARTICLE_EFFECT_PATH = "war3mapImported\\Stars_lightning_.MDX"
/*
ENERGY_PACK Configuraration (The sphere/orb effect moves away from the sphere)
*/
//Minimum distance to traval from sphere
private constant real ENERGY_PACK_MIN_DIST = 150.
//Maximum distance to traval from sphere
private constant real ENERGY_PACK_MAX_DIST = 250.
//Minimum height to travel from sphere
private constant real ENERGY_PACK_MIN_HEIGHT = 100.
//Maximum height to travel from sphere
private constant real ENERGY_PACK_MAX_HEIGHT = 200.
//Minimum speed when travelling
private constant real ENERGY_PACK_MIN_SPEED = 2.
//Maximum speed when travelling
private constant real ENERGY_PACK_MAX_SPEED = 4.
//Minimum scale for the effect (unit)
private constant real ENERGY_PACK_MIN_SCALE = .5
//Maximum scale for the effect (unit)
private constant real ENERGY_PACK_MAX_SCALE = 1.
//Color adjustment - red color component of sphere energy pack
private constant integer ENERGY_PACK_COLOR_RED = 70
//Color adjustment - green color component of sphere energy pack
private constant integer ENERGY_PACK_COLOR_GREEN = 200
//Color adjustment - blue color component of sphere energy pack
private constant integer ENERGY_PACK_COLOR_BLUE = 255
//Transparency adjustment - transparency of sphere energy pack
private constant integer ENERGY_PACK_TRANSPARENCY = 255
//Sphere energy pack effect path
private constant string ENERGY_PACK_EFFECT_PATH = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
//minimum time before energy pack would explode
//NOTE : If the energy pack reaches it destination before reaching the minimum time, it will still explode regardless
// ENERGY_PACK_EXPLODE_MIN_TIME
private constant real ENERGY_PACK_EXPLODE_MIN_TIME = 3.5
//maximum time for energy to explode
private constant real ENERGY_PACK_EXPLODE_MAX_TIME = 4.0
//Scale of the explode effect (unit)
private constant real ENERGY_PACK_EXPLODE_SCALE = 0.5
//Color adjustment - red color component of sphere energy pack explode effect
private constant integer ENERGY_PACK_EXPLODE_COLOR_RED = 50
//Color adjustment - green color component of sphere energy pack explode effect
private constant integer ENERGY_PACK_EXPLODE_COLOR_GREEN = 50
//Color adjustment - blue color component of sphere energy pack explode effect
private constant integer ENERGY_PACK_EXPLODE_COLOR_BLUE = 255
//Transparency adjustment - transparency of sphere energy pack explode effect
private constant integer ENERGY_PACK_EXPLODE_TRANSPARENCY = 255
//exploe effect of the energy pack
private constant string ENERGY_PACK_EXPLODE_PATH = "Units\\NightElf\\Wisp\\WispExplode.mdl"
/*
MISSILE Configuration (missiles that released out when energy pack exploded)
*/
//The minimum distance for the missile to tarvel along z-axis when missile just created
private constant real MISSILE_MIN_HEIGHT = 50.
//The maximum distance for the missile to tarvel along z-axis when missile just created
private constant real MISSILE_MAX_HEIGHT = 150.
//Scaling of the effect (unit)
private constant real MISSILE_SCALE = 1.
//Color adjustment - red color component of missile
private constant integer MISSILE_COLOR_RED = 255
//Color adjustment - green color component of missile
private constant integer MISSILE_COLOR_GREEN = 255
//Color adjustment - blue color component of missile
private constant integer MISSILE_COLOR_BLUE = 255
//Transparency adjustment - transparency of missile
private constant integer MISSILE_TRANSPARENCY = 255
//Missile effect attached to the unit
private constant string MISSILE_EFFECT_PATH = "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl"
//Scaling of the effect (unit) after landed
private constant real MISSILE_LANDED_SCALE = 1.
//Color adjustment - red color component of missile after landed
private constant integer MISSILE_LANDED_COLOR_RED = 255
//Color adjustment - green color component of missile after landed
private constant integer MISSILE_LANDED_COLOR_GREEN = 255
//Color adjustment - blue color component of missile after landed
private constant integer MISSILE_LANDED_COLOR_BLUE = 255
//Transparency adjustment - transparency of missile after landed
private constant integer MISSILE_LANDED_TRANSPARENCY = 255
//Missile effect attach to the unit after landed ground
private constant string MISSILE_LANDED_EFFECT_PATH = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl"
/*
ORB_ROTATION Configuration
PART 1 : summons that get re-absorb back(moving toward) to the sphere when expired
PART 2 : the effect where the orbs rotating the sphere
*/
//PART 1 Configuration :
//The missile effect attach to the unit when expired summon moving toward the sphere
private constant string ORB_REABSORB_EFFECT_MISSILE = "war3mapImported\\Stars_lightning_.MDX"
//Speed of the dummy moving toward the sphere vertically (per TIMER_TIMEOUT)
private constant real ORB_REABSORB_SPEED = 5.
//The scale of the unit(effect/missile) when moving toward the sphere
private constant real ORB_REABSORB_SCALE_MISSILE = 1.
//Color adjustment - red color component of the unit
private constant integer ORB_REABSORB_COLOR_RED_MISSILE = 255
//Color adjustment - green color component of the unit
private constant integer ORB_REABSORB_COLOR_GREEN_MISSILE = 255
//Color adjustment - blue color component of the unit
private constant integer ORB_REABSORB_COLOR_BLUE_MISSILE = 255
//Transparency adjustment - transparency of the unit
private constant integer ORB_REABSORB_TRANSPARENCY_MISSILE = 255
//The explode effect attach to the shpere when expired summons reach the sphere
private constant string ORB_REABSORB_EFFECT_EXPLODE = "war3mapImported\\IceNova.mdx"
//The scale of the unit(effect/missile) when moving toward the sphere
private constant real ORB_REABSORB_SCALE_EXPLODE = 1.
//Color adjustment - red color component of the unit
private constant integer ORB_REABSORB_COLOR_RED_EXPLODE = 255
//Color adjustment - green color component of the unit
private constant integer ORB_REABSORB_COLOR_GREEN_EXPLODE = 255
//Color adjustment - blue color component of the unit
private constant integer ORB_REABSORB_COLOR_BLUE_EXPLODE = 255
//Transparency adjustment - transparency of the unit
private constant integer ORB_REABSORB_TRANSPARENCY_EXPLODE = 0
//PART 2 Configuration :
//The effect path for the sphere rotating
private constant string ORB_ROTATING_EFFECT_PATH = "war3mapImported\\Stars_lightning_.MDX"
//The scale of the unit(effect/missile) when rotating the sphere
private constant real ORB_ROTATING_SCALE = 1.
//The angle increasement when rotating the sphere (radian per TIMER_TIMEOUT)
//larger value means faster rotation
//lower value means slower rotation
//The angle increasement of the orbs in the first shell
private constant real ORB_ROTATING_ANGLE_INCREASEMENT_FIRST = bj_PI/16
//The angle increasement of the orbs in the second shell
private constant real ORB_ROTATING_ANGLE_INCREASEMENT_SECOND = bj_PI/32
//The angle increasement of the orbs in the third shell
private constant real ORB_ROTATING_ANGLE_INCREASEMENT_THIRD = bj_PI/64
//The constant distance between the orbs and the sphere (horizontal)
private constant real ORB_ROTATING_DISTANCE = 150.
//The constant distance increasement for the path of the orbs if exceed the maximum amount of orbs in the path
private constant real ORB_ROTATING_DISTANCE_INCREASEMENT = 75.
//The maximum amount of orbs able to hold in first shell
private constant integer ORB_ROTATING_MAX_FIRST_SHELL = 2
//same as ORB_ROTATING_MAX_FIRST_SHELL, just for second shell
private constant integer ORB_ROTATING_MAX_SECOND_SHELL = 4
//same as ORB_ROTATING_MAX_FIRST_SHELL, just for third shell
private constant integer ORB_ROTATING_MAX_THIRD_SHELL = 8
//Color adjustment - red color component of the unit
private constant integer ORB_ROTATING_COLOR_RED = 255
//Color adjustment - green color component of the unit
private constant integer ORB_ROTATING_COLOR_GREEN = 255
//Color adjustment - blue color component of the unit
private constant integer ORB_ROTATING_COLOR_BLUE = 255
//Transparency adjustment - transparency of the unit
private constant integer ORB_RORATING_TRANSPARENCY = 255
/*
SPHERE_LANDING_PHASE Configuration (when the sphere is landing)
*/
//the z-axis landing speed of the sphere (per TIMER_TIMEOUT)
private constant real SPHERE_LANDING_SPEED = 4.
/*
SPHERE_LANDED Configuration (when the sphere landed to the ground)
*/
//the scale of the sphere after landed (for effect scale)
private constant real SPHERE_LANDED_SCALE = 2.0
//Color adjustment - red color component of the sphere
private constant integer SPHERE_LANDED_COLOR_RED = 255
//Color adjustment - green color component of the sphere
private constant integer SPHERE_LANDED_COLOR_GREEN = 255
//Color adjustment - blue color component of the sphere
private constant integer SPHERE_LANDED_COLOR_BLUE = 255
//Transparency adjustment - transparency of the sphere
private constant integer SPHERE_LANDED_TRANSPARENCY = 255
//Sfx when the sphere landed the ground
private constant string SPHERE_LANDED_SFX = "war3mapImported\\AquaSpikeVersion2.mdx"
/*
Level Data Configuration (data values will be affected according to spell lv)
All of them are configurable at function Init
*/
//Sphere height
private real array SPHERE_HEIGHT
//Sphere maximum scale
private real array SPHERE_MAX_SCALE
//Sphere generates time
private real array SPHERE_GENERATE_TIME
//Sphere awaiting time after whole summons killed/absorbed all expired summons before entering landing phase
private real array SPHERE_AWAITING_LANDING_TIME
//Sphere energy pack release amount
private integer array SPHERE_RELEASE_PACK_MAX
//Spere maximum absorb count of expired summons
private integer array SPHERE_ABSORB_COUNT_MAX
//Sphere maximum distance that able to absorb the expired summons
private real array SPHERE_ABSORB_MAX_DIST
//Sphere missile minimum spawn
private integer array ENERGY_PACK_MIN_MISSILE
//Sphere missile maximum spawn
private integer array ENERGY_PACK_MAX_MISSILE
//Sphere missile minimum travelling distance from shpere
private real array MISSILE_MIN_DIST
//Sphere missile maximum travelling distance from sphere
private real array MISSILE_MAX_DIST
//The damage deal to enemy after missile landed on the ground
private real array FIRST_DAMAGE
//The AoE of the FIRST_DAMAGE
private real array FIRST_DAMAGE_AOE
//Sphere summons unit type after missile landed on the ground
private integer array FIRST_SUMMON
//FIRST_SUMMON time life
private real array FIRST_SUMMON_TIME_LIFE
//The damage deal to enemy after sphere landed on thr ground
private real array SECOND_DAMAGE
//The AoE of the SECOND_DAMAGE
private real array SECOND_DAMAGE_AOE
//Sphere summons unit type after sphere landed on the ground
private integer array SECOND_SUMMON
//SECOND_SUMMON time life
private real array SECOND_SUMMON_TIME_LIFE
endglobals
private function Init takes nothing returns nothing
//[1] indicates Lv1, [2] indicates Lv2 and [3] indicates Lv3
//Arrays are used instead of globals for preventing overwhelming globals as well as unnessasary coding.
//Setting sphere height
set SPHERE_HEIGHT[1] = 300.
set SPHERE_HEIGHT[2] = 300.
set SPHERE_HEIGHT[3] = 300.
//Setting sphere maximum scale
set SPHERE_MAX_SCALE[1] = 1.5
set SPHERE_MAX_SCALE[2] = 1.7
set SPHERE_MAX_SCALE[3] = 1.9
//Setting sphere generates time
set SPHERE_GENERATE_TIME[1] = 3.0
set SPHERE_GENERATE_TIME[2] = 2.5
set SPHERE_GENERATE_TIME[3] = 2.0
//Setting the time to wait before sphere landing after absorbed all expired summons
set SPHERE_AWAITING_LANDING_TIME[1] = 5.
set SPHERE_AWAITING_LANDING_TIME[2] = 4.
set SPHERE_AWAITING_LANDING_TIME[3] = 3.
//The amount of energy pack would be released from the sphere
set SPHERE_RELEASE_PACK_MAX[1] = 3
set SPHERE_RELEASE_PACK_MAX[2] = 4
set SPHERE_RELEASE_PACK_MAX[3] = 5
//Maximum number of expired summons able to reabsorb back to the sphere
set SPHERE_ABSORB_COUNT_MAX[1] = 6
set SPHERE_ABSORB_COUNT_MAX[2] = 8
set SPHERE_ABSORB_COUNT_MAX[3] = 10
//
set SPHERE_ABSORB_MAX_DIST[1] = 1000.
set SPHERE_ABSORB_MAX_DIST[2] = 1000.
set SPHERE_ABSORB_MAX_DIST[3] = 1000.
//Minimum missle could be spawn after energy pack explode
set ENERGY_PACK_MIN_MISSILE[1] = 1
set ENERGY_PACK_MIN_MISSILE[2] = 1
set ENERGY_PACK_MIN_MISSILE[3] = 2
//Maximum missile could be spawn after energy pack explode
set ENERGY_PACK_MAX_MISSILE[1] = 2
set ENERGY_PACK_MAX_MISSILE[2] = 3
set ENERGY_PACK_MAX_MISSILE[3] = 3
//Minimum travel distance from shpere (horizontal)
set MISSILE_MIN_DIST[1] = 100.
set MISSILE_MIN_DIST[2] = 100.
set MISSILE_MIN_DIST[3] = 100.
//Maximum travel distance from sphere (horizontal)
set MISSILE_MAX_DIST[1] = 250.
set MISSILE_MAX_DIST[2] = 250.
set MISSILE_MAX_DIST[3] = 250.
//Setting the damage amount after the missile landed the ground
set FIRST_DAMAGE[1] = 10.
set FIRST_DAMAGE[2] = 20.
set FIRST_DAMAGE[3] = 30.
//Setting the AoE of the FIRST_DAMAGE
set FIRST_DAMAGE_AOE[1] = 80.
set FIRST_DAMAGE_AOE[2] = 110.
set FIRST_DAMAGE_AOE[3] = 140.
//Setting the unit-id of the first summons type after missile landed
set FIRST_SUMMON[1] = 'h001'
set FIRST_SUMMON[2] = 'h002'
set FIRST_SUMMON[3] = 'h003'
//Apply the time life for the FIRST_SUMMON unit type
set FIRST_SUMMON_TIME_LIFE[1] = 10.
set FIRST_SUMMON_TIME_LIFE[2] = 12.
set FIRST_SUMMON_TIME_LIFE[3] = 14.
//Setting the damage amount after the sphere landed the ground
set SECOND_DAMAGE[1] = 100.
set SECOND_DAMAGE[2] = 200.
set SECOND_DAMAGE[3] = 300.
//Setting the AoE of the SECOND_DAMAGE
set SECOND_DAMAGE_AOE[1] = 500.
set SECOND_DAMAGE_AOE[2] = 600.
set SECOND_DAMAGE_AOE[3] = 700.
//Setting the unit-id of the second summons type after sphere landed on the ground
//Using : Water Elemental (war3 default unit)
set SECOND_SUMMON[1] = 'h004'
set SECOND_SUMMON[2] = 'h005'
set SECOND_SUMMON[3] = 'h006'
//Apply the time life for the SECOND_SUMMON unit type
set SECOND_SUMMON_TIME_LIFE[1] = 30.
set SECOND_SUMMON_TIME_LIFE[2] = 40.
set SECOND_SUMMON_TIME_LIFE[3] = 50.
endfunction
private keyword OrbRotation
//Dynamic array is used for OrbRotation struct
//The size of the RotatingOrb should be same as SPHERE_ABSORB_COUNT_MAX[3]
type RotatingOrb extends OrbRotation array[10]
native UnitAlive takes unit id returns boolean
//Unit filter before damaging
//p = owner of casting unit
//u = unit to filter
private function EC_UnitFilter takes player p, unit u returns boolean
return IsUnitEnemy(u, p) and UnitAlive(u)
endfunction
//Unit filter before reabsorb back to the sphere
//Default set to :
//1. expired will be reabsorbed back to the sphere
//2. it is defined as absorbable
//
//Note : It is hardcoded that only FIRST_SUMMON unit type is reabsorbable by the sphere
private function EC_AbsorbFilter takes unit killedUnit, unit killingUnit, integer spellLv, boolean absorbable returns boolean
return killingUnit == null and absorbable
endfunction
//Add abilities to summon units, if any is defined
//uId is the unit id of the summon unit
private function SummonAddAbilities takes unit u, integer uId, integer spellLv returns nothing
if uId == FIRST_SUMMON[spellLv] then
//Add Energy Release to FIRST_SUMMON type unit
call UnitAddAbility(u, 'A00D')
call SetUnitAbilityLevel(u, 'A00D', spellLv)
elseif uId == SECOND_SUMMON[spellLv] then
//I have no idea what ability to create for SECOND_SUMMON type unit, add your own if you want to
endif
endfunction
//================================================================================
//==========================End Configurable Part=================================
//================================================================================
//================================================================================
//===========================Non-Configurable Part================================
//================================================================================
globals
private constant real TWO_PI = 2*bj_PI
//Sphere's phases
private constant integer SPHERE_GENERATING_PHASE = 0
private constant integer SPHERE_RELEASING_PHASE = 1
private constant integer SPHERE_AWAITING_PHASE = 2
private constant integer SPHERE_LANDING_PHASE = 3
//A constant used by SphereMissile struct for increasing verticle velocity every TIMER_TIMEOUT
private constant real MISSILE_CONSTANT = GRAVITATIONAL_FORCE*TIMER_TIMEOUT*TIMER_TIMEOUT
//Orb Rotating's phases
private constant integer ORB_ROTATION_REABSORB_PHASE = 0
private constant integer ORB_ROTATION_ROTATING_PHASE = 1
//Orb rotating constant, used for average angle calculation for each shell
private constant integer SECOND_MAX = ORB_ROTATING_MAX_FIRST_SHELL+ORB_ROTATING_MAX_SECOND_SHELL
private constant integer THIRD_MAX = SECOND_MAX+ORB_ROTATING_MAX_THIRD_SHELL
private trigger diedTrigger
endglobals
//Summons need to store the struct instance of ElementalChaos so that
//the spell able to verify which summons belong to which instance
static if LIBRARY_Table then
/**/
/**/globals
/**/ private Table tb
/**/endglobals
/**/
else
/**/
/**/globals
/**/ private hashtable hash
/**/endglobals
/**/
endif
//A location handle is generated so that when orb rotating the shpere that is not on flat ground,
//the height of orbs would still be parallel to the sphere
static if not LIBRARY_ZLibrary then
/**/
/**/globals
/**/ private location l = Location(0, 0)
/**/endglobals
/**/
endif
private function DistanceBetweenCoordinates takes real x1, real y1, real x2, real y2 returns real
local real dx = x2-x1
local real dy = y2-y1
return SquareRoot(dx*dx + dy*dy)
endfunction
static if not LIBRARY_ZLibrary then
/**/
/**/private function GetPointZ takes real x, real y returns real
/**/ call MoveLocation(l, x, y)
/**/ return GetLocationZ(l)
/**/endfunction
/**/
endif
private keyword ElementalChaos
//================================================================================
//=========================End Non-Configurable Part==============================
//================================================================================
//----------------------------------------------------------------------
//----------------------------------------------------------------------
//A linked list created by myself
module LinkedList
static method create takes nothing returns thistype
local thistype this = instanceCount + 1
set instanceCount = this
if thistype(0).next == 0 then
set thistype(0).next = this
set lastNode = this
else
set lastNode.next = this
set this.prev = lastNode
set lastNode = this
endif
set this.next = 0
set thistype(0).prev = this
return this
endmethod
method destroy takes nothing returns nothing
set this.prev.next = this.next
set this.next.prev = this.prev
if this.next == 0 and this.prev == 0 then
set instanceCount = 0
//call instanceClear if exists
static if thistype.instanceClear.exists then
call thistype.instanceClear()
endif
elseif lastNode == this then
set lastNode = this.prev
endif
endmethod
endmodule
//----------------------------------------------------------------------
//----------------------------------------------------------------------
//A struct that is made for the summon's custom spell accessment
//Able to define whether FIRST_SUMMON is able to reabsorb, or not.
struct ECData extends array
static method absorbable takes unit u, boolean b returns nothing
static if LIBRARY_Table then
set tb.boolean[GetHandleId(u)] = b
else
call SaveBoolean(hash, GetHandleId(u), 0, b)
endif
endmethod
endstruct
private struct OrbRotation extends array
//===========Orb Rotation Globals=================
static timer timerOR = CreateTimer()
unit dummy
effect e
integer phase
integer shell
real finalHeight
real height
real speedv
real x
real y
real angle
real destinationX
real destinationY
ElementalChaos ec
//================================================
//============Linked List Globals=================
//spell's instance count
private static thistype instanceCount = 0
//last allocated instance
private static thistype lastNode
private thistype next
private thistype prev
//================================================
private static method instanceClear takes nothing returns nothing
call PauseTimer(timerOR)
endmethod
implement LinkedList
static method onPeriodic takes nothing returns nothing
local thistype this = thistype(0).next
local integer i
local real averageAngle
local unit u
loop
exitwhen this == 0
if this.phase == ORB_ROTATION_REABSORB_PHASE then
set this.angle = Atan2(this.destinationY-this.y, this.destinationX-this.x)
//move the unit toward the sphere (horizontally)
set this.x = this.x + ORB_REABSORB_SPEED * Cos(this.angle)
set this.y = this.y + ORB_REABSORB_SPEED * Sin(this.angle)
call SetUnitX(this.dummy, this.x)
call SetUnitY(this.dummy, this.y)
//Set unit facing toward the sphere
call SetUnitFacing(this.dummy, this.angle*bj_RADTODEG)
//move the unit toward the sphere (vertically)
set this.height = this.height + this.speedv
call SetUnitFlyHeight(this.dummy, this.height, 0.)
if this.finalHeight - this.height <= 0 then
set this.phase = ORB_ROTATION_ROTATING_PHASE
set ec.c = ec.c + 1
set ec.order[ec.c] = this
//Increase the number of "orb" rotating the sphere by one
set ec.rotatingCount = ec.rotatingCount + 1
//This part will be the explode effect of the orb after it get absorbed
static if LIBRARY_DummyRecycler then
set u = GetRecycledDummyAnyAngle(this.destinationX, this.destinationY, this.finalHeight)
call DummyAddRecycleTimer(u, DUMMY_DEATH_TIME)
else
set u = CreateUnit(ec.owner, DUMMY_UNIT_ID, this.destinationX, this.destinationY, this.finalHeight)
call UnitApplyTimedLife(u, 'BTLF', DUMMY_DEATH_TIME)
endif
//set unit scale and color
call SetUnitScale(u, ORB_REABSORB_SCALE_EXPLODE, ORB_REABSORB_SCALE_EXPLODE, ORB_REABSORB_SCALE_EXPLODE)
call SetUnitVertexColor(u, ORB_REABSORB_COLOR_RED_EXPLODE, ORB_REABSORB_COLOR_GREEN_EXPLODE, ORB_REABSORB_COLOR_BLUE_EXPLODE, ORB_REABSORB_TRANSPARENCY_EXPLODE)
call DestroyEffect(AddSpecialEffectTarget(ORB_REABSORB_EFFECT_EXPLODE, u, ATTACHMENT_PATH))
//repeating same thing : set unit scale and color
call SetUnitScale(this.dummy, ORB_ROTATING_SCALE, ORB_ROTATING_SCALE, ORB_ROTATING_SCALE)
call SetUnitVertexColor(this.dummy, ORB_ROTATING_COLOR_RED, ORB_ROTATING_COLOR_GREEN, ORB_ROTATING_COLOR_BLUE, ORB_RORATING_TRANSPARENCY)
//destroy the effect and attach the new effect for sphere rotation
call DestroyEffect(this.e)
set this.e = AddSpecialEffectTarget(ORB_ROTATING_EFFECT_PATH, this.dummy, ATTACHMENT_PATH)
//averagely set the angle according to the number of "orb" rotating in the shell respectively
if ec.rotatingCount <= ORB_ROTATING_MAX_FIRST_SHELL then
set this.shell = 1
set i = 1
set averageAngle = TWO_PI/ec.rotatingCount
loop
set ec.order[i].angle = i*averageAngle
set i = i + 1
exitwhen ec.order[i] == 0
endloop
elseif ec.rotatingCount <= SECOND_MAX then
set this.shell = 2
set i = 1 + ORB_ROTATING_MAX_FIRST_SHELL
set averageAngle = TWO_PI/(ec.rotatingCount-ORB_ROTATING_MAX_FIRST_SHELL)
loop
set ec.order[i].angle = (i-ORB_ROTATING_MAX_FIRST_SHELL)*averageAngle
set i = i + 1
exitwhen ec.order[i] == 0
endloop
elseif ec.rotatingCount <= THIRD_MAX then
set this.shell = 3
set i = 1 + SECOND_MAX
set averageAngle = TWO_PI/(ec.rotatingCount-SECOND_MAX)
loop
set ec.order[i].angle = (i-SECOND_MAX)*averageAngle
set i = i + 1
exitwhen ec.order[i] == 0
endloop
endif
endif
elseif this.phase == ORB_ROTATION_ROTATING_PHASE then
//increase the angle according to the shell position
if this.shell == 1 then
set this.angle = this.angle + ORB_ROTATING_ANGLE_INCREASEMENT_FIRST
elseif this.shell == 2 then
set this.angle = this.angle + ORB_ROTATING_ANGLE_INCREASEMENT_SECOND
else
set this.angle = this.angle + ORB_ROTATING_ANGLE_INCREASEMENT_THIRD
endif
set this.x = this.destinationX + (ORB_ROTATING_DISTANCE + (this.shell-1)*ORB_ROTATING_DISTANCE_INCREASEMENT) * Cos(this.angle)
set this.y = this.destinationY + (ORB_ROTATING_DISTANCE + (this.shell-1)*ORB_ROTATING_DISTANCE_INCREASEMENT) * Sin(this.angle)
call SetUnitX(this.dummy, this.x)
call SetUnitY(this.dummy, this.y)
//height is vertically parallel to sphere's height
static if LIBRARY_ZLibrary then
call SetUnitFlyHeight(this.dummy, ec.terrainZ - GetSurfaceZ(this.x, this.y) + ec.sphereHeight, 0.)
else
call SetUnitFlyHeight(this.dummy, ec.terrainZ - GetPointZ(this.x, this.y) + ec.sphereHeight, 0.)
endif
endif
set this = this.next
endloop
endmethod
//destroy the orbs that rotating the sphere
static method destroyOrbs takes ElementalChaos ec returns nothing
local integer i = 1
local thistype this
loop
set this = ec.order[i]
static if LIBRARY_DummyRecycler then
call RecycleDummy(this.dummy)
else
call RemoveUnit(this.dummy)
endif
call DestroyEffect(this.e)
set this.dummy = null
set this.e = null
call this.destroy()
set ec.order[i] = 0
//exitwhen when no more orbs rotating the sphere (haven't reach max orbs rotating the sphere)
//exitwhen when destroyed orbs that rotating the sphere == maximum number of orb rotating the sphere
exitwhen ec.order[i+1] == 0 or i == ec.order.size
set i = i + 1
endloop
endmethod
endstruct
private struct SphereMissile extends array
//==========Sphere Side Effect Globals============
static timer timerSM = CreateTimer()
private static group g = CreateGroup()
player owner //Owner of the missile
unit dummy
effect e
integer spellLv
real x
real y
real speedX //speed value travelling along x-axis
real speedY //speed value travelling along y-axis
real speedv
real height
ElementalChaos ec
//================================================
//============Linked List Globals=================
//spell's instance count
private static thistype instanceCount = 0
//last allocated instance
private static thistype lastNode
private thistype next
private thistype prev
//================================================
private static method instanceClear takes nothing returns nothing
call PauseTimer(timerSM)
endmethod
implement LinkedList
static method onPeriodic takes nothing returns nothing
local thistype this = thistype(0).next
local unit u = null
loop
exitwhen this == 0
//Move the unit horizontally
set this.x = this.x + this.speedX
set this.y = this.y + this.speedY
call SetUnitX(this.dummy, this.x)
call SetUnitY(this.dummy, this.y)
//Move the unit vertically
//v = u + at
//u = this.speedv , a = -GRAVITATIONAL_FORCE, t = TIMER_TIMEOUT
set this.speedv = this.speedv + MISSILE_CONSTANT
set this.height = this.height - this.speedv
call SetUnitFlyHeight(this.dummy, this.height, 0.)
//Check if missile reached ground
if this.height <= 0 then
//Destroy missile effect
call DestroyEffect(this.e)
//Damage the units caught in it
call GroupEnumUnitsInRange(g, this.x, this.y, FIRST_DAMAGE_AOE[this.spellLv], null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
if EC_UnitFilter(this.owner, u) then
call UnitDamageTarget(this.dummy, u, FIRST_DAMAGE[this.spellLv], true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
endif
call GroupRemoveUnit(g, u)
endloop
//Spawn the summons out
set u = CreateUnit(this.owner, FIRST_SUMMON[this.spellLv], this.x, this.y, GetRandomReal(0, 360))
//Add the summons to unit group
call GroupAddUnit(ElementalChaos.unitGroup, u)
//Store the ElementalChaos instance to the summon unit
call ec.storeInstance(u)
//Set the boolean of absorbable to true
call ECData.absorbable(u, true)
//Add the ability to the unit, if any is defined
call SummonAddAbilities(u, GetUnitTypeId(u), this.spellLv)
//Apply time life (Summons)
call UnitApplyTimedLife(u, 'BTLF', FIRST_SUMMON_TIME_LIFE[this.spellLv])
//Modify unit scale (dummy)
call SetUnitScale(this.dummy, MISSILE_LANDED_SCALE, MISSILE_LANDED_SCALE, MISSILE_LANDED_SCALE)
//Modify unit color (dummy)
call SetUnitVertexColor(this.dummy, MISSILE_LANDED_COLOR_RED, MISSILE_LANDED_COLOR_GREEN, MISSILE_LANDED_COLOR_BLUE, MISSILE_LANDED_TRANSPARENCY)
//Add landed effect
call DestroyEffect(AddSpecialEffectTarget(MISSILE_LANDED_EFFECT_PATH, this.dummy, ATTACHMENT_PATH))
//Apply time life (dummy)
static if LIBRARY_DummyRecycler then
call DummyAddRecycleTimer(this.dummy, DUMMY_DEATH_TIME)
else
call UnitApplyTimedLife(this.dummy, 'BTLF', DUMMY_DEATH_TIME)
endif
call this.destroy()
set this.owner = null
set this.dummy = null
set this.e = null
endif
set this = this.next
endloop
endmethod
endstruct
private struct SphereEnergyPack extends array
//==========Sphere Side Effect Globals============
static timer timerSEP = CreateTimer()
player owner
unit dummy
effect e //effect attached to dummy
integer missileCount //Number of missile spawn after energy pack explode
integer spellLv
real x //y coordinate of energy pack
real y //x coordinate of energy pack
real height //height of energy pack from sphere
real speedh //speed horizontal (constant)
real speedv //speed verticle (non-constant)
real remainingTime //time taken for energy pack explode
real destinationX //energy's pack destination x-coordinate
real destinationY //energy's pack destination y-coordinate
real destinationDistance //distance between energy pack and destination of energy pack
ElementalChaos ec
//================================================
//============Linked List Globals=================
//spell's instance count
private static thistype instanceCount = 0
//last allocated instance
private static thistype lastNode
private thistype next
private thistype prev
//================================================
private static method instanceClear takes nothing returns nothing
call PauseTimer(timerSEP)
endmethod
implement LinkedList
static method onPeriodic takes nothing returns nothing
local SphereMissile sm
local thistype this = thistype(0).next
local integer i
local real angle
local real r
local real time
loop
exitwhen this == 0
set angle = Atan2(this.destinationY-GetUnitY(this.dummy), this.destinationX-GetUnitX(this.dummy))
//move the energy pack towards destinationX/Y (horizontally)
set this.destinationDistance = this.destinationDistance - this.speedh
set this.x = this.x + this.speedh * Cos(angle)
set this.y = this.y + this.speedh * Sin(angle)
call SetUnitX(this.dummy, this.x)
call SetUnitY(this.dummy, this.y)
//move the energy pack towards destiantionHeight (vertically)
set this.height = this.height + this.speedv
call SetUnitFlyHeight(this.dummy, this.height, 0.)
//Decrease the explode time by TIMER_TIMEOUT seconds
set this.remainingTime = this.remainingTime - TIMER_TIMEOUT
//Check if energy pack reaches its destination OR remainingTime <= 0
if this.destinationDistance <= 0 or this.remainingTime <= 0 then
//destroy the energy pack effect
call DestroyEffect(this.e)
//change the unit (effect) color
call SetUnitVertexColor(this.dummy, ENERGY_PACK_EXPLODE_COLOR_RED, ENERGY_PACK_EXPLODE_COLOR_GREEN, ENERGY_PACK_EXPLODE_COLOR_BLUE, ENERGY_PACK_EXPLODE_TRANSPARENCY)
//change the unit scale
call SetUnitScale(this.dummy, ENERGY_PACK_EXPLODE_SCALE, ENERGY_PACK_EXPLODE_SCALE, ENERGY_PACK_EXPLODE_SCALE)
//add the explode effect to the dummy
call DestroyEffect(AddSpecialEffectTarget(ENERGY_PACK_EXPLODE_PATH, this.dummy, ATTACHMENT_PATH))
//add life time to the dummy
static if LIBRARY_DummyRecycler then
//call DummyAddRecycleTimer(this.dummy, DUMMY_DEATH_TIME)
else
call UnitApplyTimedLife(this.dummy, 'BTLF', DUMMY_DEATH_TIME)
endif
set i = 0
loop
set i = i + 1
//Create a new missile
set sm = SphereMissile.create()
//Setting up missile requires data
set sm.height = GetRandomReal(MISSILE_MIN_HEIGHT, MISSILE_MAX_HEIGHT)
set sm.spellLv = this.spellLv
set sm.owner = this.owner
set sm.ec = this.ec
set sm.x = this.x
set sm.y = this.y
static if LIBRARY_DummyRecycler then
set sm.dummy = GetRecycledDummyAnyAngle(this.x, this.y, this.height)
else
set sm.dummy = CreateUnit(this.owner, DUMMY_UNIT_ID, this.x, this.y, 0.)
call SetUnitFlyHeight(sm.dummy, this.height, 0.)
endif
//Kinemtic formula is used : v*v = u*u + 2as
//Check if height is positive
if sm.height > 0 then
//Rearrange : u = SquareRoot(v*v - 2as)
//a = -GRAVITATIONAL_FORCE (against gravitational force), v = 0
//So : u = -1 * SquareRoot(2*GRAVITATIONAL_FORCE*s)
set sm.speedv = -1 * SquareRoot(2*GRAVITATIONAL_FORCE*sm.height)
else
//a = GRAVITATIONAL_FORCE, u = 0
set sm.speedv = 0.
endif
//Set the height to the height where it should be
set sm.height = this.height + sm.height
//To obtain the total time taken for the motion, t1 = upward motion (if present), t2 = downward motion
//Calculation are in unit : PER SECOND
//For t1 : use v = u + at
//v = 0, u = sm.speedv, a = -GRAVITATIONAL_FORCE, t1 = ?
//So : t1 = -u/a
//For t2 : use s = ut + (1/2)at*t
//s = sm.height (total height), u = 0, a = GRAVITATIONAL_FORCE, t2 = ?
//So : t2 = SquareRoot(2s/a)
// t2 = SquareRoot((2*sm.height)/GRAVITATIONAL_FORCE)
set time = (-sm.speedv/GRAVITATIONAL_FORCE) + SquareRoot((2*sm.height)/GRAVITATIONAL_FORCE)
//Covert sm.speedv's unit from PER SECOND to PER TIMER_TIMEOUT
set sm.speedv = sm.speedv * TIMER_TIMEOUT
//let r = distance
//speedh = r/time
//Cos(angle) = constant, Sin(angle) = constant
//so : (r/time) * Cos/Sin(angle) unit : PER SECOND
//Convert PER SECOND to PER TIMER_TIMEOUT :
// (r/time) * Cos/Sin(angle) * TIMER_TIMEOUT
set r = GetRandomReal(MISSILE_MIN_DIST[this.spellLv], MISSILE_MAX_DIST[this.spellLv])
set angle = GetRandomReal(-bj_PI, bj_PI)
set sm.speedX = (r/time) * Cos(angle) * TIMER_TIMEOUT
set sm.speedY = (r/time) * Sin(angle) * TIMER_TIMEOUT
//Setting up dummy's data
call SetUnitScale(sm.dummy, MISSILE_SCALE, MISSILE_SCALE, MISSILE_SCALE)
call SetUnitVertexColor(sm.dummy, MISSILE_COLOR_RED, MISSILE_COLOR_GREEN, MISSILE_COLOR_BLUE, MISSILE_TRANSPARENCY)
set sm.e = AddSpecialEffectTarget(MISSILE_EFFECT_PATH, sm.dummy, ATTACHMENT_PATH)
if sm == 1 then
call TimerStart(SphereMissile.timerSM, TIMER_TIMEOUT, true, function SphereMissile.onPeriodic)
endif
exitwhen i == this.missileCount //exitwhen created missile == number of missile gonna be fired
endloop
call this.destroy()
set this.owner = null
set this.dummy = null
set this.e = null
endif
set this = this.next
endloop
endmethod
endstruct
private struct SphereParticleEffect extends array
//==========Sphere Side Effect Globals============
static timer timerSPE = CreateTimer()
unit dummy
effect e //effect attached to dummy
real x //y coordinate of particle
real y //x coordinate of particle
real distance //distance between sphere and particle
real height //height between sphere and particle
real speedh //speed horizontal (constant)
real speedv //speed verticle (constant)
real destinationHeight //sphere height
real destinationX //sphere x coordinate
real destinationY //sphere y coordinate
//================================================
//============Linked List Globals=================
//spell's instance count
private static thistype instanceCount = 0
//last allocated instance
private static thistype lastNode
private thistype next
private thistype prev
//================================================
private static method instanceClear takes nothing returns nothing
call PauseTimer(timerSPE)
endmethod
implement LinkedList
static method onPeriodic takes nothing returns nothing
local thistype this = thistype(0).next
local real angle
local real x2
loop
exitwhen this == 0
if this.distance >= PARTICLE_EFFECT_END_DIST then
set this.distance = this.distance - this.speedh
set x2 = GetUnitX(this.dummy)-this.destinationX
set angle = Atan2(GetUnitY(this.dummy)-this.destinationY, x2)
//move the particle toward sphere (verticle)
set this.height = this.height - this.speedv
call SetUnitFlyHeight(this.dummy, this.destinationHeight + this.height * Cos(Atan2(this.destinationHeight-this.height,x2)), 0.)
//move the particle toward sphere (horizontal)
set this.x = this.x - this.speedh * Cos(angle)
set this.y = this.y - this.speedh * Sin(angle)
call SetUnitX(this.dummy, this.x)
call SetUnitY(this.dummy, this.y)
else
call DestroyEffect(this.e)
static if LIBRARY_DummyRecycler then
call RecycleDummy(this.dummy)
else
call RemoveUnit(this.dummy)
endif
call this.destroy()
set this.e = null
set this.dummy = null
endif
set this = this.next
endloop
endmethod
endstruct
private struct ElementalChaos extends array
//============Elemental Chaos Globals=============
private static timer timerEC = CreateTimer() //Elemental Chaos Timer
private unit triggerUnit
private unit sphere
private effect sphereEffect
public static group unitGroup = CreateGroup() //Group used for storing FIRST_SUMMON units
private static group g = CreateGroup() //Group used for enum unit in range, and damage them
public player owner
private integer spellLv
private integer phase //spell phase
private integer releasedPack //amount of energy pack released from sphere
private integer summonsDiedCount //number of expired/killed summons
private integer summonsSpawnCount //Total number of summons spawned
private integer absorbCount //number of expired summons reabsorb to the sphere
private integer maximumAbsorbCount //maximum number of expired summons able to reabsorb to the sphere
public integer rotatingCount //the number of "orb" rotating the sphere
public integer c
private real targetX //spell target X
private real targetY //spell target Y
private real duration //duration that used for calculate the time between a phase to another phase
private real sphereScale
private real sphereScaleConst //sphere scale increasement constant every TIMER_TIMEOUT seconds
private real packReleaseTimePassed //energy pack release interval, increases every TIMER_TIMEOUT seconds
private real sphereGenerateTime
public real sphereHeight
public real terrainZ //The height of the terrain at the sphere point
public RotatingOrb order //The order of the orbs that rotating around the sphere
//================================================
//============Linked List Globals=================
//spell's instance count
private static thistype instanceCount = 0
//last allocated instance
private static thistype lastNode
private thistype next
private thistype prev
//================================================
//instanceClear will be called if no more instance is running
private static method instanceClear takes nothing returns nothing
call PauseTimer(timerEC)
call DisableTrigger(diedTrigger)
endmethod
implement LinkedList
//Store the instance to a summon unit
method storeInstance takes unit u returns nothing
static if LIBRARY_Table then
set tb.integer[GetHandleId(u)] = this
else
call SaveInteger(hash, GetHandleId(u), 0, this)
endif
endmethod
//Retrieve the instance from a summon unit
static method retrieveInstance takes unit u returns ElementalChaos
static if LIBRARY_Table then
return tb.integer[GetHandleId(u)]
else
return LoadInteger(hash, GetHandleId(u), 0)
endif
endmethod
private static method onDied takes nothing returns boolean
local unit u = GetTriggerUnit()
local thistype this
local OrbRotation ort
local boolean b
if IsUnitInGroup(u, thistype.unitGroup) then
call GroupRemoveUnit(thistype.unitGroup, u)
set this = retrieveInstance(u)
//increase the died count by one
set this.summonsDiedCount = this.summonsDiedCount + 1
//Check if the died summons unit is within the range of the sphere
if DistanceBetweenCoordinates(GetUnitX(u), GetUnitY(u), this.targetX, this.targetY) <= SPHERE_ABSORB_MAX_DIST[this.spellLv] then
//load the stored boolean (absorbable) from the summon unit
static if LIBRARY_Table then
set b = tb.boolean[GetHandleId(u)]
else
set b = LoadBoolean(hash, GetHandleId(u), 0)
endif
//Filter if unit could be reabsorb back to the sphere
if EC_AbsorbFilter(u, GetKillingUnit(), this.spellLv, b) then
//if summons absorb count < maximum summons absorb count
if this.absorbCount < this.maximumAbsorbCount then
set this.absorbCount = this.absorbCount + 1
set ort = OrbRotation.create()
//Setting up the data
set ort.ec = this
set ort.finalHeight = this.sphereHeight
set ort.x = GetUnitX(u)
set ort.y = GetUnitY(u)
set ort.destinationX = this.targetX
set ort.destinationY = this.targetY
//SquareRoot((this.targetX-ort.x)*(this.targetX-ort.x) + (this.targetY-ort.y)*(this.targetY-ort.y)) : distance between dummy unit position and sphere position
//distance/SPHERE_REABSORB_SPEED = time taken
//speedv = final height/time
set ort.speedv = this.sphereHeight/(SquareRoot((this.targetX-ort.x)*(this.targetX-ort.x) + (this.targetY-ort.y)*(this.targetY-ort.y))/ORB_REABSORB_SPEED)
set ort.phase = ORB_ROTATION_REABSORB_PHASE
set ort.height = 0.
static if LIBRARY_DummyRecycler then
set ort.dummy = GetRecycledDummy(ort.x, ort.y, 0., Atan2(ort.y-this.targetY, ort.x-this.targetX)*bj_RADTODEG)
else
set ort.dummy = CreateUnit(this.owner, DUMMY_UNIT_ID, ort.x, ort.y, Atan2(ort.y-this.targetY, ort.x-this.targetX)*bj_RADTODEG)
endif
set ort.e = AddSpecialEffectTarget(ORB_REABSORB_EFFECT_MISSILE, ort.dummy, ATTACHMENT_PATH)
call SetUnitVertexColor(ort.dummy, ORB_REABSORB_COLOR_RED_MISSILE, ORB_REABSORB_COLOR_GREEN_MISSILE, ORB_REABSORB_COLOR_BLUE_MISSILE, ORB_REABSORB_TRANSPARENCY_MISSILE)
call SetUnitScale(ort.dummy, ORB_REABSORB_SCALE_MISSILE, ORB_REABSORB_SCALE_MISSILE, ORB_REABSORB_SCALE_MISSILE)
if ort == 1 then
call TimerStart(ort.timerOR, TIMER_TIMEOUT, true, function OrbRotation.onPeriodic)
endif
endif
endif
endif
static if LIBRARY_Table then
call tb.remove(GetHandleId(u))
else
call FlushChildHashtable(hash, GetHandleId(u))
endif
endif
set u = null
return false
endmethod
private static method onPeriodic takes nothing returns nothing
local SphereParticleEffect particleEffect
local SphereEnergyPack energyPack
local thistype this = thistype(0).next
local unit u = null
local real rand
local real angle
loop
exitwhen this == 0
//phase 0 indicates sphere generating phase
if this.phase == SPHERE_GENERATING_PHASE then
set this.duration = this.duration + TIMER_TIMEOUT
//=================================================================================
//Generate Effect of the sphere (the effect shows when sphere is generating)
//=================================================================================
//if (sphere generate time - passed spell timing) >= PARTICLE_EFFECT_STOP_TIMING
if (this.sphereGenerateTime-this.duration) >= PARTICLE_EFFECT_STOP_TIMING then
set particleEffect = SphereParticleEffect.create()
//Setting up particle data
set particleEffect.destinationHeight = this.sphereHeight
set particleEffect.destinationX = this.targetX
set particleEffect.destinationY = this.targetY
set particleEffect.distance = GetRandomReal(PARTICLE_EFFECT_MIN_DIST, PARTICLE_EFFECT_MAX_DIST)
set particleEffect.height = GetRandomReal(PARTICLE_EFFECT_MIN_HEIGHT, PARTICLE_EFFECT_MAX_HEIGHT)
set particleEffect.speedh = GetRandomReal(PARTICLE_EFFECT_MIN_SPEED, PARTICLE_EFFECT_MAX_SPEED)
//speedv = height / time
//time = ditance / speedh
set particleEffect.speedv = particleEffect.height/(particleEffect.distance/particleEffect.speedh)
set particleEffect.x = this.targetX + particleEffect.distance * Cos(GetRandomReal(-bj_PI, bj_PI))
set particleEffect.y = this.targetY + particleEffect.distance * Sin(GetRandomReal(-bj_PI, bj_PI))
set angle = Atan2(particleEffect.y-this.targetY, particleEffect.x-this.targetX) * bj_RADTODEG
static if LIBRARY_DummyRecycler then
set particleEffect.dummy = GetRecycledDummy(particleEffect.x, particleEffect.y, particleEffect.height, angle)
else
set particleEffect.dummy = CreateUnit(this.owner, DUMMY_UNIT_ID, particleEffect.x, particleEffect.y, angle)
//set unit height
call SetUnitFlyHeight(particleEffect.dummy, this.sphereHeight + particleEffect.height, 0)
endif
set particleEffect.e = AddSpecialEffectTarget(PARTICLE_EFFECT_PATH, particleEffect.dummy, ATTACHMENT_PATH)
//Set unit scale randomly
set rand = GetRandomReal(PARTICLE_EFFECT_MIN_SCALE, PARTICLE_EFFECT_MAX_SCALE)
call SetUnitScale(particleEffect.dummy, rand, rand, rand)
//rendomly obtain a number so that height can be appear lower or higher thn the sphere
if GetRandomInt(0, 1) == 1 then
set particleEffect.height = particleEffect.height * -1
endif
//adjust the color compoennt of the unit
call SetUnitVertexColor(particleEffect.dummy, PARTICLE_EFFECT_COLOR_RED, PARTICLE_EFFECT_COLOR_GREEN, PARTICLE_EFFECT_COLOR_BLUE, PARTICLE_EFFECT_TRANSPARENCY)
if particleEffect == 1 then
call TimerStart(SphereParticleEffect.timerSPE, TIMER_TIMEOUT, true, function SphereParticleEffect.onPeriodic)
endif
//Setting sphere scale, reason it is inside this block is to prevent sphere increasing scale when no more particle effects
//are generated.
set this.sphereScale = this.sphereScale + this.sphereScaleConst
call SetUnitScale(this.sphere, this.sphereScale, this.sphereScale, this.sphereScale)
endif
//=================================================================================
//===========================End Sphere Generate Effect============================
//=================================================================================
//Check if the spell duration >= sphere generate time
if this.duration >= this.sphereGenerateTime then
//Sphere generate phase ends, switch spell phase to 1
set this.phase = SPHERE_RELEASING_PHASE
endif
elseif this.phase == SPHERE_RELEASING_PHASE then
//If released energy pack count < SPHERE_RELEASE_PACK_MAX[spellLv]
if this.releasedPack < SPHERE_RELEASE_PACK_MAX[this.spellLv] then
/************************************************************************************************************/
/**/if this.packReleaseTimePassed >= SPHERE_PACK_RELEASE_INTERVAL then
/**/ set this.packReleaseTimePassed = 0.
/**/ set this.releasedPack = this.releasedPack + 1
/**/
/**/ set energyPack = SphereEnergyPack.create()
/**/
/**/ //Setting up energy pack data
/**/ set energyPack.destinationDistance = GetRandomReal(ENERGY_PACK_MIN_DIST, ENERGY_PACK_MAX_DIST) //final distance between sphere and energy pack
/**/ set energyPack.speedh = GetRandomReal(ENERGY_PACK_MIN_SPEED, ENERGY_PACK_MAX_SPEED)
/**/ //Height/time
/**/ set energyPack.speedv = GetRandomReal(ENERGY_PACK_MIN_HEIGHT, ENERGY_PACK_MAX_HEIGHT)/*
*//(energyPack.destinationDistance/energyPack.speedh) //formula same as particleEffect
/**/ set energyPack.remainingTime = GetRandomReal(ENERGY_PACK_EXPLODE_MIN_TIME, ENERGY_PACK_EXPLODE_MAX_TIME)
/**/ set energyPack.missileCount = GetRandomInt(ENERGY_PACK_MIN_MISSILE[this.spellLv], ENERGY_PACK_MAX_MISSILE[this.spellLv])
/**/ set this.summonsSpawnCount = this.summonsSpawnCount + energyPack.missileCount //increase the spawn count
/**/ set energyPack.spellLv = this.spellLv
/**/ set energyPack.x = this.targetX
/**/ set energyPack.y = this.targetY
/**/ set energyPack.height = this.sphereHeight
/**/ set energyPack.owner = this.owner
/**/ set energyPack.ec = this
/**/
/**/ //create a unit to obtain the destinationX and destinationY
/**/ set rand = GetRandomReal(-bj_PI, bj_PI)
/**/ set angle = rand*bj_RADTODEG
/**/
/**/ static if LIBRARY_DummyRecycler then
/**/ set energyPack.dummy = GetRecycledDummy(this.targetX, this.targetY, this.sphereHeight, angle)
/**/ else
/**/ set energyPack.dummy = CreateUnit(this.owner, DUMMY_UNIT_ID, this.targetX, this.targetY, angle)
/**/
/**/ //set unit height
/**/ call SetUnitFlyHeight(energyPack.dummy, this.sphereHeight, 0.)
/**/ endif
/**/
/**/ set energyPack.destinationX = this.targetX + energyPack.destinationDistance * Cos(rand)
/**/ set energyPack.destinationY = this.targetY + energyPack.destinationDistance * Sin(rand)
/**/ set energyPack.e = AddSpecialEffectTarget(ENERGY_PACK_EFFECT_PATH, energyPack.dummy, ATTACHMENT_PATH)
/**/
/**/ //Set unit scale
/**/ set rand = GetRandomReal(ENERGY_PACK_MIN_SCALE, ENERGY_PACK_MAX_SCALE)
/**/ call SetUnitScale(energyPack.dummy, rand, rand, rand)
/**/
/**/ //set unit color
/**/ call SetUnitVertexColor(energyPack.dummy, ENERGY_PACK_COLOR_RED, ENERGY_PACK_COLOR_GREEN, ENERGY_PACK_COLOR_BLUE, ENERGY_PACK_TRANSPARENCY)
/**/
/**/
/**/ if energyPack == 1 then
/**/ call TimerStart(SphereEnergyPack.timerSEP, TIMER_TIMEOUT, true, function SphereEnergyPack.onPeriodic)
/**/ endif
/**/else
/**/ //increase the time passed by TIMER_TIMEOUT seconds
/**/ set this.packReleaseTimePassed = this.packReleaseTimePassed + TIMER_TIMEOUT
/**/endif
/************************************************************************************************************/
else
set this.phase = SPHERE_AWAITING_PHASE
endif
elseif this.phase == SPHERE_AWAITING_PHASE then
//Ensure all summons expired, as well as all absorbed summons are rotating the sphere
if this.absorbCount == this.rotatingCount and this.summonsDiedCount == this.summonsSpawnCount then
set this.phase = SPHERE_LANDING_PHASE
set this.duration = SPHERE_AWAITING_LANDING_TIME[this.spellLv]
endif
elseif this.phase == SPHERE_LANDING_PHASE then
//wait for a certain time before the sphere starts to land down
if this.duration > 0 then
set this.duration = this.duration - TIMER_TIMEOUT
else
//decrease the sphere height periodically
set this.sphereHeight = this.sphereHeight - SPHERE_LANDING_SPEED
call SetUnitFlyHeight(this.sphere, this.sphereHeight, 0.)
//if sphere height landed on the ground
if this.sphereHeight <= 0 then
//destroy the orbs rotating the sphere
call OrbRotation.destroyOrbs(this)
//destroy the sphere's effect
call DestroyEffect(this.sphereEffect)
//change the sphere scale, color for the sfx of the sphere landing on the ground
call SetUnitScale(this.sphere, SPHERE_LANDED_SCALE, SPHERE_LANDED_SCALE, SPHERE_LANDED_SCALE)
call SetUnitVertexColor(this.sphere, SPHERE_LANDED_COLOR_RED, SPHERE_LANDED_COLOR_GREEN, SPHERE_LANDED_COLOR_BLUE, SPHERE_LANDED_TRANSPARENCY)
//attach the effect to the sphere
call AddSpecialEffectTarget(SPHERE_LANDED_SFX, this.sphere, ATTACHMENT_PATH)
//add the expire time for the sphere
static if LIBRARY_DummyRecycler then
call DummyAddRecycleTimer(this.sphere, DUMMY_DEATH_TIME)
else
call UnitApplyTimedLife(this.sphere, 'BTLF', DUMMY_DEATH_TIME)
endif
//damage nearby enemy units
call GroupEnumUnitsInRange(g, this.targetX, this.targetY, SECOND_DAMAGE_AOE[this.spellLv], null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
if EC_UnitFilter(this.owner, u) then
call UnitDamageTarget(this.triggerUnit, u, SECOND_DAMAGE[this.spellLv], true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
endif
call GroupRemoveUnit(g, u)
endloop
//if there is no rotating orb, SECOND_SUMMON unit will not get summoned.
if this.absorbCount > 0 then
//summons the SECOND_SUMMON unit
set u = CreateUnit(this.owner, SECOND_SUMMON[this.spellLv], this.targetX, this.targetY, 0.)
//apply this time life to SECOND_SUMMON unit
call UnitApplyTimedLife(u, 'BTLF', SECOND_SUMMON_TIME_LIFE[this.spellLv])
//set the life of the SECOND_SUMMON unit based on the number of reabsorbed units
call SetUnitState(u, UNIT_STATE_LIFE, GetUnitState(u, UNIT_STATE_LIFE)*this.absorbCount/this.maximumAbsorbCount)
//Add ability to SECOND_SUMMON unit
call SummonAddAbilities(u, GetUnitTypeId(u), this.spellLv)
set u = null
endif
//spell ends, destroy the instance
call this.destroy()
//destroy the dynamic array instance
call this.order.destroy()
//nulling variables
set this.triggerUnit = null
set this.sphere = null
set this.sphereEffect = null
set this.owner = null
endif
endif
endif
set this = this.next
endloop
endmethod
private static method onCast takes nothing returns nothing
local thistype this = thistype.create()
set this.order = RotatingOrb.create()
//Storing data
set this.triggerUnit = GetTriggerUnit()
set this.owner = GetOwningPlayer(this.triggerUnit)
set this.targetX = GetSpellTargetX()
set this.targetY = GetSpellTargetY()
set this.spellLv = GetUnitAbilityLevel(this.triggerUnit, SPELL_ID)
set this.phase = SPHERE_GENERATING_PHASE
set this.sphereGenerateTime = SPHERE_GENERATE_TIME[this.spellLv]
set this.sphereHeight = SPHERE_HEIGHT[this.spellLv]
set this.sphereScale = SPHERE_INITIAL_SCALE
set this.maximumAbsorbCount = SPHERE_ABSORB_COUNT_MAX[this.spellLv]
//sphereScaleConst is dependent on SPHERE_MAX_SCALE, SPHERE_GENERATE_TIME, TIMER_TIMEOUT and SHPERE_PARTICLE_STOP_TIMING
//(this.sphereGenerateTime-PARTICLE_EFFECT_STOP_TIMING) : as sphere stop increase scale if no more particle is generating
// divide by TIMER_TIMEOUT : to obtain the overall running times
// SPHERE_MAX_SCALE[this.spellLv] : obtain the maximum scale of the sphere
// maximum scale / running times : so that a constant is obtained for scale increasement every TIMER_TIMEOUT
set this.sphereScaleConst = SPHERE_MAX_SCALE[this.spellLv]/((this.sphereGenerateTime-PARTICLE_EFFECT_STOP_TIMING)/TIMER_TIMEOUT)
set this.duration = 0.
set this.packReleaseTimePassed = 0.
set this.releasedPack = 0
set this.summonsDiedCount = 0
set this.summonsSpawnCount = 0
set this.absorbCount = 0
set this.rotatingCount = 0
set this.c = 0
static if LIBRARY_DummyRecycler then
set this.sphere = GetRecycledDummyAnyAngle(this.targetX, this.targetY, this.sphereHeight)
else
set this.sphere = CreateUnit(this.owner, DUMMY_UNIT_ID, this.targetX, this.targetY, 0.)
//Set the unit height
call SetUnitFlyHeight(this.sphere, this.sphereHeight, 0.)
endif
//Store the Z height of the terrain at the sphere point
static if LIBRARY_ZLibrary then
set this.terrainZ = GetSurfaceZ(this.targetX, this.targetY)
else
set this.terrainZ = GetPointZ(this.targetX, this.targetY)
endif
//Setting the sphere to initial scale value
call SetUnitScale(this.sphere, SPHERE_INITIAL_SCALE, SPHERE_INITIAL_SCALE, SPHERE_INITIAL_SCALE)
//Setting the sphere color
call SetUnitVertexColor(this.sphere, SPHERE_COLOR_RED, SPHERE_COLOR_GREEN, SPHERE_COLOR_BLUE, SPHERE_TRANSPARENCY)
//Add the sphere effect to the unit
set this.sphereEffect = AddSpecialEffectTarget(SPHERE_EFFECT_PATH, this.sphere, ATTACHMENT_PATH)
if this == 1 then
call TimerStart(timerEC, TIMER_TIMEOUT, true, function thistype.onPeriodic)
call EnableTrigger(diedTrigger)
endif
endmethod
static if not LIBRARY_SpellEffectEvent then
private static method onCondition takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call thistype.onCast()
endif
return false
endmethod
endif
private static method onInit takes nothing returns nothing
static if not LIBRARY_SpellEffectEvent then
local trigger t = CreateTrigger()
call TriggerAddCondition(t, Condition(function thistype.onCondition))
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
set t = null
else
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endif
static if LIBRARY_Table then
set tb = Table.create()
endif
set diedTrigger = CreateTrigger()
call TriggerAddCondition(diedTrigger, Condition(function thistype.onDied))
call TriggerRegisterAnyUnitEventBJ(diedTrigger, EVENT_PLAYER_UNIT_DEATH)
call DisableTrigger(diedTrigger)
endmethod
endstruct
endlibrary
Summon's spell code :
Energy Explosion
JASS:
scope EnergyExplosion initializer Init
/*
*==========================================================================================================================*
Sub spell from Elemental Chaos : FIRST_SUMMON active spell
*==========================================================================================================================*
Spell Description :
Unstabilized element unit maximize its disordered energy inside his body, causes an AoE explosion,
dealing moderate damage to nearby enemy units. Unit will be died and will not get reabsorbed back by the sphere.
Level 1 - (Remaining Life/4) damage, 200 AoE
Level 2 - (Remaining Life/4) damage, 250 AoE
Level 3 - (Remaining Life/4) damage, 300 AoE
*==========================================================================================================================*
*/
//================================================================================
//============================Configurable Part===================================
//================================================================================
static if not LIBRARY_DummyRecycler then
globals
//Only need to set dummy unit id only if there is no DummyRecycler present
//Dummy unit id
private constant integer DUMMY_UNIT_ID = 'e000'
endglobals
endif
globals
private constant integer SPELL_ID = 'A00D'
private constant string ATTACHMENT_PATH = "origin"
private constant real DUMMY_DEATH_TIME = 2.
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
private constant string EXPLODE_SFX = "war3mapImported\\WaterBlast.mdx"
private constant real SFX_SCALE = 1.5
private constant integer SFX_COLOR_RED = 255
private constant integer SFX_COLOR_GREEN = 255
private constant integer SFX_COLOR_BLUE = 255
private constant integer SFX_TRANSPARENCY = 255
//Configure DAMAGE_AOE inside function Init
private real array DAMAGE_AOE
//Don't touch this, a permanent group for enum unit in range
private group g = CreateGroup()
endglobals
//Init the damage AoE of the spell
private function Init takes nothing returns nothing
set DAMAGE_AOE[1] = 200
set DAMAGE_AOE[2] = 250
set DAMAGE_AOE[3] = 300
endfunction
//Filter of unit for damaging
private function UnitFilter takes player p, unit u returns boolean
return IsUnitEnemy(u, p) and UnitAlive(u)
endfunction
//Damage of the spell, i'm not using spellLv, you may change it as spellLv based
private function Damage takes unit u, integer spellLv returns real
return GetUnitState(u, UNIT_STATE_LIFE)/4.
endfunction
//================================================================================
//==========================End Configurable Part=================================
//================================================================================
struct EnergyExplosion extends array
private static method onCast takes nothing returns nothing
local unit u = null
local unit triggerUnit = GetTriggerUnit()
local player p = GetOwningPlayer(triggerUnit)
local integer i = GetUnitAbilityLevel(triggerUnit, SPELL_ID)
local real x = GetUnitX(triggerUnit)
local real y = GetUnitY(triggerUnit)
call GroupEnumUnitsInRange(g, x, y, DAMAGE_AOE[i], null)
loop
set u = FirstOfGroup(g)
exitwhen u == null
if UnitFilter(p, u) then
call UnitDamageTarget(triggerUnit, u, Damage(triggerUnit, i), true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
endif
call GroupRemoveUnit(g, u)
endloop
static if LIBRARY_DummyRecycler then
set u = GetRecycledDummyAnyAngle(x, y, 0.)
call DummyAddRecycleTimer(u, DUMMY_DEATH_TIME)
else
set u = CreateUnit(p, DUMMY_UNIT_ID, x, y, 0.)
call UnitApplyTimedLife(u, 'BTLF', DUMMY_DEATH_TIME)
endif
call SetUnitVertexColor(u, SFX_COLOR_RED, SFX_COLOR_GREEN, SFX_COLOR_BLUE, SFX_TRANSPARENCY)
call SetUnitScale(u, SFX_SCALE, SFX_SCALE, SFX_SCALE)
call DestroyEffect(AddSpecialEffectTarget(EXPLODE_SFX, u, ATTACHMENT_PATH))
//Disable reabsorb for this unit
call ECData.absorbable(triggerUnit, false)
call KillUnit(triggerUnit)
set triggerUnit = null
set p = null
endmethod
static if not LIBRARY_SpellEffectEvent then
private static method onCondition takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call thistype.onCast()
endif
return false
endmethod
endif
private static method onInit takes nothing returns nothing
static if not LIBRARY_SpellEffectEvent then
local trigger t = CreateTrigger()
call TriggerAddCondition(t, Condition(function thistype.onCondition))
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
else
call RegisterSpellEffectEvent(SPELL_ID, function thistype.onCast)
endif
endmethod
endstruct
endscope
Generating Sphere
Releasing Energy Pack
Energy Pack Explode
Energy Missile Landed Ground
Summons Expired
Reabsorption of summons
Fully Reabsorbed
Sphere Landed Ground
First Summon Spells
Second Summon Spell
Spell Name
Energy Loss
Spell Icon
Spell Description
The elemental unit is constantly losing the energy from his body, resulting in loss of hit points over time.
Decreases hit point regeneration by 5/7/9 hit points per second.
Spell Name
Energy Explosion
Spell Icon
Spell Description
Unstabilized element unit maximize its disordered energy inside his body, causes an AoE explosion, dealing moderate damage to nearby enemy units. Unit will be died and will not get reabsorbed back by the sphere.
Level 1 - (Remaining Life/4) damage, 200 AoE
Level 2 - (Remaining Life/4) damage, 250 AoE
Level 3 - (Remaining Life/4) damage, 300 AoE
Spell Name
Energy Absorption
Spell Icon
Spell Description
The elemental unit is constantly absorbing the energy around him, replacing his lost energy.
Increases hit point regeneration by 5/10/15 hit points per second.
v1.0.1
Removed FIRST_SUMMON[spellLv] == GetUnitTypeId(killedUnit) from function EC_AbsorbFilter
This probably has been asked already, but I take the risk of asking again:
When exactly are we allowed to upload or spells into the spell section - after the DEADLINE or after the contest is COMPLETELY OVER?
I believe after the contest deadline is over, and after the contest thread is closed (no more modification), you may upload to the spell section. But I personally suggest to upload the spell after the contest is completely over (so that you could receive some feedback from the judges and people) and do the final modification before upload it to spell section.
I'm not really aware of a clear rule towards this question, at this moment.
It's maybe not a perfect solution, and could be set clear for future spell contests right at the begining, but I personaly would prefer you to upload the spell after results are public.
Also BPower locked (or wanted to lock ?) uploaded entries, so we follow this at the moment, I would say.
It was great if there was an option to upload a submission in our section, marked as an "contest entry".
The thread should be insantly locked, when submitted, but be unlocked when the contest is over.
This way we would not have any problems with pre-reviews, but also can ensure to have all entries in our spell data base.
It was great if there was an option to upload a submission in our section, marked as an "contest entry".
The thread should be insantly locked, when submitted, but be unlocked when the contest is over.
This way we would not have any problems with pre-reviews, but also can ensure to have all entries in our spell data base.
Note: I learned heaps on www.wc3c.net, inWarcraft.de and here, but for example I cannot find the original credits for the function SIM_ERROR in my map, which might be Vexorian from www.wc3c.net. So If you recognize a snippet by yourself, please tell me. In this case, I am very sorry not to credit you!
Credits for non-blizzard ressources, all from (www.hiveworkshop.com)
- Ancient Explosion Model: WILL THE ALMIGHTY
- yellow chicken model: maxor_gan
- yellow chicken button: maxor_gan
- talk button: KelThuzad
- dummy.mdx: Vexorian (actual link of the mdx file unknown, but I think, Vexorian created it)
Comments
I like to wish good luck to everyone, that entered this contest and thank a lot the judges and people from the hive, that organize these contests!
Calls an orb from the skies. When the orb lands, it will knockback all nearby units. After a few seconds, the orb will become
attackable for a set duration where any damage taken will be reduced to one. Enemy units that die near the orb while it is
still alive will charge the orb, eventually resetting its duration, turning the orb invulnerable, and allowing the caster to move
and use its ability.
-------- dealing damage to units within the target point --------
Custom script: set bj_wantDestroyGroup = true
Unit Group - Pick every unit in (Units within PB_BeamRange[PB_Spell_ID] of PB_TargetLoc[PB_Spell_ID]) and do (Actions)
Loop - Actions
Set PB_TempUnit = (Picked unit)
Set PB_TempReal = (Current flying height of PB_TempUnit)
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
(PB_TempUnit is Magic Immune) Equal to False
(PB_TempUnit is alive) Equal to True
(PB_TempUnit belongs to an enemy of PB_Owner[PB_Spell_ID]) Equal to True
PB_TempReal Less than or equal to PB_BeamDamage_MaxHeight
Then - Actions
Unit - Cause PB_Caster[PB_Spell_ID] to damage PB_TempUnit, dealing PB_Damage[PB_Spell_ID] damage of attack type PB_AttackType and damage type PB_DamageType
Else - Actions
-------- TempUnit did not pass filters; do not deal damage --------
Unfortunately, I won't be able to finish my entry. Too bad, this type of contest is one of the few I can participate in the first place. Maybe next time. And good luck to the contestants!
Just got back from being out this weekend, I'll be doing a final update to my table pretty much on the dot as the contest closes (I will ignore all entries that missed the deadline), or rather update the table but mark them as having missed the deadline if I've missed any of your entries/WIPs on the table now is the time to tell me
Lololol. Well, can't work on it anymore, so here it is.
I'm pretty much gunning for last place here, and I hope (Anitarf especially) you will forgive me; the trigger leaks, the tooltips are probably screwy, the map is unfinished, and there's an unconscionable bug with my methods I could not decipher. Regardless, it 'works' (for some definitions of "work"), and it's more important to me at this point to just put something in.
Boy am I rusty. I had forgotten just how hard it is, modding... And how big of a gap between "works on paper" and "works in practice". (It kills me to think, this ability was supposed to be merely 1 of 4 such abilities on a custom Hero, for a custom faction, for the latest Techtree Contest... Boy did I sink myself in deep there.)
Just so nobody is under any illusions - generally contests run on GMT time (current time is 20:12 GMT) the contest closes at 00:00 GMT which is a bit under 4 hours. Since we don't really have an applied moderator at the moment I suggest everybody work under that assumption and also assume it's strictly enforced (i.e. request an extension if you need a bit more time to complete your entry though there's no guarantee of it being granted) though obviously I'm no authority on the matter.
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.