• 🏆 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!

Small Code Snippets

Level 17
Joined
Apr 27, 2008
Messages
2,455
Guys, let's do one line then :

JASS:
function IsBuildingUnderConstruction takes unit u returns boolean
    return IsUnitType(u,UNIT_TYPE_UNDEAD) and (GetUnitAbilityLevel(u,'Abgs')!= 0 or GetUnitAbilityLevel(u,'Abgl')!= 0) or GetUnitAbilityLevel(u,'Abds')!= 0 or GetUnitAbilityLevel(u,'Abdl')!= 0
endfunction
But will this work only for default human and undead structures ?
What about custom ones and the other races like night elves ?
 
@Aniki We moved your math functions to the small code snippets thread. Since the merging is done by date, your post ended up on the previous page here:
Small Code Snippets

If possible, could you provide a brief comment on the functions/function list? :) It probably isn't necessary for ceil/floor, but it would be helpful for the "nearest" functions.
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
To check if a unit is invulnerable:

JASS:
library InvulnerabilityChecker

    function CheckInvulnerability takes unit u returns boolean
        local real origHP = GetWidgetLife(u)
        local real origMP = GetUnitState(u, UNIT_STATE_MANA)
        local boolean check
        call UnitDamageTarget(u, u, 0.01, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_UNIVERSAL, null)
        set check = GetWidgetLife(u) == origHP and GetUnitState(u, UNIT_STATE_MANA) == origMP
        call SetWidgetLife(u, origHP)
        call SetUnitState(u, UNIT_STATE_MANA, origMP)
        return check
    endfunction

endlibrary

False positives from mana shields are accounted for.
 
Last edited:
Level 13
Joined
Nov 7, 2014
Messages
571
JASS:
if newHP != origHP then
           set check = false
       endif
       if newMP != origMP then
           set check = false
       endif

-->
JASS:
if newHP != origHP or newMP != origMP then
           set check = false
       endif

-->
JASS:
set check = newHp == origHP and newMP == origMP

@AGD note that currently the function will report ethereal units as invulnerable, you need to change the attack type to ATTACK_TYPE_NORMAL, currently it's ATTACK_TYPE_CHAOS.
 
Last edited:
Level 20
Joined
Aug 13, 2013
Messages
1,696
^ Or

Does it really need to include the mana for checking? And is this tested for accuracy? Will it always return false if the unit suddenly have hp or mp regeneration?
( You are checking the life state changes btw so it affects the unit that have regen )
 
Last edited:

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Okay, the neccessary changes were made. Thanks for pointing the errors btw.

@jakeZinc These evaluations are done at a very tiny fraction of a second. And the unitstate regeneration happens at a much greater time interval. Btw, the mana evaluation is included to make sure units with mana shield dont register a false positive.
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Counted Random Units Iteration

JASS:
library RandomIteration

    globals
        private group tempGroup = CreateGroup()
        private integer unitCount = 0
    endglobals

    private function EnumUnits takes nothing returns boolean
        call GroupAddUnit(tempGroup, GetFilterUnit())
        set unitCount = unitCount + 1
        return false
    endfunction

    function EnumRandomUnitsInRangeCounted takes group g, real x, real y, real radius, integer limit returns nothing
        local real chance
        local unit u
        call GroupEnumUnitsInRange(g, x, y, radius, Filter(function EnumUnits))
        set chance = I2R(limit)/I2R(unitCount)
        loop
            set u = FirstOfGroup(tempGroup)
            exitwhen u == null or limit == 0
            call GroupRemoveUnit(tempGroup, u)
            if GetRandomReal(0, 1) <= chance then
                call GroupAddUnit(g, u)
                set limit = limit - 1
            endif
        endloop
    endfunction

endlibrary
 
Level 24
Joined
Aug 1, 2013
Messages
4,657
Why?
Just why?

function FilterRandomUnitsCounted takes group g, integer count returns group (optional)
(Remove units from the given group and return the group.)

I mean, if I wanted to get the units from a rect, or filter them first by something else, or even if I already have an existing group that has units.
In any of those cases, your function is pretty much useless. So you have to separate the two functionalities,
1 pick units in range
2 filter random units

