• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[vJASS] - Detect Reforged

Status
Not open for further replies.
Level 6
Joined
Jan 17, 2010
Messages
149
Plug and play library/snippet

Might be useful for debugging.

Requires - [vJASS] - Sync Local Booleans or equivalent ([vJASS] - Sync (Game Cache), or the new BlzAPI functions) to sync values

HOW IT WORKS:
It creates a (platform - Walkable destructable) Throne ('XOkt' destructable ID) somewhere on the map and exploits the fact they have different models (thus different return values for GetLocationZ) in classic/reforged, and then syncs the boolean to a global array for easy access.

This means you can detect other players playing Reforged in your map even if you do not have Reforged yourself or chosen to use classic.

JASS:
library DetectReforged initializer init requires BooleanSync optional TimerUtils
    globals
        private boolean array Reforged
    endglobals
 
    function IsReforged takes player p returns boolean
        return Reforged[GetPlayerId(p)]
    endfunction
 
    private function AssignReforgedFlag takes nothing returns nothing
        set Reforged[GetPlayerId(GetTriggerPlayer())] = GetSyncedBoolean()
    endfunction
 
    private function checkReforged takes nothing returns nothing
        local real x = GetRectMinX(GetWorldBounds()) + 1000
        local real y = GetRectMaxY(GetWorldBounds()) - 1000
        local location l = Location(x,y)
        local destructable d
        local real z1 = GetLocationZ(Location(x,y))
        local boolean reforged
        local integer i = 0
        set d = CreateDestructable('XOkt', x,y, 0,1,0)
        call MoveLocation(l, x, y)
   
        // 57.431 is the classic height of the throne at it's center if scale == 1
        // reforged value is 72.71558
        // so if the value is different then its reforged
        //call BJDebugMsg(R2S(GetLocationZ(l) - z1))
        set z1 = (GetLocationZ(l) - z1) * 100
        if R2I(z1) != 5743 then
            set reforged = true
            // reforged
            //call BJDebugMsg(R2S(z1))
        else
            set reforged = false
            // classic
            //call BJDebugMsg(R2S(z1))
        endif
        loop
            call SyncBoolean(Player(i), reforged, function AssignReforgedFlag)
            set i = i + 1
            exitwhen i >= bj_MAX_PLAYER_SLOTS
        endloop
   
        call RemoveLocation(l)
        call RemoveDestructable(d)
        set d = null
        set l = null
        static if LIBRARY_TimerUtils then
            call ReleaseTimer(GetExpiredTimer())
        else
            call PauseTimer(GetExpiredTimer())
            call DestroyTimer(GetExpiredTimer())
        endif
    endfunction
 
    private function init takes nothing returns nothing
        static if LIBRARY_TimerUtils then
            call TimerStart(NewTimer(), 0.0, false, function checkReforged)
        else
            call TimerStart(CreateTimer(), 0.0, false, function checkReforged)
        endif
    endfunction
endlibrary
 
Last edited:
Level 6
Joined
Jan 17, 2010
Messages
149
They are welcome to patch this out as it will involve GetLocationZ() changes or a complete overhaul of dozens of models. Probably a safe bet they'll never do that.

