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

[Lua] I wrote my first lines of Lua code

Hello,

starting with a new project and I thought it was finally time to switch from vJASS to Lua.

1 kAWsD5OPdEWjQQGv8OwOpA.jpg

I'm working my way through the tutorial. It's quite alright, until I get to the OOP part, where I'm having trouble, and/or Lua is just not very good at.

Here is some code I wrote with my questions in comments. Thanks a lot if you can answer any of these plus if there are any other issues with this code you might find!

A small function that used hashtables rewritten in Lua.

Lua:
do
    data = {}

    ---@param whichUnit unit
    ---@param whichBuff integer
    ---@param interval real
    ---@param callbackFunc code
    ---@param immediateAction boolean
	---@param endCallback code
    function AsLongAsUnitHasBuffDo(whichUnit, whichBuff, interval, callbackFunc, immediateAction, endCallback)
        local t = CreateTimer()
        data[t] = {whichUnit, whichBuff, callbackFunc, endCallback}
        TimerStart(t, interval, true, AsLongAsUnitHasBuffLoop)

        if immediateAction then
            callbackFunc(whichUnit)
        end
    end

    function AsLongAsUnitHasBuffLoop()
        local t = GetExpiredTimer()
        local whichUnit, whichBuff, callbackFunc, endCallback = table.unpack(data[t]) --is this the most convenient way to imitate hashtables?

        if GetUnitAbilityLevel(whichUnit, whichBuff) > 0 then
            callbackFunc(whichUnit)
        else
			if endCallback ~= nil then
				endCallback(whichUnit)
			end
            DestroyTimer(t)
            t = nil --Is this correctly removing the memory leak? t is nulled, data[t] loses reference, is garbage collected?
        end
    end
end

Trying to create a class:

Lua:
do
    AnimalList = {}
    AnimnalListSize = 0
    NumberOfAnimals = 0
    AnimalOfUnit = {}

    Animal = {
        x = nil , ---@type real
        y = nil , ---@type real
        avatar = nil ,  ---@type unit
        species = nil , ---@type string
        stamina = nil , ---@type real
        owner = nil , ---@type player
        deathTrigger = nil , ---@type trigger
        listIndex = nil , ---@type integer
        interactions = {} , ---@type table
    }

    setmetatable(Animal, {__call = function(t, ...) return t.create(...) end} )

    do
        local metaTable = {__index = Animal} --I copied this from the tutorial. I didnt understand what this actually does.

		---@param x real
		---@param y real
        ---@param species integer
        ---@param whichPlayer player
        function Animal.create(x, y, species, whichPlayer, facing)
            local new = {} ---@class Animal
            setmetatable(new,metaTable)
            new.x = x
            new.y = y
            new.owner = whichPlayer
            new.avatar = CreateUnit(whichPlayer, ANIMAL_UNIT_ID[species], x, y, facing)
			
			--Minor thing:
			--I can either set up ANIMAL_UNIT_ID with
			--TURTLE = 1
			--WOLF = 2
			--etc...
			--ANIMAL_UNIT_ID = { TURTLE = FourCC('atur') , WOLF = FourCC('awol') }
			--then pass integers into Animal.create.
			
			--Or with
			--ANIMAL_UNIT_ID = { turtle = FourCC{'atur'} , wolf = FourCC{'awol'} }
			--and pass strings into Animal.create, but then I can use ANIMAL_UNIT_ID.species
			--syntax. In vJASS, I would have used the former method. In Lua, does the second make more sense?
			--Having to pass strings is kinda ugly.

            AnimalOfUnit[new.avatar] = new
            new.species = species

            new.deathTrigger = CreateTrigger()
            TriggerRegisterUnitEvent(new.deathTrigger, new.avatar, EVENT_UNIT_DEATH)
            TriggerAddAction(new.deathTrigger, OnAnimalDeath)

            new:InitInteractions()

            new.listIndex = new:AssignAnimalIndex()
            NumberOfAnimals = NumberOfAnimals + 1

            return new
        end
    end

    function Animal:InitInteractions()
        for i = 1, ObjectListSize do
            if ObjectList[i] ~= nil then
                self.interactions[i] = Interaction(self,ObjectList[i])
            end
        end
    end

    function Animal:AnimalDestroy()
        AnimalList[self.listIndex] = nil
        DestroyTrigger(self.deathTrigger)
    end

    function OnAnimalDeath()
        local u = GetTriggerUnit()
        AnimalOfUnit[u]:AnimalDestroy()
        AnimalOfUnit[u] = nil
    end

    function Animal:AssignAnimalIndex() --If I declare this function as local, I get an unknown compile error. Because its not local, I have to give it a unique name and cannot just call it AssignIndex.
        local i = 1
        while AnimalList[i] ~= nil do
            i = i + 1
        end
        if i > AnimalListSize then
            AnimalListSize = i
        end
        AnimalList[i] = self
        return i
    end
end

Finally, @Bawbz gave me the suggestion to save the map in folder mode and have that opened in Visual Studio so that the war3map.lua is always present in the workspace and you have all the function previews. But this gives me a lot of Duplicate field warnings. Of course, I don't want to disable those in case I do have a duplicate field because the compiler just let's you compile anything. Any fixes to this?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,543
I'm no expert so I'll just share something regarding the first bit of code:
Lua:
    ---@param whichUnit unit
    ---@param whichBuff integer
    ---@param interval real
    ---@param callbackFunc code
    ---@param immediateAction boolean
    ---@param endCallback code
    function AsLongAsUnitHasBuffDo(whichUnit, whichBuff, interval, callbackFunc, immediateAction, endCallback)
        local t = CreateTimer()

        TimerStart(t, interval, true, function() {
            if GetUnitAbilityLevel(whichUnit, whichBuff) > 0 then
                callbackFunc(whichUnit)
            else
                if endCallback ~= nil then
                    endCallback(whichUnit)
                end
                PauseTimer(t) -- I believe you want to pause timers before destroying them. Could be outdated info
                DestroyTimer(t)
                -- You don't need to null (nil) locals anymore, but you still need to destroy/remove the object
            end
        })

        if immediateAction then
            callbackFunc(whichUnit)
        end
    end
Assuming I got the syntax correct, you can avoid the need for the outside function + tracking data in a table. Instead, you create what I guess would be considered a local function? This has a reference to all of the local variables declared before it.
 
Last edited:
Oh right, I can do it like this:

Lua:
do
    ---@param whichUnit unit
    ---@param whichBuff integer
    ---@param interval real
    ---@param callbackFunc code
    ---@param immediateAction boolean
	---@param endCallback code
    function AsLongAsUnitHasBuffDo(whichUnit, whichBuff, interval, callbackFunc, immediateAction, endCallback)
        local t = CreateTimer()
        TimerStart(t, interval, true, function()
            if GetUnitAbilityLevel(whichUnit, whichBuff) > 0 then
                callbackFunc(whichUnit)
            else
                if endCallback ~= nil then
                    endCallback(whichUnit)
                end
                DestroyTimer(t)
            end
        end)

        if immediateAction then
            callbackFunc(whichUnit)
        end
    end
end

This is even better! Thanks!

The idea behind nulling the timer t here is not that the timer would leak, but to clean up the "data"-table's entries with t as the key.

Lua:
-- I believe you want to pause timers before destroying them. Could be outdated info

I can do this then?

Lua:
DestroyTimerNew = DestroyTimer
function DestroyTimer(whichTimer)
    PauseTimer(whichTimer)
    DestroyTimerNew(whichTimer)
end
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,198
local metaTable = {__index = Animal} --I copied this from the tutorial. I didnt understand what this actually does.
__index meta table entry is needed so that if the Animal instance table has a look up performed that does not exist, it calls the Animal function to get the value.
setmetatable(Animal, {__call = function(t, ...) return t.create(...) end} )
Animal is a table, but with a __call meta table entry so the table itself is callable. The specified anonymous function gets called by the __index entry above. I think that then creates and returns a new instance, possibly used to initialise some complex fields.

