| 09-26-2009, 10:31 PM | #1 |
Pyroclasm (1.01) Channels a stream of fiery, exploding tendrils that curve towards a target point. Channeling lasts until all of the tendrils have been created. Level 1 - 10 tendrils, 50 damage per explosion. Level 2 - 15 tendrils, 75 damage per explosion. Level 2 - 20 tendrils, 100 damage per explosion. This is a spell I've been working on recently - it is in essence a total revamp of the spell I submitted to the 15th spell making session. As a side note, it was inspired by spells written by Vexorian (Stormy Dreams) and Anitarf (Spirit Helix) - they both use helical motion on the x/y axes. The spell requires:
v1.0 code:// Pyroclasm // Created by Zerzax // Details: A point-target spell that is channeled. Various aspects can be configured, such as damage, radius, effect references, and length parameters. // Requires: // Unit Indexing Utils (Red version is used here) // BoundSentinel // TimerUtils (Red version is used here) // XE: // xebasic // xefx // xedamage // Credits to Vexorian for JassHelper, these three libraries, and inspiration for the spell's movement method. // Thanks to Rising_Dusk for Unit Indexing Utils, as well as the makers of JNGP. // // The spell uses Bezier interpolation in two dimensions, similar to Vexorian's Stormy Dreams spell. It was also inspired by Anitarf's Spirit Helix spell. // It is a total rework of my submission to Wc3c's 15th spell making session, also called Pyroclasm. // Configuration / Calibration scope Pyroclasm globals private constant integer SPELL_ID = 'A001' private constant string SPELL_ORDER = "hex" // The channel order that the spell uses private constant real TENDRIL_SPAWN_INTERVAL = .5 // Time between creations of tendrils private constant real TENDRIL_HEIGHT = 50.00 // Starting and ending height of the tendril private constant real TENDRIL_TIME = 1.00 // The period of time over which the curve is traversed private constant real TENDRIL_DISTANCE = 700.00 // The x/y linear distance between the endpoints of the curve private constant real TENDRIL_WIDTH = 500 // This actually isn't the width, it is the distance from the central line to the control points of the curve. // However, this value is directly proportional to the actual tendril width. private constant effecttype TENDRIL_MODEL = EFFECT_TYPE_MISSILE // These effect types are configurable within the spell's page in the object editor. private constant effecttype DAMAGE_EFFECT = EFFECT_TYPE_TARGET private constant effecttype EXPLOSION_MODEL = EFFECT_TYPE_SPECIAL endglobals private constant function MaxTendrils takes integer level returns integer // The spell continues channeling until this number of tendrils has been created. return 5 + 5*level endfunction private constant function ExplosionCount takes integer level returns integer // Number of explosions per tendril. return 4 // The total distance of the curve is divided by this number to define the explosion points. endfunction // I would stick with four - it looks best that way, private constant function ExplosionDamage takes integer level returns real return 25.00 + 25.00*level endfunction private constant function ExplosionRadius takes integer level returns real return 175.00 endfunction // ".aoe" is the xedamage object created on init that the spell refers to. This allows the user to externally modify this object's properties. //! textmacro DamageConfig set .aoe.exception = UNIT_TYPE_FLYING call .aoe.factor(UNIT_TYPE_STRUCTURE, 0.0) set .aoe.dtype = DAMAGE_TYPE_UNIVERSAL set .aoe.damageTrees = true call .aoe.useSpecialEffect(GetAbilityEffectById(SPELL_ID, DAMAGE_EFFECT, 0), "chest") //! endtextmacro // Spell code private struct tendril static timer tim // This timer loops through a list of active instances static tendril array list static integer index = 0 // The index of the last created active instance static xedamage aoe // The xedamage object used for AoE damage delegate xefx m // Delegating the object allows its internal variables to be used as members of this struct unit cast = null integer count = 1 // A counter used in determining whether it is time to create an explosion integer level = 0 // Cast level real x1 = 0.0 // Points on the curve through which the timer interpolates. real x2 = 0.0 real x3 = 0.0 real x4 = 0.0 real y1 = 0.0 real y2 = 0.0 real y3 = 0.0 real y4 = 0.0 real t = 0.0 // Time parameter static method update takes nothing returns nothing // Timer method for moving the local tendril this = 0 local integer i = 0 local real t = 0 local real s = 0 loop exitwhen i >= .index set this = .list[i] set t = .t + XE_ANIMATION_PERIOD/TENDRIL_TIME set .t = t if t > 1 then set t = 1 endif set s = 1-t set .x = t*t*t*.x4 + 3*t*t*s*.x3 + 3*t*s*s*.x2 + s*s*s*.x1 // This is the magical part - the interpolation of the curve. set .y = t*t*t*.y4 + 3*t*t*s*.y3 + 3*t*s*s*.y2 + s*s*s*.y1 set .count = .count + 1 if .count >= TENDRIL_TIME/XE_ANIMATION_PERIOD * 1/ExplosionCount(.level) then // If the count/total counts is equal to the fraction of counts needed for an explosion, explode. set .count = 1 // Reset the count. This way, no ugly modulo checks for remainder. call DestroyEffect(AddSpellEffectById(SPELL_ID, EXPLOSION_MODEL, .x,.y)) call .aoe.damageAOE(.cast,.x,.y, ExplosionRadius(.level), ExplosionDamage(.level)) endif if t == 1 then // Basically, just shift the places down by one in the list when the spell is done. set .index = .index - 1 set .list[i] = .list[.index] call .destroy() else set i = i + 1 endif endloop endmethod static method create takes unit c, integer lvl, real x, real y, real xvector, real yvector, integer alt returns tendril local tendril this = tendril.allocate() set .m = xefx.create(x,y,0) set .m.fxpath = GetAbilityEffectById(SPELL_ID, TENDRIL_MODEL, 0) set .z = TENDRIL_HEIGHT set .cast = c set .level = lvl set .x1 = x set .x2 = x + .25*TENDRIL_DISTANCE*xvector + alt*TENDRIL_WIDTH*-yvector // Set the points using sum and difference formulas for sine and cosine. set .x3 = x + .75*TENDRIL_DISTANCE*xvector + alt*TENDRIL_WIDTH*yvector set .x4 = x + TENDRIL_DISTANCE*xvector set .y1 = y set .y2 = y + .25*TENDRIL_DISTANCE*yvector + alt*TENDRIL_WIDTH*xvector set .y3 = y + .75*TENDRIL_DISTANCE*yvector + alt*TENDRIL_WIDTH*-xvector set .y4 = y + TENDRIL_DISTANCE*yvector if .index == 0 then call TimerStart(.tim, XE_ANIMATION_PERIOD, true, function tendril.update) endif set .list[.index] = this // add to the active list set .index = .index + 1 return this endmethod method onDestroy takes nothing returns nothing // Nothing much to this... call .m.destroy() endmethod static method onInit takes nothing returns nothing // Init stuff for this struct specifically set .tim = CreateTimer() set .aoe = xedamage.create() //! runtextmacro DamageConfig() endmethod endstruct private struct cast static cast array instances unit caster = null integer level = 0 timer periodic = null real xi = 0.0 real yi = 0.0 real xvector = 0.0 real yvector = 0.0 integer spawned = 0 integer alternate = 1 boolean willDestroy = false static method update takes nothing returns nothing // Update method of the TimerUtils timer. Basically makes sure that the spell is still running, and then proceeds to create a tendril. local cast this = cast(GetTimerData(GetExpiredTimer())) if GetUnitCurrentOrder(.caster) == OrderId(SPELL_ORDER) and not .willDestroy then call tendril.create(.caster, .level, .xi, .yi, .xvector, .yvector, .alternate) set .alternate = -.alternate set .spawned = .spawned + 1 if .spawned == MaxTendrils(.level) then call SetUnitPosition(.caster,.xi,.yi) set .instances[GetUnitId(.caster)] = 0 call .destroy() endif else call .destroy() endif endmethod static method create takes unit c, real x, real y returns cast // Set the stuff for the timer to work off of local cast this = cast.allocate() local real a = Atan2(y-GetUnitY(c),x-GetUnitX(c)) set .caster = c set .level = GetUnitAbilityLevel(c, SPELL_ID) set .xi = GetUnitX(c) set .yi = GetUnitY(c) set .xvector = Cos(a) // These "vectors" are really just sine and cosine of the cast angle. set .yvector = Sin(a) // I call them vectors because they represent base vectors on the x and y axes. set .periodic = NewTimer() call SetTimerData(.periodic, integer(this)) call TimerStart(.periodic, TENDRIL_SPAWN_INTERVAL, true, function cast.update) return this endmethod method onDestroy takes nothing returns nothing // Easy cleanup call ReleaseTimer(.periodic) endmethod static method spellExecute takes nothing returns boolean // Register the cast local integer id = 0 if GetSpellAbilityId() == SPELL_ID then set id = GetUnitId(GetTriggerUnit()) if .instances[id] != 0 then set .instances[id].willDestroy = true endif set .instances[id] = cast.create(GetTriggerUnit(),GetSpellTargetX(),GetSpellTargetY()) endif return false endmethod static method onInit takes nothing returns nothing // Trigger creation on init local trigger t = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(t, Condition(function cast.spellExecute)) endmethod endstruct endscope I hope you enjoy it. EDIT: I am a little confused as to how the bullet points work - would someone mind telling me how to do it? D1000's snake spell has it perfectly. EDIT2: Thanks Vexorian. EDIT3: 1.01 is up - an icon change, more specific research and use tooltips, changed the on damage effect to something a little more spicy - it looks much more natural now. |
| 09-27-2009, 12:59 AM | #2 |
whenever are interested on bbcode plagiarism, use the quote button. |
| 09-27-2009, 01:14 AM | #3 |
Is this plagiarism? Or are you saying I should quote d1000's post? I get a little antsy when I see the word :P. EDIT: I'm assuming its the bullet point thing... EDIT2: Got the bullet points like I want them now, thanks. |
| 09-29-2009, 07:51 PM | #4 |
Please tell me the status of the submission - I know at least Vexorian has seen the thread. If all of the mods are busy, I understand, and I'd like to know what I can do. If it's totally an issue of no free time for the mods, it would be great to know at least that. |
| 09-29-2009, 08:00 PM | #5 |
I have 300 papers to grade by Thursday. Sorry, I probably won't do any looking at it before then. (If I do, then I'm seriously procrastinating..) |
| 09-30-2009, 01:43 AM | #6 |
That's what I wanted to know, thank you. Anybody have some feedback on the spell? I personally like the effect patterns and alternating, though there isn't much to the damage mechanism. |
| 10-08-2009, 02:10 AM | #7 |
Just realized I could fix some stuff up for multi-instanceability - I will address it in a day or two. |
| 10-17-2009, 03:52 PM | #8 |
|
| 10-17-2009, 07:33 PM | #9 | |
Well thanks a bunch Dusk, very much appreciated. Quote:
I've had tepid feedback from others and no feedback from people here, so I figured pretty quickly that the spell was lacking something. I'll go back to the drawing board and see if I can make it more interesting - most of the components will probably stay the same though. In that train of thought, what do you guys think I could do to make this more interesting? Add a DoT mechanism? change the effects? make the pattern random/different? Even change casting mechanism (not channeling)? I'd appreciate any feedback, because I think it has potential to have that "wow" factor. |
| 10-28-2009, 07:20 PM | #10 |
I have two requests to do - after that, this will see a really cool revamp that involves 3d projectiles and delayed explosions. I promise it will be nice. |
| 01-04-2010, 03:24 AM | #11 |
Now that the hero contest is out of the way, I will devote some time to the proposed update. |
