- Joined
- Feb 3, 2013
- Messages
- 277
Hi all once again, I've been messing around with some more vJass, trying to understand how to use structs; This time I've made a meat hook like spell from DotA. The trigger works completely as intended (this is because I looked at another script to start me off) - but I'm COMPLETELY STUMPED as to how it works. I'm confused because the function Create and Destroy return a boolexpr value - and whether the spell works properly is dependent on the return value. And this is where I need help - can someone explain to me why the boolexpr values are needed in order for this spell to work?
I feel like the problem for me starts when timer's are involved - I understand that I'm using the system to attach struct handles to a timer initially, but the TT system uses only one timer. If i set the Create and Destroy functions so that they don't have any return values, I get an error in game and the hook doesn't work properly. And I know, it shouldn't work because that will lead to stacking struct handles on a single timer. But why do functions that return boolexpr values work?
the Time Ticker System by Cohadar.
once again thanks in advance for all your help <3
I feel like the problem for me starts when timer's are involved - I understand that I'm using the system to attach struct handles to a timer initially, but the TT system uses only one timer. If i set the Create and Destroy functions so that they don't have any return values, I get an error in game and the hook doesn't work properly. And I know, it shouldn't work because that will lead to stacking struct handles on a single timer. But why do functions that return boolexpr values work?
JASS:
scope MHStruct initializer init
globals
private constant real INC = 30. // Distance between hooks
private constant real BASE_RANGE = 2000. //Base range of the hook; Formula is BASE_RANGE + (INC_RANGE * LVL)
private constant real INC_RANGE = 250. // See above
private constant real BASE_DAMAGE = 100. //Base damage of the hook; Formauls is BASE_DAMAGE + (INC_DAMAGE * LVL)
private constant real INC_DAMAGE = 50. // See above
private constant real AOE = 115. // AoE for hook to hit
private constant real STR_RATIO = 0.1 // Drag damage is based on Heroes strength ratio
private constant integer DC_ID = 'h000' // Hook Chain Dummy raw ID code
private constant integer DH_ID = 'h001' // Hook Head Dummy raw ID code
private constant integer MH_ID = 'A003' // Hook Ability raw ID code
private constant string FX = "Objects\\Spawnmodels\\Human\\HumanBlood\\BloodElfSpellThiefBlood.mdl" // Effects displayed upon enemy encounter
endglobals
private struct dat
unit c // Caster
unit array d_c[300] // Dummy hookchain - Maximum number of dummy chains per instance is 300
unit d_h // Dummy hookhead -
unit t // Target
location p // Location of caster
location pp // PolarOffset from Location of Caster towards ang
real ang // Angle between location of caster and spell target location
real rng // Current offset distance of meathook
real str // 15% of Hero's Str including bonuses
integer lvl // Level of Meathook for user
integer i // Count how many hook chains there are
boolean boo // Boolean expression deciding whether a unit is hooked or not
endstruct
//=======================================================
// DO NOT TOUCH BELOW THIS LINE
//========================================================
private function MHS_Filter takes nothing returns boolean // Filter unit must be a non structure, alive unit; Filter unit can not be a unit type of dummy hookchain or dummy hookhead; Filter Unit can not be the casting unit
return GetUnitState(GetFilterUnit(), UNIT_STATE_LIFE) > 0 and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == FALSE and GetUnitTypeId(GetFilterUnit()) != DH_ID and GetUnitTypeId(GetFilterUnit()) != DC_ID and GetFilterUnit() != bj_ghoul[50]
endfunction
private function Destroy takes nothing returns boolean
local dat d = TT_GetData()
set d.rng = d.rng - INC
set d.pp = PolarProjectionBJ(d.p, d.rng, d.ang)
if d.boo == TRUE then
call SetUnitPositionLoc(d.t, d.pp)
if IsUnitEnemy(d.t, GetOwningPlayer(d.c)) then
call UnitDamageTarget(d.c, d.t, d.str, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
endif
endif
if d.i == 1 then
call RemoveUnit(d.d_h)
else
call SetUnitPositionLoc(d.d_h, d.pp)
call RemoveUnit(d.d_c[d.i])
endif
if d.i == 0 then
call SetUnitPathing(d.t, true)
set d.c = null
set d.t = null
call d.destroy()
return TRUE // HERE!!
endif
set d.i = d.i - 1
return FALSE // HERE!
endfunction
private function Create takes nothing returns boolean
local dat d = TT_GetData()
local group g = CreateGroup()
set d.rng = d.rng + INC
set d.pp = PolarProjectionBJ(d.p, d.rng, d.ang)
call GroupEnumUnitsInRangeOfLoc(g, d.pp, AOE, Filter(function MHS_Filter))
set d.t = FirstOfGroup(g)
if d.i == 1 and d.t == null then
set d.d_h = CreateUnitAtLoc(GetOwningPlayer(d.c), DH_ID, d.pp, d.ang)
call SetUnitScale(d.d_h, 15., 15., 15.)
elseif d.i >= 2 and d.t == null then
call SetUnitPositionLoc(d.d_h, d.pp)
set d.d_c[d.i] = CreateUnitAtLoc(GetOwningPlayer(d.c), DC_ID,d.pp, d.ang)
endif
if d.rng >= BASE_RANGE + INC_RANGE * I2R(d.lvl) then
set d.boo = FALSE
call TT_Start(function Destroy, d)
return TRUE /// HERE!!!
elseif d.t != null then
set d.boo = TRUE
call TT_Start(function Destroy, d)
if IsUnitEnemy(d.t, GetOwningPlayer(d.c)) == TRUE then
call UnitDamageTarget(d.c, d.t, BASE_DAMAGE + INC_DAMAGE * d.lvl, false, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null)
call DestroyEffect(AddSpecialEffectTarget(FX, d.t, "chest"))
endif
call SetUnitPathing(d.t, FALSE)
return TRUE // HERE!!!
endif
set d.i = d.i + 1
return FALSE // HERE!!!
endfunction
private function MHS_Cond takes nothing returns boolean
return GetSpellAbilityId() == MH_ID
endfunction
private function MHS_Actions takes nothing returns nothing
local dat d = dat.create()
local location loc = GetSpellTargetLoc()
set bj_ghoul[50] = GetTriggerUnit()
set d.rng = INC
set d.i = 1
set d.c = GetTriggerUnit()
set d.p = GetUnitLoc(GetTriggerUnit())
set d.lvl = GetUnitAbilityLevel(d.c, MH_ID)
set d.ang = AngleBetweenPoints(d.p, loc)
set d.str = I2R(GetHeroStr(d.c, true)) * STR_RATIO
call TT_Start(function Create, d)
call RemoveLocation(loc)
set loc = null
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
loop
exitwhen i == 15
call TriggerRegisterPlayerUnitEvent(t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
set i = i + 1
endloop
call TriggerAddCondition(t, Condition(function MHS_Cond))
call TriggerAddAction(t, function MHS_Actions)
endfunction
endscope
the Time Ticker System by Cohadar.
JASS:
//==============================================================================
// TT -- TIMER TICKER SYSTEM BY COHADAR -- v3.4
//==============================================================================
//
// PURPOUSE OF TT:
// * Passing data to timers
// * using only one timer for all actions
//
// PROS:
// * It is ridiculously easy to use.
// * It is faster than ABC (and any other attaching system)
//
// CONS:
// * It can be used only for high-frequency timers
// This method fails if PERIOD > 0.1 second
//
// FUNCTIONS:
// * TT_Start(userFunc, struct)
// TT_GetData() -> struct
//
// * userFunc is a user function that takes nothing and return boolean
// it will be periodically called by the system until it returns true.
//
// * TT_GetData() is a function that can be used inside userFunc
// TT_GetData() will return struct passed to Start function
//
// DETAILS:
// * All user functions are stored in an array.
// Timer will call all those functions each period.
//
// * While user function returns false timer will continue to call it each period
// Once user function returns true it will be removed from system
//
// REQUIREMENTS:
// * NewGen v4c and above (there might be some problems with older NewGen's)
//
// THANKS TO:
// * Vexorian - he was nagging me so much about how attaching to timers is bad
// that I finally had to do something about it.
//
// HOW TO IMPORT:
// * Just create a trigger named TT
// * convert it to text and replace the whole trigger text with this one
//
//==============================================================================
library TT initializer Init
globals
// List of recommended periods:
// 0.04 = 25 calls per second
// 0.03125 = 32 calls per second
// 0.025 = 40 calls per second
// 0.02 = 50 calls per second
public constant real PERIOD = 0.02
// One Timer to rule them all, One Timer to find them,
// One Timer to call them all and in the jass bind them
// In the land of warcraft where the desyncs lie.
private timer Timer = CreateTimer()
private integer Data
private integer Counter = 0
private trigger array Triggz
private integer array Dataz
endglobals
//==============================================================================
private function Handler takes nothing returns nothing
local trigger swap
local integer i = Counter
loop
exitwhen i<=0
set Data = Dataz[i]
if TriggerEvaluate(Triggz[i]) then
set swap = Triggz[i]
call TriggerClearConditions(swap)
set Triggz[i] = Triggz[Counter]
set Triggz[Counter] = swap
set Dataz[i] = Dataz[Counter]
set Counter = Counter - 1
endif
set i = i - 1
endloop
// who can guess why am I not nulling swap here?
endfunction
//==============================================================================
public function Start takes code userFunc, integer data returns nothing
debug if userFunc == null then
debug call BJDebugMsg("ERROR: TT_Start - null userFunc")
debug return
debug endif
set Counter = Counter + 1
if Triggz[Counter] == null then
set Triggz[Counter] = CreateTrigger()
endif
call TriggerAddCondition(Triggz[Counter], Condition(userFunc))
set Dataz[Counter] = data
endfunction
//==============================================================================
// Call this function only inside the userFunc you passed to Start
//==============================================================================
public function GetData takes nothing returns integer
return Data
endfunction
//==============================================================================
private function Init takes nothing returns nothing
call TimerStart(Timer, PERIOD, true, function Handler)
endfunction
endlibrary
//==============================================================================
// END OF TIMER TICKER SYSTEM
//==============================================================================
once again thanks in advance for all your help <3