When programming with Lua in Warcraft III you need to be aware that Warcraft III has a garbage collection rate limit. If you create tables too quickly then Warcraft III will never be able to complete a garbage collection cycle, resulting in no garbage collection happening and effectively all tables leaking until the table creation rate slows down enough. For this reason it is advisable to introduce static programming practices for objects with short lives, such as pools holding recycled table objects for those types that are re-used for new instances rather than a fresh table being allocated each time. Relying on the garbage collector to collect some infrequently allocated tables with long lives is fine, but anything with a high allocation rate and short life should be avoided.
 
Thanks a lot, I understand now how the __index works.

I did some tests with tables. I created a giant 10-million entry 3D-table, nulled the parent table and watched the memory usage of Warcraft 3 in my task manager. It seemed like the memory didn't move at all. I could have waited for hours. But also, each table entry accounts for only ~50 bytes and the fps didn't seem to be impacted by their creation, so it seems pretty hard to create so many tables that this will become an issue.

So, I think I fixed my class method issue, but the Visual Studio extension isn't working how I'd like it to yet. First issue is, I think the emmy notations and the disable diagnostic lines make the code utterly unreadable.
VS.png

Is there a way to change the colors of those lines to the standard comment color?

The second problem is that the extension doesn't recognize these methods and thinks they don't exist when I want to call them, giving me errors all over the code. Any fix for this? Probably not? :pmeh:
 
Level 20
Joined
Jul 10, 2009
Messages
478
The idea behind nulling the timer t here is not that the timer would leak, but to clean up the "data"-table's entries with t as the key.
Nilling t is just nilling the variable, not the object stored inside, so it doesn't have any effect on the entries of data. Bear in mind that writing data[t] means that you use the object stored within t as a key, not the variable itself.

Here is some code I wrote with my questions in comments. Thanks a lot if you can answer any of these plus if there are any other issues with this code you might find!
I have some general remarks:
  • Use local data as much as possible to avoid name clashes
    Lua:
    do
        local data = {} --local keyword prevents name clashes with other data-structures elsewhere in your map
        ...
    end
  • Use function instead of code and number instead of real for your type/param annotations.
    These are the correct names used in Lua, so the sumneko-extension will properly recognize them.
  • Use ---@class annotation on top of the table representing the class, not inside the create-function.
    Use ---@type annotation for the new object inside the create-function.
    Lua:
    --This is the correct way:
    
    ---@class Animal
    Animal = {
        ...
    }
    
    function Animal.create(...)
        local new = {} ---@type Animal
        ...
    end
  • Use local functions when possible. For instance, if OnAnimalDeath() is not used outside the do ... end-block, you can define it via local function OnAnimalDeath() ... end to prevent name clashes and increase performance. It is advisable to only use class-tables as globals and nothing else. That also holds for things like NumberOfAnimals, which you could just put into the class-table instead of leaving it in global scope.
  • Regarding ANIMAL_UNIT_ID, I personally favour integers over strings. Strings are ugly, unperformant and prone to mistakes.
  • function Animal:AssignAnimalIndex(). You can perfectly name this Animal:AssignIndex despite not being local, because it's not global either, but stored in the class-table (no risk of name clashes).
    You can even make it local, but local function definitions can't use the colon-operator. It would rather look like this:
    Lua:
    local function assignAnimalIndex(animal)
        ...
        AnimalList[i] = animal
        return i
    end

I can do this then?

Lua:
DestroyTimerNew = DestroyTimer
function DestroyTimer(whichTimer)
    PauseTimer(whichTimer)
    DestroyTimerNew(whichTimer)
end
Yes, you can. Overriding Wc3 natives to fix bad behaviour has become a trend in the community since Lua was integrated.

The second problem is that the extension doesn't recognize these methods and thinks they don't exist when I want to call them, giving me errors all over the code. Any fix for this? Probably not? :pmeh:
Yes, there is a fix. First the sumneko extension needs to know what the class table is (simply put the ---@class at the proper place above the class table, as mentioned earlier). Second, the extension needs to know that an object is an animal, which you can ensure by adding the ---@return Animal annotation to the create method.
If done right, not only the syntax errors will vanish, but VSCode will also show you a list of available methods everytime after typing a:.

Finally, @Bawbz gave me the suggestion to save the map in folder mode and have that opened in Visual Studio so that the war3map.lua is always present in the workspace and you have all the function previews. But this gives me a lot of Duplicate field warnings.
Storing all your script files in VSCode and copy-pasting changes from VSCode to Wc3 would also do that. Folder mode can be neat, but it requires all code to be located in a single big file, which is really ugly. Really big files also make sumneko extension running slow and can cause intellisense issues with code that comes after unresolved warnings.
Regarding duplicate field warnings, can you show an example?

I'm working my way through the tutorial. It's quite alright, until I get to the OOP part
Any feedback is welcome. If you have suggestions on how to improve, feel free to share ;)
 
Yes, there is a fix. First the sumneko extension needs to know what the class table is (simply put the ---@class at the proper place above the class table, as mentioned earlier). Second, the extension needs to know that an object is an animal, which you can ensure by adding the ---@return Animal annotation to the create method.
If done right, not only the syntax errors will vanish, but VSCode will also show you a list of available methods everytime after typing a:.
Thank you for your in-depth reply! I created this topic three months ago, so I have solved most of the issues by now, but this issue was something I was still struggling with.

Would be great to add a small section about that in the tutorial.
 
Level 20
Joined
Jul 10, 2009
Messages
478
I just noticed that the sumneko extension intoduced a new diagnostic "inject-field" in version 3.7.0 published on 2023-8-24.
Since that date, the following code produces a warning:
Lua:
---@class Animal
Animal = {
    owner = Player(0) ---@type player
}

---@param owner player
---@return Animal
function Animal.create(owner)
    local new = {} ---@type Animal
    new.owner = owner
    return new
end
Warning:
1707072094269.png


The warning message sounds like the reason you replaced ---@type Animal by ---@class Animal in the first place. And indeed, the warning goes away when you do that, so it's probably fine.
I'm confused about this extension behaviour though, because it's not documented in the LuaCats Wiki. Class statements should normally be unique for any given class and not be repeated like this, so I don't understand why it's being recommended in the warning message.

I personally "solved" this by disabling the "inject-field" diagnostic in the settings.json.
JSON:
    "Lua.diagnostics.disable": [
        "lowercase-global",
        "inject-field"
    ],
 
Yes, you can. Overriding Wc3 natives to fix bad behaviour has become a trend in the community since Lua was integrated.
So, here's something that I wanted to overwrite. I always hated player to integer and back conversions and felt that player should just be an integer like unitType etc. are as well. I thought that in Lua it would get better because you can store data with a player key instead of only at integer keys, but it's still annoying when you have to use these weird loops that could desync etc. and often you need to use the player index for arithmetic, so you're back to converting again.

So... I wrote a script to replace every native to use integers instead of players as arguments.
Lua:
SetPlayerTeamNative = SetPlayerTeam
---@param whichPlayer integer
---@param whichTeam integer
SetPlayerTeam = function(whichPlayer, whichTeam) SetPlayerTeamNative(Player(whichPlayer-1), whichTeam) end

SetPlayerStartLocationNative = SetPlayerStartLocation
---@param whichPlayer integer
---@param startLocIndex integer
SetPlayerStartLocation = function(whichPlayer, startLocIndex) SetPlayerStartLocationNative(Player(whichPlayer-1), startLocIndex) end

ForcePlayerStartLocationNative = ForcePlayerStartLocation
---@param whichPlayer integer
---@param startLocIndex integer
ForcePlayerStartLocation = function(whichPlayer, startLocIndex) ForcePlayerStartLocationNative(Player(whichPlayer-1), startLocIndex) end

SetPlayerColorNative = SetPlayerColor
---@param whichPlayer integer
---@param color playercolor
SetPlayerColor = function(whichPlayer, color) SetPlayerColorNative(Player(whichPlayer-1), color) end

