| 12-01-2009, 05:31 PM | #1 |
Hello everyone. Lately I tried to make some spells, which are worth submitting here. However, since I have no idea, if my spells can compare with the quality of spells already submitted (which I doubt somehow), I decided to play safe and post the spell here at first to maybe get some opinions, like how it is coded, how it looks like, what to improve to be worth of submitting. I am kind of unconfident with my code, somehow it tends to look strange to me when compared to other mappers. Also I think my comments lack a bit. Also I did not really know where to but this spell, since I made it in zinc, and currently there are spells and vjass spells sections, but when i wanted to submit this regularly where to put zinc spells there (how to submit a spell to the database anyway? Just post it in Spells thread?)? Note that I am still experimenting with zinc and maybe there are things which can be done better using features of zinc, which I dont know yet. The spell itself is not very spectacular or most original, but not too boring either, I think. Just have a look at he test map ;) Anyway, feedback would be very appreciated, it would be nice if someone takes the time to look through my code or to have a look at the spell itself and to tell me what I can improve. : re-uploaded, fixed some typos Zinc:////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // // Chains of Torment // // // // Author: Kueken // // // // Requirements: xe by Vexorian (modules used: xebasic, xepreload, xecast, xefx, xedamage) // // TimerUtils by Vexorian // // IsTerrainWalkable by Antinarf // // GetTerrainZ by (dunno who gets the original credit) // // AdvLightning by me, a custom library to deal with lightnings. // // // // Details: Spell is written in zinc and freely customizable for the most part; // // have a look at the configurations block // // // // The spell itself as it is now is an area-target spell, which causes all targets // // in the area to be bound to the area. Moving out of the area will result in damage // // as well as a forced movement back to the target point dependant on the distance to // // the point (the further you move out, the more damage you get and the more you will // // be forced to move back). By exceeding a maximum distance, you can break the spell // // before the duration wears off, but causing a stun and damage to the target. // // // // Implementation: Copy this trigger to your map and implement the needed systems, if not present already. // // For the GetTerrainZ and AdvLightning Librarys, just copy the triggers to your map. // // For xe, TimerUtils and IsTerrainWalkable follow their respective implementation advices // // Also you need the abilities and the buff described in the configurations block. // // // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //! zinc library ChainsOfTorment requires xepreload,xecast,xedamage,TimerUtils,AdvLightning,IsTerrainWalkable { ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // CONFIGURATION // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //Constants constant integer ID = 'A000' ; //Base ID of the Spell, based on Blizzard in this map constant integer STUNID = 'A002' ; //ID of the Stun Spell, based on Stormbolt. //Modify the spell values as you need them. constant integer BUFFSPELLID = 'A005' ; //ID of the Spell providing the Buff, //based on Tornado Slow Aura constant integer BUFFID = 'B000' ; //ID of the provided Buff constant string SFXPATH = "Abilities\\Spells\\Human\\FlameStrike\\FlameStrikeTarget.mdl"; //Path of the Area Effect of the Spell, which is visible in the target area for the Duration constant string CASTSFX = "Abilities\\Weapons\\DemolisherFireMissile\\DemolisherFireMissile.mdl"; //Effect on the target point when casting the Spell constant string BUFFSFX = "Abilities\\Spells\\Human\\AerialShackles\\AerialShacklesTarget.mdl"; //Effect attached to the targets of the Spell constant string BUFFATTACHPNT = "chest" ; //Attachment Point for the target Effect constant string BREAKSFX = "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl"; //Effect when a target breaks the spell constant string BREAKATTACHPNT = "head" ; //Attachment Point for the break Effect constant string LIGHTNING = "LEAS" ; //general used lightning effect constant string FADELIGHTNING = "FORK" ; //lightning effect used for the lightning to fade out, //when a target breaks the spell constant real LIGHTNING_R = 1 ; // constant real LIGHTNING_G = .5 ; // RGB color values of the lightning constant real LIGHTNING_B = .2 ; // constant real LIGHTNING_DUR = 20 ; //duration of the lightning. It fades out automatically //when the spell finishes, so just pick a value higher //than the maximum spell duration here constant real LIGHTNING_FADEDUR = .5 ; //the time the lightning needs to fade out and disappear constant real INTERVAL = XE_ANIMATION_PERIOD ; //animation interval, use the same as xe (0.025 in this map) constant real MAX_COLLISION_SIZE= XE_MAX_COLLISION_SIZE ; //maximum collision size, use the same as xe constant real DAMAGEINTERVAL = .25 ; //the interval in which damage will be caused, //should be a multiple of INTERVAL for accurate damage. constant integer MAX_LEVEL = 3 ; //maximum level of the spell constant boolean DESTROY_TREES = false ; //this makes units affected by this spell destroy nearby //trees, if you need to be able to pull targets in trees //Target Filter //multi-line and still inline-friendly, zinc <3 function TargetFilter(unit caster, unit target) -> boolean //here you can specify your own target filter { //currently the targets need to be: return (GetWidgetLife(target)>.405) //-alive && (IsUnitEnemy(target,GetOwningPlayer(caster))) //-enemies && not (IsUnitType(target,UNIT_TYPE_STRUCTURE)) //-no buildings && not (IsUnitType(target,UNIT_TYPE_MAGIC_IMMUNE)) //-not magic immune ; } //Spell Setup function SpellSetup() //Setup some values of the Spell for each level. In case you modified MAX_LEVEL, //you need to add or remove values according to the new level. { DURATION[1] = 6 ; //Duration of the Spell DURATION[2] = 8 ; DURATION[3] = 10 ; AREA[1] = 300 ; //affected Area (recommended to match with the radius of the used spell) AREA[2] = 300 ; AREA[3] = 300 ; MIN_DISTANCE[1] = 150 ; //minimum distance for a target to the target point MIN_DISTANCE[2] = 150 ; //to be affected by the forced movement MIN_DISTANCE[3] = 150 ; BREAK_DISTANCE[1] = 600 ; //distance needed to break the spell BREAK_DISTANCE[2] = 750 ; BREAK_DISTANCE[3] = 900 ; DAMAGE[1] = 0 ; //raw damage per second to all targets DAMAGE[2] = 0 ; DAMAGE[3] = 0 ; DAMAGEFACTOR[1] = .08 ; //damage based on distance to target point per second. DAMAGEFACTOR[2] = .10 ; //0.08 means 8% of the distance to the target point is DAMAGEFACTOR[3] = .12 ; //dealt as damage per second. BREAK_DAMAGE[1] = 75 ; //damage to a unit when breaking the spell by moving out of BREAK_DISTANCE BREAK_DAMAGE[2] = 125 ; BREAK_DAMAGE[3] = 175 ; MOVEMENT[1] = .2 ; //forced movement to the target point. MOVEMENT[2] = .2 ; //0.2 means if the unit is not within MIN_DISTANCE, it is moved MOVEMENT[3] = .2 ; //20% of the distance to the target point in its direction per second. } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // END OF CONFIGURATION // // // // Only change something below this line, if you know what you are doing! // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //some needed variables for the spell constant integer DIVISOR=R2I(DAMAGEINTERVAL/INTERVAL); group TempGroup=CreateGroup(); rect TempRect=Rect(0,0,0,0); boolexpr TargFilter,DestFilter; real DURATION[MAX_LEVEL],AREA[MAX_LEVEL],MIN_DISTANCE[MAX_LEVEL],DAMAGEFACTOR[MAX_LEVEL], DAMAGE[MAX_LEVEL],BREAK_DISTANCE[MAX_LEVEL],BREAK_DAMAGE[MAX_LEVEL],MOVEMENT[MAX_LEVEL]; xedamage Damage; xecast Spell; function IsMultiple (integer i,integer n) -> boolean {return i == (i / n) * n ;} //function to check, if i is a multiple of n struct SpellData //spell struct { unit caster,target; real x,y; timer t; integer ticks,lvl; effect sfx; AdvLightning bolt; static method create(unit caster, unit target, real x, real y) -> SpellData //create struct, set all needed values { SpellData data = SpellData.allocate(); data.caster = caster; data.target = target; data.lvl = GetUnitAbilityLevel(caster,ID); data.ticks = R2I(DURATION[data.lvl]/INTERVAL); data.x = x; data.y = y; data.t = NewTimer(); data.sfx = AddSpecialEffectTarget(BUFFSFX,target,BUFFATTACHPNT); data.bolt = AdvLightningPointToUnit(LIGHTNING,x,y,target,LIGHTNING_DUR,LIGHTNING_FADEDUR); //using AdvLightning here to create attached lightnings data.bolt.recolor(LIGHTNING_R,LIGHTNING_G,LIGHTNING_B,1); //and to recolor them SetTimerData(data.t,data); TimerStart(data.t,INTERVAL,true,function SpellData.timerCallback); //start spell timer UnitAddAbility(target,BUFFSPELLID); //apply buff SetUnitAbilityLevel(target,BUFFSPELLID,data.lvl); return data; } method move() { real targx = GetUnitX(target), targy = GetUnitY(target), dx = x-targx, dy = y-targy, decr = MOVEMENT[lvl]*INTERVAL, newx = targx+dx*decr, newy = targy+dy*decr, dist = dx*dx+dy*dy; if (IsMultiple(ticks,DIVISOR)) //damage only each damageinterval { Damage.damageTarget(caster,target,((SquareRoot(dist)*DAMAGEFACTOR[lvl])+DAMAGE[lvl])*DAMAGEINTERVAL); } if (ticks>0 && GetWidgetLife(target)>.405) //if duration is not over and unit is alive, proceed { if (dist>MIN_DISTANCE[lvl]*MIN_DISTANCE[lvl] && IsTerrainWalkable(newx,newy)) //move only if not in MIN_DISTANCE //and when Terrain is pathable. { SetUnitX(target,newx); SetUnitY(target,newy); } ticks-=1; if (DESTROY_TREES) //in case you wanted to destroy trees { SetRect(TempRect,newx-150,newy-150,newx+150,newy+150); EnumDestructablesInRect(TempRect,DestFilter,null); } if (dist>BREAK_DISTANCE[lvl]*BREAK_DISTANCE[lvl]) //unit broke chain, run break method { breakChain(); } } else {destroy();} //unit dead or duration expired, destroy } method breakChain() { DestroyEffect(AddSpecialEffectTarget(BREAKSFX,target,BREAKATTACHPNT)); Damage.damageTarget(caster,target,BREAK_DAMAGE[lvl]); //damage, breakeffect and stun... Spell.level=lvl; Spell.castOnTarget(target); if (bolt.lightningType!=FADELIGHTNING) {bolt.lightningType=FADELIGHTNING;} //in case you added a differend fade //lightning effect, change it now destroy(); } static method timerCallback() //Callback for the Spell Timer { timer t = GetExpiredTimer(); SpellData data = GetTimerData(t); data.move(); } static method destructableFilterFunc() -> boolean //filter for destroying trees { KillDestructable(GetFilterDestructable()); return false; } static method targetFilterFunc() -> boolean //filter for target units, might look awkward //but it takes collision size into account, //meaning if your Spell Radius matches the AREA //constant it affects exactly the units marked //green by the AoE circle this way. { if (IsUnitInRangeXY(GetFilterUnit(),GetSpellTargetX(),GetSpellTargetY(),AREA[GetUnitAbilityLevel(GetTriggerUnit(),ID)]) && TargetFilter(GetTriggerUnit(),GetFilterUnit())) { SpellData.create(GetTriggerUnit(),GetFilterUnit(),GetSpellTargetX(),GetSpellTargetY()); } return false; } static method destroySfx() //destroy the effect of the spell { timer temp = GetExpiredTimer(); xefx sfx = GetTimerData(temp); sfx.destroy(); ReleaseTimer(temp); } static method onSpellCast() { timer temp = NewTimer(); xefx sfx = xefx.create(GetSpellTargetX(),GetSpellTargetY(),0); sfx.fxpath = SFXPATH; //create the effect of the spell sfx.flash(CASTSFX); SetTimerData(temp,sfx); TimerStart(temp,DURATION[GetUnitAbilityLevel(GetTriggerUnit(),ID)],false,function SpellData.destroySfx); GroupEnumUnitsInRange(TempGroup,GetSpellTargetX(),GetSpellTargetY(), AREA[GetUnitAbilityLevel(GetTriggerUnit(),ID)]+MAX_COLLISION_SIZE,TargFilter); //pick the targets for the spell } private method onDestroy() //spell finished, destroy buff effect, remove buff, release timer { DestroyEffect(sfx); UnitRemoveAbility(target,BUFFSPELLID); UnitRemoveAbility(target,BUFFID); ReleaseTimer(t); bolt.fadeOut(); //using an AdvLightning feature here to make the lightning fade out //instantly, using its specified fade duration. } } function onInit() //wanted to use onInit method, but xepreload did not want ;) { trigger t = CreateTrigger(); //create n register trigger TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT); TriggerAddCondition(t,Condition(function() -> boolean {return GetSpellAbilityId()==ID;})); //anonymous function, great stuff :D TriggerAddAction(t,function SpellData.onSpellCast); SpellSetup(); //call the setup TargFilter = Condition(function SpellData.targetFilterFunc); DestFilter = Condition(function SpellData.destructableFilterFunc); //init boolexprs Damage=Damage.create(); //init xedamage instance Damage.dtype = DAMAGE_TYPE_FIRE; Spell=Spell.create(); //init xecast instance Spell.orderid = OrderId("thunderbolt"); Spell.abilityid = STUNID; XE_PreloadAbility(STUNID); //preload abilities XE_PreloadAbility(BUFFSPELLID); t=null; //unneeded stuff } } //! endzinc |
| 12-04-2009, 08:22 PM | #2 |
Bump No single opinion yet? Good sign or bad sign? Is it because of zinc? Not many ppl around who use/ can read zinc it seems.. Well, if you cannot/ do not want to comment the code itself, I would like to hear, if at least this spell could fulfill the requirements to be added to the spells page (how do I submit a spell for the site anyway? ). Does it have the needed standards, if you look at the configuration part? Or do I have to add/change something important? Any answer is appreciated, and be it your spell sucks, plz never submit it ;) |
| 12-05-2009, 06:36 AM | #3 |
Tested it and it works great! Very cool spell! If you want to submit, go to "Resources" ( located at top of page ) and click on " Submit Recource ". Here is the rating I will give to it, my IMO Rating.---------------------->>>>>RATING<<<<<------------------------ Visual Effects 8.5/10 ( looks great, but still room for more ) Usefullness 6/10 ( very usefull 4 ranged units, but not melee ) Effectiveness 4.5/10 ( units can attack and move a good distance ) wc3c worthiness 8/10 ( Not the best, but better than some ) Total IMO Score 30/40 |
| 12-05-2009, 07:25 AM | #4 |
I have no WC at the moment and no skill in zinc but I will try to give an advice anyways: use [hiddenjass]-tags instead of [hidden] and [jass] if you wan't it hidden :) |
| 12-05-2009, 12:33 PM | #5 | ||
Quote:
Aah, found it ;). I looked at the submission rules, but did not find, where exactly to submit a resource. Thx. Visuals: I dont like effect overloading, I try to use few, decent effects. But I agree with you, it could be a little more flashy. However, you can customize any of the used effects in the configuration part. Usefulness: Well, the spell is some sort of "Move out here, and you are screwed". As it is now, a unit can sit out the spell and stay in mid for almost no damage or try to break the spell, which is quite hard without some movespeed or, lets say, a blink spell. This way it synergizes best with some heavy area skills, where you want to move out usually (lets say a flame strike). Effectiveness: This can be configurated for the most part, values like stun duration, break chain distance, damage etc etc can all be easily modified by the user. wc3c worthiness: Thanks for your opinion there :) Quote:
edit: Well, I do not know what I did wrong back then, but now [hiddenzinc]-tags work fine. Guess I had a typo somewhere. Thank you for the Feedback, I guess, I will try to submit the spell :). edit: Submitted the Spell (submission), this thread is now obsolete. |
