• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Point/Target ability pré-cast detection

Status
Not open for further replies.
Level 13
Joined
Mar 24, 2013
Messages
1,105
As Wiet said, not really.

Can you describe what you want to do? Someone might think of a way that is agreeable to you.

One way off the top of my head that I think could work would be...

The unit you want to know when they are in the precast phase abilities are dummy instant cast abilities.

Upon cast you select a dummy unit and force the player to press a specific hotkey, this should bring up the targeting image of the "real ability."

At this point you would know they are in the preselect phase and do whatever you need to.
 
Level 11
Joined
Oct 9, 2015
Messages
721
Good job, pOke, you have an great idea! but instead I think it doesn't need the use of an dummy unit, I think I can simply detect the instant cast ability then remove that ability from the unit, add the new ability (point/target) and force the hotkey so that the target shows up then after it is cast, reset all the settings, the only setback would be to detect if the player cancels the targeting, but 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. what are you guys toughts about it ?
 
Last edited:
Level 24
Joined
Aug 1, 2013
Messages
4,658
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.
 
Level 45
Joined
Feb 27, 2007
Messages
5,578
I feel like what I'm doing with my system is highly relevant. It's using forceuikey to recast a spell for additional targets. Basically just rebuilt an old vanilla JASS pre-1.24b code into vJASS, haven't perfected the method yet but it mostly works. The main spell targets the first point though, so not exactly what you're describing.
JASS:
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
 
Last edited:
Level 45
Joined
Feb 27, 2007
Messages
5,578
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.
Has anyone tried assigning an unused numerical order to an object through LUA and then ordering it through orderid functions?

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.
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.
 
Last edited by a moderator:
Level 45
Joined
Feb 27, 2007
Messages
5,578
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.

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.
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.

I just changed the loop code (and other stuff significantly) and edited the above post to include those changes.
 
Status
Not open for further replies.
Top