Flare
Stops copies me!
- Reaction score
- 662
My latest vJASS spell - Meathook
New name pending. If you have an idea, let me know
Read this before jumping to conclusions
BEWARE!
[del]Do NOT set your hook's range greater than MAXLINKS * DISTANCE. It will cause a bug which leaves the first hook link in existence, and I can't find how to fix it. My automatic range reduction feature doesn't seem to fix it...[/del]
Nevermind, I found a solution. If you exceed DISTANCE * MAXLINKS, the range will be reduce by (DISTANCE * 2)
Configurable options:
Requirements:
Importing instructions: (also located within map)
Updates:
Screenshots:
Spell code:
Huge thanks to Trollvottel for pointing out that I never attached my struct to .hittrig Couldn't have got this working without your help.
Bugs/suggestions:
If I have left anything out (either in this post, or in the map) or you have found a problem with the spell, let me know.
Credits:
If you use this spell, please credit me for making it (since I did put alot of time and effort into making it). You should also credit Trollvottel (unless he/she says otherwise) since he/she pointed out my mistake, thus fixing the ability.
New name pending. If you have an idea, let me know
Read this before jumping to conclusions
Before you say something like "this isn't the same as DotA's meathook", please stop yourself. This is NOT meant to be an exact replica of DotA's meathook, just another version of it that suits my personal taste. If you wish, you can alter the values to make it more DotA-like (altering the constants should be sufficient to make it DotA-like)
And yes, I know there is already a JASS version of Meathook made by Tinki3, but his version uses [DEL]Handle Vars[/DEL] Timer Ticker, unlike this version which uses HAIL
And yes, I know there is already a JASS version of Meathook made by Tinki3, but his version uses [DEL]Handle Vars[/DEL] Timer Ticker, unlike this version which uses HAIL
BEWARE!
[del]Do NOT set your hook's range greater than MAXLINKS * DISTANCE. It will cause a bug which leaves the first hook link in existence, and I can't find how to fix it. My automatic range reduction feature doesn't seem to fix it...[/del]
Nevermind, I found a solution. If you exceed DISTANCE * MAXLINKS, the range will be reduce by (DISTANCE * 2)
Configurable options:
Maximum number of links to any hook chain (this is automatically rectified if you go above a certain distance)
Collision radius for the hook
Scaling value of the hook
Scaling value of the hook segments
Distance between caster and hook when initially spawned
Special effect used for impact
Whether the effect is displayed on allies
Special effect used for pullback
Damage
Range
Collision radius for the hook
Scaling value of the hook
Scaling value of the hook segments
Distance between caster and hook when initially spawned
Special effect used for impact
Whether the effect is displayed on allies
Special effect used for pullback
Damage
Range
Requirements:
Code:
-NewGen World Editor
-HAIL system
-Ability to read :P (everything that can/should be altered is given a fairly descriptive comment. If you can't understand them, let me know)
Importing instructions: (also located within map)
Code:
//Importing instructions\\
----------------------------------------
//Copy the HAIL trigger into your map
//Copy the Meathook trigger into your map
//Copy the Hook Dummy unit, Link Dummy unit and Meathook ability into your map
*Copy the Hook (2) model from the Import Manager if you want the Abomination's hook.
*Copy the Meathookchain model from the Import Manager if you want the cool chain link.
//Adjust the SPELLID, HOOKID and LINKID values in the global block.
//They MUST match the rawcodes shown in Object Editor (press Ctrl - D to view the rawcodes)
//Adjust the remainder of the constants to suit your needs
//Add the Meathook ability to a unit.
//Cast the ability, and enjoy.
----------------------------------------
//End of importing instructions\\
Updates:
Code:
v1: Initial release
v2: Fixed a bug with ALLYHITSFX. If set to true, it would damage allies instead of displaying the special effect
v3: In range trigger was set to deal flat damage instead of the intended damage. Fixed
v4: Reverted to v1 to fix that bug with first segment. v2 and v3 fixes are included in this
v5: Implemented scaling (forgot it before), changed the hook model to Spellbreaker projectile
v6: Fixed the automatic range reducer. Now, if you exceed the maximum range, the range is reduced sufficiently to remove the first link
v7: Uses SetUnitX/Y instead of SetUnitPosition. Changed the dragback effect. New hook model - Kodo Beast axe. Previous coordinates are stored in real arrays, so the hook doesn't travel behind caster if it goes beyond map boundaries ^^
v8: Replaced RemoveUnit with a combination of KillUnit and ShowUnit
v9: Replaced the use of a triggeraction with a triggercondition. Added new hook model (Abomination hook, thanks to Uberplayer/OneBadPsycho) and chain link model (taken from Abomination, thanks to Metal-Guard)
Screenshots:
Hooks flying - No targets
Spell code:
JASS:
//Meat Hook
//By Flare
//Constructed using vJASS and Strilanc's HAIL system
//Thanks to Strilanc for creating HAIL
//Thanks to DotA for the inspiration to make my own version of the spell
//Thanks to Trollvottel for pointing out that I never attached the struct to the In Range trigger.
//Thanks to Metal-Guard/Uberplayer for the awesome hook and chain link, and to OneBadPsycho for the skinning of both <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite1" alt=":)" title="Smile :)" loading="lazy" data-shortname=":)" />
//This spell would not have been possible without the above people/map
//REQUIRES NEWGEN WORLDEDITOR TO OPEN\\
//Importing instructions\\
//----------------------------------------\\
//Importing instructions are located in the trigger comment called "Importing"
scope Meathook
//Initializing HAIL
//Do -NOT-, under ANY circumstances, change the following line unless you know exactly what you are doing
//! runtextmacro HAIL_CreateProperty("HookData", "integer", "")
//SetUnitXY function used for moving the hook
private function SetUnitXY takes unit u, real x, real y returns nothing
if x<GetRectMaxX(bj_mapInitialPlayableArea) and x>GetRectMinX(bj_mapInitialPlayableArea) and y<GetRectMaxY(bj_mapInitialPlayableArea) and y>GetRectMinY(bj_mapInitialPlayableArea) then
call SetUnitX(u,x)
call SetUnitY(u,y)
endif
endfunction
globals
//This limits the number of links to the hook's chain.
//Setting it higher will limit the MUI capability (currently, 81 instances max)
//Setting it lower will potentially limit the maximum distance travelled
//^--Depends on how close the links will be spawned beside each other
constant integer MAXLINKS = 100
//Rawcodes\\
//Base ability (Meathook) rawcode
private constant integer SPELLID = 039;A000039;
//Hook dummy rawcode
private constant integer HOOKID = 039;h001039;
//Link dummy ID
private constant integer LINKID = 039;h000039;
//Other constants\\
//Timer interval
private constant real TIMERINTERVAL = 0.03
//Distance travelled per second by hook
private constant real SPEED = 800
//Distance moved per timer interval
//Also used for distance between hook links
private constant real DISTANCE = SPEED*TIMERINTERVAL
//DISTANCE * MAXLINKS = Maximum possible distance. Increase SPEED, TIMERINTERVAL (not highly advised) or MAXLINKS to increase the maximum possible range.
//Currently, maximum range is 2400 (100 * (800*0.03) = 100 * 24 = 2400)
//-180 degrees in radians
//This is used for hook retraction
private constant real REVERSEANGLE = Deg2Rad (-180)
//Collision range
private constant real COLLIDERANGE = 50
//Hook size scale
private constant real HOOKSCALE = .75
//Link size scale
private constant real LINKSCALE = .75
//How far in front of the hero the hook will form
private constant real SPAWNOFFSET = 50
//Effect created on hook hit
//NOTE: Make sure you replace a single \ with \\ if you change this i.e.
//Abilities\Spells\Other\Stampede\StampedeMissileDeath.mdl --> Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl
private constant string FXSTRING = "Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl"
//Pullback effect
//Set this to "" to remove the effect created
private constant string PULLFX = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl"
//Determines whether the above effect is created when an ally is hit
//By default, no SFX will be created when hitting an ally.
private constant boolean ALLYHITSFX = false
endglobals
//Functions for customizable damage/range
//To have a constant damage dealt at all levels, change the "return base + level*multiplier" to "return 100" (for example)
//DONT INCLUDE QUOTE MARKS
private function Damage takes unit u returns real
local real base = 0
local real level = I2R (GetUnitAbilityLevel (u, SPELLID))
local real multiplier = 100.
return base + level*multiplier
endfunction
//To have a constant range dealt at all levels, change the "return base + level*multiplier" to "return 1000" (for example)
//DONT INCLUDE QUOTE MARKS
//NOTE:
//If hook range exceeds DISTANCE * MAXLINKS, it will be reduced by (DISTANCE * 2)
//Best thing to do is just not exceed the limit.
private function Range takes unit u returns real
local real base = 200
local real level = I2R (GetUnitAbilityLevel (u, SPELLID))
local real multiplier = 200.
return base + level*multiplier
endfunction
//DO NOT EDIT BELOW HERE
//--------------------------------------------------------------------------------\\
//Struct data for the hook
private struct Hookdata
unit hookcaster //The unit who casts Meathook
unit hooktarget //The target struck
unit array hooklink[MAXLINKS] //The links/segments of the hook chain
integer linkcounter = 0 //Used for creating/destroying the above units
real hookangle //Angle at which the hook travels
real currentdistance //Current distance travelled by the hook
real maxdistance //Maximum distance travelled before the hook retracts
real damage//The damage, obviously
unit hook//The hook itself
real array x[MAXLINKS]
real array y[MAXLINKS]
real targetangle //Used to keep the angle between hook and victim correct on retract
real targetdistance //Same as above, except relates to distance instead of angle
boolean retracton
boolean hooked = false //When 0, nothing is hooked. When 1, something is hooked
timer hooktimer //Timer used for extension/retraction
trigger hittrig //Trigger used for detecting when a unit is hit
triggeraction hitaction //Action set for hittrig
method onDestroy takes nothing returns nothing //Cleaning up triggers/timers/HAIL
call KillUnit (.hook)
call ShowUnit (.hook, false)
call KillUnit (.hooklink[0])
call ShowUnit (.hooklink[0], false)
set .retracton = false
set .hooked = false
call TriggerRemoveAction (.hittrig, .hitaction)
call ResetHookData (.hooktimer)
call DestroyTimer (.hooktimer)
call ResetHookData (.hittrig)
call DestroyTrigger (.hittrig)
endmethod
endstruct
//Hit trigger\\
//Hit trigger conditions
private function HitCond takes nothing returns boolean
local Hookdata a = GetHookData (GetTriggeringTrigger ())
local unit u = GetTriggerUnit ()
local real hx = GetUnitX (a.hook)
local real hy = GetUnitY (a.hook)
local real tx = GetUnitX (u)
local real ty = GetUnitY (u)
local real x = tx - hx
local real y = ty - hy
if GetWidgetLife (u) >= 1. and IsUnitType (u, UNIT_TYPE_STRUCTURE) != true and IsUnitType (u, UNIT_TYPE_FLYING) != true and IsUnitType (u, UNIT_TYPE_ETHEREAL) != true and IsUnitType (u, UNIT_TYPE_MECHANICAL) != true then
if a.hooked == false and u != a.hookcaster then
set a.hooktarget = u
set a.hooked = true
set a.retracton = true
set a.targetangle = Atan2 (y, x)
set a.targetdistance = SquareRoot ((x*x) + (y*y))
if IsUnitEnemy (u, GetOwningPlayer (a.hookcaster)) then
call DestroyEffect (AddSpecialEffect (FXSTRING, tx, ty))
call UnitDamageTarget (a.hookcaster, u, a.damage, true, true, ATTACK_TYPE_HERO, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
else
if ALLYHITSFX == true then
call DestroyEffect (AddSpecialEffect (FXSTRING, tx, ty))
endif
endif
endif
endif
set u = null
return true
endfunction
private function TimerActions takes nothing returns nothing
local timer t = GetExpiredTimer ()
local Hookdata a = GetHookData (t)
local real hx1 = GetUnitX (a.hook)
local real hy1 = GetUnitY (a.hook)
local real tx2
local real ty2
local real x
local real y
if a.retracton == false then
set a.linkcounter = a.linkcounter + 1
set a.hooklink[a.linkcounter] = CreateUnit (GetOwningPlayer (a.hookcaster), LINKID, hx1, hy1, Rad2Deg (a.hookangle))
call SetUnitScale (a.hooklink[a.linkcounter], LINKSCALE, LINKSCALE, LINKSCALE)
call SetUnitTimeScale (a.hooklink[a.linkcounter], 0)
set a.x[a.linkcounter] = hx1 + Cos(a.hookangle)*DISTANCE
set a.y[a.linkcounter] = hy1 + Sin (a.hookangle)*DISTANCE
call SetUnitXY (a.hook, a.x[a.linkcounter], a.y[a.linkcounter])
else
call KillUnit (a.hooklink[a.linkcounter])
call ShowUnit (a.hooklink[a.linkcounter], false)
set a.linkcounter = a.linkcounter - 1
endif
call SetUnitXY (a.hook, a.x[a.linkcounter], a.y[a.linkcounter])
if a.linkcounter == 0 and a.retracton == true then
set a.currentdistance = 0
call a.destroy ()
endif
if a.currentdistance >= a.maxdistance then
set a.retracton = true
else
set a.currentdistance = a.currentdistance + DISTANCE
endif
if a.hooked == true then
set tx2 = a.x[a.linkcounter] + Cos (a.targetangle) * a.targetdistance
set ty2 = a.y[a.linkcounter] + Sin (a.targetangle) * a.targetdistance
call SetUnitXY (a.hooktarget, tx2, ty2)
call DestroyEffect (AddSpecialEffect (PULLFX, tx2, ty2))
endif
set t = null
endfunction
private function MeathookCond takes nothing returns boolean
return GetSpellAbilityId () == SPELLID
endfunction
private function MeathookActions takes nothing returns nothing
local Hookdata a = Hookdata.create ()
local location targetloc
local real cx
local real cy
local real tx
local real ty
local real sx
local real sy
set a.hookcaster = GetTriggerUnit ()
set a.linkcounter = 0
if GetSpellTargetUnit () == null then
set targetloc = GetSpellTargetLoc ()
set tx = GetLocationX (targetloc)
set ty = GetLocationY (targetloc)
call RemoveLocation (targetloc)
else
set tx = GetUnitX (GetSpellTargetUnit ())
set ty = GetUnitY (GetSpellTargetUnit ())
endif
set a.retracton = false
set a.maxdistance = Range (a.hookcaster)
if a.maxdistance >= DISTANCE * MAXLINKS then
set a.maxdistance = (DISTANCE * MAXLINKS) - 2*DISTANCE
endif
set a.damage = Damage (a.hookcaster)
set cx = GetUnitX (a.hookcaster)
set cy = GetUnitY (a.hookcaster)
set a.hookangle = Atan2 (ty-cy, tx-cx)
set sx = cx + Cos (a.hookangle)*SPAWNOFFSET
set sy = cy + Sin (a.hookangle)*SPAWNOFFSET
set a.hook = CreateUnit (GetOwningPlayer (a.hookcaster), HOOKID, sx, sy, Rad2Deg (a.hookangle))
set a.x[a.linkcounter] = sx
set a.y[a.linkcounter] = sy
call SetUnitTimeScale (a.hook, 0)
call SetUnitScale (a.hook, HOOKSCALE, HOOKSCALE, HOOKSCALE)
set a.hooklink[a.linkcounter] = CreateUnit (GetOwningPlayer (a.hookcaster), LINKID, sx, sy, Rad2Deg (a.hookangle))
call SetUnitScale (a.hooklink[a.linkcounter], LINKSCALE, LINKSCALE, LINKSCALE)
call SetUnitTimeScale (a.hooklink[a.linkcounter], 0)
set a.hooktimer = CreateTimer ()
set a.hittrig = CreateTrigger ()
call SetHookData (a.hooktimer, a)
call TimerStart (a.hooktimer, TIMERINTERVAL, true, function TimerActions)
call TriggerRegisterUnitInRange (a.hittrig, a.hook, COLLIDERANGE, null)
call TriggerAddCondition (a.hittrig, Condition (function HitCond))
call SetHookData (a.hittrig, a)
//set a.hitaction = TriggerAddAction (a.hittrig, function HitActions)
set targetloc = null
endfunction
public function InitTrig takes nothing returns nothing
local trigger Meathook = CreateTrigger ()
call TriggerRegisterAnyUnitEventBJ (Meathook, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition (Meathook, Condition (function MeathookCond))
call TriggerAddAction (Meathook, function MeathookActions)
endfunction
endscope
Huge thanks to Trollvottel for pointing out that I never attached my struct to .hittrig Couldn't have got this working without your help.
Bugs/suggestions:
If I have left anything out (either in this post, or in the map) or you have found a problem with the spell, let me know.
Credits:
If you use this spell, please credit me for making it (since I did put alot of time and effort into making it). You should also credit Trollvottel (unless he/she says otherwise) since he/she pointed out my mistake, thus fixing the ability.