The first one, we already have, the second one, that is what you have to create.

Also, you leak a unit handle "exitwhen limit == 0"

You should have that check after lowering the limit:
if limit == 0 then
set u = null
exitwhen true
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Okay, I'm too lazy to update that randomiterator now, for now lets have this.

JASS:
//Requires WorldBounds
//! textmacro IMPLEMENT_BOUNDS takes X, Y
if $X$ < WorldBounds.minX then
    set $X$ = WorldBounds.minX
elseif $X$ > WorldBounds.maxX then
    set $X$ = WorldBounds.maxX
endif
if $Y$ < WorldBounds.minY then
    set $Y$ = WorldBounds.minY
elseif $Y$ > WorldBounds.maxY then
    set $Y$ = WorldBounds.maxY
endif
//! endtextmacro

Doesn't need an extra function call but creates spaghetti code =P

EDIT: It doesn't have to be in a library after all
 
Last edited:
Level 1
Joined
Mar 7, 2016
Messages
5
Must take a boolean parameter to add from filtering that prevents dead units or somewhat to be picked.
I agree with it,but the main question is that it is a library,many codes will quote it,then if different code have different boolean,what boolean should I to add from fitering?
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Credits goes to Vexorian. I just rewrote his script and made it shorter using zinc.
JASS:
//! zinc
library BoolexprUtils {

    public boolexpr BOOLEXPR_TRUE, BOOLEXPR_FALSE;

    module Init {
        static method onInit() {
            BOOLEXPR_TRUE = Filter(function() -> boolean {return true;});
            BOOLEXPR_FALSE = Filter(function() -> boolean {return false;});
        }
    }

    struct S extends array {module Init;}

}
//! endzinc
 
Last edited:

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Most people do not realize amphibious units are not the same as ground unit. This is the condition to check it:

1. Checks if unit is amphibious
JASS:
function IsUnitAmphibious takes unit u returns boolean
    return not (IsUnitType(u, UNIT_TYPE_GROUND) or IsUnitType(u, UNIT_TYPE_FLYING))
endfunction

2. Checks if unit is ground or amphibious:
JASS:
function IsUnitGroundOrAmphibiousFail takes unit u returns boolean
    @ return IsUnitAmphibious(u) or IsUnitType(u, UNIT_TYPE_GROUND) <= not nerdly efficient@
endfunction
use this instead
JASS:
function IsUnitGroundOrAmphibious takes unit u returns boolean
    return IsUnitType(u, UNIT_TYPE_GROUND) or not IsUnitType(u, UNIT_TYPE_FLYING)
endfunction

3. Checks if unit is flying or amphibious (will be rarely useful):
JASS:
function IsUnitFlyingOrAmphibious takes unit u returns boolean
    return IsUnitType(u, UNIT_TYPE_FLYING) or not IsUnitType(u, UNIT_TYPE_GROUND)
endfunction
 
Last edited:
Maybe someone will find a use, as well:

JASS:
struct Repair extends array
    public static constant integer HUMAN_SPELL           = 'Ahrp'
    public static constant integer HUMAN_ORDER           = 852024
    public static constant integer HUMAN_ORDER_ON        = 852025
    public static constant integer HUMAN_ORDER_OFF       = 852026
   
    public static constant integer ORC_SPELL             = 'Arep'
    public static constant integer ORC_ORDER             = 852024
    public static constant integer ORC_ORDER_ON          = 852025
    public static constant integer ORC_ORDER_OFF         = 852026
   
    public static constant integer NIGHT_ELVES_SPELL     = 'Aren'
    public static constant integer NIGHT_ELVES_ORDER     = 852161
    public static constant integer NIGHT_ELVES_ORDER_ON  = 852162
    public static constant integer NIGHT_ELVES_ORDER_OFF = 852163
   
    public static constant integer UNDEAD_SPELL          = 'Arst'
    public static constant integer UNDEAD_ORDER          = 852202
    public static constant integer UNDEAD_ORDER_ON       = 852203
    public static constant integer UNDEAD_ORDER_OFF      = 852204
   
    public static method IsRepairAbility takes integer id returns boolean
        return id == HUMAN_SPELL or id == ORC_SPELL or id == NIGHT_ELVES_SPELL or id == UNDEAD_SPELL
    endmethod
   
    public static method IsRepairOrder takes integer id returns boolean
        return id == HUMAN_ORDER or id == ORC_ORDER or id == NIGHT_ELVES_ORDER or id == UNDEAD_ORDER
    endmethod
   
    public static method IsRepairOrderOn takes integer id returns boolean
        return id == HUMAN_ORDER_ON or id == ORC_ORDER_ON or id == NIGHT_ELVES_ORDER_ON or id == UNDEAD_ORDER_ON
    endmethod
   
    public static method IsRepairOrderOff takes integer id returns boolean
        return id == HUMAN_ORDER_OFF or id == ORC_ORDER_OFF or id == NIGHT_ELVES_ORDER_OFF or id == UNDEAD_ORDER_OFF
    endmethod
endstruct
 
Level 38
Joined
Feb 27, 2007
Messages
4,951
For getting locusted units in range/of player

JASS:
library LocustUnits initializer init //has optional GroupUtils
    globals
        private boolexpr filt
        private integer ID = 'Aloc'
    endglobals
   
    private function IsLocust takes nothing returns boolean
        return GetUnitAbilityLevel(GetFilterUnit(), ID) > 0
    endfunction
   
    function GroupAddLocustUnitsOfPlayer takes group TarG, player TarP returns nothing
        call GroupEnumUnitsOfPlayer(TarG, TarP, filt)
    endfunction
   
    function GroupAddLocustUnitsInRange takes group TarG, real TarX, real TarY, real R, boolexpr someFilter returns nothing
        local integer i = -1
        local unit u
        local group g
        local real x
        local real y
        set R = R*R
       
        static if LIBRARY_GroupUtils then
            set g = NewGroup()
        else
            set g = CreateGroup()
        endif
       
        loop
            set i = i+1
            exitwhen i > 15
            call GroupEnumUnitsOfPlayer(g,Player(i),And(filt, someFilter))
           
            loop
                set u = FirstOfGroup(g)
                exitwhen u == null
                call GroupRemoveUnit(g,u)
               
                set x = TarX-GetUnitX(u)
                set y = TarY-GetUnitY(u)
                if x*x+y*y <= R then
                    call GroupAddUnit(TarG,u)
                endif
            endloop
        endloop

        static if LIBRARY_GroupUtils then
            call ReleaseGroup(g)
        else
            call DestroyGroup(g)
        endif
        set g = null
        set u = null
    endfunction
   
    private function init takes nothing returns nothing
        set filt = Filter(function IsLocust)
    endfunction
endlibrary
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Maybe this can be useful =)
JASS:
//! zinc
library ExitWarcraft { //Credits: Aniki

    //! novjass
    /*=========== API ===========*/
        function ExitWarcraft()
    /* Exits the Warcraft 3 Game */
    /*===========================*/
    //! endnovjass

    public function ExitWarcraft() {ExecuteFunc("ExitWarcraft");}

}
//! endzinc
 
Last edited:

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Recursion can be deadly =):
JASS:
function crash takes nothing returns nothing
   call ExecuteFunc("crash")
endfunction
Nice! lol, I always complicate myself =D

Edited my post

EDIT:

maYbe GUI support can be added to run the trigger. >_>
How about hook DoNothing ExitWarcraft? GUI users should not be using this function after all =).
 
Last edited:

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Another one
JASS:
library CodeArray /*


    */requires /*

    */Typecast /*  - http://www.hiveworkshop.com/threads/accessing-memory-from-the-script-its-time-of-the-revolution.279262/

    |=====|
    | API |
    |=====|

        //! runtextmacro GLOBAL_CODE_ARRAY("PRIVACY", "NAME")
            - Declares a global code array

    */
    //! textmacro GLOBAL_CODE_ARRAY takes PRIVACY, NAME
    $PRIVACY$ struct $NAME$ extends array

        private static integer codes

        static method operator [] takes integer index returns code
            return I2C(codes[index])
        endmethod

        static method operator []= takes integer index, code c returns nothing
            set codes[index] = C2I(c)
        endmethod

    endstruct
    //! endtextmacro


