1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[vJASS] [Snippet] LockBone

Discussion in 'JASS Resources' started by Quilnez, Nov 1, 2014.

  1. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,268
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    Code
    Code (vJASS):
    library LockBone uses optional TimerUtils, optional Table, optional MissileRecycler
        /* v1.6 */
       
        globals
            // If only you don't use MissileRecycler
            private constant integer DUMMY_ID = 'dumi'
        endglobals
       
        /*
            Description
            ¯¯¯¯¯¯¯¯¯¯¯
                Allows you to lock a unit's body part to face certain angle or point.
               
               
            External Dependencies
            ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
                (Requires) - Nothing
                (Optional) - These libs are completely optional. But it's a win win to have all of these to go with the system.
               
                    1. This lib allows the system to save data into timers, which is useful for struct based spells/systems
                            | TimerUtils      @ wc3c.net/showthread.php?t=101322
                       
                    2. This lib can ease the handling of hashtables as well as make it perfectly efficient uses of hashtables.
                            | Table           @ hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
                       
                    3. This system uses a lot of dummy units. Unit creations are heavy process, and other problems as well.
                       Here is where dummy recycler comes important.
                            | MissileRecycler @ hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
               
               
            API
            ¯¯¯
                struct LockBone
                   
                    1. Lock body part at given coordinate
                        | static method lockAtPoint takes unit whichUnit, string whichBone, real targetX, real targetY, real zOffset returns thistype
                            - targetX : x-axis of the target point
                            - targetY : y-axis of the target point
                            - zOffset : height level at which the bone will be locked
                   
                    2. Lock body part at given angle (in radian)
                        | static method lockAtAngle takes unit whichUnit, string whichBone, real radian, real hOffset, real vOffset returns thistype
                            - radian  : at what angle (in radian) unit's bone will be locked
                            - hOffset : horizontal offset
                            - vOffset : vertical offset
                               
                                     0 ............... -> lock point
                             unit <- | ______________: -> vOffset
                                      \_____________/
                                          hOffset
                                     
                    3. Reset any body locking
                        | static method resetLock takes unit whichUnit returns nothing
               
                    4. Bones part constants
                        BONE_PART_CHEST => rotate chest bone
                        BONE_PART_HEAD  => rotate head bone
                   
                       
            Resource Link
            ¯¯¯¯¯¯¯¯¯¯¯¯¯
                hiveworkshop.com/forums/submissions-414/snippet-lockbone-259005/
        */

       
        globals
            constant string BONE_PART_CHEST  = "bone_chest"
            constant string BONE_PART_HEAD   = "bone_head"
            private constant real   INTERVAL = 0.03125
            private constant player PASSIVE  = Player(PLAYER_NEUTRAL_PASSIVE)
        endglobals
       
        struct LockBone
           
            private timer t
            private unit  u
            private unit  d
           
            private real cos
            private real sin
           
            private static real MapMaxX
            private static real MapMaxY
            private static real MapMinX
            private static real MapMinY
           
            static if LIBRARY_Table then
                private static Table Ht
            else
                private static hashtable Ht
            endif
           
            private static method onPeriodic takes nothing returns nothing
               
                local thistype this
                local real x
                local real y
               
                static if LIBRARY_TimerUtils then
                    set this = GetTimerData(GetExpiredTimer())
                else
                    static if LIBRARY_Table then
                        set this = Ht.integer[GetHandleId(GetExpiredTimer())]
                    else
                        set this = LoadInteger(Ht, GetHandleId(GetExpiredTimer()), 0)
                    endif
                endif
                set x = GetUnitX(.u)+.cos
                set y = GetUnitY(.u)+.sin
                if x > MapMinX and x < MapMaxX and y > MapMinY and y < MapMaxY then
                    call SetUnitX(.d, x)
                    call SetUnitY(.d, y)
                else
                    call SetUnitPosition(.d, x, y)
                endif
               
            endmethod
           
            static method resetLock takes unit whichUnit returns nothing
           
                local thistype this
                local integer  hand = GetHandleId(whichUnit)
               
                static if LIBRARY_Table then
                    set this = Ht.integer[hand]
                else
                    set this = LoadInteger(Ht, hand, 0)
                endif
                if this != 0 then
                    call ResetUnitLookAt(.u)
                    static if LIBRARY_MissileRecycler then
                        call RecycleMissile(.d)
                        call SetUnitX(.d, MapMaxX)
                        call SetUnitY(.d, MapMaxY)
                    else
                        call RemoveUnit(.d)
                    endif
                    static if LIBRARY_TimerUtils then
                        call ReleaseTimer(.t)
                    else
                        call DestroyTimer(.t)
                        static if LIBRARY_Table then
                            call Ht.integer.remove[GetHandleId(.t)]
                        else
                            call RemoveSavedInteger(Ht, GetHandleId(.t), 0)
                        endif
                    endif
                    static if LIBRARY_Table then
                        set Ht.integer[hand] = 0
                    else
                        call SaveInteger(Ht, hand, 0, 0)
                    endif
                    call destroy()
                    set .u = null
                    set .d = null
                    set .t = null
                endif
               
            endmethod
           
            static method lockAtPoint takes unit whichUnit, string whichBone, real targetX, real targetY, real zOffset returns thistype
           
                local thistype this
               
                static if LIBRARY_Table then
                    set this = Ht.integer[GetHandleId(whichUnit)]
                else
                    set this = LoadInteger(Ht, GetHandleId(whichUnit), 0)
                endif
                if this == 0 then
                    set this = allocate()
                    static if LIBRARY_Table then
                        set Ht.integer[GetHandleId(whichUnit)] = this
                    else
                        call SaveInteger(Ht, GetHandleId(whichUnit), 0, this)
                    endif
                   
                    static if LIBRARY_TimerUtils then
                        set .t = NewTimerEx(this)
                    else
                        set .t = CreateTimer()
                        static if LIBRARY_Table then
                            set Ht.integer[GetHandleId(.t)] = this
                        else
                            call SaveInteger(Ht, GetHandleId(.t), 0, this)
                        endif
                    endif
                   
                    static if LIBRARY_MissileRecycler then
                        set .d = GetRecycledMissile(0, 0, 0, 0)
                    else
                        set .d = CreateUnit(PASSIVE, DUMMY_ID, 0, 0, 0)
                        static if not LIBRARY_AutoFly then
                            if UnitAddAbility(.d, 'Amrf') and UnitRemoveAbility(.d, 'Amrf') then
                            endif
                        endif
                    endif
                   
                    set .u = whichUnit
                    call UnitRemoveAbility(.d, 'Amov')
                    call SetUnitLookAt(.u, whichBone, .d, 0, 0, 0)
                endif
               
                if TimerGetTimeout(.t) > 0 then
                    call TimerStart(.t, 0, false, null)
                    call PauseTimer(.t)
                endif
                call SetUnitFlyHeight(.d, zOffset, 0)
                call SetUnitX(.d, targetX)
                call SetUnitY(.d, targetY)
               
                return this
            endmethod
           
            static method lockAtAngle takes unit whichUnit, string whichBone, real radian, real hOffset, real vOffset returns thistype
           
                local thistype this
               
                static if LIBRARY_Table then
                    set this = Ht.integer[GetHandleId(whichUnit)]
                else
                    set this = LoadInteger(Ht, GetHandleId(whichUnit), 0)
                endif
                if this == 0 then
                    set this = allocate()
                    static if LIBRARY_Table then
                        set Ht.integer[GetHandleId(whichUnit)] = this
                    else
                        call SaveInteger(Ht, GetHandleId(whichUnit), 0, this)
                    endif
                   
                    static if LIBRARY_TimerUtils then
                        set .t = NewTimerEx(this)
                    else
                        set .t = CreateTimer()
                        static if LIBRARY_Table then
                            set Ht.integer[GetHandleId(.t)] = this
                        else
                            call SaveInteger(Ht, GetHandleId(.t), 0, this)
                        endif
                    endif
                   
                    static if LIBRARY_MissileRecycler then
                        set .d = GetRecycledMissile(0, 0, 0, 0)
                    else
                        set .d = CreateUnit(PASSIVE, DUMMY_ID, 0, 0, 0)
                        static if not LIBRARY_AutoFly then
                            if UnitAddAbility(.d, 'Amrf') and UnitRemoveAbility(.d, 'Amrf') then
                            endif
                        endif
                    endif
                   
                    set .u = whichUnit
                    call UnitRemoveAbility(.d, 'Amov')
                    call SetUnitLookAt(.u, whichBone, .d, 0, 0, 0)
                endif
               
                if TimerGetTimeout(.t) == 0 then
                    call TimerStart(.t, INTERVAL, true, function thistype.onPeriodic)
                endif
                set .cos = hOffset*Cos(radian)
                set .sin = hOffset*Sin(radian)
                call SetUnitFlyHeight(.d, vOffset, 0)
                call SetUnitX(.d, GetUnitX(.u)+.cos)
                call SetUnitY(.d, GetUnitY(.u)+.sin)
               
                return this
            endmethod
           
            private static method onDeath takes nothing returns boolean
           
                local unit u = GetTriggerUnit()
                local thistype this
               
                static if LIBRARY_Table then
                    set this = Ht.integer[GetHandleId(u)]
                else
                    set this = LoadInteger(Ht, GetHandleId(u), 0)
                endif
                if this != 0 then
                    call resetLock(u)
                endif
                set u = null
               
                return false
            endmethod
           
            private static method onInit takes nothing returns nothing
               
                local trigger t = CreateTrigger()
               
                static if LIBRARY_Table then
                    set Ht = Table.create()
                else
                    set Ht = InitHashtable()
                endif
                set MapMaxX = GetRectMaxX(bj_mapInitialPlayableArea)
                set MapMaxY = GetRectMaxY(bj_mapInitialPlayableArea)
                set MapMinX = GetRectMinX(bj_mapInitialPlayableArea)
                set MapMinY = GetRectMinY(bj_mapInitialPlayableArea)
                call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
                call TriggerAddCondition(t, Condition(function thistype.onDeath))
               
            endmethod
           
        endstruct
       
    endlibrary


    Demo
    Demo spell
     
    Last edited: May 13, 2016
  2. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,426
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    lookAtAngle should be able to be achieved just by forcing an angle relative to the units current position (but super far away).
    Code (vJASS):
    function SetDummyFacing takes unit u, real angle returns nothing
        call SetUnitLookAt(u, "Bone_Head", u, Cos(angle)*1000000., Sin(angle)*1000000., 0.)
    endfunction

    http://www.wc3c.net/showthread.php?t=105830

    But using a dummy for lockAtPoint is fine.

    And you should add "bone_head" and "bone_chest" as constants. Those are actually the only two inputs. "bone_chest" will lock the chest. Any other input (apart from null) and ones that don't start with "bone_chest" will lock the head. Sadly, we can't lock any other bones. :\
    For more information.
     
  3. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,268
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    Code (vJASS):
    function SetDummyFacing takes unit u, real angle returns nothing
        call SetUnitLookAt(u, "Bone_Head", u, Cos(angle)*1000000., Sin(angle)*1000000., 0.)
    endfunction


    That one is glitched if you use it too often (every 0.05 second for example) even with 0 blend time.

    What happen if we input null anyway? I haven't tested it.
     
  4. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,426
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    Oh. Do you have a test map for that bug? I can't edit at the moment, but I can run wc3 if necessary.

    About inputting null, it just does nothing iirc.

    As for your code, I think it is good. The only thing I'd recommend is recycling dummies instead of removing them.
     
  5. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,268
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    No, I don't have. But believe me, I wouldn't write this snippet if the former function works correctly.
     
  6. gorillabull

    gorillabull

    Joined:
    Jul 17, 2011
    Messages:
    1,365
    Resources:
    2
    Spells:
    2
    Resources:
    2
    have fun making the cinematic people use vjass >_> <_< <_> >_<
     
  7. Arhowk

    Arhowk

    Joined:
    Aug 8, 2007
    Messages:
    2,753
    Resources:
    0
    Resources:
    0
    ehm.. what?
     
  8. BPower

    BPower

    Joined:
    Mar 18, 2012
    Messages:
    1,709
    Resources:
    21
    Spells:
    15
    Tutorials:
    1
    JASS:
    5
    Resources:
    21
    UnitIndexer API has changed. This will not compile with the current version.

    You could also do a periodic check if the unit does exists GetUnitTypeId(unit) != 0
    and store allocated units via Table and HandleId. ( Just an idea, UnitIndexer is also fine )

    I guess this works in its current state. So we can approve it soon.
     
  9. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,059
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    The UnitIndexer API issue does need to be addressed. Also, this should have integration with a dummy recycler.
     
  10. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    8,059
    Resources:
    25
    Maps:
    3
    Spells:
    10
    Tutorials:
    3
    JASS:
    9
    Resources:
    25
    Graveyarding until author is ready to update.
     
  11. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,268
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    How about making the Dummy library optional? I understand that it's important, but not always the same for users. They will probably think, "another requirement just for a simple task? NO!".
    I will also make the TimerUtils and UnitIndexer optional.
     
  12. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,839
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    The new UnitIndexer (written by nes) is a mess >.<

    It has so many dependencies, the installation is too much and sometimes may lead to confusion.

    I suggest you use UnitDex or UnitIndexerGUI instead.


    can you explain to me what xOffset, yOffset and zOffset is for?

    have you tried making the unit look at a point outside the map?
     
  13. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,268
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    - I won't use UnitIndexer then, if the old indexer is not allowed. UnitDex hasn't been approved either. The last time I used it, it was still bugged.

    - Those offsets will affect where the unit's bone will face. As example, you set zOffset to high value such as 500.0, so that the unit will face to the sky. But yeah, there's a mistake, we only need two offset, xyOffset and zOffset. Or better yet to call'em hOffset (horizontal) and vOffset (vertical).

    - Not yet, as far as I recall. But I will test it before uploading the next update.

    EDIT:
    Btw, which dummy recycler lib should I use? Can I use Bribe's MissileRecycler?
     
    Last edited: May 7, 2016
  14. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,268
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    Update:
    - Now uses MissileRecycler
    - Now it requires nothing
    - A lot of improvements and optimizations

    The current demo spell is still valid to test this system.

    @Almia: I haven't tested the thing you mentioned tho.
     
  15. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,268
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    Update. Dummy goes beyond map bounds is now anticipated.
     
  16. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,426
    Resources:
    18
    Icons:
    1
    Spells:
    4
    Tutorials:
    9
    JASS:
    4
    Resources:
    18
    I've moved it back to submissions.

    Looks pretty good overall, I vote for approval. :thumbs_up:
     
  17. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,268
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    Hey, thank you :)
     
  18. neo_sluf

    neo_sluf

    Joined:
    Feb 5, 2012
    Messages:
    1,432
    Resources:
    5
    Maps:
    5
    Resources:
    5
    This is really good for cinematics.

    Plus it has less amount of requirements make it more good.

    Although sadly it is really just for cinematics.
     
  19. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,188
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Wouldnt it be possible to use
    GetWorldBounds
    over
    bj_mapInitialPlayableArea
    for some more room for the dummies.

    For integrity check "have" natives could be used, for example:

    Code (vJASS):

            private static method onDeath takes nothing returns boolean
         
                local unit u = GetTriggerUnit()
                local thistype this
             
                static if LIBRARY_Table then
                    set this = Ht.integer[GetHandleId(u)]
                else
                    set this = LoadInteger(Ht, GetHandleId(u), 0)
                endif
                if this != 0 then
                    call resetLock(u)
                endif
                set u = null
             
                return false
            endmethod
           

    can be ==>
    Code (vJASS):

            private static method onDeath takes nothing returns boolean
         
                local unit u = GetTriggerUnit()
             
                static if LIBRARY_Table then
                    if Ht.has(GetHandleId(u)) then
                        call resetLock(u)
                    endif
                else
                    if HaveSavedInteger(Ht, GetHandleId(u), 0) then
                        call resetLock(u)
                    endif
                endif
             
                set u = null
                return false
            endmethod


    0.03125
    is maybe not required here, you could higher it a bit if you want.

    Code (vJASS):

    constant string BONE_PART_CHEST  = "bone_chest"
    constant string BONE_PART_HEAD   = "bone_head"
     


    ^I personaly would do them static constants of the struct so access them like "LockBone.Chest", "LockBone.Head", but it does not really matter.

    Optional requirements -> pretty cool.

    Resource is good. :)
     
  20. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,188
    Resources:
    22
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    4
    JASS:
    3
    Resources:
    22
    Submission: LockBone v1.6
    Date: 3. October 16
    Status: Approved
    Note:

    A good snippet to lock a unit's chest or head to a certain angle or point.
    Some things can be slightly optimized, mentioned here. Though it's nothing
    that will really affect the usage, so I just trust you change it by time soon.