| 08-23-2008, 10:33 PM | #1 | ||
vJass: Yes. MUI: Yes. Laggless: Yes. Leakless: Yes Requires: NewGen Editor Description: (Is copied from tooltip)
Screenshots:
Code: JASS:struct LightningBlastBall // Requires TimerUtils ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Implementing Instructions: // 1: Create a trigger named LightningBlastBall and copy all this code into that trigger. // 2: Import TimerUtils if you haven't them already imported into your map already. // 3: Create the dummy unit and the ability // 4: Set the SPELL_ID and DUMMY_ID to the ID's of your created dummy and ability. // 5: Configure the spell the way you want it. // // // WARNING: If you see that the handle amount goes up in the handle counter that is NOT because this spell leaks. // It is because I do not remove the units, I kill them and then lets WC3 take care of them as they should be taken care of // //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Configuration vars: private static constant integer SPELL_ID = 'A000' //the ID of the spell. private static constant integer DUMMY_ID = 'h001' // the dummys ID private static constant real TIMER_PERIOD = 0.025 // How often the timer runs, increasing this will lower the speed of the balls. private static constant real SPEED = 10 // The speed of the lightning ball. private static constant real DAMAGE_INCREASE = 1.6 // This value is used to calculate the damage, the damage is calculated this way // lvl*DAMAGE_INCREASE private static constant real HEIGHT_INCREASE = 7 // How much the dummy's height increases per interval. private static constant real DROP_SPEED_INCREASE = 7 // How much the drop speed increases per interval private static constant real START_SCALING = 0.40 // The size of the unit when the spell starts private static constant real SCALE_INCREASE = 0.03 // How much the units size increases every interval private static constant integer MAX_AMOUNT_OF_DUMMIES_USED_IN_NOVA = 30 // The max amount of dummies the nova can use. private static constant string SOUND_PATH = "Abilities\\Spells\\Human\\StormBolt\\ThunderBoltMissileDeath.wav" // The sound you want this spell to use. // The damage filter, Edit this to make it target the types of units you want to private static constant method DamageFilter takes unit caster, unit target returns boolean return (IsUnitEnemy(target, GetOwningPlayer(caster))) and (IsUnitType(target, UNIT_TYPE_GROUND)) and (GetUnitState(target, UNIT_STATE_LIFE) > 0.405) endmethod // The Damage amount this spell deas, edit this to configure the damage you want it to deal. private static constant method DamageAmount takes integer lvl returns real return lvl*.DAMAGE_INCREASE endmethod // The function that controls the amount of dummies that the nova creates. If you want it to spawn dummies based on level then just do something like: 20*lvl. private static constant method NovaDummyAmount takes integer lvl returns integer return 20 endmethod // The function that controls the radius of the Nova. private static constant method NovaRadius takes integer lvl returns integer return 300 endmethod // The function that controls the radius around every unit in the nova that should be damaged. private static constant method DamageRadius takes integer lvl returns integer return 30 endmethod // The function that controls the speed of the nove units. private static constant method NovaSpeed takes integer lvl returns integer return 15 endmethod // The function that controls the size of the dummies used in the nova. private static constant method NovaSize takes integer lvl returns integer return 8 endmethod // Don't edit below this line if you don't know what you're doing. private static boolexpr RETURN_TRUE private static location globalLoc = Location(0,0) private static LightningBlastBall array LBB private real currX private real currY private real distdone = 0 private real totaldist private real cos private real sin private real angle private real currheight = 0 private real totaldrop private unit whichUnit private real currscale = .START_SCALING private real dropspeed = .DROP_SPEED_INCREASE private unit damager private real currdamage = 0 private unit array novaUnits[.MAX_AMOUNT_OF_DUMMIES_USED_IN_NOVA] private real array novasin[.MAX_AMOUNT_OF_DUMMIES_USED_IN_NOVA] private real array novacos[.MAX_AMOUNT_OF_DUMMIES_USED_IN_NOVA] private real array novaUnitsX[.MAX_AMOUNT_OF_DUMMIES_USED_IN_NOVA] private real array novaUnitsY[.MAX_AMOUNT_OF_DUMMIES_USED_IN_NOVA] private boolean novaStarted = false private group damagedUnits private timer t private static method H2I takes handle h returns integer i return h return 0 endmethod private static method create takes nothing returns LightningBlastBall local LightningBlastBall LBBT = .allocate() set LBBT.damagedUnits = CreateGroup() return LBBT endmethod private method onDestroy takes nothing returns nothing local integer i = 0 loop exitwhen i >= .MAX_AMOUNT_OF_DUMMIES_USED_IN_NOVA call ShowUnit(.novaUnits[i], false) call KillUnit(.novaUnits[i]) set .novaUnits[i] = null set i = i+1 endloop call DestroyGroup(.damagedUnits) set .damagedUnits = null set .whichUnit = null set .damager = null call ReleaseTimer(.t) endmethod private static method Nova takes nothing returns nothing local timer t = GetExpiredTimer() local LightningBlastBall l = .LBB[.H2I(t)-0x100000] local real totalAngle = 0 local real between local integer i = 0 local real Nova_D_A = .NovaDummyAmount(GetUnitAbilityLevel(l.damager, .SPELL_ID)) local real scale = (l.currscale/Nova_D_A)*.NovaSize(GetUnitAbilityLevel(l.damager, .SPELL_ID)) local group unitsInRange = CreateGroup() local unit u local real D_R = .DamageRadius(GetUnitAbilityLevel(l.damager, .SPELL_ID)) local real N_Speed if l.novaStarted == false then set between = 360/Nova_D_A set N_Speed = .NovaSpeed(GetUnitAbilityLevel(l.damager, .SPELL_ID)) loop exitwhen totalAngle >= 360 set l.novacos[i] = N_Speed * Cos(bj_DEGTORAD * totalAngle) set l.novasin[i] = N_Speed * Sin(bj_DEGTORAD * totalAngle) set l.novaUnits[i] = CreateUnit(GetOwningPlayer(l.damager), .DUMMY_ID, l.currX, l.currY, totalAngle) set l.novaUnitsX[i] = GetUnitX(l.novaUnits[i]) set l.novaUnitsY[i] = GetUnitY(l.novaUnits[i]) call SetUnitScale(l.novaUnits[i], scale,scale,scale) set totalAngle = totalAngle+between set i = i+1 endloop set i = 1 set l.novaStarted = true endif if l.distdone >= .NovaRadius(GetUnitAbilityLevel(l.damager, .SPELL_ID)) then call l.destroy() else loop exitwhen i >= Nova_D_A set l.novaUnitsX[i] = l.novaUnitsX[i] + l.novacos[i] set l.novaUnitsY[i] = l.novaUnitsY[i] + l.novasin[i] call SetUnitX(l.novaUnits[i], l.novaUnitsX[i]) call SetUnitY(l.novaUnits[i], l.novaUnitsY[i]) call GroupClear(unitsInRange) call GroupEnumUnitsInRange(unitsInRange, l.novaUnitsX[i], l.novaUnitsY[i], .DamageRadius(GetUnitAbilityLevel(l.damager, .SPELL_ID)), .RETURN_TRUE) loop set u = FirstOfGroup(unitsInRange) exitwhen u == null if .DamageFilter(l.damager, u) == true and IsUnitInGroup(u, l.damagedUnits) == false then call UnitDamageTarget(l.damager, u, l.currdamage , false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, null) endif call GroupRemoveUnit(unitsInRange, u) call GroupAddUnit(l.damagedUnits, u) set u = null endloop set i = i+1 endloop set l.distdone = l.distdone + .NovaSpeed(GetUnitAbilityLevel(l.damager, .SPELL_ID)) endif set unitsInRange = null set u = null set t = null endmethod private static method Drop takes nothing returns nothing local timer t = GetExpiredTimer() local sound s local LightningBlastBall l = .LBB[.H2I(t)-0x100000] if l.currheight >= l.totaldrop-.DROP_SPEED_INCREASE then set l.distdone = 0 call PauseTimer(l.t) call TimerStart(l.t, .TIMER_PERIOD, true, function LightningBlastBall.Nova) call ShowUnit(l.whichUnit, false) call KillUnit(l.whichUnit) set s = CreateSound(.SOUND_PATH, false, true, true, 0,0,"a") call SetSoundPosition(s, l.currX, l.currY, 0) call SetSoundVolume(s, 110) if (s != null) then call StartSound(s) endif call KillSoundWhenDone(s) else call SetUnitFlyHeight(l.whichUnit, l.totaldrop-l.currheight, 0) set l.currheight = l.currheight + l.dropspeed set l.dropspeed = l.dropspeed + .DROP_SPEED_INCREASE endif set t = null set s = null endmethod private static method TimerAct takes nothing returns nothing local timer t = GetExpiredTimer() local LightningBlastBall l = .LBB[.H2I(t)-0x100000] if l.distdone >= l.totaldist then set l.totaldrop = l.currheight set l.currheight = 0 call PauseTimer(l.t) call TimerStart(l.t, .TIMER_PERIOD, true, function LightningBlastBall.Drop) else set l.currX = l.currX + l.cos set l.currY = l.currY + l.sin call SetUnitX(l.whichUnit, l.currX) call SetUnitY(l.whichUnit, l.currY) call SetUnitFlyHeight(l.whichUnit, l.currheight, 0) call SetUnitScale(l.whichUnit, l.currscale , l.currscale , l.currscale) set l.currscale = l.currscale + .SCALE_INCREASE set l.distdone = l.distdone + .SPEED set l.currheight = l.currheight + .HEIGHT_INCREASE set l.currdamage = l.currdamage + .DamageAmount(GetUnitAbilityLevel(l.damager, .SPELL_ID)) endif set t = null endmethod private static method Actions takes nothing returns nothing local real tarx local real tary local LightningBlastBall l = .create() local unit caster = GetSpellAbilityUnit() set l.t = NewTimer() set l.damager = caster set .globalLoc = GetSpellTargetLoc() set tarx = GetLocationX(.globalLoc) set tary = GetLocationY(.globalLoc) set l.currX = GetUnitX(caster) set l.currY = GetUnitY(caster) set l.angle = Atan2((tary - l.currY), (tarx - l.currX)) set l.totaldist = SquareRoot((tarx-l.currX)*(tarx-l.currX) + (tary-l.currY)*(tary-l.currY)) set l.sin = .SPEED * Sin(l.angle) set l.cos = .SPEED * Cos(l.angle) set l.whichUnit = CreateUnit(GetOwningPlayer(caster), .DUMMY_ID, l.currX, l.currY, l.angle*bj_RADTODEG) call SetUnitScale(l.whichUnit, .START_SCALING , .START_SCALING , .START_SCALING) set .LBB[.H2I(l.t)-0x100000] = l call TimerStart(l.t, .TIMER_PERIOD, true, function LightningBlastBall.TimerAct) set caster = null call RemoveLocation(.globalLoc) set .globalLoc = null endmethod private static method Cond takes nothing returns boolean return GetSpellAbilityId() == .SPELL_ID endmethod private static method BoolexprReturnTrue takes nothing returns boolean return true endmethod private static method onInit takes nothing returns nothing local trigger Trig = CreateTrigger() local integer i = 0 set .RETURN_TRUE=Condition(function LightningBlastBall.BoolexprReturnTrue) loop call TriggerRegisterPlayerUnitEvent(Trig, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null) set i = i+1 exitwhen i == bj_MAX_PLAYER_SLOTS endloop call TriggerAddCondition(Trig, Condition(function LightningBlastBall.Cond)) call TriggerAddAction(Trig, function LightningBlastBall.Actions) endmethod endstruct |
| 08-24-2008, 02:48 AM | #2 |
|
| 08-24-2008, 04:30 PM | #3 |
> Why the nonstandard format? I say you should change it to being a scope like most sane people. While having it all contained in the struct works, it just makes more sense if you have a scope. Additionally, by not having this be a scope, it makes it harder for a user to change the name of the spell. (Can't just change the scope name, etc.) Because I wanted to try how this way worked out when I scripted it. (And I actully liked it.) > Some of your constant globals should be changed into constant functions. For example: DAMAGE_INCREASE, SPEED, NOVA_RADIUS, DAMAGE_RADIUS. And then a few of your other constants could be changed though it wouldn't probably be needed. Why would I change them into constant methods? What's the benefit of doing that? > The timer interval should be configurable. It was but I lost the map so I had to dl the old version so I forgot to add that. Changed it. > Just work in radians directly instead of working in degrees and then converting. I do work with radians? The only place I use bj_RADTODEG is when I create them and just makes them face to the target. > You know, some BJs are good. TriggerRegisterAnyUnitEventBJ is one of them. That was the only BJ in the spell so I felt like making it only using natives :P > If you expanded your custom .create method, your Actions method could be cleaned up and made a bit more concise. But then I would have to transfer all locations and caster data.. It's easier to do it in a method that can use the event responses. The *8 part.. I'll make that a constant variable.. I used that to make the nova look good :P Uploaded the updated version and code. |
| 08-24-2008, 09:31 PM | #4 |
The benefit of functions is that they take arguments (such as level). |
| 08-24-2008, 09:48 PM | #5 |
Thank you , thank you. I wanted to see people do this years ago. structs are cleaner and less hacky in what scoping is than scopes, though it reminds you of Java's ultra verbosity sometimes. It is just a style choice... Is it possible to do private real currscale = .START_SCALING instead of private real currscale = LightningBlastBall.START_SCALING I think it would be better if you had to type only the main struct's name once, it can be hard when you have the data struct as well, well JESP allows a text replace to work anyway. This doesn't really a lot but it might be an indications a struct with static members can't replace a scope in convenience yet. |
| 08-24-2008, 10:30 PM | #6 |
Yeah that's possible, but you need to use the prefix when you use them as functions. One way to solve it would be creating the whole spell as a text macro but it wouldn't look clean. |
| 08-24-2008, 10:31 PM | #7 |
You need a screenshot attached to the thread. |
| 08-24-2008, 10:36 PM | #8 |
Added. |
| 08-31-2008, 09:58 PM | #9 |
Sometimes bumps make it harder for us to see your threads. Ok, might end up reviewing it. |
| 08-31-2008, 10:18 PM | #10 |
Ok, preliminary review, checked what the spell actually does: The idea is not bad, however, and this is very important, your candy is lacking right now, it makes no sense that the spell has no sound whatsoever, when you see a giantic lightning ball being created, then impacting the ground and creating 10 other small balls, you expect sound, yet when I played your spell it was very quiet. Edit: Oh, and the death animations look very unnatural. |
| 09-01-2008, 02:28 PM | #11 |
Ok, I'll add a sound. How do you add sounds without the use of the sound editor? I tried to do that before but failed. What do you mean with the "death animation"? |
| 09-01-2008, 03:51 PM | #12 |
You can create sounds in Jass using 'labels' check out SimError. But another way is to use a model that actually has sound in its animations. |
| 10-02-2008, 10:49 PM | #13 |
Can we get an update on this? |
| 10-09-2008, 12:48 PM | #14 |
Oh sorry, I've forgotten about this, I'll update today or tomorrow :) Edit - Updated |
| 10-11-2008, 08:50 PM | #15 |
There's still no sound, what did you even change? |