endlibrary
 
Last edited:
Level 33
Joined
Apr 24, 2012
Messages
5,113
JASS:
/**********************************************************
*
*   FatalError
*   v1.1.2.1
*   By Magtheridon96 (Special Thanks to Nestharus)
*
*   Uses:
*   -----
*
*       - Annoying a player. lol
*
*   API:
*   ----
*
*       - function ExitGame             takes nothing           returns nothing
*       - function ExitGameForPlayer    takes player p          returns nothing
*
*       - function FreezeGame           takes nothing           returns nothing
*       - function FreezeGameForPlayer  takes player p          returns nothing
*
*       - function CrashGame            takes nothing           returns nothing
*       - function CrashGameForPlayer   takes player p          returns nothing
*
*       - function DesyncGameForPlayer  takes player p          returns nothing
*
*       - function EatRAM               takes nothing           returns nothing
*           - This function can take up about 1 GB of memory in a minute.
*           - Use this on cheaters ;D
*           - This function may crash the game at one point.
*
*       - function StopThread          takes nothing           returns nothing
*
**********************************************************/
library FatalError
   
    globals
        private trigger Q = CreateTrigger()
    endglobals
   
    function ExitGame takes nothing returns nothing
        loop
            call ExecuteFunc("ExitGame")
        endloop
    endfunction
   
    function ExitGameForPlayer takes player p returns nothing
        if GetLocalPlayer()==p then
            call ExitGame()
        endif
    endfunction
   
    function FreezeGame takes nothing returns nothing
        loop
            call TriggerSyncReady()
            call ExecuteFunc("FreezeGame")
        endloop
    endfunction
   
    function FreezeGameForPlayer takes player p returns nothing
        if GetLocalPlayer()==p then
            call FreezeGame()
        endif
    endfunction
   
    function CrashGame takes nothing returns nothing
        call Player(-1)
    endfunction
   
    function CrashGameForPlayer takes player p returns nothing
        if GetLocalPlayer()==p then
            call CrashGame()
        endif
    endfunction
   
    function DesyncGameForPlayer takes player p returns nothing
        if GetLocalPlayer()==p then
            call CreateTrigger()
        endif
    endfunction
   
    private function Z takes nothing returns boolean
        local location l
        local integer i = 1000
        loop
            exitwhen 0==i
            set l = Location(0,0)
            set l = Location(0,0)
            set l = Location(0,0)
            set i=i-1
        endloop
        return false
    endfunction
   
    function EatRAM takes nothing returns boolean
        loop
            call TriggerEvaluate(Q)
        endloop
        return false
    endfunction
   
    function StopThread takes nothing returns nothing
        local integer i = 1/0
    endfunction
   
    private module Init
        private static method onInit takes nothing returns nothing
            call TriggerAddCondition(Q,Condition(function Z))
        endmethod
    endmodule
    private struct Inits extends array
        implement Init
    endstruct
endlibrary

you forgot that this existed lol

link: Fatal Error
 
Level 38
Joined
Feb 27, 2007
Messages
4,951
This is dumb and really simple, but I needed a way to remove an effect without showing its death animation. (Specifically to replace it with something else.) I didn't see something like this in this thread from my searching.

Edit: see below
 
Last edited:

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
This is dumb and really simple, but I needed a way to remove an effect without showing its death animation. (Specifically to replace it with something else.) I didn't see something like this in this thread from my searching.
JASS:
library RemoveEffect
  //Destroying an effect always plays its death animation and any associated sounds.
  //This library fixes it in a simple way: move the effect to a location where nobody can see or hear it.

  globals
    private real SAFE_X = -3900.
    private real SAFE_Y =  3900.
    private real SAFE_Z = -1000. //not strictly necessary

    private real TIMESCALE = 10. //doesn't really matter but we set it to > 0 so the animations actually finish
  endglobals

  function RemoveEffect takes effect e returns nothing
    call BlzSetSpecialEffectPosition(e, SAFE_X, SAFE_Y, SAFE_Z)
    call BlzSetSpecialEffectTimeScale(e, TIMESCALE)
    call DestroyEffect(e)
  endfunction
