| 08-03-2008, 10:30 AM | #1 | |
Hey! This is currently Darkwulfv. Goldendercon and I formed a spellmaking duo called "EquippedChaos". We completed our first spell today, and I decided to come here for some last minute feedback before we moved into our next spell. So, our first spell is an "omgwtfbbqhax" spell, in that it's insane with SFX and damage and stuff (I weakened it a bit, but making it more insane is easy). We decided to come in with a "BANG!", and this was the result. Any script optimizations, etc. are appreciated. NOTE: I've got a bit of a dilemma. This spell has a cooldown greater than its channel time, which is fine and dandy. But if, for some reason, someone were to lower the cooldown and cast the spell again while the first instance were active, the SFX do not die. I'd like to know the best way to accomplish this without ruining any MUI this spell has. Thanks! Now, first off: The code: JASS:scope PhoenixCircle initializer Init globals //ID of the spell private constant integer ABILITY_ID = 'A000' //ID of the dummy spell private constant integer DUMMY_ID = 'A003' //ID of the unit that will move around the caster private constant integer DUMMY_UNIT = 'h000' //ID of the dummy caster private constant integer DUMMY_CASTER = 'h001' //Number of air dummies private constant integer AIR_DUMMIES = 6 //Number of ground dummies private constant integer GROUND_DUMMIES = 18 //Number of Flame Strikes private constant integer FLAME_STRIKES = 6 //Distance from the caster air dummies will be private constant real AIR_DISTANCE = 200. //Distance from the caster ground dummies will be private constant real GROUND_DISTANCE = 300. //Interval of the movement timer //Higher values = less chance of lag, choppier movement //Lower values = more chance of lag, smoother movement //.035 is usually very good. private constant real TIMER_INTERVAL = .035 //The height the dummies will float to private constant real DUMMY_HEIGHT = 450. //The height the caster will float to private constant real CASTER_HEIGHT = 225. //How long the spell will last (maximum), in seconds //Always make it about 1/2 second less, for accuracy with effects private constant real SPELL_DURATION = 15 //Order string for the casting spell private constant string CAST_STRING = "starfall" //Order string for the dummy spell private constant string ORDER_STRING = "flamestrike" //Path the dummy uses for its model //This is here for preloading purposes, to stop cast-time lag. private constant string DUMMY_MODEL = "Abilities\\Weapons\\PhoenixMissile\\Phoenix_Missile.mdl" endglobals //NO TOUCHY!!! (from here and below) struct PhoenixCircleData unit caster = null unit array dummies[8191] unit array groundies[8191] real rotation = 0 timer runtimer = null endstruct globals private group PhoenixCasters = CreateGroup() endglobals private function Callback takes nothing returns nothing local timer t = GetExpiredTimer() local PhoenixCircleData dat = GetHandleInt(t, "PhoenixData") local unit F = dat.caster local unit dummy local integer i = 1 local real x = GetUnitX(F) local real y = GetUnitY(F) local real x2 = 0 local real y2 = 0 local real angle = 0 local real rotation = dat.rotation loop exitwhen i > AIR_DUMMIES set dummy = dat.dummies[i] set angle = (Atan2(GetUnitY(dummy) - y, GetUnitX(dummy) - x)) + (bj_DEGTORAD *5.) set x2 = x + AIR_DISTANCE * Cos(angle) set y2 = y + AIR_DISTANCE * Sin(angle) call SetUnitPosition(dummy, x2, y2) set i = i + 1 endloop set i = 1 set rotation = rotation + 5. set angle = 0 loop exitwhen i > GROUND_DUMMIES set angle = rotation + ((360 / GROUND_DUMMIES) * i) set x2 = x + GROUND_DISTANCE * Cos(angle * bj_DEGTORAD) set y2 = y + GROUND_DISTANCE * Sin(angle * bj_DEGTORAD) call SetUnitPosition(dat.groundies[i], x2, y2) set i = i + 1 endloop set dat.rotation = rotation set F = null set dummy = null set t = null endfunction private function EmergencyClean takes unit caster returns boolean local PhoenixCircleData C = GetHandleInt(caster, "EmergencyClean") local integer i = 1 loop exitwhen i > AIR_DUMMIES call RemoveUnit(C.dummies[i]) set i = i + 1 endloop set i = 1 loop exitwhen i > GROUND_DUMMIES call RemoveUnit(C.groundies[i]) set i = i + 1 endloop call PauseTimer(C.runtimer) call DestroyTimer(C.runtimer) call SetUnitFlyHeight(caster, 0, 99999.) return true endfunction private function Conditions takes nothing returns boolean return GetSpellAbilityId() == ABILITY_ID endfunction private function Actions takes nothing returns nothing local unit dummy local unit C = GetTriggerUnit() local integer lvl = GetUnitAbilityLevel(C, ABILITY_ID) local integer i = 1 local real X = GetUnitX(C) local real Y = GetUnitY(C) local real R = 360. / AIR_DUMMIES local real x2 = 0 local real y2 = 0 local player p = GetOwningPlayer(C) local timer t = CreateTimer() local boolean DoubleCast = false local PhoenixCircleData Data = PhoenixCircleData.create() set Data.caster = C set Data.runtimer = t if IsUnitInGroup(C, PhoenixCasters) then set DoubleCast = EmergencyClean(C) call GroupRemoveUnit(PhoenixCasters, C) endif call GroupAddUnit(PhoenixCasters, C) call UnitAddAbility(C, 'Amrf') call UnitRemoveAbility(C, 'Amrf') call SetUnitFlyHeight(C, CASTER_HEIGHT, 75.) if AIR_DUMMIES > 0 then loop exitwhen i > AIR_DUMMIES set x2 = X + AIR_DISTANCE * Cos((R * i) * bj_DEGTORAD) set y2 = Y + AIR_DISTANCE * Sin((R * i) * bj_DEGTORAD) set Data.dummies[i] = CreateUnit(p, DUMMY_UNIT, x2, y2, GetRandomReal(0, 360.)) call SetUnitFlyHeight(Data.dummies[i], DUMMY_HEIGHT, 75.00 ) set i = i + 1 endloop endif set i = 1 set x2 = 0 set y2 = 0 set R = 360. / GROUND_DUMMIES if GROUND_DUMMIES > 0 then loop exitwhen i > GROUND_DUMMIES set x2 = X + GROUND_DISTANCE * Cos((R * i) * bj_DEGTORAD) set y2 = Y + GROUND_DISTANCE * Sin((R * i) * bj_DEGTORAD) set Data.groundies[i] = CreateUnit(p, DUMMY_UNIT, x2, y2, GetRandomReal(0, 360.)) set i = i + 1 endloop endif call SetHandleInt(C, "EmergencyClean", Data) call SetHandleInt(t, "PhoenixData", Data) call TimerStart(t, TIMER_INTERVAL, true, function Callback) loop exitwhen OrderId2String(GetUnitCurrentOrder(C)) != CAST_STRING call PolledWait(.2) endloop call SetUnitFlyHeight(C, 0, 75.00 ) set i = 1 loop exitwhen i > AIR_DUMMIES call KillUnit(Data.dummies[i]) set i = i + 1 endloop set i = 1 loop exitwhen i > GROUND_DUMMIES call KillUnit(Data.groundies[i]) set i = i + 1 endloop call PauseTimer(t) call DestroyTimer(t) call PolledWait(1.) set i = 1 set x2 = 0 set y2 = 0 set R = 0 if FLAME_STRIKES > 0 and DoubleCast == false then loop exitwhen i > FLAME_STRIKES set dummy = CreateDummyUnit(p, DUMMY_CASTER, X, Y, DUMMY_ID, lvl, true) set x2 = X + (GROUND_DISTANCE * Cos(R * bj_DEGTORAD)) set y2 = Y + (GROUND_DISTANCE * Sin(R * bj_DEGTORAD)) call IssuePointOrder(dummy, ORDER_STRING, x2, y2) set R = ( R + ( 360.00 / FLAME_STRIKES ) ) set i = i + 1 endloop endif call GroupRemoveUnit(PhoenixCasters, C) call FlushHandleLocals(C) set dummy = null set C = null set p = null set t = null endfunction //=========================================================================== private function Init takes nothing returns nothing local trigger t = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT ) call TriggerAddCondition(t, Condition( function Conditions ) ) call TriggerAddAction(t, function Actions ) call Preload(DUMMY_MODEL) set t = null endfunction endscope
Just so you know, this spell is quite graphic intensive at the end, so lowering FLAME_STRIKES from 6 to 3-4 might be a good idea, since Flame Strike is a very graphic SFX. Hope you guys enjoy our opening spell. More to come soon! |
| 08-04-2008, 02:36 AM | #2 |
I wish there was an easier way to do teams that didn't require these dummy accounts, regarding your question, well, you don't really have to understand it, do you? Shouldn't you add a screenshot? |
| 08-04-2008, 03:05 AM | #3 |
Thank you Vex, much appreciated. I don't get your reply to my question, but if it's not a big deal I won't worry about it. Oh, and yeah, I probably should add a screenshot or two. I hope Wc3 doesn't F them up like usual. EDIT: Sorry, no screenshots. They come up completely black, so nothing is seen. Can I ask someone to take a few for me? Best shot: When the caster + air circle stops floating upwards. Phoenix Fire being shown is even better. |
| 08-04-2008, 05:46 AM | #4 |
Looks cool, was only able to take one as my net is being extra silly this morning. http://i20.photobucket.com/albums/b2...Untitled-6.jpg |
| 08-04-2008, 05:48 AM | #5 | |
Quote:
|
| 08-04-2008, 07:10 AM | #6 |
Why don't you just add the caster to a group of active casters and if it were to be cast again... you get the idea destroy or reset the spell or however you want to do it -Av3n |
| 08-04-2008, 03:45 PM | #7 |
@Dusk: Yes, he should :D @Chuckle Brother: Thanks a ton, you got a really good shot. Just gotta trim the edges. @Av3n: That method could work, yes. I'd have to add the fires and stuff to groups and things and detect which instance their in... Hmm, that's a pain to think about. I mean there's no problem as long as the ability isn't cast by the same person at the same time... Because it's a channeling spell, if you cast it again while channeling the code is set to run while channeling "starfall", and since you'd still be on that order, it starts the spell up but never ends the old one. I suppose giving them timed life would work, and rigging the flame strikes to a timer... Off to work! Oh btw, someone reported being able to select the fires. Has anyone else been able to do this? They have locust... |
| 08-04-2008, 03:51 PM | #8 | |
Quote:
Only the dummy caster has locust, not the Dummy (SFX). |
| 08-04-2008, 04:11 PM | #9 |
Really? Well I'll be damned. Thanks for finding that, it'll be fixed immediately (can't believe I missed that) |
| 08-04-2008, 05:09 PM | #10 |
Very cool looking spell. I suggest making the immolation's damage the red fire (Abilities\Spells\Other\ImmolationRed\ImmolationRedDamage.mdl) and not the green fire. |
| 08-04-2008, 05:27 PM | #11 |
We were in a bit of a rush to put this out, so none of the buffs were changed. They will be in the final release, so yes, the fire will be red and not green. |
| 08-04-2008, 06:22 PM | #12 |
If one day I have to leave this community, I'll do it but first I'll teach to darkwulfv how to say good bye to polledwait function of doom and use the timer for that. The day you learn this, you will be a new man. |
| 08-04-2008, 06:38 PM | #13 |
Meh. I'm still not fully understanding how one goes about doing that... I mean, the whole reason I use polledwait functions in this particular spell is to WAIT for something without the use of timers (and thus lots of extra attaching and extracting everywhere), and timers don't stop the rest of the trigger from running, so... I'm currently finetuning a way to make this spell work even if double-casted. Right now there are 3 ways (Timer, Timed life, and after-timer check) for the flames to die. I'm just making sure they'll all work. Oh, and I'm (cohadar will shoot me) attaching a struct to the caster, which is apparently the devil's spawnchild because of handle corruption... or something stupid I don't understand. So uh, is this going to be an issue? (I'm attaching the struct so I can access the flames if the caster tries double-casting). |
| 08-04-2008, 07:38 PM | #14 |
~Bumpdate~ Okay, I added a safety fallback in the event someone decides to change the cooldown so the spell will double cast. The flames will be immediately removed and replaced. However, for some reason, the newly spawned flames will not die normally (with the epic burst of fire). This is fixed when casting normally. But you know, that's what people get for messing with things like that. v1.1 is up. Changelog added to first post and code updated. Thanks for all the kind words and advice we've been getting. Goldendercon and I will start our next spell as soon as he's back from school (we only really get to work together), and we'll put a pack out as soon as we can. PS: If anyone wants to take more screenshots, they're welcome to! |
| 08-05-2008, 07:23 AM | #15 |
Fail at configuration. Some things simply need config functions (such as spell duration) because they depend on the level and may have their own formulas. |
