| 03-18-2008, 02:18 AM | #1 | ||||
Background: The spell is made specifically for the Spell Session #10 on wc3c. The spell also complies with the JESP manifest, is fully multi-instanceable, and requires Grimoire and vJass.
Spell Code: Spell Code://*************************************************************************************************************** //* * //* S H R A P N A D E * //* Actual Code * //* v1.07 * //* * //* By: Rising_Dusk * //* * //*************************************************************************************************************** scope Shrapnade globals //********************************************************* //* These are the configuration constants for the spell //* //* AbilityID: The ability ID that triggers the spell //* DummyUnitID: The dummy unit's ID for who to attach effects to //* DamageLocal: Where the DamageEffect gets played on a unit //* DamageEffect: Which effect plays when you damage an enemy //* ExplodeEffect: Which effect plays when a Shrapnade explodes //* DummyUnitEffect: Which effect is attached to the dummy unit //* AttackType: What attack type the damage dealt is of //* DamageType: What damage type the damage dealt is of //* BaseSpeed: Smaller numbers make it move faster (Normal: 25) //* MinSpeed: Iteration speed minimum for fragmented shards //* MaxSpeed: Iteration speed maximum for fragmented shards //* BaseDamage: The damage the spell deals at level 1 //* DamagePerLevel: The damage each extra level makes the spell do //* Fragments: Base number of fragments each spell cast breaks into //* FragmentsPerLevel: How many extra fragments each level adds //* MinFragRange: Minimum range fragments will target from initial blast //* MaxFragRange: Maximum range fragments will target from initial blast //* BlastAreaOfEffect: How big of an area the damage is dealt within //* InitialBounceHeight: The max height of the spell-cast bomb //* MinBounceHeight: The minimum max height of the fragmented shards //* MaxBounceHeight: The maximum max height of the fragmented shards //* MomentumAngle: The angle spread in radians that fragments can have //* private integer AbilityID = 'A001' private integer DummyUnitID = 'h000' private string DamageLocal = "chest" private string DamageEffect = "Abilities\\Weapons\\Rifle\\RifleImpact.mdl" private string ExplodeEffect = "Abilities\\Weapons\\Mortar\\MortarMissile.mdl" private string DummyUnitEffect = "Abilities\\Spells\\Other\\Transmute\\GoldBottleMissile.mdl" private attacktype AttackType = ATTACK_TYPE_NORMAL private damagetype DamageType = DAMAGE_TYPE_UNIVERSAL private real BaseSpeed = 20. private real MinSpeed = 15. private real MaxSpeed = 35. private real BaseDamage = 80. private real DamagePerLevel = 20. private integer Fragments = 1 private integer FragmentsPerLevel = 1 private real MinFragRange = 100. private real MaxFragRange = 400. private real BlastAreaOfEffect = 200. private real InitialBounceHeight = 250. private real MinBounceHeight = 100. private real MaxBounceHeight = 350. private real MomentumAngle = 1.04719 //********************************************************* //* These are static constants used by the spell and shouldn't be changed //* //* Timer: The timer that runs all of the effects for the spell //* Counter: The counter for how many spell instances exist //* Nades: The array of all struct instances that exist //* TimerInterval: The interval for the timer that gets run. //* Boolexpr: The boolean expression used for damage checking. //* private timer Timer = CreateTimer() private integer Counter = 0 private integer array Nades private real TimerInterval = 0.04 private boolexpr Boolexpr = null //* This is the trigger for reference outside of the scope if desired public trigger Trig = CreateTrigger() endglobals //********************************************************* //* Some quick configuration functions private function Check takes nothing returns boolean //* This is the targeting BoolExpr for the spell (Who it hits) //* bj_meleeNearestMine references the spell caster return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(bj_meleeNearestMine)) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_FLYING) and GetWidgetLife(GetFilterUnit()) > 0.405 and GetUnitAbilityLevel(GetFilterUnit(), 'Avul') <= 0 endfunction private constant function FinalDamage takes integer lvl returns real //* This just does the calculation for the spell's damage return BaseDamage+(DamagePerLevel*(lvl-1)) endfunction private constant function FragCount takes integer lvl returns integer //* This just does the calculation for the spell's fragmentation return Fragments+(FragmentsPerLevel*(lvl-1)) endfunction //********************************************************* //* Struct for the spell, I recommend leaving it alone private struct nade unit Dummy unit Caster effect DummyEffect integer Level boolean isPrimary real Speed real MaxHeight real DistanceCheck real DistanceTarget real Angle static method create takes unit u, real x, real y, integer level, real angle, real TotalDist, real max, real speed, boolean primary returns nade local nade n = nade.allocate() set n.Caster = u set n.Dummy = CreateUnit(GetOwningPlayer(u), DummyUnitID, x, y, angle) set n.DummyEffect = AddSpecialEffectTarget(DummyUnitEffect, n.Dummy, "origin") set n.DistanceCheck = TotalDist set n.DistanceTarget = TotalDist set n.isPrimary = primary set n.MaxHeight = max set n.Speed = speed set n.Angle = angle set n.Level = level call SetUnitFacing(n.Dummy, angle*57.29583) if IsUnitType(n.Dummy, UNIT_TYPE_GROUND) then call UnitAddAbility(n.Dummy, 'Amrf') call UnitRemoveAbility(n.Dummy, 'Amrf') endif return n endmethod private method onDestroy takes nothing returns nothing call DestroyEffect(AddSpecialEffect(ExplodeEffect, GetUnitX(this.Dummy), GetUnitY(this.Dummy))) call DestroyEffect(this.DummyEffect) call KillUnit(this.Dummy) endmethod endstruct //****************************************************************************** //* The spell code itself. private function Conditions takes nothing returns boolean return GetSpellAbilityId() == AbilityID endfunction private function Callback takes nothing returns nothing local unit u local unit d local unit s local nade n = 0 local nade m = 0 local integer i = Counter - 1 local integer j = 0 local integer lvl local group g = CreateGroup() local real xi local real yi local real xf local real yf local real z local real di1 local real di2 loop exitwhen i < 0 set n = Nades[i] set u = n.Caster set d = n.Dummy set di1 = n.DistanceCheck set di2 = n.DistanceTarget set lvl = n.Level set xi = GetUnitX(d) set yi = GetUnitY(d) if di1 <= 0 then set bj_meleeNearestMine = u call GroupEnumUnitsInRange(g, xi, yi, BlastAreaOfEffect, Boolexpr) loop set s = FirstOfGroup(g) exitwhen s == null call DestroyEffect(AddSpecialEffectTarget(DamageEffect, s, DamageLocal)) call UnitDamageTarget(u, s, FinalDamage(lvl), false, false, AttackType, DamageType, null) call GroupRemoveUnit(g, s) endloop call GroupClear(g) if n.isPrimary then loop exitwhen j >= FragCount(lvl) set m = nade.create(u, xi, yi, lvl, GetRandomReal(n.Angle-MomentumAngle,n.Angle+MomentumAngle), GetRandomReal(MinFragRange,MaxFragRange), GetRandomReal(MinBounceHeight, MaxBounceHeight), GetRandomReal(MinSpeed, MaxSpeed), false) set Nades[Counter] = m set Counter = Counter + 1 set j = j + 1 endloop endif call n.destroy() set Counter = Counter - 1 if Counter < 0 then call PauseTimer(Timer) set Counter = 0 else set Nades[i] = Nades[Counter] endif else set xf = xi + (di2/n.Speed) * Cos(n.Angle) set yf = yi + (di2/n.Speed) * Sin(n.Angle) set z = ((4*di1)/di2)*(1-(di1/di2))*n.MaxHeight set n.DistanceCheck = di1 - (di2/n.Speed) call SetUnitFacing(d, n.Angle*57.29583) call SetUnitFlyHeight(d, z, 0) call SetUnitX(d, xf) call SetUnitY(d, yf) endif set i = i - 1 endloop call GroupClear(g) call DestroyGroup(g) set g = null set u = null set d = null set s = null endfunction private function Actions takes nothing returns nothing local unit u = GetSpellAbilityUnit() local location l = GetSpellTargetLoc() local integer lvl = GetUnitAbilityLevel(u, AbilityID) local real xi = GetUnitX(u) local real yi = GetUnitY(u) local real xf = GetLocationX(l) local real yf = GetLocationY(l) local real an = Atan2(yf - yi, xf - xi) local real di = SquareRoot((xi-xf)*(xi-xf) + (yi-yf)*(yi-yf)) local nade n = 0 if di == 0 then set di = 1 set an = GetUnitFacing(u) endif set n = nade.create(u, xi, yi, lvl, an, di, InitialBounceHeight, BaseSpeed, true) if Counter == 0 then call TimerStart(Timer, TimerInterval, true, function Callback) endif set Nades[Counter] = n set Counter = Counter + 1 call RemoveLocation(l) set l = null set u = null endfunction //****************************************************************************** //* The InitTrig that breathes life into the husk that is my spell. public function InitTrig takes nothing returns nothing set Trig = CreateTrigger() set Boolexpr = Condition(function Check) call TriggerRegisterAnyUnitEventBJ(Trig, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(Trig, Condition(function Conditions)) call TriggerAddAction(Trig, function Actions) endfunction endscope Special Thanks: All credits to anything used in the spell are in the testmap, but I'll do it again here. There's really not much to say, thanks to Vex for vJass and JESP and his dummy model. Thanks to wc3c for another cool contest. Thanks to everyone who participated in the contest for being awesome. Thanks to whoever took the time to test and play with my spell... And thank you WC3C for being the coolest WC3 site evar. <3 |
| 03-19-2008, 01:28 AM | #2 |
Ok, some comments:
What else... no, no more... Fix this and I'll move it where it belongs. |
| 03-19-2008, 01:35 AM | #3 | ||
Quote:
Quote:
Oh, and I deleted your double post. |
| 03-19-2008, 03:02 AM | #4 | ||
Quote:
Quote:
I've tested with several heroes and such to ensure the MUI (no problems...), so... don't give to this baby more await. *approved* EDIT: I fixed my double post :P EDIT2: I forgot to say that I'm glad you've learned vJASS, keep the good work. |
| 03-19-2008, 02:55 PM | #5 | |
Quote:
|
| 03-19-2008, 10:31 PM | #6 |
JASS:function InitTrig_Shrapnade takes nothing returns nothing set gg_trg_Shrapnade = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(gg_trg_Shrapnade, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(gg_trg_Shrapnade, Condition(function Conditions)) call TriggerAddAction(gg_trg_Shrapnade, function Actions) endfunction Not to be picky, but for the sake of the user, who may already have a spell named Shrapnade, would this not be more appropriate? JASS:public function InitTrig 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) endfunction Then, changing the name of the spell is as easy as changing the scope name. If you really were hellbent on having the global, then something like: JASS:globals public trigger Trig endglobals public function InitTrig takes nothing returns nothing set Trig = CreateTrigger() call TriggerRegisterAnyUnitEventBJ(Trig, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(Trig, Condition(function Conditions)) call TriggerAddAction(Trig, function Actions) endfunction But really, that's a long way to go... And it's just a suggestion. |
| 03-19-2008, 10:50 PM | #7 |
I agree with you, but I know from personal experience that I always like to have that global trigger handy for use as necessary. I think it may be wiser to do it your way, though, for the sake of full copy/pasta of the spell across the map. I'll update momentarily. EDIT: Updated. |
| 06-06-2008, 04:09 AM | #9 |
I love this spell and quite nice terrain, keep up your good job ^^ |