If they patch GetLocationZ to work synchronously (by forcing player 0's values or something) I'd be pleasently surprised as it fixes SO MUCH.

If they patch models we can just create HD/SD model paths and rely on this using custom models...

I think overall this method is relatively bulletproof. But who knows... Bliz done some dumb sht in the past.



Here is a prototype version without a sync library (still syncing just embedded now)

I'll test tonight (tested.. it works).

JASS:
library DetectReforged initializer init requires optional TimerUtils
    globals
        private boolean array Reforged
        private unit dummyReforged
        private unit dummyClassic
        private trigger selectTrigger
        private timer cleanupTimer
    endglobals
  
    function IsReforged takes player p returns boolean
        return Reforged[GetPlayerId(p)]
    endfunction
    private function cleanup takes nothing returns nothing
        static if LIBRARY_TimerUtils then
            call ReleaseTimer(GetExpiredTimer())
        else
            call PauseTimer(GetExpiredTimer())
            call DestroyTimer(GetExpiredTimer())
        endif
        call DisableTrigger(selectTrigger)
        call RemoveUnit(dummyReforged)
        call RemoveUnit(dummyClassic)
        set dummyReforged = null
        set dummyClassic = null
        set selectTrigger = null
        set cleanupTimer = null
    endfunction
    private function onSelect takes nothing returns boolean
      
        if GetTriggerUnit() == dummyReforged then
            // assume all selection events resolve in 3 sec or less
            set Reforged[GetPlayerId(GetTriggerPlayer())] = true
            call TimerStart(cleanupTimer, 3.0, false, function cleanup)
            //call BJDebugMsg(GetPlayerName(GetTriggerPlayer()) + " reforged ")
        elseif GetTriggerUnit() == dummyClassic then
            set Reforged[GetPlayerId(GetTriggerPlayer())] = false // not really needed, just for consistency
            call TimerStart(cleanupTimer, 3.0, false, function cleanup)
            //call BJDebugMsg(GetPlayerName(GetTriggerPlayer()) + " classic ")
        endif
        return false
    endfunction
  
    private function CreateDummy takes real x, real y returns unit
        local unit u
        local integer i = 0
        // create dummy unit
        set u = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), 'nrat', x, y, 0)
        // Make dummy only selectable through triggers
        call UnitAddAbility(u, 'Amrf')
        call SetUnitFlyHeight(u, 5000, 0)
        call UnitAddAbility(u, 'Aeth')
        call SetUnitScale(u, 0, 0, 0)
        call PauseUnit(u, true)
        // Hide health bar
        call UnitAddAbility(u , 'Aloc')
        call ShowUnit(u, false)
        call UnitRemoveAbility(u , 'Aloc')
        call ShowUnit(u, true)
          
        // ensure it doesn't die
        call SetUnitInvulnerable(u, true)
        // share vision
        loop
            call UnitShareVision(u, Player(i), true)
            set i = i + 1
            exitwhen i >= bj_MAX_PLAYER_SLOTS
        endloop
        return u
    endfunction
      
    private function checkReforged takes nothing returns nothing
        local real x = GetRectMinX(GetWorldBounds()) + 1000
        local real y = GetRectMaxY(GetWorldBounds()) - 1000
        local location l = Location(x,y)
        local destructable d
        local real z1 = GetLocationZ(Location(x,y))
        local integer i = 0
        local unit u
      
        set cleanupTimer = GetExpiredTimer()
        set dummyReforged = CreateDummy(x,y)
        set dummyClassic = CreateDummy(x,y)
        set d = CreateDestructable('XOkt', x, y, 0, 1, 0)
        call MoveLocation(l, x, y)
        // 57.431 is the classic height of the throne at it's center if scale == 1
        // reforged value is 72.71558
        // so if the value is different then its reforged
        //call BJDebugMsg(R2S(GetLocationZ(l) - z1))
        set z1 = (GetLocationZ(l) - z1) * 100
        if R2I(z1) == 5743 then
            // probably classic
            //call BJDebugMsg(R2S(z1))
            call SelectUnit(dummyClassic, true)
            call SelectUnit(dummyClassic, false)
        else
            call SelectUnit(dummyReforged, true)
            call SelectUnit(dummyReforged, false)
            // probably reforged
            //call BJDebugMsg(R2S(z1))
        endif
      
        call RemoveLocation(l)
        call RemoveDestructable(d)
        set d = null
        set l = null
        set u = null
    endfunction
  
    private function init takes nothing returns nothing
        local integer i = 0
        set selectTrigger = CreateTrigger()
        loop
            call TriggerRegisterPlayerUnitEvent(selectTrigger, Player(i), EVENT_PLAYER_UNIT_SELECTED, null)
            set i = i + 1
            exitwhen i >= bj_MAX_PLAYER_SLOTS
        endloop
        call TriggerAddCondition(selectTrigger, Filter(function onSelect))
        static if LIBRARY_TimerUtils then
            call TimerStart(NewTimer(), 0.0, false, function checkReforged)
        else
            call TimerStart(CreateTimer(), 0.0, false, function checkReforged)
        endif
    endfunction
endlibrary
 
Last edited:
Status
Not open for further replies.
Top