| 12-29-2009, 01:18 AM | #1 | |
Created for a spell contest Quote:
JASS:/* water vortex v1.03 by scorpion182 requires: timer utils, bound sentinel, xedamage, xefx by Vexorian grouputils by Rising_Dusk */ library WaterVortex requires TimerUtils, GroupUtils, xefx, xedamage, TerrainPathability, BoundSentinel private keyword data //don't touch this //--------------------------------------------------------------------------------------------- //--------------CALIBRATION SECTION------------------------------------------------------------ globals private constant integer SPELL_ID='A000' //ability rawcode private constant string ORDER_ID="starfall" //ability order string private constant integer MAXMISSCOUNT=50 // keep this value higher than number of GetMissileCount*GetLayerCount private constant string PATH="Abilities\\Weapons\\WaterElementalMissile\\WaterElementalMissile.mdl" //missile fx path private constant string CRUSH_FX="Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" //on-death effect private constant string CRUSH_ATTCH="origin" //on-death effect attachment point private constant string DAMAGE_FX="" //on-damage effect private constant string DAMAGE_ATTCH="origin" //on-damage effect attachment point private constant real SCALE=1. //missile scale private constant integer RED=255 //vertex coloring in RGB , private constant integer GREEN=255 // alpha is the opacity value private constant integer BLUE=255 private constant integer ALPHA=255 private constant real HEIGHT_INC=50. //missile height increment private constant real ANGLE_SPEED=.15 //missile angle speed, it's in radians private constant boolean INSTANTKILL=true //instant kill the sucked unit wandering too close if true private constant boolean DISABLEPATHING=true // If this is true, the spell will look better but can make units stop in weird places if the channeling is stopped. // If it is false, the spell will look a bit worse, but shouldn't be able to create those problems. endglobals private constant function GetTargetCount takes integer lvl returns integer return 5+lvl*0 // how many units the maximum can suck into it at the same time endfunction private constant function GetAoE takes integer lvl returns real return 600.+lvl*0. // area of effect of the spell endfunction private constant function GetAngleSpeed takes integer lvl returns real return 1.309+lvl*0. // how much the targets turn while being sucked in per interval, it's in radians endfunction private constant function GetSpeed takes integer lvl returns real return 50.00+lvl*0. // how fast the units are sucked in endfunction private constant function DistanceToAbsorb takes integer lvl returns real return 80.00+lvl*0. // how close a sucked unit must be to the center to disappear. endfunction private constant function GetDamage takes integer lvl returns real return 30.*lvl //deal damage per interval endfunction private constant function GetMissileCount takes integer lvl returns integer return 5*lvl+0 //number of missiles each layer endfunction private constant function GetLayerCount takes integer lvl returns integer return 2*lvl+0 //number of layers endfunction //damage filter private function DamageOptions takes xedamage spellDamage returns nothing set spellDamage.dtype=DAMAGE_TYPE_UNIVERSAL set spellDamage.atype=ATTACK_TYPE_NORMAL set spellDamage.exception=UNIT_TYPE_STRUCTURE set spellDamage.visibleOnly=true set spellDamage.damageAllies=false //damage allies if true endfunction //filter the targets, should match the value from DamageOptions private function IsValidTarget takes unit u, data s returns boolean return not IsUnitType(u, UNIT_TYPE_DEAD) and GetUnitTypeId(u) != 0 and IsUnitType(u,UNIT_TYPE_STRUCTURE)==false and IsUnitEnemy(u,GetOwningPlayer(s.caster))==true and IsUnitInGroup(u,s.victim)==false and IsUnitVisible(u,GetOwningPlayer(s.caster))==true endfunction //------------END OF CALIBRATION---------------------------------------------------------------- globals private group casters=CreateGroup() private xedamage xed endglobals private struct data unit caster timer t real x real y integer lvl integer count=0 group victim xefx array fx[MAXMISSCOUNT] real array angle[MAXMISSCOUNT] private static thistype temp static method create takes unit c, real x, real y returns data local data this=data.allocate() local integer i=0 local integer j=0 local integer k=0 local real height=HEIGHT_INC local real a=2*bj_PI call GroupAddUnit(casters,c) set .caster=c set .x=x set .y=y set .lvl=GetUnitAbilityLevel(c,SPELL_ID) set .victim=NewGroup() set .t=NewTimer() loop exitwhen i==GetLayerCount(.lvl) loop exitwhen j==GetMissileCount(.lvl)*(i+1) set .fx[j]=xefx.create(x,y,k*a/GetMissileCount(.lvl)) set .fx[j].fxpath=PATH set .fx[j].scale=SCALE set .fx[j].z=height set .angle[j]=k*a/GetMissileCount(.lvl) set height=height+HEIGHT_INC set k=k+1 set j=j+1 endloop set height=HEIGHT_INC //to synchronize each layer missile' height set k=0 set i=i+1 endloop return this endmethod static method movecallback takes nothing returns nothing local unit f=GetEnumUnit() local real x local real y local real dist local real a local real d //move the target unit set x=temp.x-GetUnitX(f) set y=temp.y-GetUnitY(f) set dist=SquareRoot(x*x+y*y) set a=Atan2(GetUnitY(f)-temp.y, GetUnitX(f)-temp.x)+GetAngleSpeed(temp.lvl)*XE_ANIMATION_PERIOD set d=dist-GetSpeed(temp.lvl)*XE_ANIMATION_PERIOD set x = temp.x+d*Cos(a) set y = temp.y+d*Sin(a) if (DISABLEPATHING==true) or (DISABLEPATHING==false and (IsTerrainWalkable(x,y) or IsUnitType(f,UNIT_TYPE_FLYING))) then call SetUnitX(f, x) call SetUnitY(f, y) endif if dist<=DistanceToAbsorb(temp.lvl) and INSTANTKILL==true then call xed.useSpecialEffect(CRUSH_FX,CRUSH_ATTCH) //instant kill the unit call xed.damageTarget(temp.caster,f,99999.) endif //kick the dead units from the victim group if (IsUnitType(f, UNIT_TYPE_DEAD) or GetUnitTypeId(f) == 0) then call GroupRemoveUnit(temp.victim,f) set temp.count=temp.count-1 endif set f=null endmethod //moving the missiles and the targets static method move takes nothing returns nothing local real a local integer i=0 local integer j=0 loop exitwhen i==GetLayerCount(temp.lvl) loop exitwhen j==GetMissileCount(temp.lvl)*(i+1) set a=temp.angle[j]+ANGLE_SPEED set temp.fx[j].x=temp.x+GetAoE(temp.lvl)/(i+1)*Cos(a) set temp.fx[j].y=temp.y+GetAoE(temp.lvl)/(i+1)*Sin(a) set temp.angle[j]=a set j=j+1 endloop set i=i+1 endloop call ForGroup(temp.victim,function data.movecallback) endmethod private method onDestroy takes nothing returns nothing local unit f local integer i=0 call ReleaseTimer(.t) call GroupRemoveUnit(casters,.caster) //i just hate using ForGroup here :) loop set f=FirstOfGroup(.victim) exitwhen f==null call PauseUnit(f,false) call GroupRemoveUnit(.victim,f) endloop //destroy fx loop exitwhen i==GetLayerCount(.lvl)*GetMissileCount(.lvl) call .fx[i].destroy() set i=i+1 endloop call ReleaseGroup(.victim) //unit f will already be null by the end of the FirstOfGroup loop endmethod //filter the targets static method VictimFilter takes nothing returns boolean local unit u=GetFilterUnit() local integer a=GetTargetCount(temp.lvl) if temp.count==a then set u=null return false endif if IsValidTarget(u,temp)==true then call GroupAddUnit(temp.victim,u) call PauseUnit(u,true) set temp.count=temp.count+1 endif set u=null return false endmethod static method Loop takes nothing returns nothing local data this=data(GetTimerData(GetExpiredTimer())) local integer a=GetTargetCount(.lvl) local unit f local boolexpr be if (GetUnitCurrentOrder(.caster)==OrderId(ORDER_ID)) then set .temp=this //damage nearby units, include the targets call GroupEnumUnitsInArea(ENUM_GROUP,.x,.y,GetAoE(.lvl),BOOLEXPR_TRUE) call xed.useSpecialEffect(DAMAGE_FX,DAMAGE_ATTCH) call xed.damageGroup(.caster,ENUM_GROUP,GetDamage(.lvl)*XE_ANIMATION_PERIOD) //search targets if .count<a then set be=Condition(function data.VictimFilter) call GroupEnumUnitsInArea(ENUM_GROUP,.x,.y,GetAoE(.lvl),be) call DestroyBoolExpr(be) endif call data.move() else call .destroy() endif set be=null set f=null endmethod //trigger conditions are faster than trigger actions :D static method SpellEffect takes nothing returns boolean local data this local unit caster=GetSpellAbilityUnit() if GetSpellAbilityId() == SPELL_ID and IsUnitInGroup(caster,casters)==false then set this=data.create(caster,GetUnitX(caster),GetUnitY(caster)) call SetTimerData(.t,this) call TimerStart(.t,XE_ANIMATION_PERIOD,true,function data.Loop) endif set caster=null return false endmethod //spell trigger actions static method onInit takes nothing returns nothing local trigger t=CreateTrigger() call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT) call TriggerAddCondition(t,Condition(function data.SpellEffect)) //init xedamage set xed=xedamage.create() call DamageOptions(xed) endmethod endstruct endlibrary Requires: - TimerUtils by Vexorian - xe by Vexorian - BoundSentinel by Vexorian - GroupUtils by Rising_Dusk - TerrainPathability by Rising_Dusk History: ~ v1.00 First Release ~ v1.01 Fixed code a little bit :D ~ v1.01b Remove unnecessary codes. ~ v1.01c Fixed minor bugs. ~ v1.01d Make disable unit pathing configurable. ~ v1.01e Get rid UnitAlive native. ~ v1.02 Fixed disablepathing issues, number of missiles and number of layers are configurable now. ~ v1.02b Replace GroupEnumUnitsInRange with GroupEnumUnitsInArea ~ v1.03 Added TerrainPathability to requirements. |
| 12-29-2009, 01:42 AM | #2 |
The visuals look strikingly similar to something I made awhile ago called Hurricane, but whatever. I'll check it out when the time reveals itself to me. |
| 12-29-2009, 11:43 AM | #3 | |
Quote:
Take your time Updated Version 1.01 |
| 12-29-2009, 05:25 PM | #4 | |
Quote:
I think like 50% of all mapmaker created something with water elemental attack missile circling around once... |
| 12-29-2009, 11:26 PM | #5 |
Updated Version 1.01b |
| 12-29-2009, 11:41 PM | #6 |
I think adding some effect to units as they're being pulled around might help make it look more exciting. The spiraling water is cool and all, but it's boring when units get dragged along. Also, units entering the vortex don't get pulled along. They just walk straight up to the caster and die (because they're in the center of the vortex, I presume). |
| 12-30-2009, 12:11 AM | #7 | |||
Quote:
Quote:
JASS:private constant function GetTargetCount takes integer lvl returns integer return 5+lvl*0 // how many units the maximum can suck into it at the same time endfunction Quote:
you can add the effect. JASS:private constant string DAMAGE_FX="" //damage fx |
| 12-30-2009, 12:32 AM | #8 |
Oh, nice! I didn't see that config line there. Sorry. Maybe explain the effects a little more in the comments? I know the crush effect is the on-death effect, but only after testing the spell in the game. And I only know that DAMAGE_FX is the on-damage effect after you explained it to me. ;) |
| 12-30-2009, 03:03 AM | #9 | |
Quote:
![]() Updated Version 1.01c |
| 12-30-2009, 03:50 PM | #10 |
cool spell , i would like to use it , but for some reason it gets units stuck up cliffs/threw walls . this is quite problematic when a hero or unit has no escape ability, will it be possible to prevent this? |
| 12-30-2009, 11:24 PM | #11 | |
Quote:
Updated Version 1.01d disable unit pathing is configurable now, then set DISABLEPATHING to false. JASS:private constant boolean DISABLEPATHING=true // If this is true, the spell will look better but can make units stop in weird places if the channeling is stopped. // If it is false, the spell will look a bit worse, but shouldn't be able to create those problems. EDIT: Updated Version 1.01e |
| 12-31-2009, 12:18 PM | #12 |
thanks alot blanc_dummy , nice spell :) +rep to you . would you mind if i use this? |
| 12-31-2009, 03:33 PM | #13 |
Why did you stop using the UnitAlive native? You essentially removed some safety. And probably speed, but we don't know that yet. |
| 12-31-2009, 06:50 PM | #14 |
I think the number of layers of orbiting projectiles should be configurable, as well as the number of projectiles in each layer. It would look better with a third layer, I think. In fact the projectile count and number of layers could increase per level. The rotation effect of the enemies should also become stronger with each level of the ability, and should be configurable. |
| 12-31-2009, 08:14 PM | #15 |
damn :( i tried it, this is what i have JASS:private constant boolean DISABLEPATHING=false is their any library you can attach to this to prevent units getting stuck up cliffs or going threw buildings? if you stop casting this spell and your right next to a building and time it correctly, the unit will be stuck in the middle of the building. :P |