SetPlayerAllianceNative = SetPlayerAlliance
---@param sourcePlayer integer
---@param otherPlayer integer
---@param whichAllianceSetting alliancetype
---@param value boolean
SetPlayerAlliance = function(sourcePlayer, otherPlayer, whichAllianceSetting, value) SetPlayerAllianceNative(Player(sourcePlayer-1), Player(otherPlayer-1), whichAllianceSetting, value) end

SetPlayerTaxRateNative = SetPlayerTaxRate
---@param sourcePlayer integer
---@param otherPlayer integer
---@param whichResource playerstate
---@param rate integer
SetPlayerTaxRate = function(sourcePlayer, otherPlayer, whichResource, rate) SetPlayerTaxRateNative(Player(sourcePlayer-1), Player(otherPlayer-1), whichResource, rate) end

SetPlayerRacePreferenceNative = SetPlayerRacePreference
---@param whichPlayer integer
---@param whichRacePreference racepreference
SetPlayerRacePreference = function(whichPlayer, whichRacePreference) SetPlayerRacePreferenceNative(Player(whichPlayer-1), whichRacePreference) end

SetPlayerRaceSelectableNative = SetPlayerRaceSelectable
---@param whichPlayer integer
---@param value boolean
SetPlayerRaceSelectable = function(whichPlayer, value) SetPlayerRaceSelectableNative(Player(whichPlayer-1), value) end

SetPlayerControllerNative = SetPlayerController
---@param whichPlayer integer
---@param controlType mapcontrol
SetPlayerController = function(whichPlayer, controlType) SetPlayerControllerNative(Player(whichPlayer-1), controlType) end

SetPlayerNameNative = SetPlayerName
---@param whichPlayer integer
---@param name string
SetPlayerName = function(whichPlayer, name) SetPlayerNameNative(Player(whichPlayer-1), name) end

SetPlayerOnScoreScreenNative = SetPlayerOnScoreScreen
---@param whichPlayer integer
---@param flag boolean
SetPlayerOnScoreScreen = function(whichPlayer, flag) SetPlayerOnScoreScreenNative(Player(whichPlayer-1), flag) end

GetPlayerTeamNative = GetPlayerTeam
---@param whichPlayer integer
GetPlayerTeam = function(whichPlayer) return GetPlayerTeamNative(Player(whichPlayer-1)) end

GetPlayerStartLocationNative = GetPlayerStartLocation
---@param whichPlayer integer
GetPlayerStartLocation = function(whichPlayer) return GetPlayerStartLocationNative(Player(whichPlayer-1)) end

GetPlayerColorNative = GetPlayerColor
---@param whichPlayer integer
---@diagnostic disable-next-line: param-type-mismatch
GetPlayerColor = function(whichPlayer) return GetPlayerId(GetPlayerColorNative(Player(whichPlayer-1))) + 1 end

GetPlayerSelectableNative = GetPlayerSelectable
---@param whichPlayer integer
GetPlayerSelectable = function(whichPlayer) return GetPlayerSelectableNative(Player(whichPlayer-1)) end

GetPlayerControllerNative = GetPlayerController
---@param whichPlayer integer
GetPlayerController = function(whichPlayer) return GetPlayerControllerNative(Player(whichPlayer-1)) end

GetPlayerSlotStateNative = GetPlayerSlotState
---@param whichPlayer integer
---@diagnostic disable-next-line: param-type-mismatch
GetPlayerSlotState = function(whichPlayer) return GetPlayerId(GetPlayerSlotStateNative(Player(whichPlayer-1))) + 1 end

GetPlayerTaxRateNative = GetPlayerTaxRate
---@param sourcePlayer integer
---@param otherPlayer integer
---@param whichResource playerstate
GetPlayerTaxRate = function(sourcePlayer, otherPlayer, whichResource) return GetPlayerTaxRateNative(Player(sourcePlayer-1), Player(otherPlayer-1), whichResource) end

IsPlayerRacePrefSetNative = IsPlayerRacePrefSet
---@param whichPlayer integer
---@param pref racepreference
IsPlayerRacePrefSet = function(whichPlayer, pref) return IsPlayerRacePrefSetNative(Player(whichPlayer-1), pref) end

GetPlayerNameNative = GetPlayerName
---@param whichPlayer integer
GetPlayerName = function(whichPlayer) return GetPlayerNameNative(Player(whichPlayer-1)) end

GroupEnumUnitsOfPlayerNative = GroupEnumUnitsOfPlayer
---@param whichGroup group
---@param whichPlayer integer
---@param filter? boolexpr
GroupEnumUnitsOfPlayer = function(whichGroup, whichPlayer, filter) GroupEnumUnitsOfPlayerNative(whichGroup, Player(whichPlayer-1), filter) end

GroupEnumUnitsSelectedNative = GroupEnumUnitsSelected
---@param whichGroup group
---@param whichPlayer integer
---@param filter? boolexpr
GroupEnumUnitsSelected = function(whichGroup, whichPlayer, filter) GroupEnumUnitsSelectedNative(whichGroup, Player(whichPlayer-1), filter) end

ForceAddPlayerNative = ForceAddPlayer
---@param whichForce force
---@param whichPlayer integer
ForceAddPlayer = function(whichForce, whichPlayer) ForceAddPlayerNative(whichForce, Player(whichPlayer-1)) end

ForceRemovePlayerNative = ForceRemovePlayer
---@param whichForce force
---@param whichPlayer integer
ForceRemovePlayer = function(whichForce, whichPlayer) ForceRemovePlayerNative(whichForce, Player(whichPlayer-1)) end

BlzForceHasPlayerNative = BlzForceHasPlayer
---@param whichForce force
---@param whichPlayer integer
BlzForceHasPlayer = function(whichForce, whichPlayer) return BlzForceHasPlayerNative(whichForce, Player(whichPlayer-1)) end

ForceEnumAlliesNative = ForceEnumAllies
---@param whichForce force
---@param whichPlayer integer
---@param filter? boolexpr
ForceEnumAllies = function(whichForce, whichPlayer, filter) ForceEnumAlliesNative(whichForce, Player(whichPlayer-1), filter) end

ForceEnumEnemiesNative = ForceEnumEnemies
---@param whichForce force
---@param whichPlayer integer
---@param filter? boolexpr
ForceEnumEnemies = function(whichForce, whichPlayer, filter) ForceEnumEnemiesNative(whichForce, Player(whichPlayer-1), filter) end

GetFilterPlayerNative = GetFilterPlayer
GetFilterPlayer = function() return GetPlayerId(GetFilterPlayerNative()) + 1 end

GetEnumPlayerNative = GetEnumPlayer
GetEnumPlayer = function() return GetPlayerId(GetEnumPlayerNative()) + 1 end

GetWinningPlayerNative = GetWinningPlayer
GetWinningPlayer = function() return GetPlayerId(GetWinningPlayerNative()) + 1 end

GetTournamentFinishNowPlayerNative = GetTournamentFinishNowPlayer
GetTournamentFinishNowPlayer = function() return GetPlayerId(GetTournamentFinishNowPlayerNative()) + 1 end

GetTournamentScoreNative = GetTournamentScore
---@param whichPlayer integer
GetTournamentScore = function(whichPlayer) return GetTournamentScoreNative(Player(whichPlayer-1)) end

TriggerRegisterPlayerEventNative = TriggerRegisterPlayerEvent
---@param whichTrigger trigger
---@param whichPlayer integer
---@param whichPlayerEvent playerevent
TriggerRegisterPlayerEvent = function(whichTrigger, whichPlayer, whichPlayerEvent) return TriggerRegisterPlayerEventNative(whichTrigger, Player(whichPlayer-1), whichPlayerEvent) end

GetTriggerPlayerNative = GetTriggerPlayer
GetTriggerPlayer = function() return GetPlayerId(GetTriggerPlayerNative()) + 1 end