endlibrary

If the effect is some kind of missile with a trail (like Phoenix Fire) this will cause a trail to appear between the special effect and its destination.
 
Level 38
Joined
Feb 27, 2007
Messages
4,951
If the effect is some kind of missile with a trail (like Phoenix Fire) this will cause a trail to appear between the special effect and its destination.
Huh, didn't notice that. This one shouldn't.
JASS:
library RemoveEffect
  //Destroying an effect always plays its death animation and any associated sounds.
  //This library fixes it in a simple way: move the effect to a location where nobody can see or hear it.

  globals
    private real SAFE_X = -3900.
    private real SAFE_Y =  3900.
    private real SAFE_Z = -1000. //not strictly necessary

    private real TIMESCALE = 10. //doesn't really matter but we set it to > 0 so the animations actually finish
  endglobals

  function RemoveEffect takes effect e returns nothing
    call BlzSetSpecialEffectAlpha(e, 255)
    call BlzSetSpecialEffectZ(e, SAFE_Z)
    call BlzSetSpecialEffectPosition(e, SAFE_X, SAFE_Y, SAFE_Z)
    call BlzSetSpecialEffectTimeScale(e, TIMESCALE)
    call DestroyEffect(e)
  endfunction
endlibrary
 
Level 6
Joined
Jun 30, 2017
Messages
41
A bit of code used for functions that are not allowed to have arguments, such as timer functions, triggeraction/condition functions, enumDestruct/Item functions, filterfuncs, etc...

Lua:
FunctionUtils = {}

function SetFunctionParameters(func, ...)
    FunctionUtils[func] = {...}
end

function GetFunctionParameters(func)
    return table.unpack(FunctionUtils[func])
end

How to use:

Lua:
function test1()

    local argument1, argument2, argument3 = GetFunctionParameters(test1)

-- function does stuff...

end

SetFunctionParameters(test1, argument1, argument2, argument3)
-- call the test1 function in whatever way.
 
Last edited:
Level 20
Joined
May 16, 2012
Messages
635
JASS:
library TimerOne
    private module Init
        private static method onInit takes nothing returns nothing
            call TimerStart(CreateTimer(),1.0,true,function e)
        endmethod
    endmodule
    struct TimerOne extends array
        private static trigger t = CreateTrigger()
        private static method e takes nothing returns nothing
            call TriggerEvaluate(t)
        endmethod
        static method register takes boolexpr b returns nothing
            call TriggerAddCondition(t,b)
        endmethod
        implement Init
    endstruct
endlibrary

I wrote this because I found that the most common timer I'm using in any of my maps is one that expires every second.
I used this system to replace about 20 timer registerations in my map :p

It may seem useful if you have countless 1-second periodic timers :p

Sorry for replaying to a post from 9 years, but i think this is very interesting and potentially useful to me, since this concept could be extrapolated to multiple timeouts. I'm trying to understand how i would convert multiple triggers that use a timer and a hash-table that maps handles to the timer created to this snipped.
Example: How something as simple as this could be done with this snippet? What i mean is, how can i access the stuff that i would normally save in a hash-table in the parent key of the timer with this code?

JASS:
scope Example initializer Init

private function DoSomethingPeriodically takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit  u = LoadUnitHandle(aHashTable, GetHandleId(t), 1)

    //Doing something periodically with unit u
  
    //if conditions were met then i would do this
    call FlushChildHashtable(aHashTable, GetHandleId(t))
    call PauseTimer(t)
    call DestroyTimer(t)
  
    set t = null
    set u = null
endfunction

private function Conditions takes nothing returns nothing
    local unit  u = GetTriggerUnit()
    local timer t = CreateTimer()

    call SaveUnitHandle(aHashTable, GetHandleId(t), 1, u)
    call TimerStart(t, 1.0, true, function DoSomethingPeriodically)

    set u = null
    set t = null
