| 06-21-2007, 02:47 AM | #1 | ||||
Mana Shot and Spirit Orb v1.30
Here are two spells I made as requests from people at TheHelper.net. As such, neither of the ideas for these spells was my own concoction; each person dictated how the spell was to function. Both spells comply with the JESP standard. ![]() Spell Descriptions:
Because I use Mac OS X, my code tends to get all funky when opened in the World Editor; to rectify this, simply use the attached text files in substitution of the code in the map. Alternatively, you can copy-paste the code below: Mana Shot://********************************************************************* //* Spell Name: Mana Shot * //* Spell Author: Pyrogasm * // Follows the JESP Standard * //********************************************************************* constant function ManaShot_AbilityId takes nothing returns integer return 'A001' //Rawcode of the main ability. endfunction constant function ManaShot_Damage takes integer Level, real CasterMana returns real return (0.05*Level)*CasterMana //How much damage the spell should deal; this function endfunction //Takes the caster's mana as an additional argument constant function ManaShot_Distance takes integer Level returns real return 700.00 //The total distance the cone should travel forward. endfunction constant function ManaShot_EndWidth takes integer Level returns real return 475.00 //The ending width of the cone. endfunction constant function ManaShot_CircleOffset takes integer Level returns real return 15.00 //How far in the direction of the caster's facing the circle will be offset. endfunction //The circle ensures that units that aren't directly in the way of the spell //But are still near enough that they should get hit will get hit. constant function ManaShot_CircleRadius takes integer Level returns real return 22.00 //Radius of the aforementioned circle. endfunction constant function ManaShot_MissileSpeed takes nothing returns real return 1100.00 //The speed of the cone missle. endfunction //This is to make sure units get damaged at the correct time. constant function ManaShot_NumberOfDamageGroups takes integer Level returns integer return 8 //Number of groups that the damaged units will be divided in to estimate when endfunction //They should take damage. High number = more precise; low number = less precise. constant function ManaShot_DamageEffectPath takes nothing returns string return "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareBoltImpact.mdl" endfunction //The effect to be played on damaged units. constant function ManaShot_DamageEffectAttach takes nothing returns string return "origin" endfunction //The attachment point for the above effect. //************************************************** //*************Start External Functions************* //************************************************** function AngleBetweenPointsXY takes real X1, real Y1, real X2, real Y2 returns real return Atan2((Y2-Y1),(X2-X1))*57.295827 endfunction function Angles_IsAngleBetweenAngles takes real angle, real angle1, real angle2 returns boolean local real x set angle = ModuloReal(angle,360) set angle1 = ModuloReal(angle1,360) set angle2 = ModuloReal(angle2,360) if (angle1>angle2) then set x=angle1 set angle1=angle2 set angle2=x endif if (angle2-angle1)>(angle1 - (angle2-360)) then set angle2=angle2-360 if angle > 180 then set angle=angle-360 endif return angle>=angle2 and angle<=angle1 endif return (angle>=angle1) and (angle<=angle2) endfunction //*************************************************** //*************End of External Functions************* //*************************************************** function ManaShot_CastConditions takes nothing returns boolean return GetSpellAbilityId() == ManaShot_AbilityId() endfunction function ManaShot_AngleEnemyFilter takes nothing returns boolean return Angles_IsAngleBetweenAngles(AngleBetweenPointsXY(bj_meleeNearestMineDist, bj_randomSubGroupChance, GetUnitX(GetFilterUnit()), GetUnitY(GetFilterUnit())), bj_enumDestructableRadius, bj_lastTransmissionDuration) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false) endfunction function ManaShot_EnemyFilter takes nothing returns boolean return IsUnitEnemy(GetFilterUnit(), bj_forceRandomCurrentPick) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false) endfunction function ManaShot_DamageCallbackNew takes nothing returns nothing local timer T = GetExpiredTimer() local unit Damager = GetAttachedUnit(T, "ManaShot Damager") local group DamageGroup = GetAttachedGroup(T, "ManaShot Damage Group") local real Damage = GetAttachedReal(T, "ManaShot Damage") local integer Countup = GetAttachedInt(T, "ManaShot Countup") local integer TotalIterations = GetAttachedInt(T, "ManaShot Total Iterations") local unit U local group G = CreateGroup() local real X = GetAttachedReal(T, "ManaShot X") local real Y = GetAttachedReal(T, "ManaShot Y") local real X2 local real Y2 local real DamageDistanceIncrement = GetAttachedReal(T, "ManaShot Damage Distance Increment") set Countup = Countup+1 if Countup > TotalIterations then call DestroyGroup(DamageGroup) call PauseTimer(T) call CleanAttachedVars(T) call DestroyTimer(T) else call AttachInt(T, "ManaShot Countup", Countup) set DamageDistanceIncrement = (DamageDistanceIncrement*Countup)*(DamageDistanceIncrement*Countup) call GroupAddGroup(DamageGroup, G) loop set U = FirstOfGroup(G) exitwhen U == null set X2 = GetUnitX(U) set Y2 = GetUnitY(U) if (X-X2)*(X-X2)+(Y-Y2)*(Y-Y2) <= DamageDistanceIncrement then call UnitDamageTarget(Damager, U, Damage, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS) call DestroyEffect(AddSpecialEffectTarget(ManaShot_DamageEffectPath(), U, ManaShot_DamageEffectAttach())) call GroupRemoveUnit(DamageGroup, U) endif call GroupRemoveUnit(G, U) endloop endif call DestroyGroup(G) set T = null set G = null set DamageGroup = null set Damager = null endfunction function ManaShot_Cast takes nothing returns nothing local unit U = GetTriggerUnit() local unit U2 local integer Level = GetUnitAbilityLevel(U, ManaShot_AbilityId()) local real X1 = GetUnitX(U) local real Y1 = GetUnitY(U) local real TX local real TY local location L = GetSpellTargetLoc() local real Distance = ManaShot_Distance(Level) local real Width = ManaShot_EndWidth(Level) local real TargAngle local real MaxAngle local real AngleAdjust = Atan((Width/2)/Distance)*57.295827 local group TotalGroup = CreateGroup() local real Offset = ManaShot_CircleOffset(Level) local integer DamageDivisions = ManaShot_NumberOfDamageGroups(Level) local player P = GetOwningPlayer(U) local real DamageDistanceIncrement = Distance/DamageDivisions local real DamageDelay = DamageDistanceIncrement/ManaShot_MissileSpeed() local real Damage = ManaShot_Damage(Level, GetUnitState(U, UNIT_STATE_MANA)) local timer T = CreateTimer() local group CircleGroup = CreateGroup() local boolexpr B = Condition(function ManaShot_AngleEnemyFilter) if L == null then set L = GetUnitLoc(GetSpellTargetUnit()) endif set TX = GetLocationX(L) set TY = GetLocationY(L) call RemoveLocation(L) set TargAngle = AngleBetweenPointsXY(X1, Y1, TX, TY) set bj_enumDestructableRadius = TargAngle-AngleAdjust set bj_lastTransmissionDuration = TargAngle+AngleAdjust set bj_meleeNearestMineDist = X1 set bj_randomSubGroupChance = Y1 call GroupEnumUnitsInRange(TotalGroup, X1, Y1, Distance, B) call DestroyBoolExpr(B) set bj_forceRandomCurrentPick = P set TX = X1 + Offset * Cos(TargAngle*0.0174532) set TY = Y1 + Offset * Sin(TargAngle*0.0174532) set B = Condition(function ManaShot_EnemyFilter) call GroupEnumUnitsInRange(CircleGroup, TX, TY, ManaShot_CircleRadius(Level), B) call GroupAddGroup(CircleGroup, TotalGroup) call DestroyGroup(CircleGroup) call DestroyBoolExpr(B) set bj_forceRandomCurrentPick = null call AttachObject(T, "ManaShot Damage Group", TotalGroup) call AttachObject(T, "ManaShot Damager", U) call AttachReal(T, "ManaShot Damage", Damage) call AttachInt(T, "ManaShot Countup", 0) call AttachInt(T, "ManaShot Total Iterations", DamageDivisions) call AttachReal(T, "ManaShot X", X1) call AttachReal(T, "ManaShot Y", Y1) call AttachReal(T, "ManaShot Damage Distance Increment", DamageDistanceIncrement) call TimerStart(T, DamageDelay, true, function ManaShot_DamageCallbackNew) set B = null set U = null set U2 = null set L = null set TotalGroup = null set P = null set CircleGroup = null endfunction //=========================================================================== function InitTrig_Mana_Shot takes nothing returns nothing set gg_trg_Mana_Shot = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ(gg_trg_Mana_Shot, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(gg_trg_Mana_Shot, Condition(function ManaShot_CastConditions)) call TriggerAddAction(gg_trg_Mana_Shot, function ManaShot_Cast) endfunction Spirit Orb://********************************************************************* //* Spell Name: Spirit Orb * //* Spell Author: Pyrogasm * // Follows the JESP Standard * //********************************************************************* constant function SO_AbilityId takes nothing returns integer return 'A000' //Rawcode of the main ability. endfunction constant function SO_SFXUnitId takes nothing returns integer return 'h002' //Rawcode of the unit that will be killed every interval. endfunction //To not destroy a unit, simply make this function return 0. constant function SO_WardUnitId takes nothing returns integer return 'h002' //Rawcode of the unit that will be used as the ward; endfunction //This unit may be moveable/selectable. constant function SO_DummyId takes nothing returns integer return 'h000' //Rawcode of your generic dummy unit; this endfunction //Unit MUST NOT have negative regeneration. constant function SO_StealManaLightningAbilityId takes nothing returns integer return 'A004' //Rawcode of the "Spirit Orb Lightning (Drain Mana)" ability. endfunction constant function SO_StealBothLightningAbilityId takes nothing returns integer return 'A002' //Rawcode of the "Spirit Orb Lightning (Drain Both)" ability. endfunction constant function SO_GiveLightningAbilityId takes nothing returns integer return 'A003' //Rawcode of the "Spirit Orb Give Lightning" ability. endfunction constant function SO_GroundEffectPath takes nothing returns string return "Abilities\\Spells\\Human\\Flamestrike\\FlameStrikeTarget.mdl" endfunction //The effect to be played on the ground during the duration of the spell. constant function SO_StealEffectPath takes nothing returns string return "Abilities\\Spells\\NightElf\\ManaBurn\\ManaBurnTarget.mdl" endfunction //The effect to be played on a unit that has its mana stolen by the orb. constant function SO_StealEffectAttach takes nothing returns string return "chest" //The attachment point for the above effect. endfunction constant function SO_GiveEffectPath takes nothing returns string return "Abilities\\Spells\\Items\\AIma\\AImaTarget.mdl" endfunction //The effect to be played on a unit that is given mana from the orb. constant function SO_GiveEffectAttach takes nothing returns string return "chest" endfunction //The attachment point for the above effect. constant function SO_MinimumRange takes integer Level returns real return (160.00 + 35.00*Level) //The minimum range a unit must be in to receive endfunction //Full effect of the spell. constant function SO_MaxRange takes integer Level returns real return (200.00 + 125.00*Level) //The maximum range a unit must be in to endfunction //Be targeted by the spell. constant function SO_StealInterval takes integer Level returns real return 1.00 //The interval at which the orb will steal mana from units. endfunction //This is in seconds. constant function SO_TotalDuration takes integer Level, real StealInterval returns real return StealInterval*5.00 //The total duration of the spell; this function takes endfunction //The Steal Interval as an additional argument constant function SO_MaxStolenMana takes integer Level returns real return 80.00 //The maximum amount of mana that can be stolen from a unit endfunction constant function SO_MinStolenMana takes integer Level returns real return 20.00 //The minimum amount of mana that can be stolen from a unit endfunction //**************************************************************************** //********************End of Configuration Functions************************* //**************************************************************************** function SO_EnemyFilter takes nothing returns boolean return (IsUnitEnemy(GetFilterUnit(), bj_forceRandomCurrentPick) and GetUnitState(GetFilterUnit(), UNIT_STATE_MAX_MANA) > 0.00) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false) endfunction function SO_AllyFilter takes nothing returns boolean return (IsUnitAlly(GetFilterUnit(), bj_forceRandomCurrentPick) and GetUnitState(GetFilterUnit(), UNIT_STATE_MAX_MANA) > 0.00) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false) endfunction function SO_CastConditions takes nothing returns boolean return GetSpellAbilityId() == SO_AbilityId() endfunction function SO_ManaStealCallback takes nothing returns nothing local timer T = GetExpiredTimer() local unit Ward = GetAttachedUnit(T, "SO Ward") local real X = GetUnitX(Ward) local real Y = GetUnitY(Ward) local integer Level = GetAttachedInt(T, "SO Level") local group G local unit U local unit U2 local real MaxStolenMana = SO_MaxStolenMana(Level) local real MinStolenMana = SO_MinStolenMana(Level) local real StolenMana local real X2 local real Y2 local real D local real MinRange = SO_MinimumRange(Level) local real MaxRange = SO_MaxRange(Level) local real Mana local real UnitMaxMana local real ManaGiven = 0.00 local real BurnedMana = 0.00 local real BurnedManaEx = 0.00 local texttag ManaBurnText local real GivenFactor = (MaxStolenMana-MinStolenMana)/(MaxRange-MinRange) local integer DummyAbilityId = SO_StealManaLightningAbilityId() local unit Dummy local integer SFXUnitId if GetWidgetLife(Ward) < 0.406 then call DestroyEffect(GetAttachedEffect(T, "SO Ground Effect")) call PauseTimer(T) call CleanAttachedVars(T) call DestroyTimer(T) else set G = CreateGroup() set SFXUnitId = SO_SFXUnitId() if SFXUnitId > 0 then set U = CreateUnit(GetOwningPlayer(Ward), SFXUnitId, X, Y, 0) //*****OPTIONAL***** call SetUnitTimeScale(U, 1.50) call KillUnit(U) //*****END OPTIONAL***** endif set bj_forceRandomCurrentPick = GetOwningPlayer(Ward) call GroupEnumUnitsInRange(G, X, Y, MaxRange, Condition(function SO_EnemyFilter)) set U = GroupPickRandomUnit(G) call GroupClear(G) if U != null then set Mana = GetUnitState(U, UNIT_STATE_MANA) set X2 = GetUnitX(U) set Y2 = GetUnitY(U) set D = (X-X2)*(X-X2)+(Y-Y2)*(Y-Y2) if D > MinRange*MinRange then set StolenMana = MinStolenMana + ((MaxStolenMana*(D-MinRange))/MaxRange) else set StolenMana = MaxStolenMana endif set Mana = Mana-StolenMana if Mana < 0.00 then set StolenMana = StolenMana+Mana endif call SetUnitState(U, UNIT_STATE_MANA, Mana) call GroupEnumUnitsInRange(G, X, Y, MaxRange, Condition(function SO_AllyFilter)) set U2 = GroupPickRandomUnit(G) if U2 != null then set UnitMaxMana = GetUnitState(U2, UNIT_STATE_MAX_MANA) set Mana = GetUnitState(U2, UNIT_STATE_MANA) set ManaGiven = StolenMana set X2 = GetUnitX(U2) set Y2 = GetUnitY(U2) set D = (X-X2)*(X-X2)+(Y-Y2)*(Y-Y2) if D > MinRange*MinRange then set D = (D-MinRange)*GivenFactor if D < ManaGiven then set BurnedManaEx = ManaGiven-D set ManaGiven = D endif endif if Mana+ManaGiven > UnitMaxMana then set ManaGiven = UnitMaxMana - Mana endif call SetUnitState(U2, UNIT_STATE_MANA, Mana+ManaGiven) else set BurnedManaEx = StolenMana endif set BurnedMana = StolenMana-ManaGiven+BurnedManaEx if BurnedMana > 0.00 then set DummyAbilityId = SO_StealBothLightningAbilityId() call UnitDamageTarget(Ward, U, BurnedMana, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS) set ManaBurnText = CreateTextTag() call SetTextTagPos(ManaBurnText, GetUnitX(U), GetUnitY(U), 0.00) call SetTextTagText(ManaBurnText, "-"+I2S(R2I(BurnedMana)), TextTagSize2Height(10.00)) call SetTextTagVelocity(ManaBurnText, 0, 0.04) call SetTextTagColor(ManaBurnText, 82, 82, 255, 255) call SetTextTagPermanent(ManaBurnText, false) call SetTextTagLifespan(ManaBurnText, 5.00) call SetTextTagFadepoint(ManaBurnText, 2.00) call SetTextTagVisibility(ManaBurnText, true) if U2 != null then set ManaBurnText = CreateTextTag() call SetTextTagPos(ManaBurnText, X2, Y2, 0.00) call SetTextTagText(ManaBurnText, "+"+I2S(R2I(ManaGiven)), TextTagSize2Height(10.00)) call SetTextTagVelocity(ManaBurnText, 0, 0.04) call SetTextTagColor(ManaBurnText, 82, 82, 255, 255) call SetTextTagPermanent(ManaBurnText, false) call SetTextTagLifespan(ManaBurnText, 5.00) call SetTextTagFadepoint(ManaBurnText, 2.00) call SetTextTagVisibility(ManaBurnText, true) call DestroyEffect(AddSpecialEffectTarget(SO_GiveEffectPath(), U2, SO_GiveEffectAttach())) set Dummy = CreateUnit(GetOwningPlayer(Ward), SO_DummyId(), X, Y, 0.00) call UnitAddAbility(Dummy, SO_GiveLightningAbilityId()) call IssueTargetOrder(Dummy, "fingerofdeath", U2) call UnitApplyTimedLife(Dummy, 'BTLF', 2.00) set U2 = null set ManaBurnText = null endif endif call DestroyEffect(AddSpecialEffectTarget(SO_StealEffectPath(), U, SO_StealEffectAttach())) set Dummy = CreateUnit(GetOwningPlayer(Ward), SO_DummyId(), X, Y, 0.00) call UnitAddAbility(Dummy, DummyAbilityId) call IssueTargetOrder(Dummy, "fingerofdeath", U) call UnitApplyTimedLife(Dummy, 'BTLF', 2.00) set Dummy = null set U = null endif call DestroyGroup(G) set G = null endif set T = null set Ward = null endfunction function SO_Cast takes nothing returns nothing local unit U = GetTriggerUnit() local location L = GetSpellTargetLoc() local real X = GetLocationX(L) local real Y = GetLocationY(L) local player P = GetOwningPlayer(U) local unit Ward = CreateUnit(P, SO_WardUnitId(), X, Y, 0) local timer T = CreateTimer() local integer Level = GetUnitAbilityLevel(U, SO_AbilityId()) local real Interval = SO_StealInterval(Level) call RemoveLocation(L) call UnitApplyTimedLife(Ward, 'BTLF', SO_TotalDuration(Level, Interval)+0.05) //To make sure everything runs on the last iteration call AttachObject(T, "SO Ward", Ward) call AttachInt(T, "SO Level", GetUnitAbilityLevel(U, SO_AbilityId())) call AttachObject(T, "SO Ground Effect", AddSpecialEffect(SO_GroundEffectPath(), X, Y)) call TimerStart(T, Interval, true, function SO_ManaStealCallback) set L = null set Ward = null set T = null set U = null endfunction //=========================================================================== function InitTrig_Spirit_Orb takes nothing returns nothing set gg_trg_Spirit_Orb = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ(gg_trg_Spirit_Orb, EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(gg_trg_Spirit_Orb, Condition(function SO_CastConditions)) call TriggerAddAction(gg_trg_Spirit_Orb, function SO_Cast) endfunction |
| 06-23-2007, 08:15 AM | #2 |
Erm... bump? |
| 06-23-2007, 08:55 AM | #3 |
It's been two days. The site has just come back. People are busy. The code is long and requires major review because a large portion appears useless. |
| 06-23-2007, 09:36 AM | #4 |
I wasn't bumping to get it reviewed; I was bumping to get it in the "New Posts" section to rouse some people who like to play around with stuff and find bugs/problems. It wasn't getting much activity. Oh well. EDIT: What part exactly "appears useless"? |
| 06-23-2007, 10:24 AM | #5 |
You do a whole lot of triangle stuff for grouping when you could simply get a sector (ie cone) by getting all units in X range of the caster and filter them by a limited angle range. |
| 06-23-2007, 10:33 AM | #6 |
Oh snap. I'll get that fixed up as soon as possible, probably tomorrow evening. |
| 07-01-2007, 10:46 PM | #7 |
I took the freedom to modify the test map so Windows users can see the code without problem. Some comments:
For other things, I think it's ok. Because you are using many timers, why not add CSSafety to your spells??, with that you can make your timer usage more efficient. This is the code: JASS://****************************************************************************************** //* //* CSSafety 14.0 //* ¯¯¯¯¯¯¯¯ //* //* Utilities to make things safer. Currently this simply includes a timer recycling //* Stack. Once you replace CreateTimer with NewTimer and DestroyTimer with ReleaseTimer //* you no longer have to care about setting timers to null nor about timer related issues //* with the handle index stack. //* //****************************************************************************************** //library CSSafety //========================================================================================== globals timer array cs_timers integer cs_timern = 0 endglobals //========================================================================================== function NewTimer takes nothing returns timer if (cs_timern==0) then return CreateTimer() endif set cs_timern=cs_timern-1 return cs_timers[cs_timern] endfunction //========================================================================================== function ReleaseTimer takes timer t returns nothing call PauseTimer(t) if (cs_timern==8191) then debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!") //stack is full, the map already has much more troubles than the chance of bug call DestroyTimer(t) else set cs_timers[cs_timern]=t set cs_timern=cs_timern+1 endif endfunction //endlibrary You replace the command CreateTimer() by NewTimer() and you don't have destroy timers anymore, even pause them, just call the command ReleaseTimer(t) and that's all :) |
| 07-02-2007, 06:56 AM | #8 |
Ok; I'll add in the CSSaftey functions. I used multiple timers because I hadn't thought of a way to get an undetermined number of groups that are attached to a timer, but now I've figured it out. I'm going to have to prefix all the CSSafety functions beacause I must change the variable names (I can't use freeform globals); is that OK? Screenshots will come... I'm still on dialup, but I think I can manage the upload. About what blu said, what are your thoughts? If I use a sector like he said, then I have units that are technically outside of that triangle but still in the arc. Thank you for converting the map to a windows-readable format; but you're going to have to do it again after I upload a new version. |
| 07-11-2007, 01:43 PM | #9 |
I don't see the advantage to CSSafety in a spell submission. Less things to add to your map for implementation the better, and anyone that uses CSSafety on their own and wants to use this spell would know very easily how to modify it to their desires. I say leave it out. Going through Spirit Orb first... JASS:constant function SO_MinimumRange takes integer Level returns real return (320.00 + 70.00*Level)/2 //Divide by 2 to get a radius endfunction constant function SO_MaxRange takes integer Level returns real return (400.00 + 250.00*Level)/2 //Divide by 2 to get a radius endfunction It could get confusing for noob importers and they might remove it accidentally and screw things up. JASS://**************************************************************************** //*****************Blade.dk's Mobile Lightning Functions********************** //**************************************************************************** function AddLightningFromUnitToUnitTimed_Child takes nothing returns nothing local timer t = GetExpiredTimer() local unit from = GetAttachedUnit(t, "from") local unit to = GetAttachedUnit(t, "to") local lightning bolt = GetAttachedLightning(t, "bolt") local real z1 = GetUnitFlyHeight(from) local real z2 = GetUnitFlyHeight(to) if z1 == 0 then set z1 = 60 endif if z2 == 0 then set z2 = 60 endif call MoveLightningEx(bolt, GetAttachedBoolean(t, "checkVisibility"), GetUnitX(from), GetUnitY(from), z1, GetUnitX(to), GetUnitY(to), z2) call AttachReal(t, "timeelapsed", GetAttachedReal(t, "timeelapsed")+0.01) if ((GetAttachedReal(t, "timeelapsed") > GetAttachedReal(t, "duration")) or (GetUnitState(from, UNIT_STATE_LIFE) <= 0) or (GetUnitState(to, UNIT_STATE_LIFE) <= 0)) then call CleanAttachedVars(t) call DestroyTimer(t) call DestroyLightning(bolt) endif set t = null set from = null set to = null set bolt = null endfunction function AddLightningFromUnitToUnitTimed takes string codeName, boolean checkVisibility, unit from, unit to, real duration returns lightning local timer t = CreateTimer() local lightning bolt local real z1 = GetUnitFlyHeight(from) local real z2 = GetUnitFlyHeight(to) if z1 == 0 then set z1 = 60 endif if z2 == 0 then set z2 = 60 endif set bolt = AddLightningEx(codeName, checkVisibility, GetUnitX(from), GetUnitY(from), z1, GetUnitX(to), GetUnitY(to), z2) call AttachReal(t, "duration", duration) call AttachObject(t, "from", from) call AttachObject(t, "to", to) call AttachBoolean(t, "checkVisibility", checkVisibility) call AttachObject(t, "bolt", bolt) call TimerStart(t, 0.01, true, function AddLightningFromUnitToUnitTimed_Child) return bolt endfunction //*********************************************************************************** //*****************End of Blade.dk's Mobile Lightning Functions********************** //*********************************************************************************** They are inaccurate and will never perfectly reflect the flying heights of flying units due to WC3 fly height smoothing. I recommend changing the ENTIRE system to using a Finger of Death dummy ability between the units. It works much better, is cleaner, and removes itself after the OE specified duration. That means no need for attachment or timers or any of that stuff. JASS:set U = CreateUnit(GetOwningPlayer(Ward), SO_SFXUnitId(), X, Y, 0) //*****OPTIONAL***** call SetUnitTimeScale(U, 1.50) call KillUnit(U) set U = null //*****END OPTIONAL***** set bj_forceRandomCurrentPick = GetOwningPlayer(Ward) call GroupEnumUnitsInRange(G, X, Y, MaxRange, Condition(function SO_EnemyFilter)) set U = GroupPickRandomUnit(G) call DestroyGroup(G) Setting them to a new value does the same, exact thing as nullifying. JASS:set D = SquareRoot((X-X2)*(X-X2)+(Y-Y2)*(Y-Y2)) if D > MinRange then set D = (D-MinRange)*GivenFactor if D < ManaGiven then set BurnedManaEx = ManaGiven-D set ManaGiven = D endif endif Square root is a VERY slow function for any programming language, and in distance comparisons it's easily avoided. Blu will appreciate me telling you to do this. :p ============================================================ And now Mana Shot... All of Blu's notes about grouping the triangle stuff is true. It's a lot less work, accurate enough, and saves like 40-60 LoC. I say do it. JASS:set X2 = X1 + Hypotenuse * Cos((TargAngle-Angle)*bj_DEGTORAD) set Y2 = Y1 + Hypotenuse * Sin((TargAngle-Angle)*bj_DEGTORAD) set X3 = X1 + Hypotenuse * Cos((TargAngle+Angle)*bj_DEGTORAD) set Y3 = Y1 + Hypotenuse * Sin((TargAngle+Angle)*bj_DEGTORAD) You do a lot of excess work and use a hell of a lot of unnecessary timers to make it appear as if the energy is moving in a "wave" through the cone. Why not use a single periodic timer for the entire thing instead of like 8-12 single shot timers? That would improve performance tremendously. You also would have to attach less. Just attach the whole group of units in the cone to the single timer, then do the square of the distance check inside the timer callback to pick who gets hit. Then remove units from the group that have already been hit, so they don't get hit again. Simple, easy, saves like 30 LoC, and saves the use of 99% of your timers and groups. And yeah, a screenshot. One screenshot with both spells being shown would be preferable. |
| 07-11-2007, 07:23 PM | #10 |
Sorry, I've been lazy about fixing these up, but I'll get on it right away. Thanks for the review, Rising_Dusk. |
| 07-12-2007, 12:17 AM | #11 |
Fixed and updated. I did everything but use the CSSafety functions and remove the GroupAddUnitsInTriangle() stuff. The reason I kept the triangle is because I would have had to do trigonometry anyway to get the sector of the circle when given the length of the end of the triangle as input (in the constants); it didn't seem like the greatest tradeoff to me. |
| 07-12-2007, 12:30 AM | #12 | |
Quote:
I think that's plenty of a trade off. I'll review it again later. |
| 07-12-2007, 12:46 AM | #13 |
Dur again. I was only thinking about my code/calculations, not the GroupAddUnitsInTriangle() one, as that's where most of the calculations lay. In that case, hold out on reviewing this 'till I get back from seeing Harry Potter this evening and fix that code. Sorry for being so stubborn >< |
| 07-17-2007, 05:29 PM | #14 |
Mana Shot still hasn't been updated to stop using the triangle group stuff. Spirit Orb:
|
| 07-27-2007, 02:51 AM | #15 |
Fix'd everything. No longer uses funky triangulation, and all the other mentioned things have been fixed. |