TriggerRegisterPlayerUnitEventNative = TriggerRegisterPlayerUnitEvent
---@param whichTrigger trigger
---@param whichPlayer integer
---@param whichPlayerUnitEvent playerunitevent
---@param filter? boolexpr
TriggerRegisterPlayerUnitEvent = function(whichTrigger, whichPlayer, whichPlayerUnitEvent, filter) return TriggerRegisterPlayerUnitEventNative(whichTrigger, Player(whichPlayer-1), whichPlayerUnitEvent, filter) end

GetChangingUnitPrevOwnerNative = GetChangingUnitPrevOwner
GetChangingUnitPrevOwner = function() return GetPlayerId(GetChangingUnitPrevOwnerNative()) + 1 end

TriggerRegisterPlayerAllianceChangeNative = TriggerRegisterPlayerAllianceChange
---@param whichTrigger trigger
---@param whichPlayer integer
---@param whichAlliance alliancetype
TriggerRegisterPlayerAllianceChange = function(whichTrigger, whichPlayer, whichAlliance) return TriggerRegisterPlayerAllianceChangeNative(whichTrigger, Player(whichPlayer-1), whichAlliance) end

TriggerRegisterPlayerStateEventNative = TriggerRegisterPlayerStateEvent
---@param whichTrigger trigger
---@param whichPlayer integer
---@param whichState playerstate
---@param opcode limitop
---@param limitval number
TriggerRegisterPlayerStateEvent = function(whichTrigger, whichPlayer, whichState, opcode, limitval) return TriggerRegisterPlayerStateEventNative(whichTrigger, Player(whichPlayer-1), whichState, opcode, limitval) end

TriggerRegisterPlayerChatEventNative = TriggerRegisterPlayerChatEvent
---@param whichTrigger trigger
---@param whichPlayer integer
---@param chatMessageToDetect string
---@param exactMatchOnly boolean
TriggerRegisterPlayerChatEvent = function(whichTrigger, whichPlayer, chatMessageToDetect, exactMatchOnly) return TriggerRegisterPlayerChatEventNative(whichTrigger, Player(whichPlayer-1), chatMessageToDetect, exactMatchOnly) end

GetEventDetectingPlayerNative = GetEventDetectingPlayer
GetEventDetectingPlayer = function() return GetPlayerId(GetEventDetectingPlayerNative()) + 1 end

GetItemPlayerNative = GetItemPlayer
---@param whichItem item
GetItemPlayer = function(whichItem) return GetPlayerId(GetItemPlayerNative(whichItem)) + 1 end

SetItemPlayerNative = SetItemPlayer
---@param whichItem item
---@param whichPlayer integer
---@param changeColor boolean
SetItemPlayer = function(whichItem, whichPlayer, changeColor) SetItemPlayerNative(whichItem, Player(whichPlayer-1), changeColor) end

CreateUnitNative = CreateUnit
---@param id integer
---@param unitid integer
---@param x number
---@param y number
---@param face number
CreateUnit = function(id, unitid, x, y, face) return CreateUnitNative(Player(id-1), unitid, x, y, face) end

CreateUnitByNameNative = CreateUnitByName
---@param whichPlayer integer
---@param unitname string
---@param x number
---@param y number
---@param face number
CreateUnitByName = function(whichPlayer, unitname, x, y, face) return CreateUnitByNameNative(Player(whichPlayer-1), unitname, x, y, face) end

CreateUnitAtLocNative = CreateUnitAtLoc
---@param id integer
---@param unitid integer
---@param whichLocation location
---@param face number
CreateUnitAtLoc = function(id, unitid, whichLocation, face) return CreateUnitAtLocNative(Player(id-1), unitid, whichLocation, face) end

CreateUnitAtLocByNameNative = CreateUnitAtLocByName
---@param id integer
---@param unitname string
---@param whichLocation location
---@param face number
CreateUnitAtLocByName = function(id, unitname, whichLocation, face) return CreateUnitAtLocByNameNative(Player(id-1), unitname, whichLocation, face) end

CreateCorpseNative = CreateCorpse
---@param whichPlayer integer
---@param unitid integer
---@param x number
---@param y number
---@param face number
CreateCorpse = function(whichPlayer, unitid, x, y, face) return CreateCorpseNative(Player(whichPlayer-1), unitid, x, y, face) end

SetUnitOwnerNative = SetUnitOwner
---@param whichUnit unit
---@param whichPlayer integer
---@param changeColor boolean
SetUnitOwner = function(whichUnit, whichPlayer, changeColor) SetUnitOwnerNative(whichUnit, Player(whichPlayer-1), changeColor) end

SetUnitRescuableNative = SetUnitRescuable
---@param whichUnit unit
---@param byWhichPlayer integer
---@param flag boolean
SetUnitRescuable = function(whichUnit, byWhichPlayer, flag) SetUnitRescuableNative(whichUnit, Player(byWhichPlayer-1), flag) end

GetOwningPlayerNative = GetOwningPlayer
---@param whichUnit unit
GetOwningPlayer = function(whichUnit) return GetPlayerId(GetOwningPlayerNative(whichUnit)) + 1 end

IsUnitOwnedByPlayerNative = IsUnitOwnedByPlayer
---@param whichUnit unit
---@param whichPlayer integer
IsUnitOwnedByPlayer = function(whichUnit, whichPlayer) return IsUnitOwnedByPlayerNative(whichUnit, Player(whichPlayer-1)) end

IsUnitAllyNative = IsUnitAlly
---@param whichUnit unit
---@param whichPlayer integer
IsUnitAlly = function(whichUnit, whichPlayer) return IsUnitAllyNative(whichUnit, Player(whichPlayer-1)) end

IsUnitEnemyNative = IsUnitEnemy
---@param whichUnit unit
---@param whichPlayer integer
IsUnitEnemy = function(whichUnit, whichPlayer) return IsUnitEnemyNative(whichUnit, Player(whichPlayer-1)) end

IsUnitVisibleNative = IsUnitVisible
---@param whichUnit unit
---@param whichPlayer integer
IsUnitVisible = function(whichUnit, whichPlayer) return IsUnitVisibleNative(whichUnit, Player(whichPlayer-1)) end

IsUnitDetectedNative = IsUnitDetected
---@param whichUnit unit
---@param whichPlayer integer
IsUnitDetected = function(whichUnit, whichPlayer) return IsUnitDetectedNative(whichUnit, Player(whichPlayer-1)) end

IsUnitInvisibleNative = IsUnitInvisible
---@param whichUnit unit
---@param whichPlayer integer
IsUnitInvisible = function(whichUnit, whichPlayer) return IsUnitInvisibleNative(whichUnit, Player(whichPlayer-1)) end

IsUnitFoggedNative = IsUnitFogged
---@param whichUnit unit
---@param whichPlayer integer
IsUnitFogged = function(whichUnit, whichPlayer) return IsUnitFoggedNative(whichUnit, Player(whichPlayer-1)) end

IsUnitMaskedNative = IsUnitMasked
---@param whichUnit unit
---@param whichPlayer integer
IsUnitMasked = function(whichUnit, whichPlayer) return IsUnitMaskedNative(whichUnit, Player(whichPlayer-1)) end

IsUnitSelectedNative = IsUnitSelected
---@param whichUnit unit
---@param whichPlayer integer
IsUnitSelected = function(whichUnit, whichPlayer) return IsUnitSelectedNative(whichUnit, Player(whichPlayer-1)) end

UnitShareVisionNative = UnitShareVision
---@param whichUnit unit
---@param whichPlayer integer
---@param share boolean
UnitShareVision = function(whichUnit, whichPlayer, share) UnitShareVisionNative(whichUnit, Player(whichPlayer-1), share) end

IssueNeutralImmediateOrderNative = IssueNeutralImmediateOrder
---@param forWhichPlayer integer
---@param neutralStructure unit
---@param unitToBuild string
IssueNeutralImmediateOrder = function(forWhichPlayer, neutralStructure, unitToBuild) return IssueNeutralImmediateOrderNative(Player(forWhichPlayer-1), neutralStructure, unitToBuild) end