endfunction
//===========================================================================
private function Init takes nothing returns nothing
    call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function Conditions)
endfunction

endscope
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
I said it's impossible to do it using only one timer in the manner of method you described above, like
JASS:
private function DoSomethingPeriodically takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit  u = LoadUnitHandle(aHashTable, GetHandleId(t), 1)

    //Doing something periodically with unit u
 
    //if conditions were met then i would do this
    call FlushChildHashtable(aHashTable, GetHandleId(t))
    call PauseTimer(t)
    call DestroyTimer(t)
 
    set t = null
    set u = null
endfunction
i.e, using hashtable + GetHandleId() as parent key.

But of course no problem cannot be solved by adding layers of abstraction in one's approach.
JASS:
    module TimerOne
        private thistype prev
        private thistype next
        private thistype data

        private static method onTimerExpire takes nothing returns nothing
            local thistype node = thistype(0).next
            loop
                exitwhen node == list
                if not node.data.onTimerPeriod() then
                    set node.next.prev = node.prev
                    set node.prev.next = node.next
                endif
                set node = node.next
            endloop
        endmethod

        static method startTimerOne takes integer data returns nothing
            local thistype node = Node.allocate()
            if thistype(0).next == 0 then
                call TimerOne.register(function thistype.onTimerExpire)
            endif
            set node.prev = list.prev
            set node.next = list
            set list.prev = node
            set node.prev.next = node
            set node.data = data
        endmethod
    endmodule

...

struct MyStruct
    private method onTimerPeriod takes nothing returns boolean
        set this.xxx // this -> the attached data

        ...

        return <conditionToContinue>
    endmethod

    implement TimerOne

    private static method onInit takes nothing returns nothing
        call startTimerOne(<dataAttachment>) // Now you can attach data while still using only 1 timer
    endmethod
endstruct
 

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Overrides ForGroup() so that GUI users can use waits inside unitgroup enumerations now that we have [Lua] Perfect PolledWait (GUI-friendly).

forgroupoverride.lua
Lua:
-- forgroupoverride.lua
do
    local enumUnit = nil ---@type unit

    ---@param whichGroup group
    ---@param callback function
    function ForGroup(whichGroup, callback, ...)
        for i = 1, BlzGroupGetSize(whichGroup) do
            local prevEnumUnit = enumUnit
            enumUnit = BlzGroupUnitAt(whichGroup, i)
            callback(...)
            enumUnit = prevEnumUnit
        end
    end

    ---@return unit
    function GetEnumUnit() return enumUnit end
end
 
Last edited:
Level 20
Joined
Jul 10, 2009
Messages
474
Typechecker for Lua, including Warcraft native objects. Can be useful for pseudo-overloading.
Lua:
    ---Returns the type of any warcraft object, e.g. "unit", when using a unit.
    ---@param input any
    ---@return string
    function Wc3Type(input)
        local typeString = type(input)
        if typeString == 'number' then
            return (math.type(input) =='float' and 'real') or 'integer'
        elseif typeString == 'userdata' then
            typeString = tostring(input) --toString returns the warcraft type plus a colon and some hashstuff.
            return string.sub(typeString, 1, (string.find(typeString, ":", nil, true) or 0) -1) --string.find returns nil, if the argument is not found, which would break string.sub. So we need or as coalesce.
        else
            return typeString
        end
    end
 
Level 12
Joined
Jan 10, 2023
Messages
191
I think this is the right place to post this.
A snippet for detecting if a game is hosted from a Single Player lobby, or if it was a Battle.Net/ LAN lobby:
JASS:
set isOfflineGame = ( BlzFrameGetText( BlzGetFrameByName( "UpperButtonBarChatButton", 0 ) ) == "Log (|Cfffed312F12|R)" )
set isOnlineGame = not isOfflineGame

You can't set it as a constant because the frame does not exist yet when constants are being initialized.
However, it works when called from the library's initializer.

