NeuroToxin
New Member
- Reaction score
- 46
Okay, the spell is called Robot Arms by Inferior. I love the spell and am using it, the only problem, is that in my map its like a hero FFA. And the problem is that, if its a 1v1, then the arms will continuously stun the other hero until it's dead. That's a problem. Because then they can't do anything. But if I set the stun too small, the sfx are crap. Heres the code, what can I add so that way the hero can only be damaged say per 10 seconds once.
JASS:
library RobotArms initializer Init needs Table, GroupUtils, TimerUtils, ABuff, ABuffHeroSkill, BoundSentinel, UnitStatus, IsUnitSpellResistant, xebasic, xefx, xedamage, Rounding
//********************************************************************************************\\
//********************************************************************************************\\
//* Robot Arms By Inferior *\\
//* *\\
//* v1.3 *\\
//* *\\
//* ************************** *\\
//* * Requirements: * *\\
//* * - JassNewGenPack * *\\
//* * - Table * *\\
//* * - GroupUtils * *\\
//* * - TimerUtils * *\\
//* * - ABuff * *\\
//* * - ABuffHeroSkill * *\\
//* * - BoundSentinel * *\\
//* * - UnitStatus * *\\
//* * - IsUnitSpellResistant * *\\
//* * - xebasic * *\\
//* * - xefx * *\\
//* * - xedamage * *\\
//* * - Rounding * *\\
//* ************************** *\\
//* *\\
//* *\\
//* *\\
//* How To Import: *\\
//* - Copy the Spell called 'Robot Arms', the dummy called facingdummy into your map *\\
//* and import the "fdummy.mdl" and "RoboticArmsHand" included in this map into yours *\\
//* - Copy the whole Triggerscript into your map *\\
//* - Change the model of your new Dummymodel and change the RawCodes of the ABILID *\\
//* and CHAINID to the Rawcodes in your map *\\
//* *\\
//* Spelldescription: *\\
//* The Electrician uses his technical knowlegdes to create 3 Robot Arms that act on *\\
//* their own. If the Arm detects an enemy, it moves to that enemy and knocks it out on *\\
//* contact, dealing absorbing the enemies Life. If the Electrician moves out of the *\\
//* enemies Range the Arm moves back. After every action, the Arms have to move back into *\\
//* their default Position. *\\
//* *\\
//********************************************************************************************\\
//********************************************************************************************\\
// Globals Setting
globals
private constant integer ABILID = 039;A00J039;
// The AbilityId of your Main Ability
private constant integer CHAINID = 039;e002039;
// The Rawcode for your dummy. This dummy needs a specific model included in that map. You need this model if you want the full eyecandy
private constant integer MAXARMS = 3
// The Number of Arms. The more arms you use, the higher is the performance lack.
private constant integer CHAINPARTS = 16
// The Parts of Dummies for the Chain of the arm. Do not forget to change the .chain array in the struct.
private constant real CHAINSIZE = 1.3
// The Size of the Chain parts
private constant real BIRTHSIZE = 0.5
// The Size at which the Chain parts start
private constant real BIRTHDURATION = 1.
// The Duration until the Chain reach its full size
private constant real ARMSPEED = 12.
// The Speed an arm is moving back or to a target
private constant real DMGTOHEALTH = 1.
// Conversionfactor from damage dealt to absorbed Health. This value is only active if USEABSORB = true
private constant real HEALTHFACTOR = 0.05
// The Factor of the targets MaxHealth that is absorbed. This value is only active if USEABSORB = true
private constant real DEALTDAMAGE = 1.5
// Damage dealt per Period: 0.025*40=1 -> 40*1.5= 60 damage dealt per second. This value is only active if USEABSORB = false
private constant real DETECTIONRADIUS = 500.
// The Radius in which an Arm detects an enemy
private constant real DEFAULTOFFSET = 250.
// The Default offset from the Electricians Position. Also defines the DefaultPosition where the arms move back to
private constant real DEFAULTHEIGHT = 75.
// The Default Height of the Chains
private constant real MAXHEIGHT = 350.
// The Maximum Height that can be reached by the arm
private constant real MINHEIGHT = 150.
// The Minimum Height that can be reached by the arm
private constant real MAXOFFSET = 750.
// The Maximum Range an arm can bent
private constant string CHAINMDL = "RoboticArmsHand.mdx"
// The Chains Model effect
private constant string CHAINATTACH = "origin"
// DO NOT CHANGE THIS (the dummy model has no other attachement)
private constant string CHAINHAND = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl"
// The End of the Arms effect
private constant string ABSORBMDL = "Abilities\\Spells\\Undead\\AbsorbMana\\AbsorbManaBirthMissile.mdl"
// The Effect that is used if USEABSORB = true
private constant boolean USEABSORB = true
// This value defines the type of damage that is dealt. if USEABSORB = false then the damage is dealt periodicly.
// For more informations check the .OnAbsorb method.
private real array SHOCKTIME
// The Value that defines the time an arm deals damage to a target.
// DO NOT CHANGE THIS. Defines the Damage types.
private xedamage dmgOptions
// DO NOT CHANGE THIS.
private unit TEMPUNIT
private constant integer StunCode = 039;BPSE039;
endglobals
private keyword Arm
private function SetupSpellVariables takes nothing returns nothing
set SHOCKTIME[1]=2
set SHOCKTIME[2]=3
set SHOCKTIME[3]=4
set SHOCKTIME[4]=5
endfunction
private function ConfigureDamageOptions takes nothing returns nothing
set dmgOptions=xedamage.create()
set dmgOptions.atype=ATTACK_TYPE_CHAOS
set dmgOptions.dtype=DAMAGE_TYPE_UNIVERSAL
set dmgOptions.wtype=WEAPON_TYPE_WHOKNOWS
endfunction
private function UnitFilter takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(),GetOwningPlayer(TEMPUNIT)) and IsUnitType(GetFilterUnit(),UNIT_TYPE_GROUND) and not IsUnitSpellImmune(GetFilterUnit()) and not Arm.stunned[GetUnitId(GetFilterUnit())] and GetWidgetLife(GetFilterUnit())>0.405 and GetUnitTypeId(GetFilterUnit())!=CHAINID
endfunction
// DO NOT TOUCH ANYTHING BELOW THIS COMMENT, ALTHOUGH YOU KNOW WHAT YOU DO.
// HERE BEGINS THE SPELLS SCRIPT.
private function AngleConv takes real angle returns real
local integer factor=0
if angle>=360 then
set factor=R2I(angle/360)
return angle-360*factor
elseif angle<0 then
set factor=R2I(AbsReal(angle)/360)
return angle+360*(factor+1)
endif
return angle
endfunction
private function HeightCalc takes real d returns real
return ((MINHEIGHT-MAXHEIGHT+DEFAULTHEIGHT)/MAXOFFSET)*d+MAXHEIGHT-DEFAULTHEIGHT
endfunction
// This function defines the Arm parts height
private function ArmHeight takes real d, integer z returns real
return HeightCalc(d)*Sin( bj_PI/CHAINPARTS * z ) + DEFAULTHEIGHT
endfunction
// Defines the Arm parts Z-Facing.
// THIS FUNCTION STILL NEEDS PERFECTION. IM STILL SEARCHING FOR A BETTER FORMULA
private function UnitZFacing takes unit u, real d, integer z returns nothing
local real s=(MAXHEIGHT-(MAXHEIGHT-MINHEIGHT)/2)/HeightCalc(d)
local real a=s*HeightCalc(d) * bj_PI/CHAINPARTS * Cos(bj_PI/CHAINPARTS * z)
local boolean b=a<0
local integer v=RealRounding(AbsReal(a))
if not b then
call SetUnitAnimationByIndex(u,v)
else
call SetUnitAnimationByIndex(u,252-v)
endif
endfunction
// Defines an arms Defaultpositions Facing
private constant function Angles takes integer int returns real
return 360./MAXARMS * int
endfunction
private struct Arm
unit caster
unit array chain[17] // Add here the value of the CHAINPARTS + 1
unit hand
unit targ
integer pos=CHAINPARTS
integer phase=0
// 0=birth
// 1=search after an enemy
// 2=haunt an enemy
// 3=shock an enemy
// 4=out of range
real angle
real startangle
real offset
real timed
xefx absorb
// this effect is only used if USEABSORB = true
readonly integer index
static integer count=0
static boolean array done
static boolean array stunned
static timer ArmTimer
static Arm array Data
static Table array ArmTable
static Table array HTable
// You may change this function if you want.
method OnAbsorb takes nothing returns nothing
if USEABSORB then
// the time between these too actions is proportional to the number of Chainparts
if .pos==CHAINPARTS then
// this is executed when the Absorption effect runs off the target.
call dmgOptions.damageTarget(.caster,.targ,GetUnitState(.targ,UNIT_STATE_MAX_LIFE)*HEALTHFACTOR)
elseif .pos<0 then
// this is executed when the Absorption effect reaches the caster
call SetWidgetLife(.caster,GetWidgetLife(.caster)+GetUnitState(.targ,UNIT_STATE_MAX_LIFE)*HEALTHFACTOR*DMGTOHEALTH)
endif
else
// this is executed when USEABSORB=false
// this is executed once per Timerperiod: every 0.025 seconds
call dmgOptions.damageTarget(.caster,.targ,DEALTDAMAGE)
endif
endmethod
// destroys the Arm on Casters death or Unlearning of the MainAbility
method KillArm takes nothing returns boolean
local integer int=0
loop
exitwhen int>CHAINPARTS
call KillUnit(.chain[int])
set int=int+1
endloop
call KillUnit(.hand)
return true
endmethod
// sets an Arms Position and the parts Height and facing
method PosNormal takes real offset returns nothing
local real dx=GetUnitX(.hand)-GetUnitX(.caster)
local real dy=GetUnitY(.hand)-GetUnitY(.caster)
local integer i=0
local real step=0.
set .offset=offset
set .angle=Atan2(dy,dx)
if .offset>MAXOFFSET then
set .offset=MAXOFFSET
endif
set step=.offset/CHAINPARTS
loop
exitwhen i>CHAINPARTS
call SetUnitX(.chain<i>,GetUnitX(.caster)+step*i*Cos(.angle))
call SetUnitY(.chain<i>,GetUnitY(.caster)+step*i*Sin(.angle))
call SetUnitFacing(.chain<i>,.angle*180/bj_PI)
call SetUnitFlyHeight(.chain<i>,ArmHeight(.offset,i),0)
set i=i+1
endloop
call SetUnitX(.hand,GetUnitX(.chain[CHAINPARTS]))
call SetUnitY(.hand,GetUnitY(.chain[CHAINPARTS]))
call SetUnitFlyHeight(.hand,GetUnitFlyHeight(.chain[CHAINPARTS]),0)
endmethod
// Creates an arm on Casters revive or Learning of the Ability
method createChain takes nothing returns nothing
local real x=GetUnitX(.caster)
local real y=GetUnitY(.caster)
local integer count=CHAINPARTS
local real dx=.offset/count
local integer int=0
loop
exitwhen int>count
set .chain[int]=CreateUnit(GetOwningPlayer(.caster),CHAINID,x,y,.angle)
call SetUnitScale(.chain[int],CHAINSIZE,CHAINSIZE,CHAINSIZE)
call SetUnitTimeScale(.chain[int],0.)
call UnitZFacing(.chain[int],.offset,int)
call UnitAddAbility(.chain[int],XE_HEIGHT_ENABLER)
call UnitRemoveAbility(.chain[int],XE_HEIGHT_ENABLER)
call SetUnitX(.chain[int],x+dx*Cos(.angle*bj_PI/180)*int)
call SetUnitY(.chain[int],y+dx*Sin(.angle*bj_PI/180)*int)
call SetUnitFlyHeight(.chain[int],ArmHeight(.offset,int),0)
call AddSpecialEffectTarget(CHAINMDL,.chain[int],CHAINATTACH)
set int=int+1
endloop
set .hand=CreateUnit(GetOwningPlayer(.caster),CHAINID,GetUnitX(.chain[CHAINPARTS]),GetUnitY(.chain[CHAINPARTS]),.angle)
call UnitAddAbility(.hand,XE_HEIGHT_ENABLER)
call UnitRemoveAbility(.hand,XE_HEIGHT_ENABLER)
call SetUnitFlyHeight(.hand,GetUnitFlyHeight(.chain[CHAINPARTS]),0)
call AddSpecialEffectTarget(CHAINHAND,.hand,CHAINATTACH)
endmethod
static method callback takes nothing returns nothing
local integer int=0
local Arm data
local group g
local unit a
local real dx
local real dy
loop
exitwhen int==Arm.count
if not Arm.done[int] then
set data=Arm.Data[int]
// Searchs possible targets for the Arm
if data.phase==0 then
set TEMPUNIT=data.caster
call GroupEnumUnitsInArea(ENUM_GROUP,GetUnitX(data.hand),GetUnitY(data.hand),DETECTIONRADIUS,Condition(function UnitFilter))
set data.targ=GroupPickRandomUnit(ENUM_GROUP)
set TEMPUNIT=null
call GroupClear(ENUM_GROUP)
if data.targ!=null then
set data.phase=1
else
set data.angle=GetUnitFacing(data.caster)+data.startangle
call SetUnitX(data.hand,GetUnitX(data.caster)+DEFAULTOFFSET*Cos(data.angle*bj_DEGTORAD))
call SetUnitY(data.hand,GetUnitY(data.caster)+DEFAULTOFFSET*Sin(data.angle*bj_DEGTORAD))
call data.PosNormal(DEFAULTOFFSET)
endif
// Moves the Arm to an target. If the target is out of range the arm moves back into DefaultPosition
elseif data.phase==1 then
set dx=GetUnitX(data.targ)-GetUnitX(data.hand)
set dy=GetUnitY(data.targ)-GetUnitY(data.hand)
set data.angle=Atan2(dy,dx)
call SetUnitX(data.hand,GetUnitX(data.hand)+ARMSPEED*Cos(data.angle))
call SetUnitY(data.hand,GetUnitY(data.hand)+ARMSPEED*Sin(data.angle))
set dx=GetUnitX(data.hand)-GetUnitX(data.caster)
set dy=GetUnitY(data.hand)-GetUnitY(data.caster)
set data.angle=Atan2(dy,dx)
set data.offset=SquareRoot(dx*dx+dy*dy)
call data.PosNormal(data.offset)
if data.offset>=MAXOFFSET then
set data.phase=3
endif
if Arm.stunned[GetUnitId(data.targ)] then
set data.phase=3
else
if IsUnitInRange(data.hand,data.targ,5.0) then
call SetUnitX(data.hand,GetUnitX(data.targ))
call SetUnitY(data.hand,GetUnitY(data.targ))
set dx=GetUnitX(data.hand)-GetUnitX(data.caster)
set dy=GetUnitY(data.hand)-GetUnitY(data.caster)
set data.angle=Atan2(dy,dx)
set data.offset=SquareRoot(dx*dx+dy*dy)
if data.offset>=MAXOFFSET then
set data.phase=3
else
call data.PosNormal(data.offset)
call StunUnit(data.targ,true)
set Arm.stunned[GetUnitId(data.targ)]=true
set data.phase=2
if USEABSORB then
set data.absorb=xefx.create(GetUnitX(data.chain[.chain.size]),GetUnitY(data.chain[.chain.size]),GetUnitFacing(data.chain[.chain.size]))
set data.absorb.fxpath=ABSORBMDL
endif
endif
endif
endif
// If the arm reached an enemy, the enemy is stunned and is sucked hp or damaged based on the USEABSORB value.
// The Arm moves back to default Position if the target moves out of range, or the Buff on the target is removed.
elseif data.phase==2 then
set dx=GetUnitX(data.targ)-GetUnitX(data.caster)
set dy=GetUnitY(data.targ)-GetUnitY(data.caster)
set data.angle=Atan2(dy,dx)
set data.offset=SquareRoot(dx*dx+dy*dy)
if data.offset>=MAXOFFSET then
set data.phase=3
call StunUnit(data.targ,false)
set Arm.stunned[GetUnitId(data.targ)]=false
set data.timed=0
set data.pos=CHAINPARTS
set data.targ=null
if USEABSORB then
call data.absorb.hiddenDestroy()
endif
else
call SetUnitX(data.hand,GetUnitX(data.targ))
call SetUnitY(data.hand,GetUnitY(data.targ))
call data.PosNormal(data.offset)
set data.timed=data.timed+0.025
if USEABSORB then
set data.absorb.x=GetUnitX(data.chain[data.pos])
set data.absorb.y=GetUnitY(data.chain[data.pos])
set data.absorb.z=GetUnitFlyHeight(data.chain[data.pos])
if data.pos<0 then
call data.absorb.hiddenDestroy()
call data.OnAbsorb()
set data.pos=CHAINPARTS
set data.absorb=xefx.create(GetUnitX(data.chain[.chain.size]),GetUnitY(data.chain[.chain.size]),GetUnitFacing(data.chain[.chain.size]))
set data.absorb.fxpath=ABSORBMDL
endif
if data.pos==CHAINPARTS then
call data.OnAbsorb()
endif
else
call data.OnAbsorb()
endif
set data.pos=data.pos-1
if data.timed>SHOCKTIME[GetUnitAbilityLevel(data.caster,ABILID)] or GetWidgetLife(data.targ)<0.405 then
set data.phase=3
call StunUnit(data.targ,false)
set Arm.stunned[GetUnitId(data.targ)]=false
set data.timed=0
set data.pos=CHAINPARTS
if USEABSORB then
call data.absorb.hiddenDestroy()
endif
else
call data.PosNormal(data.offset)
endif
endif
// Moves the Arm back to DefaultPosition
elseif data.phase==3 then
set dx=GetUnitX(data.hand)-GetUnitX(data.caster)-DEFAULTOFFSET*Cos((GetUnitFacing(data.caster)+data.startangle)*bj_DEGTORAD)
set dy=GetUnitY(data.hand)-GetUnitY(data.caster)-DEFAULTOFFSET*Sin((GetUnitFacing(data.caster)+data.startangle)*bj_DEGTORAD)
set data.angle=Atan2(dy,dx)
call SetUnitX(data.hand,GetUnitX(data.hand)-ARMSPEED*Cos(data.angle))
call SetUnitY(data.hand,GetUnitY(data.hand)-ARMSPEED*Sin(data.angle))
set dx=GetUnitX(data.caster)+DEFAULTOFFSET*Cos((GetUnitFacing(data.caster)+data.startangle)*bj_DEGTORAD)
set dy=GetUnitY(data.caster)+DEFAULTOFFSET*Sin((GetUnitFacing(data.caster)+data.startangle)*bj_DEGTORAD)
if IsUnitInRangeXY(data.hand,dx,dy,10) then
set data.phase=0
set data.targ=null
set data.offset=DEFAULTOFFSET
else
set dx=GetUnitX(data.hand)-GetUnitX(data.caster)
set dy=GetUnitY(data.hand)-GetUnitY(data.caster)
set data.angle=Atan2(dy,dx)
set data.offset=SquareRoot(dx*dx+dy*dy)
call data.PosNormal(data.offset)
endif
endif
endif
set int=int+1
endloop
endmethod
static method create takes nothing returns Arm
local Arm data=Arm.allocate()
if Arm.count==0 then
set Arm.ArmTimer=NewTimer()
call TimerStart(Arm.ArmTimer,0.025,true,function Arm.callback)
endif
set data.index=Arm.count
set Arm.Data[Arm.count]=data
set Arm.done[Arm.count]=false
set Arm.count=Arm.count+1
return data
endmethod
endstruct
globals
private aBuffType buffType = 0
endglobals
private function AbilLearn takes aBuff buffType returns nothing
local integer int=0
local Arm data
if Arm.HTable[GetHandleId(buffType.caster)][int]==0 then
if Arm.ArmTable[buffType]==0 then
set Arm.ArmTable[buffType]=Table.create()
endif
loop
exitwhen int==MAXARMS
set Arm.HTable[GetHandleId(buffType.caster)][int]=Table.create()
set data=Arm.create()
set Arm.ArmTable[buffType][int]=data
set data.caster=buffType.caster
set data.angle=GetUnitFacing(data.caster)+Angles(int)
set data.startangle=Angles(int)
set data.offset=DEFAULTOFFSET
set Arm.HTable[GetHandleId(buffType.caster)][int]=data
set data.phase=0
call data.createChain()
set int=int+1
endloop
else
loop
exitwhen int==MAXARMS
set data=Arm.HTable[GetHandleId(buffType.caster)][int]
set Arm.done[data.index]=false
set data.phase=0
set data.angle=GetUnitFacing(data.caster)+Angles(int)
set data.startangle=Angles(int)
set data.offset=DEFAULTOFFSET
call data.createChain()
set int=int+1
endloop
endif
endfunction
private function CleanArms takes aBuff buffType returns nothing
local integer int=0
local Arm data
loop
exitwhen int==MAXARMS
set data=Arm.ArmTable[buffType][int]
call data.KillArm()
set Arm.done[data.index]=true
set int=int+1
endloop
endfunction
private function Init takes nothing returns nothing
set buffType=aBuffType.create()
set buffType.eventCreate=AbilLearn
set buffType.eventCleanup=CleanArms
set buffType.countsAsBuff=false
call NewABuffHeroSkill(ABILID,buffType)
call SetupSpellVariables()
call ConfigureDamageOptions()
call Preload(CHAINMDL)
call Preload(CHAINHAND)
call Preload(ABSORBMDL)
call PreloadStart()
endfunction
endlibrary
</i></i></i></i>