IssueNeutralImmediateOrderByIdNative = IssueNeutralImmediateOrderById
---@param forWhichPlayer integer
---@param neutralStructure unit
---@param unitId integer
IssueNeutralImmediateOrderById = function(forWhichPlayer, neutralStructure, unitId) return IssueNeutralImmediateOrderByIdNative(Player(forWhichPlayer-1), neutralStructure, unitId) end

IssueNeutralPointOrderNative = IssueNeutralPointOrder
---@param forWhichPlayer integer
---@param neutralStructure unit
---@param unitToBuild string
---@param x number
---@param y number
IssueNeutralPointOrder = function(forWhichPlayer, neutralStructure, unitToBuild, x, y) return IssueNeutralPointOrderNative(Player(forWhichPlayer-1), neutralStructure, unitToBuild, x, y) end

IssueNeutralPointOrderByIdNative = IssueNeutralPointOrderById
---@param forWhichPlayer integer
---@param neutralStructure unit
---@param unitId integer
---@param x number
---@param y number
IssueNeutralPointOrderById = function(forWhichPlayer, neutralStructure, unitId, x, y) return IssueNeutralPointOrderByIdNative(Player(forWhichPlayer-1), neutralStructure, unitId, x, y) end

IssueNeutralTargetOrderNative = IssueNeutralTargetOrder
---@param forWhichPlayer integer
---@param neutralStructure unit
---@param unitToBuild string
---@param target widget
IssueNeutralTargetOrder = function(forWhichPlayer, neutralStructure, unitToBuild, target) return IssueNeutralTargetOrderNative(Player(forWhichPlayer-1), neutralStructure, unitToBuild, target) end

IssueNeutralTargetOrderByIdNative = IssueNeutralTargetOrderById
---@param forWhichPlayer integer
---@param neutralStructure unit
---@param unitId integer
---@param target widget
IssueNeutralTargetOrderById = function(forWhichPlayer, neutralStructure, unitId, target) return IssueNeutralTargetOrderByIdNative(Player(forWhichPlayer-1), neutralStructure, unitId, target) end

PlayerNative = Player
---@param number integer
Player = function(number) return GetPlayerId(PlayerNative(number)) + 1 end

GetLocalPlayerNative = GetLocalPlayer
GetLocalPlayer = function() return GetPlayerId(GetLocalPlayerNative()) + 1 end

IsPlayerAllyNative = IsPlayerAlly
---@param whichPlayer integer
---@param otherPlayer integer
IsPlayerAlly = function(whichPlayer, otherPlayer) return IsPlayerAllyNative(Player(whichPlayer-1), Player(otherPlayer-1)) end

IsPlayerEnemyNative = IsPlayerEnemy
---@param whichPlayer integer
---@param otherPlayer integer
IsPlayerEnemy = function(whichPlayer, otherPlayer) return IsPlayerEnemyNative(Player(whichPlayer-1), Player(otherPlayer-1)) end

IsPlayerInForceNative = IsPlayerInForce
---@param whichPlayer integer
---@param whichForce force
IsPlayerInForce = function(whichPlayer, whichForce) return IsPlayerInForceNative(Player(whichPlayer-1), whichForce) end

IsPlayerObserverNative = IsPlayerObserver
---@param whichPlayer integer
IsPlayerObserver = function(whichPlayer) return IsPlayerObserverNative(Player(whichPlayer-1)) end

IsVisibleToPlayerNative = IsVisibleToPlayer
---@param x number
---@param y number
---@param whichPlayer integer
IsVisibleToPlayer = function(x, y, whichPlayer) return IsVisibleToPlayerNative(x, y, Player(whichPlayer-1)) end

IsLocationVisibleToPlayerNative = IsLocationVisibleToPlayer
---@param whichLocation location
---@param whichPlayer integer
IsLocationVisibleToPlayer = function(whichLocation, whichPlayer) return IsLocationVisibleToPlayerNative(whichLocation, Player(whichPlayer-1)) end

IsFoggedToPlayerNative = IsFoggedToPlayer
---@param x number
---@param y number
---@param whichPlayer integer
IsFoggedToPlayer = function(x, y, whichPlayer) return IsFoggedToPlayerNative(x, y, Player(whichPlayer-1)) end

IsLocationFoggedToPlayerNative = IsLocationFoggedToPlayer
---@param whichLocation location
---@param whichPlayer integer
IsLocationFoggedToPlayer = function(whichLocation, whichPlayer) return IsLocationFoggedToPlayerNative(whichLocation, Player(whichPlayer-1)) end

IsMaskedToPlayerNative = IsMaskedToPlayer
---@param x number
---@param y number
---@param whichPlayer integer
IsMaskedToPlayer = function(x, y, whichPlayer) return IsMaskedToPlayerNative(x, y, Player(whichPlayer-1)) end

IsLocationMaskedToPlayerNative = IsLocationMaskedToPlayer
---@param whichLocation location
---@param whichPlayer integer
IsLocationMaskedToPlayer = function(whichLocation, whichPlayer) return IsLocationMaskedToPlayerNative(whichLocation, Player(whichPlayer-1)) end

GetPlayerRaceNative = GetPlayerRace
---@param whichPlayer integer
GetPlayerRace = function(whichPlayer) return GetPlayerRaceNative(Player(whichPlayer-1)) end

GetPlayerIdNative = GetPlayerId
---@param whichPlayer integer
GetPlayerId = function(whichPlayer) return GetPlayerIdNative(Player(whichPlayer-1)) end

GetPlayerUnitCountNative = GetPlayerUnitCount
---@param whichPlayer integer
---@param includeIncomplete boolean
GetPlayerUnitCount = function(whichPlayer, includeIncomplete) return GetPlayerUnitCountNative(Player(whichPlayer-1), includeIncomplete) end

GetPlayerTypedUnitCountNative = GetPlayerTypedUnitCount
---@param whichPlayer integer
---@param unitName string
---@param includeIncomplete boolean
---@param includeUpgrades boolean
GetPlayerTypedUnitCount = function(whichPlayer, unitName, includeIncomplete, includeUpgrades) return GetPlayerTypedUnitCountNative(Player(whichPlayer-1), unitName, includeIncomplete, includeUpgrades) end

GetPlayerStructureCountNative = GetPlayerStructureCount
---@param whichPlayer integer
---@param includeIncomplete boolean
GetPlayerStructureCount = function(whichPlayer, includeIncomplete) return GetPlayerStructureCountNative(Player(whichPlayer-1), includeIncomplete) end

GetPlayerStateNative = GetPlayerState
---@param whichPlayer integer
---@param whichPlayerState playerstate
GetPlayerState = function(whichPlayer, whichPlayerState) return GetPlayerStateNative(Player(whichPlayer-1), whichPlayerState) end

GetPlayerScoreNative = GetPlayerScore
---@param whichPlayer integer
---@param whichPlayerScore playerscore
GetPlayerScore = function(whichPlayer, whichPlayerScore) return GetPlayerScoreNative(Player(whichPlayer-1), whichPlayerScore) end

GetPlayerAllianceNative = GetPlayerAlliance
---@param sourcePlayer integer
---@param otherPlayer integer
---@param whichAllianceSetting alliancetype
GetPlayerAlliance = function(sourcePlayer, otherPlayer, whichAllianceSetting) return GetPlayerAllianceNative(Player(sourcePlayer-1), Player(otherPlayer-1), whichAllianceSetting) end

GetPlayerHandicapNative = GetPlayerHandicap
---@param whichPlayer integer
GetPlayerHandicap = function(whichPlayer) return GetPlayerHandicapNative(Player(whichPlayer-1)) end

GetPlayerHandicapXPNative = GetPlayerHandicapXP
---@param whichPlayer integer
GetPlayerHandicapXP = function(whichPlayer) return GetPlayerHandicapXPNative(Player(whichPlayer-1)) end