This is useful because often Map Editors don't care how many players there are so much as whether the game is hosted from a single-player lobby in order to ensure:

  • Cheats don't conflict with or destroy other systems.
  • To determine which (F12) menu will be in the game.
  • To safeguard single-player games from multi-player-only systems.
  • To safeguard multi-player games from single-player-only systems.
Something I could use help with - how to make this compatible with all languages wc3 might use (unless there is a superior method I should be using)

I think it makes more sense to check if the text is the "Log" button text and then to set isOnlineGame to be the opposite of isOfflineGame, because in a multiplayer game, players may be using different languages and if we checked the "Chat" button text maybe it desyncs (but maybe a local language solution solves this anyway?).
If we check for the "Log" text, in a multiplayer game, all players of any language would return false anyway because the "Chat" text would be there instead.
So we would only need to know one player's language at most.
UNLESS this is all solved by using Custom Game Interface options, in that case we could just solve it that way and keep it simple.



Sorry about that, I've realized through a little more research a better approach that is already known:
JASS:
constant boolean OFFLINE_GAME = ReloadGameCachesFromDisk()
 
Last edited:
Level 23
Joined
Jun 26, 2020
Messages
1,838
I think this is the right place to post this.
A snippet for detecting if a game is hosted from a Single Player lobby, or if it was a Battle.Net/ LAN lobby:
JASS:
set isOfflineGame = ( BlzFrameGetText( BlzGetFrameByName( "UpperButtonBarChatButton", 0 ) ) == "Log (|Cfffed312F12|R)" )
set isOnlineGame = not isOfflineGame

You can't set it as a constant because the frame does not exist yet when constants are being initialized.
However, it works when called from the library's initializer.

This is useful because often Map Editors don't care how many players there are so much as whether the game is hosted from a single-player lobby in order to ensure:

  • Cheats don't conflict with or destroy other systems.
  • To determine which (F12) menu will be in the game.
  • To safeguard single-player games from multi-player-only systems.
  • To safeguard multi-player games from single-player-only systems.
Something I could use help with - how to make this compatible with all languages wc3 might use (unless there is a superior method I should be using)

I think it makes more sense to check if the text is the "Log" button text and then to set isOnlineGame to be the opposite of isOfflineGame, because in a multiplayer game, players may be using different languages and if we checked the "Chat" button text maybe it desyncs (but maybe a local language solution solves this anyway?).
If we check for the "Log" text, in a multiplayer game, all players of any language would return false anyway because the "Chat" text would be there instead.
So we would only need to know one player's language at most.
UNLESS this is all solved by using Custom Game Interface options, in that case we could just solve it that way and keep it simple.



Sorry about that, I've realized through a little more research a better approach that is already known:
JASS:
constant boolean OFFLINE_GAME = ReloadGameCachesFromDisk()
A more precise snippet would be (Based on the Wurst Standard Library):
vJASS:
library GameStatus initializer Init

    globals
        public constant integer UNKNOWN = 0
        public constant integer ONLINE = 1
        public constant integer OFFLINE = 2
        public constant integer REPLAY = 3
        integer GameStatus = UNKNOWN
    endglobals

    function Init takes nothing returns nothing
        local player firstPlayer = Player(0)
        local unit u
        local boolean selected

        loop
            exitwhen GetPlayerSlotState(firstPlayer) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(firstPlayer) == MAP_CONTROL_USER
            set firstPlayer = Player(GetPlayerId(firstPlayer) + 1)
        endloop
        
        // Force the player to select a dummy unit
        set u = CreateUnit(firstPlayer, 'hfoo', 0, 0, 0)
        call SelectUnit(u, true)
        set selected = IsUnitSelected(u, firstPlayer)
        call RemoveUnit(u)
        set u = null

        if selected then
            // Detect if replay or offline game
            if ReloadGameCachesFromDisk() then
                set GameStatus = OFFLINE
            else
                set GameStatus = REPLAY
            endif
        else
            // If the unit wasn't selected instantly, the game is online
            set GameStatus = ONLINE
        endif
    endfunction
    
endlibrary
 
Top