| 02-06-2009, 10:48 PM | #1 |
Requirements
As well the requirements for the systems listed above are in the test map and in the links provided. Devour://======================================================= // // Devour version 7 // by Av3n // Made for Power of Corruption by moyack // // Needs: // - JASSHelper to compile the vJass code // - LastOrder & AbortSpell libraries by Rising_Dusk // - TimerUtils (Any flavor) & ARGB by Vexorian // - Custom Bar System by Deaod // // Notes: // Since Devour uses "UnitDamageTarget" to deal damage to the target this function has been added: // " public function IsUnitDevoured takes unit who returns boolean " // So it's _kind of_ compatible with Damage Detect systems. // // All of the effects played during the spell can all be edited within the Object Editor. // // When the spell is cast the "Art - Area Effect" effect is played where the target was. // //======================================================= library Devour initializer InitSpell needs AbortSpell, TimerUtils, TTBARS // Configuration section globals private constant integer SpellID = 'A000' // Devour's spell rawcode, based on the Storm Bolt spell. (To find rawcodes, push Ctrl+D in the Object Editor). private constant integer SpellOrderId = 852095 // Devour's spell order id, use this tool: "http://www.wc3c.net/showthread.php?t=85864" // if you are converting an order string to an order id private constant string SpellErrorMessage = "You are already devouring a unit!" // Devour's spell error message when you are use the spell and the effects of Devour are still active. private constant boolean KillIfCasterDies = false // If the caster dies, the target dies with it. private constant attacktype AttackType = ATTACK_TYPE_CHAOS private constant damagetype DamageType = DAMAGE_TYPE_UNKNOWN // Devour's attacktype and damagetype combo for dealing damage. // Please refer to this post when making decisions or the combination // "http://wc3campaigns.net/showpost.php?p=1030046&postcount=19" private constant string EffectExecutionPoint = "chest" // Devour's effect attachment point on execution. This effect is related to the caster. // This effect is determined by the "Art - Effect" field in SpellID's object fields. private constant real EffectExecutionRate = 10 // Determines how often the "Art - Effect" is played, where: // GetRandomReal(0,1) < EffectExecutionRate*ExecutionRate private constant string EffectFinishPoint = "origin" // Devour's effect attachment point on the spell finish. This effect is related to the target. // This effect is determined by the "Art - Special" field in SpellID's object fields. private constant real CorpseDropRange = -100. // If the target dies via Devour, how far away from the from the caster is the corpse dropped. // Positive values to be in front of the caster, negative for behind the caster. private constant real ExecutionRate = 0.02 // Devour's timer execution rate. DamageDealt, ManaDrained and EffectExecutionRate is multiplied by this value // For DamageDealt and ManaDrained you would want to use a value totals up to 1. // For example 25*0.02 is .5, so .5 damage is dealt per execution // = Progress Bar Configuration = // private constant string BarChar = "|" // The Progress Bar's character (letter/symbol) which is used to make up the bar private constant integer NumberOfChars = 50 // The amount of BarChars is in a Progress Bar private constant real BarSize = 8. // The Progress Bar size private constant real VerticalOffset = -50. private constant real HeightOffset = -100. // The Progress Bar Height Offset private constant integer ForeGroundR = 255 private constant integer ForeGroundG = 0 private constant integer ForeGroundB = 0 private constant integer ForeGroundA = 0 // Colours/Alpha for the Foreground of the Progress Bar private constant integer BackGroundR = 200 private constant integer BackGroundG = 200 private constant integer BackGroundB = 200 private constant integer BackGroundA = 0 // Colours/Alpha for the Background of the Progress Bar // = End of Progress Bar Configuration = // endglobals private function DamageDealt takes unit caster returns real return 25.*GetUnitAbilityLevel(caster,SpellID) // How much damage is dealt. You can calculate how much is done per ExecutionRate by // DamageDealt(<caster>)*ExecutionRate endfunction private function ManaDrained takes unit caster returns real return 25.*GetUnitAbilityLevel(caster,SpellID) // How much mana is drained. You can calculate how much is done per ExecutionRate by // ManaDrained(<caster>)*ExecutionRate endfunction // End of Configuration section globals private group isDevouring private group isDevoured private string targetE private string exeE private string finishE endglobals // public function IsUnitDevoured takes unit who returns boolean return IsUnitInGroup(who,isDevoured) endfunction // private struct Devour static ARGB ForeGround static ARGB BackGround unit cast unit targ real dam real mana real lifeonCast texttag pb TTBar bar static method onLoop takes nothing returns nothing local Devour D = Devour(GetTimerData(GetExpiredTimer())) local real r = D.dam if GetWidgetLife(D.targ) > .405 and GetWidgetLife(D.cast) > .405 then call SetWidgetLife(D.cast,GetWidgetLife(D.cast)+D.dam) call UnitDamageTarget(D.cast,D.targ,D.dam,false,false,AttackType,DamageType,null) if GetUnitState(D.targ, UNIT_STATE_MAX_MANA) > 0. and GetUnitState(D.cast, UNIT_STATE_MANA) < GetUnitState(D.cast, UNIT_STATE_MAX_MANA) then set r = D.mana if GetUnitState(D.targ, UNIT_STATE_MANA) < D.mana then set r = GetUnitState(D.targ, UNIT_STATE_MANA) endif call SetUnitState(D.targ, UNIT_STATE_MANA, GetUnitState(D.targ, UNIT_STATE_MANA) - r) call SetUnitState(D.cast, UNIT_STATE_MANA, GetUnitState(D.cast, UNIT_STATE_MANA) + r) endif if GetRandomReal(0.,1.) < EffectExecutionRate*ExecutionRate then call DestroyEffect(AddSpecialEffectTarget(exeE,D.cast,EffectExecutionPoint)) endif set D.bar.Value=R2I((GetWidgetLife(D.targ)/D.lifeonCast*100.)+.5) else call D.destroy() call ReleaseTimer(GetExpiredTimer()) endif endmethod static method create takes nothing returns Devour local Devour a = Devour.allocate() local timer t = NewTimer() local integer lvl = GetUnitAbilityLevel(GetTriggerUnit(),SpellID) set a.cast = GetTriggerUnit() set a.targ = GetSpellTargetUnit() call DestroyEffect(AddSpecialEffect(targetE,GetUnitX(a.targ),GetUnitY(a.targ))) set a.dam = DamageDealt(a.cast)*ExecutionRate set a.mana = ManaDrained(a.cast)*ExecutionRate set a.pb = CreateTextTag() set a.lifeonCast = GetUnitState(a.targ,UNIT_STATE_LIFE) set a.bar = TTBar.create(BarChar,NumberOfChars,BarSize,GetUnitX(a.cast)+VerticalOffset,GetUnitY(a.cast),HeightOffset) call a.bar.LockToUnit(a.cast,VerticalOffset,0.,HeightOffset) call a.bar.SetForeground(Devour.ForeGround) call a.bar.SetBackground(Devour.BackGround) set a.bar.Value=100 call GroupAddUnit(isDevouring,a.cast) call ShowUnit(a.targ,false) call PauseUnit(a.targ,true) call GroupAddUnit(isDevoured,a.targ) call SetTimerData(t,integer(a)) call TimerStart(t,ExecutionRate,true,function Devour.onLoop) set t = null return a endmethod method onDestroy takes nothing returns nothing local real rad = GetUnitFacing(.cast)*bj_DEGTORAD if KillIfCasterDies and GetWidgetLife(.targ) > .405 then call KillUnit(.targ) endif call SetUnitPosition(.targ,GetUnitX(.cast)+Cos(rad)*CorpseDropRange,GetUnitY(.cast)+Sin(rad)*CorpseDropRange) call GroupRemoveUnit(isDevouring,.cast) call ShowUnit(.targ,true) call PauseUnit(.targ,false) call GroupRemoveUnit(isDevoured,.targ) call DestroyEffect(AddSpecialEffectTarget(finishE,.targ,EffectFinishPoint)) call .bar.destroy() endmethod endstruct // private function Conditions takes nothing returns boolean return GetSpellAbilityId() == SpellID endfunction private function OnOrder takes nothing returns boolean if IsUnitInGroup(GetTriggerUnit(),isDevouring) and GetIssuedOrderId() == SpellOrderId then call AbortSpell(GetTriggerUnit(),SpellErrorMessage,"") endif return false endfunction // private function InitSpell 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 Devour.create ) set t = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER ) call TriggerAddCondition(t, Condition( function OnOrder ) ) set isDevouring = CreateGroup() set isDevoured = CreateGroup() set targetE = GetAbilityEffectById(SpellID,EFFECT_TYPE_AREA_EFFECT,0) call Preload(targetE) set exeE = GetAbilityEffectById(SpellID,EFFECT_TYPE_EFFECT,0) call Preload(exeE) set finishE = GetAbilityEffectById(SpellID,EFFECT_TYPE_SPECIAL,0) call Preload(finishE) set Devour.ForeGround = ARGB.create(ForeGroundA,ForeGroundR,ForeGroundG,ForeGroundB) set Devour.BackGround = ARGB.create(BackGroundA,BackGroundR,BackGroundG,BackGroundB) endfunction endlibrary -Av3n |
| 02-07-2009, 01:23 AM | #2 |
lovely... but expect the critic. There's stuff to improve :) |
| 02-07-2009, 02:29 AM | #3 |
Yeah I know xD. I haven't even set up the thread yet, nor the map to even say it's ready to get moved. I haven't coded in awhile as well. Anything you want to point out? -Av3n |
| 02-08-2009, 10:37 PM | #4 |
Ok, my comments: for portability, set an initializer function and make that function private: JASS:library Devour initializer init needs SimError Make the variable trigger as local. You can use only one EFFECT_TYPE instead separate ones, it gives to the user one place to edit the effects. You can use a more accurate event to detect if a unit is going to cast the spell and allow you to reuse the Conditions function to detect the spell cast. Change the onOrder by returning nothing so it can be an action. See those changes here. JASS:private function OnOrder takes nothing returns nothing local unit u = GetTriggerUnit() if IsUnitInGroup(u,isDevouring) then call SimError(GetOwningPlayer(u),SpellErrorMessage) call PauseUnit(u,true) call IssueImmediateOrder(u,"stop") call PauseUnit(u,false) endif set u = 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 Devour.create ) set t = CreateTrigger( ) call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_CAST ) call TriggerAddCondition( t, Condition( function Conditions ) ) call TriggerAddAction( t, function OnOrder ) set isDevouring = CreateGroup() set isDevoured = CreateGroup() set devourTimer = CreateTimer() set index = -1 set Devour.onLoopFunc = function Devour.onLoop set targetE = GetAbilityEffectById(SpellID,EFFECT_TYPE_SPECIAL,0) set exeE = GetAbilityEffectById(SpellID,EFFECT_TYPE_SPECIAL,1) set finishE = GetAbilityEffectById(SpellID,EFFECT_TYPE_SPECIAL,2) call Preload(targetE) call Preload(exeE) call Preload(finishE) set t = null endfunction Well, it seems you're using one timer to manage all the spells, that's not bad, but... because this spell is focused for heroes, doing that is exaggerated, because it's less chances of being cast respect a custom unit ability. I suggest to make it dependable of TimerUtils and use a timer per spell cast., which means that the timer variable will say bye bye. WAit for more comments... on MSN |
| 02-09-2009, 01:52 AM | #5 |
Yeah I was thinking TimerUtils would be better for the execution accuracy and kills off 2+ vars and the indexing stuff. -Av3n |
| 02-10-2009, 06:23 AM | #6 |
I'm keeping the way I done the effects since it just that much easier object editor wise. Switched to TimerUtils for accuracy over performance (Well not really if you use Red Flavor). -Av3n |
| 02-18-2009, 11:03 AM | #7 |
Moved so people can give feedback. |
| 02-18-2009, 11:50 AM | #8 | |
I think one timer method is always better for periodic stuff, although in this case it maybe isn't because you're using such long periods; why do so, though? It's much more intuitive to define it in damage per second rather than damage per x seconds, especially since the original devour ability also uses dps... For cancelling the spell, you should use the order event. When cancelling the spell if the unit is already full, why press the hotkey? This is wrong: call SetWidgetLife(D.cast,GetWidgetLife(D.cast)+(D.dam-GetWidgetLife(D.targ))) So is the mana part. Quote:
|
| 02-18-2009, 12:14 PM | #9 | |
Quote:
|
| 02-18-2009, 01:51 PM | #10 |
The idea of re-making a warcraft ability that perhaps could've been done better by Blizzard themselves is nice, but there is no way I'm going to ever use a spell that requires 4 additional libraries to import. Thats luggage. |
| 02-18-2009, 02:32 PM | #11 |
It requires 3, and as far as I'm concerned TimerUtils should be used in any map anyways. LastOrder/AbortSpell should be in any map that needs custom spell error messages. That it requires these three libraries is not extra luggage, it is a sensible necessity for a spell such as this. |
| 02-18-2009, 02:51 PM | #12 |
LastOrder requires another library behind it to support it, so thats 4. Also, TimerUtils isn't something that all maps should have, there are quite a few alternatives. Y'know, pretty soon you're not going to be able to go to wc3campaigns.net and download a spell without also downloading several other systems. This isn't very good for uniting the wc3 community, but who says thats at all what anybody here wants to do. I just pulled this out of the Triggers forum: http://www.wc3c.net/showthread.php?t=104584 //Requires: // - TimerUtils // - Table // - ABuff // - ADamage // - SimError // - LastOrder // - AbortSpell What happens if something goes wrong, ever? Your map is entirely gone to shit because there is no way in hell you're going to be debugging 7+ "helpers" and then your own code. |
| 02-18-2009, 03:18 PM | #13 | ||
Quote:
Secondly, TimerUtils is as close to a timer handling standard as we have. If you don't use it in your maps, it is your own fault and your own loss. It is the best way to handle timers (recycling) and offers different flavors (at no compatibility penalty to the user) for user preference. Thirdly, there are no compatibility errors between Table, TimerUtils, SimError, LastOrder, AbortSpell, or Anitarf's systems. We go to extreme lengths to ensure that users don't face compatibility problems when we approve scripts. If there are any compatibility dilemmas that may arise, we require users specifically list them in the first post. (UnitUserData for example) At that point, if the user still experiences them, it is their own fault. Having many libraries is the point of libraries. They exist to be used, in unison and as necessary, for the abstraction of common or repetitive code within maps. I personally use something like 21 libraries from the database for one of my current maps and experience zero conflicts or troubles. I even find use for those libraries in many of my spells, making them that much more valuable. Quote:
|
| 02-18-2009, 05:05 PM | #14 | |
I think what he's trying to get at is with so many systems, debugging can become a shit not because of cross-system conflicts, but because you don't know what system is causing the problem in the first place. Quote:
ONTOPIC OMG: I highly suggest a "progress" bar, since the original devour showed the unit's health so you knew how much longer it had to go. I think it'd add a nice touch. |
| 02-18-2009, 05:28 PM | #15 |
Well, to finish this offtopic: systems posted here have something called support and hard review, and if any submitter has a resource in the database, it has to give support to that spell/system/sample. So... if users have problems with a system they must: 1. Check if they have a bug in their code and not in the system. 2. Post the issue in the resource thread so the script developer can check this problem. So in general, using other systems makes your modding faster and supported. If I see other comment offtopic, it will be deleted. On topic: Devour.onLoopFunc variable can be avoided... in fact I dont' understand how using it you can keep it organized. |