GetPlayerHandicapReviveTimeNative = GetPlayerHandicapReviveTime
---@param whichPlayer integer
GetPlayerHandicapReviveTime = function(whichPlayer) return GetPlayerHandicapReviveTimeNative(Player(whichPlayer-1)) end

GetPlayerHandicapDamageNative = GetPlayerHandicapDamage
---@param whichPlayer integer
GetPlayerHandicapDamage = function(whichPlayer) return GetPlayerHandicapDamageNative(Player(whichPlayer-1)) end

SetPlayerHandicapNative = SetPlayerHandicap
---@param whichPlayer integer
---@param handicap number
SetPlayerHandicap = function(whichPlayer, handicap) SetPlayerHandicapNative(Player(whichPlayer-1), handicap) end

SetPlayerHandicapXPNative = SetPlayerHandicapXP
---@param whichPlayer integer
---@param handicap number
SetPlayerHandicapXP = function(whichPlayer, handicap) SetPlayerHandicapXPNative(Player(whichPlayer-1), handicap) end

SetPlayerHandicapReviveTimeNative = SetPlayerHandicapReviveTime
---@param whichPlayer integer
---@param handicap number
SetPlayerHandicapReviveTime = function(whichPlayer, handicap) SetPlayerHandicapReviveTimeNative(Player(whichPlayer-1), handicap) end

SetPlayerHandicapDamageNative = SetPlayerHandicapDamage
---@param whichPlayer integer
---@param handicap number
SetPlayerHandicapDamage = function(whichPlayer, handicap) SetPlayerHandicapDamageNative(Player(whichPlayer-1), handicap) end

SetPlayerTechMaxAllowedNative = SetPlayerTechMaxAllowed
---@param whichPlayer integer
---@param techid integer
---@param maximum integer
SetPlayerTechMaxAllowed = function(whichPlayer, techid, maximum) SetPlayerTechMaxAllowedNative(Player(whichPlayer-1), techid, maximum) end

GetPlayerTechMaxAllowedNative = GetPlayerTechMaxAllowed
---@param whichPlayer integer
---@param techid integer
GetPlayerTechMaxAllowed = function(whichPlayer, techid) return GetPlayerTechMaxAllowedNative(Player(whichPlayer-1), techid) end

AddPlayerTechResearchedNative = AddPlayerTechResearched
---@param whichPlayer integer
---@param techid integer
---@param levels integer
AddPlayerTechResearched = function(whichPlayer, techid, levels) AddPlayerTechResearchedNative(Player(whichPlayer-1), techid, levels) end

SetPlayerTechResearchedNative = SetPlayerTechResearched
---@param whichPlayer integer
---@param techid integer
---@param setToLevel integer
SetPlayerTechResearched = function(whichPlayer, techid, setToLevel) SetPlayerTechResearchedNative(Player(whichPlayer-1), techid, setToLevel) end

GetPlayerTechResearchedNative = GetPlayerTechResearched
---@param whichPlayer integer
---@param techid integer
---@param specificonly boolean
GetPlayerTechResearched = function(whichPlayer, techid, specificonly) return GetPlayerTechResearchedNative(Player(whichPlayer-1), techid, specificonly) end

GetPlayerTechCountNative = GetPlayerTechCount
---@param whichPlayer integer
---@param techid integer
---@param specificonly boolean
GetPlayerTechCount = function(whichPlayer, techid, specificonly) return GetPlayerTechCountNative(Player(whichPlayer-1), techid, specificonly) end

SetPlayerUnitsOwnerNative = SetPlayerUnitsOwner
---@param whichPlayer integer
---@param newOwner integer
SetPlayerUnitsOwner = function(whichPlayer, newOwner) SetPlayerUnitsOwnerNative(Player(whichPlayer-1), newOwner) end

CripplePlayerNative = CripplePlayer
---@param whichPlayer integer
---@param toWhichPlayers force
---@param flag boolean
CripplePlayer = function(whichPlayer, toWhichPlayers, flag) CripplePlayerNative(Player(whichPlayer-1), toWhichPlayers, flag) end

SetPlayerAbilityAvailableNative = SetPlayerAbilityAvailable
---@param whichPlayer integer
---@param abilid integer
---@param avail boolean
SetPlayerAbilityAvailable = function(whichPlayer, abilid, avail) SetPlayerAbilityAvailableNative(Player(whichPlayer-1), abilid, avail) end

SetPlayerStateNative = SetPlayerState
---@param whichPlayer integer
---@param whichPlayerState playerstate
---@param value integer
SetPlayerState = function(whichPlayer, whichPlayerState, value) SetPlayerStateNative(Player(whichPlayer-1), whichPlayerState, value) end

RemovePlayerNative = RemovePlayer
---@param whichPlayer integer
---@param gameResult playergameresult
RemovePlayer = function(whichPlayer, gameResult) RemovePlayerNative(Player(whichPlayer-1), gameResult) end

CachePlayerHeroDataNative = CachePlayerHeroData
---@param whichPlayer integer
CachePlayerHeroData = function(whichPlayer) CachePlayerHeroDataNative(Player(whichPlayer-1)) end

SetFogStateRectNative = SetFogStateRect
---@param forWhichPlayer integer
---@param whichState fogstate
---@param where rect
---@param useSharedVision boolean
SetFogStateRect = function(forWhichPlayer, whichState, where, useSharedVision) SetFogStateRectNative(Player(forWhichPlayer-1), whichState, where, useSharedVision) end

SetFogStateRadiusNative = SetFogStateRadius
---@param forWhichPlayer integer
---@param whichState fogstate
---@param centerx number
---@param centerY number
---@param radius number
---@param useSharedVision boolean
SetFogStateRadius = function(forWhichPlayer, whichState, centerx, centerY, radius, useSharedVision) SetFogStateRadiusNative(Player(forWhichPlayer-1), whichState, centerx, centerY, radius, useSharedVision) end

SetFogStateRadiusLocNative = SetFogStateRadiusLoc
---@param forWhichPlayer integer
---@param whichState fogstate
---@param center location
---@param radius number
---@param useSharedVision boolean
SetFogStateRadiusLoc = function(forWhichPlayer, whichState, center, radius, useSharedVision) SetFogStateRadiusLocNative(Player(forWhichPlayer-1), whichState, center, radius, useSharedVision) end
CreateFogModifierRectNative = CreateFogModifierRect
---@param forWhichPlayer integer
---@param whichState fogstate
---@param where rect
---@param useSharedVision boolean
---@param afterUnits boolean
CreateFogModifierRect = function(forWhichPlayer, whichState, where, useSharedVision, afterUnits) return CreateFogModifierRectNative(Player(forWhichPlayer-1), whichState, where, useSharedVision, afterUnits) end

CreateFogModifierRadiusNative = CreateFogModifierRadius
---@param forWhichPlayer integer
---@param whichState fogstate
---@param centerx number
---@param centerY number
---@param radius number
---@param useSharedVision boolean
---@param afterUnits boolean
CreateFogModifierRadius = function(forWhichPlayer, whichState, centerx, centerY, radius, useSharedVision, afterUnits) return CreateFogModifierRadiusNative(Player(forWhichPlayer-1), whichState, centerx, centerY, radius, useSharedVision, afterUnits) end

CreateFogModifierRadiusLocNative = CreateFogModifierRadiusLoc
---@param forWhichPlayer integer
---@param whichState fogstate
---@param center location
---@param radius number
---@param useSharedVision boolean
---@param afterUnits boolean
CreateFogModifierRadiusLoc = function(forWhichPlayer, whichState, center, radius, useSharedVision, afterUnits) return CreateFogModifierRadiusLocNative(Player(forWhichPlayer-1), whichState, center, radius, useSharedVision, afterUnits) end

