- Joined
- Oct 9, 2015
- Messages
- 721
Is it possible to detect when an unit enters the "select a target" state of an point or target ability ?
library MPTS initializer Init requires GroupUtils, LinkedList, xefx, B2Sguys
globals
private constant integer MAIN_AID = 'A004'
private constant integer NEXT_AID = 'A005'
private constant integer CNCL_AID = 'A003'
private constant integer BUFF_AID = 'A001'
private constant integer BUFF_BID = 'B000'
private integer NEXT_ORD = OrderId("rainoffire")
private integer CNCL_ORD = OrderId("taunt")
private integer BUFF_ORD = OrderId("cripple")
private constant string NEXT_HKY = "j" //always lowercase
private constant string CNCL_HKY = "l"
private constant string INDICATOR_EFFECT = "Objects\\InventoryItems\\BattleStandard\\BattleStandard.mdl"
private constant real INDICATOR_FACING = 270.00*bj_PI/180.00
private constant real CheckInterval = 0.075 //I wouldn't recommend anything lower than 0.06
private constant real LOCKOUT = 0.20
private constant real CastHotkeyPressDelay = 0.027 //It takes 1 frame of the game to update the UI for the ForceUIKey to work, so
//This value must be the length of 1 frame or more, and that changes constantly.
//The best you can do is guess... if the game is laggy (for any player!) this value
//Will probably need to be higher.
private group Casting
private integer array NumCasting
private integer LP //GetLocalPlayer() stored
private timer CancelTimer
private List Instances
private hashtable HT
private constant integer INSTANCE_STRUCT = 0
endglobals
/*
constant function Flare_DummyUnitId takes nothing returns integer
return 'h000' //Must have 0 cast point and cast backswing
endfunction
*/
//private keyword CancelCheck
private keyword CastData
private function CancelCheck takes nothing returns nothing
local integer i = 0
local Link l = Instances.first
local CastData cd
loop
exitwhen l == 0
set cd = CastData(l.data)
if cd.NoDetect > 0 then
set cd.NoDetect = cd.NoDetect-1
endif
set l = l.next
endloop
loop
if NumCasting[i] > 0 and LP == i then
call ForceUIKey(CNCL_HKY)
endif
set i = i+1
exitwhen i > 11
endloop
endfunction
private struct target
unit u
real x
real y
real z
xefx f
static method create takes unit u, real x, real y, real z, string fx returns target
local target t = target.allocate()
set t.u = u
set t.x = x
set t.y = y
set t.z = z
set t.f = xefx.create(x,y,INDICATOR_FACING)
set t.f.fxpath = fx
return t
endmethod
endstruct
private struct CastData
unit u
integer p
string fxp
List tgts
integer xNoDetect
method TargetXY takes real x, real y returns nothing
call Link.create(.tgts, target.create(null, x, y, 0.00, .fxp))
call BJDebugMsg("Target "+I2S(.tgts.size)+": ("+R2S(target(.tgts.first.data).x)+", "+R2S(target(.tgts.first.data).y)+")")
endmethod
// method TargetUnit takes unit u returns nothing
// endmethod
method operator NoDetect takes nothing returns integer
return .xNoDetect
endmethod
method operator NoDetect= takes integer i returns nothing
if i > .xNoDetect then
if .xNoDetect == 0 then
set NumCasting[.p] = NumCasting[.p]-1
endif
set .xNoDetect = i
elseif i < .xNoDetect and i >= 0 then
if i == 0 then
set NumCasting[.p] = NumCasting[.p]+1
endif
set .xNoDetect = i
endif
endmethod
method onDestroy takes nothing returns nothing
call GroupRemoveUnit(Casting, .u)
set NumCasting[.p] = NumCasting[.p]-1
call RemoveSavedInteger(HT, INSTANCE_STRUCT, GetHandleId(.u))
call .tgts.destroy()
call Instances.search(this).destroy()
if Instances.size == 0 then
call PauseTimer(CancelTimer)
endif
endmethod
static method create takes unit u, string fx returns CastData
local CastData cd = CastData.allocate()
set cd.u = u
set cd.p = GetPlayerId(GetOwningPlayer(u))
set cd.tgts = List.create()
set cd.NoDetect = 0
call GroupAddUnit(Casting, cd.u)
if LP == cd.p then
set cd.fxp = fx
else
set cd.fxp = ""
endif
set NumCasting[cd.p] = NumCasting[cd.p]+1
call Link.create(Instances, cd)
if Instances.size == 1 then
call TimerStart(CancelTimer, CheckInterval, true, function CancelCheck)
endif
return cd
endmethod
endstruct
private function CancelCast takes nothing returns nothing
local unit u = GetTriggerUnit()
local CastData cd
call BJDebugMsg("None order: "+I2S(GetIssuedOrderId())+OrderId2String(GetIssuedOrderId()))
// if GetSpellAbilityId() == CNCL_AID and IsUnitInGroup(u, Casting) then
if GetIssuedOrderId() == CNCL_ORD and IsUnitInGroup(u, Casting) then
set cd = CastData(LoadInteger(HT, INSTANCE_STRUCT, GetHandleId(u)))
if cd.NoDetect == 0 then
call UnitRemoveAbility(cd.u, NEXT_AID)
call UnitRemoveAbility(cd.u, CNCL_AID)
//call SetUnitState(cd.u, UNIT_STATE_MANA, GetUnitState(U, UNIT_STATE_MANA)+Flare_ManaCost(Level))
call UnitRemoveAbility(cd.u, MAIN_AID)
call UnitAddAbility(cd.u, MAIN_AID)
call UnitMakeAbilityPermanent(cd.u, true, MAIN_AID)
//call SetUnitAbilityLevel(U, MAIN_AID, Level)
//Destroy Shit here
call cd.destroy()
else
//set cd.NoDetect = cd.NoDetect-1
endif
endif
set u = null
endfunction
private function NextPointCast takes nothing returns nothing
local unit u = GetTriggerUnit()
local CastData cd
call BJDebugMsg("Point order: "+I2S(GetIssuedOrderId())+OrderId2String(GetIssuedOrderId()))
if IsUnitInGroup(u, Casting) and GetIssuedOrderId() == NEXT_ORD then
set cd = CastData(LoadInteger(HT, INSTANCE_STRUCT, GetHandleId(u)))
call cd.TargetXY(GetOrderPointX(), GetOrderPointY())
set cd.NoDetect = R2I(LOCKOUT/CheckInterval+0.50)
call IssueImmediateOrder(cd.u, OrderId2String(CNCL_ORD))
//call IssueImmediateOrder(cd.u,"stop")
if LP == cd.p then
call ForceUIKey(NEXT_HKY)
endif
endif
endfunction
/*
if PointNum >= 3 then
set I = 0
loop
set I = I+1
exitwhen I > PointNum
call DestroyEffect(GetAttachedEffect(gg_trg_Flare, Reference+"Effect"+I2S(I)))
endloop
call UnitRemoveAbility(U, Flare_NextPointAbilityId())
call Flare_CastFinished(P, Level, U, Reference)
else
call AttachBoolean(U, "Flare_NoDetect", true)
call IssueImmediateOrder(U, Flare_CancelDetectorOrderId())
if GetLocalPlayer() == P then
call ForceUIKey(Flare_NextPointHotkey())
endif
endif
*/
private function ForceKey takes nothing returns nothing
local timer t = GetExpiredTimer()
local CastData cd = CastData(GetTimerData(t))
set cd.NoDetect = R2I(LOCKOUT/CheckInterval+0.50)
call IssueImmediateOrder(cd.u, OrderId2String(CNCL_ORD))
if LP == cd.p then
call ForceUIKey(NEXT_HKY)
endif
call ReleaseTimer(t)
set t = null
endfunction
private function MainCast takes nothing returns nothing
local integer Aid = GetSpellAbilityId()
local CastData cd
if Aid == MAIN_AID then
set cd = CastData.create(GetTriggerUnit(), INDICATOR_EFFECT)
call cd.TargetXY(GetSpellTargetX(),GetSpellTargetY())//GetOrderPointX(),GetOrderPointY())
call SaveInteger(HT, INSTANCE_STRUCT, GetHandleId(cd.u), cd)
call UnitAddAbility(cd.u, NEXT_AID)
call UnitAddAbility(cd.u, CNCL_AID)
call TimerStart(NewTimerEx(cd), CastHotkeyPressDelay, false, function ForceKey)
endif
endfunction
/*
function Flare_NextPointCast takes nothing returns nothing
local unit U = GetTriggerUnit()
local player P = GetOwningPlayer(U)
local string Reference = "Flare_PlayerData"+I2S(GetPlayerId(P))+"_"
local integer PointNum = GetAttachedInt(gg_trg_Flare, Reference+"PointsDone")+1
local string EffectPath = ""
local integer Level = GetAttachedInt(gg_trg_Flare, Reference+"Level")
local integer I
local real X = GetOrderPointX()
local real Y = GetOrderPointY()
if GetLocalPlayer() == P then
set EffectPath = Flare_PointIndicatorEffect()
endif
call AttachInt(gg_trg_Flare, Reference+"PointsDone", PointNum)
call AttachReal(gg_trg_Flare, Reference+"X"+I2S(PointNum), X)
call AttachReal(gg_trg_Flare, Reference+"Y"+I2S(PointNum), Y)
call AttachObject(gg_trg_Flare, Reference+"Effect"+I2S(PointNum), AddSpecialEffect(EffectPath, X, Y))
if PointNum >= 3 then
set I = 0
loop
set I = I+1
exitwhen I > PointNum
call DestroyEffect(GetAttachedEffect(gg_trg_Flare, Reference+"Effect"+I2S(I)))
endloop
call UnitRemoveAbility(U, Flare_NextPointAbilityId())
call Flare_CastFinished(P, Level, U, Reference)
else
call AttachBoolean(U, "Flare_NoDetect", true)
call IssueImmediateOrder(U, Flare_CancelDetectorOrderId())
if GetLocalPlayer() == P then
call ForceUIKey(Flare_NextPointHotkey())
endif
endif
set U = null
endfunction
*/
private function Init takes nothing returns nothing
local trigger T = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(T, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(T, function MainCast)
set T = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(T, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
call TriggerAddAction(T, function NextPointCast)
set T = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(T, EVENT_PLAYER_UNIT_ISSUED_ORDER)//EVENT_PLAYER_UNIT_SPELL_CAST)
call TriggerAddAction(T, function CancelCast)
set T = null
set Casting = NewGroup()
set LP = GetPlayerId(GetLocalPlayer())
set Instances = List.create()
set HT = InitHashtable()
set CancelTimer = NewTimer()
call BJDebugMsg(I2S(CNCL_ORD))
set CNCL_ORD = OrderId("taunt")
set NEXT_ORD = OrderId("rainoffire")
set BUFF_ORD = OrderId("cripple")
call BJDebugMsg(I2S(CNCL_ORD))
endfunction
endlibrary
Has anyone tried assigning an unused numerical order to an object through LUA and then ordering it through orderid functions?I had that idea too a while back to create custom targeting images... but you cant really track mouse position that well.
And there is the problem that you only have a few abilities that you can use to detect when the player clicks on it... unless you dont care about order interruption.
Watching for escape won't catch when you right click to cancel casting or (hilariously) clicking the escape icon on the UI to stop casting. You need to periodically try to order a player to press a key (haven't tried just ordering the unit) to that corresponds to a cancel ability.I think I can set an boolean to true when I remove the instant cast ability and detect escape key press and if that boolean is set true, then remove the point/target ability and re-add the instant ability and reset the boolean to false.
if NumCasting[i] > 0 and LP == i then
if NumCasting[LP] > 0 then
Why that? That's my GetLocalPlayer() check with the LP == i, otherwise it causes hotkey force for ALL players.if NumCasting[i] > 0 and LP == i then
->
if NumCasting[LP] > 0 then
There is a loop because at least 1 unit for each player could be casting a multi-point target spell at a time and I'm just iterating over the players with active casts. I'm not sure if 2 is possible, but the system is written so it would work with multiple units per player simultaneously. The cancel check minimum is I think linked to the frame rate as well, hence it not working below a certain threshold.You dont require a loop... nor anything else really.
I assume that you can use 0.03 as interval on the cancel check.
I'd have to check this out for myself.
loop
if NumCasting[i] > 0 and LP == i then
call ForceUIKey(CNCL_HKY)
endif
set i = i+1
exitwhen i > 11
endloop
if NumCasting[LP] > 0 then
call ForceUIKey(CNCL_HKY)
endif