DialogDisplayNative = DialogDisplay
---@param whichPlayer integer
---@param whichDialog dialog
---@param flag boolean
DialogDisplay = function(whichPlayer, whichDialog, flag) DialogDisplayNative(Player(whichPlayer-1), whichDialog, flag) end

RestoreUnitNative = RestoreUnit
---@param cache gamecache
---@param missionKey string
---@param key string
---@param forWhichPlayer integer
---@param x number
---@param y number
---@param facing number
RestoreUnit = function(cache, missionKey, key, forWhichPlayer, x, y, facing) return RestoreUnitNative(cache, missionKey, key, Player(forWhichPlayer-1), x, y, facing) end

SavePlayerHandleNative = SavePlayerHandle
---@param table hashtable
---@param parentKey integer
---@param childKey integer
---@param whichPlayer integer
SavePlayerHandle = function(table, parentKey, childKey, whichPlayer) return SavePlayerHandleNative(table, parentKey, childKey, Player(whichPlayer-1)) end

LoadPlayerHandleNative = LoadPlayerHandle
---@param table hashtable
---@param parentKey integer
---@param childKey integer
---@diagnostic disable-next-line: param-type-mismatch
LoadPlayerHandle = function(table, parentKey, childKey) return GetPlayerId(LoadPlayerHandleNative(table, parentKey, childKey)) + 1 end

PlaceRandomUnitNative = PlaceRandomUnit
---@param whichPool unitpool
---@param forWhichPlayer integer
---@param x number
---@param y number
---@param facing number
PlaceRandomUnit = function(whichPool, forWhichPlayer, x, y, facing) return PlaceRandomUnitNative(whichPool, Player(forWhichPlayer-1), x, y, facing) end

DisplayTextToPlayerNative = DisplayTextToPlayer
---@param toPlayer integer
---@param x number
---@param y number
---@param message string
DisplayTextToPlayer = function(toPlayer, x, y, message) DisplayTextToPlayerNative(Player(toPlayer-1), x, y, message) end

DisplayTimedTextToPlayerNative = DisplayTimedTextToPlayer
---@param toPlayer integer
---@param x number
---@param y number
---@param duration number
---@param message string
DisplayTimedTextToPlayer = function(toPlayer, x, y, duration, message) DisplayTimedTextToPlayerNative(Player(toPlayer-1), x, y, duration, message) end

DisplayTimedTextFromPlayerNative = DisplayTimedTextFromPlayer
---@param toPlayer integer
---@param x number
---@param y number
---@param duration number
---@param message string
DisplayTimedTextFromPlayer = function(toPlayer, x, y, duration, message) DisplayTimedTextFromPlayerNative(Player(toPlayer-1), x, y, duration, message) end

LeaderboardAddItemNative = LeaderboardAddItem
---@param lb leaderboard
---@param label string
---@param value integer
---@param p integer
LeaderboardAddItem = function(lb, label, value, p) LeaderboardAddItemNative(lb, label, value, Player(p-1)) end

LeaderboardRemovePlayerItemNative = LeaderboardRemovePlayerItem
---@param lb leaderboard
---@param p integer
LeaderboardRemovePlayerItem = function(lb, p) LeaderboardRemovePlayerItemNative(lb, Player(p-1)) end

LeaderboardHasPlayerItemNative = LeaderboardHasPlayerItem
---@param lb leaderboard
---@param p integer
LeaderboardHasPlayerItem = function(lb, p) return LeaderboardHasPlayerItemNative(lb, Player(p-1)) end

LeaderboardGetPlayerIndexNative = LeaderboardGetPlayerIndex
---@param lb leaderboard
---@param p integer
LeaderboardGetPlayerIndex = function(lb, p) return LeaderboardGetPlayerIndexNative(lb, Player(p-1)) end

PlayerSetLeaderboardNative = PlayerSetLeaderboard
---@param toPlayer integer
---@param lb leaderboard
PlayerSetLeaderboard = function(toPlayer, lb) PlayerSetLeaderboardNative(Player(toPlayer-1), lb) end

PlayerGetLeaderboardNative = PlayerGetLeaderboard
---@param toPlayer integer
PlayerGetLeaderboard = function(toPlayer) return PlayerGetLeaderboardNative(Player(toPlayer-1)) end

SetBlightNative = SetBlight
---@param whichPlayer integer
---@param x number
---@param y number
---@param radius number
---@param addBlight boolean
SetBlight = function(whichPlayer, x, y, radius, addBlight) SetBlightNative(Player(whichPlayer-1), x, y, radius, addBlight) end

SetBlightRectNative = SetBlightRect
---@param whichPlayer integer
---@param r rect
---@param addBlight boolean
SetBlightRect = function(whichPlayer, r, addBlight) SetBlightRectNative(Player(whichPlayer-1), r, addBlight) end

SetBlightPointNative = SetBlightPoint
---@param whichPlayer integer
---@param x number
---@param y number
---@param addBlight boolean
SetBlightPoint = function(whichPlayer, x, y, addBlight) SetBlightPointNative(Player(whichPlayer-1), x, y, addBlight) end

SetBlightLocNative = SetBlightLoc
---@param whichPlayer integer
---@param whichLocation location
---@param radius number
---@param addBlight boolean
SetBlightLoc = function(whichPlayer, whichLocation, radius, addBlight) SetBlightLocNative(Player(whichPlayer-1), whichLocation, radius, addBlight) end

CreateBlightedGoldmineNative = CreateBlightedGoldmine
---@param id integer
---@param x number
---@param y number
---@param face number
CreateBlightedGoldmine = function(id, x, y, face) return CreateBlightedGoldmineNative(Player(id-1), x, y, face) end

StartMeleeAINative = StartMeleeAI
---@param num integer
---@param script? string
StartMeleeAI = function(num, script) StartMeleeAINative(Player(num-1), script) end

StartCampaignAINative = StartCampaignAI
---@param num integer
---@param script string
StartCampaignAI = function(num, script) StartCampaignAINative(Player(num-1), script) end

CommandAINative = CommandAI
---@param num integer
---@param command integer
---@param data integer
CommandAI = function(num, command, data) CommandAINative(Player(num-1), command, data) end

PauseCompAINative = PauseCompAI
---@param p integer
---@param pause boolean
PauseCompAI = function(p, pause) PauseCompAINative(Player(p-1), pause) end

GetAIDifficultyNative = GetAIDifficulty
---@param num integer
GetAIDifficulty = function(num) return GetAIDifficultyNative(Player(num-1)) end

RemoveAllGuardPositionsNative = RemoveAllGuardPositions
---@param num integer
RemoveAllGuardPositions = function(num) RemoveAllGuardPositionsNative(Player(num-1)) end

BlzSetSpecialEffectColorByPlayerNative = BlzSetSpecialEffectColorByPlayer
---@param whichEffect effect
---@param whichPlayer integer
BlzSetSpecialEffectColorByPlayer = function(whichEffect, whichPlayer) BlzSetSpecialEffectColorByPlayerNative(whichEffect, Player(whichPlayer-1)) end

BlzDecPlayerTechResearchedNative = BlzDecPlayerTechResearched
---@param whichPlayer integer
---@param techid integer
---@param levels integer
BlzDecPlayerTechResearched = function(whichPlayer, techid, levels) BlzDecPlayerTechResearchedNative(Player(whichPlayer-1), techid, levels) end

RequestExtraIntegerDataNative = RequestExtraIntegerData
---@param dataType integer
---@param whichPlayer integer
---@param param1 string
---@param param2 string
---@param param3 boolean
---@param param4 integer
---@param param5 integer
---@param param6 integer
RequestExtraIntegerData = function(dataType, whichPlayer, param1, param2, param3, param4, param5, param6) return RequestExtraIntegerDataNative(dataType, Player(whichPlayer-1), param1, param2, param3, param4, param5, param6) end

RequestExtraBooleanDataNative = RequestExtraBooleanData
---@param dataType integer
---@param whichPlayer integer
---@param param1 string
---@param param2 string
---@param param3 boolean
---@param param4 integer
---@param param5 integer
---@param param6 integer
RequestExtraBooleanData = function(dataType, whichPlayer, param1, param2, param3, param4, param5, param6) return RequestExtraBooleanDataNative(dataType, Player(whichPlayer-1), param1, param2, param3, param4, param5, param6) end

RequestExtraStringDataNative = RequestExtraStringData
---@param dataType integer
---@param whichPlayer integer
---@param param1 string
---@param param2 string
---@param param3 boolean
---@param param4 integer
---@param param5 integer
---@param param6 integer
RequestExtraStringData = function(dataType, whichPlayer, param1, param2, param3, param4, param5, param6) return RequestExtraStringDataNative(dataType, Player(whichPlayer-1), param1, param2, param3, param4, param5, param6) end

RequestExtraRealDataNative = RequestExtraRealData
---@param dataType integer
---@param whichPlayer integer
---@param param1 string
---@param param2 string
---@param param3 boolean
---@param param4 integer
---@param param5 integer
---@param param6 integer
RequestExtraRealData = function(dataType, whichPlayer, param1, param2, param3, param4, param5, param6) return RequestExtraRealDataNative(dataType, Player(whichPlayer-1), param1, param2, param3, param4, param5, param6) end

BlzTriggerRegisterPlayerSyncEventNative = BlzTriggerRegisterPlayerSyncEvent
---@param whichTrigger trigger
---@param whichPlayer integer
---@param prefix string
---@param fromServer boolean
BlzTriggerRegisterPlayerSyncEvent = function(whichTrigger, whichPlayer, prefix, fromServer) return BlzTriggerRegisterPlayerSyncEventNative(whichTrigger, Player(whichPlayer-1), prefix, fromServer) end

BlzTriggerRegisterPlayerKeyEventNative = BlzTriggerRegisterPlayerKeyEvent
---@param whichTrigger trigger
---@param whichPlayer integer
---@param key oskeytype
---@param metaKey integer
---@param keyDown boolean
BlzTriggerRegisterPlayerKeyEvent = function(whichTrigger, whichPlayer, key, metaKey, keyDown) return BlzTriggerRegisterPlayerKeyEventNative(whichTrigger, Player(whichPlayer-1), key, metaKey, keyDown) end

BlzDisplayChatMessageNative = BlzDisplayChatMessage
---@param whichPlayer integer
---@param recipient integer
---@param message string
BlzDisplayChatMessage = function(whichPlayer, recipient, message) BlzDisplayChatMessageNative(Player(whichPlayer-1), recipient, message) end

BlzCreateUnitWithSkinNative = BlzCreateUnitWithSkin
---@param id integer
---@param unitid integer
---@param x number
---@param y number
---@param face number
---@param skinId integer
BlzCreateUnitWithSkin = function(id, unitid, x, y, face, skinId) return BlzCreateUnitWithSkinNative(Player(id-1), unitid, x, y, face, skinId) end

BlzGetPlayerTownHallCountNative = BlzGetPlayerTownHallCount
---@param whichPlayer integer
BlzGetPlayerTownHallCount = function(whichPlayer) return BlzGetPlayerTownHallCountNative(Player(whichPlayer-1)) end

BlzQueueNeutralImmediateOrderByIdNative = BlzQueueNeutralImmediateOrderById
---@param forWhichPlayer integer
---@param neutralStructure unit
---@param unitId integer
BlzQueueNeutralImmediateOrderById = function(forWhichPlayer, neutralStructure, unitId) return BlzQueueNeutralImmediateOrderByIdNative(Player(forWhichPlayer-1), neutralStructure, unitId) end

BlzQueueNeutralPointOrderByIdNative = BlzQueueNeutralPointOrderById
---@param forWhichPlayer integer
---@param neutralStructure unit
---@param unitId integer
---@param x number
---@param y number
BlzQueueNeutralPointOrderById = function(forWhichPlayer, neutralStructure, unitId, x, y) return BlzQueueNeutralPointOrderByIdNative(Player(forWhichPlayer-1), neutralStructure, unitId, x, y) end

BlzQueueNeutralTargetOrderByIdNative = BlzQueueNeutralTargetOrderById
---@param forWhichPlayer integer
---@param neutralStructure unit
---@param unitId integer
---@param target widget
BlzQueueNeutralTargetOrderById = function(forWhichPlayer, neutralStructure, unitId, target) return BlzQueueNeutralTargetOrderByIdNative(Player(forWhichPlayer-1), neutralStructure, unitId, target) end

8etdc3.jpg

I don't know, what do you think?
 
Level 20
Joined
Jul 10, 2009
Messages
478
Your way would work from a technical point of view, but it's a bit overkill for what you are trying to achieve. You can prevent basically all player-to-integer conversions (which I also hate) by simply creating a single player-array at the beginning of the game:
Lua:
--(untested code)
--beginning of game
PLAYERS_PLAYING = {} ---@type Player[]
--save all players to the array who are playing the map at game start. Beware of creating holes.
OnInit.map( function() --requires Total Initialization
    local arrayIndex = 1
    for i = 0, GetBJMaxPlayers() - 1 do
        if GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING then
            PLAYERS_PLAYING[arrayIndex] = Player(i)
            arrayIndex = arrayIndex + 1
        end
    end
end)

This is basically the only piece of code where you would ever need the Player(i)-native.
In all other cases, you can either use event responses (GetTriggerPlayer()) or retreive the player from any data structure that you have previously defined.

Looping can be done this way:
Lua:
--Loop through all players in the game
for _, loopPlayer in ipairs(PLAYERS_PLAYING) do
    print(loopPlayer)
end
Removing players leaving the game from the array might be necessary (depending on the use case). Don't leave holes when doing so, but downshift higher indices immediately to retain functionality of ipairs.

It's also good practice to never use player-indices as parameters for functions, but only the player itself (which further prevents integer-conversion).
Lua:
--Bad:
function printPlayer(playerId)
    print(Player(playerId))
end
--Good:
function printPlayer(whichPlayer)
    print(whichPlayer)
end

Edit: Using the player as a key for saving properties (current kills, whatever) is still a good idea. I have plenty of data structures like that. If you need to loop over such a table t containing player-data, you would either use a SyncedTable or do it this way:
Lua:
for _, loopPlayer in ipairs(PLAYERS_PLAYING) do
    print( t[loopPlayer] )
end

Edit2: I personally use a Set instead of an array to save my players, because the data structure perfectly resembles what we try to achieve here.
 
Last edited:
Hm, yea, that would work well. I was doing something similar, but I'm not really using the ipairs loop a whole lot yet. Definitely makes it more convenient. Only situation where you'd have to convert is when you need to do arithmetic based on player number, but that's not too often.

But then again, overwriting all natives would send a message! :plol:
 
Level 20
Joined
Jul 10, 2009
Messages
478
I'd even argue that arithmetic based on player number would rather need to be based on player array position instead in most cases :)

Take the example where Player(0), Player(5) and Player(8) are playing the game, all others players are not, and you want to represent these 3 in a multiboard. You would have a title row, Player(0) gets row 2, Player(5) gets row 3 and Player(8) gets row 4.

These measures could perfectly be extracted from PLAYERS_PLAYING, but it would be very cumbersome if done by player number.

Lua:
for pos, loopPlayer in ipairs(PLAYERS_PLAYING) do
    print("Player " .. GetPlayerName(loopPlayer) .. " has multiboard row " .. (pos + 1))
end
 
Last edited:
You are right, of course. In vJASS, my loops would always look like

vJASS:
loop
   exitwhen i > numPlayers
    set P = playerNumberOfSlot[i]
    call DisplayTextToPlayer(Player(P), "You are player " + i + " out of " + numPlayers + ".")
    set i = i + 1
endloop

So, in Lua this would be simply:
Lua:
for i, P in ipairs(PLAYERS) do
    call DisplayTextToPlayer(P, "You are player " .. i .. " out of " .. #PLAYERS .. ".")
end
 
Top