CustomCommand / addCustomCode Library Script (for tracking individual ships)

Hi everyone… I’ve developed a custom code library for all to use.

  • Usage is below the code.
  • There is also a caveat that you need to be aware of, should you choose to use this code. This is also mentioned below.

data:scripts/lib/custom.lua:

if CustomShipScripts_Loaded == nil then
    CustomShipScripts_Loaded = 1

    ships = {}

    function __CheckShipsTable()
        if ships == nil then error("ships table does not exist") end
    end
    function __CheckShipIndex(index)
        __CheckShipsTable()
        if ships[index] == nil then
            print("ships["..index.."] does not exist. Are you sure it was added to the `ships' table?")
            return true
        end
        return false
    end

    function CustomShip_Init(CustomGroup, PlayerId, ShipId)
        __CheckShipsTable()
        ships[ShipId] =
        {
            ShipId = ShipId,
            PlayerId = PlayerId,
            OnCreate = { count = 0 },
            OnUpdate = { count = 0 },
            OnDestroy = { count = 0 },
            OnStart = { count = 0 },
            OnDo = { count = 0 },
            OnFinish = { count = 0 }
        }
    end

    function CustomShip_Create(CustomGroup, PlayerId, ShipId)
        if __CheckShipIndex(ShipId) then return end
        count = ships[ShipId].OnCreate.count
        index = 0
        while index < count do
            ships[ShipId].OnCreate[index](CustomGroup, PlayerId, ShipId)
            index = index + 1
        end
    end

    function CustomShip_Update(CustomGroup, PlayerId, ShipId)
        if __CheckShipIndex(ShipId) then return end
        count = ships[ShipId].OnUpdate.count
        index = 0
        while index < count do
            ships[ShipId].OnUpdate[index](CustomGroup, PlayerId, ShipId)
            index = index + 1
        end
    end

    function CustomShip_Destroy(CustomGroup, PlayerId, ShipId)
        if __CheckShipIndex(ShipId) then return end
        count = ships[ShipId].OnUpdate.count
        index = 0
        while index < count do
            ships[ShipId].OnDestroy[index](CustomGroup, PlayerId, ShipId)
            index = index + 1
        end
    end

    function CustomShip_Start(CustomGroup, PlayerId, ShipId)
        if __CheckShipIndex(ShipId) then return end
        count = ships[ShipId].OnStart.count
        index = 0
        while index < count do
            ships[ShipId].OnStart[index](CustomGroup, PlayerId, ShipId)
            index = index + 1
        end
    end

    function CustomShip_Do(CustomGroup, PlayerId, ShipId)
        if __CheckShipIndex(ShipId) then return end
        count = ships[ShipId].OnDo.count
        index = 0
        while index < count do
            ships[ShipId].OnDo[index](CustomGroup, PlayerId, ShipId)
            index = index + 1
        end
    end

    function CustomShip_Finish(CustomGroup, PlayerId, ShipId)
        if __CheckShipIndex(ShipId) then return end
        count = ships[ShipId].OnFinish.count
        index = 0
        while index < count do
            ships[ShipId].OnFinish[index](CustomGroup, PlayerId, ShipId)
            index = index + 1
        end
    end

    function CustomShip_AddOnCreate(ShipId, func)
        count = ships[ShipId].OnCreate.count
        ships[ShipId].OnCreate[count] = func
        ships[ShipId].OnCreate.count = count + 1
    end

    function CustomShip_AddOnUpdate(ShipId, func)
        count = ships[ShipId].OnUpdate.count
        ships[ShipId].OnUpdate[count] = func
        ships[ShipId].OnUpdate.count = count + 1
    end

    function CustomShip_AddOnDestroy(ShipId, func)
        count = ships[ShipId].OnDestroy.count
        ships[ShipId].OnDestroy[count] = func
        ships[ShipId].OnDestroy.count = count + 1
    end

    function CustomShip_AddOnStart(ShipId, func)
        count = ships[ShipId].OnStart.count
        ships[ShipId].OnStart[count] = func
        ships[ShipId].OnStart.count = count + 1
    end

    function CustomShip_AddOnDo(ShipId, func)
        count = ships[ShipId].OnDo.count
        ships[ShipId].OnDo[count] = func
        ships[ShipId].OnDo.count = count + 1
    end

    function CustomShip_AddOnFinish(ShipId, func)
        count = ships[ShipId].OnFinish.count
        ships[ShipId].OnFinish[count] = func
        ships[ShipId].OnFinish.count = count + 1
    end


    function __CustomShip_FindFunction(functype, ShipId, func)
        if __CheckShipIndex(ShipId) then return -1 end
        local array = nil
        if functype == "OnCreate" then
            array = ships[ShipId].OnCreate
        elseif functype == "OnUpdate" then
            array = ships[ShipId].OnUpdate
        elseif functype == "OnDestroy" then
            array = ships[ShipId].OnDestroy
        elseif functype == "OnStart" then
            array = ships[ShipId].OnStart
        elseif functype == "OnDo" then
            array = ships[ShipId].OnDo
        elseif functype == "OnFinish" then
            array = ships[ShipId].OnFinish
        else
            error("Unknown function `"..functype.."'")
            return -1
        end
        
        count = array.count
        i = 0
        while i < count do
            if array[i] == func then
                return i
            end
            i = i + 1
        end
        return -1
    end

    function CustomShip_FindOnCreate(ShipId, func)
        return __CustomShip_FindFunction("OnCreate", ShipId, func)
    end

    function CustomShip_FindOnUpdate(ShipId, func)
        return __CustomShip_FindFunction("OnUpdate", ShipId, func)
    end

    function CustomShip_FindOnDestroy(ShipId, func)
        return __CustomShip_FindFunction("OnDestroy", ShipId, func)
    end

    function CustomShip_FindOnStart(ShipId, func)
        return __CustomShip_FindFunction("OnStart", ShipId, func)
    end

    function CustomShip_FindOnDo(ShipId, func)
        return __CustomShip_FindFunction("OnDo", ShipId, func)
    end

    function CustomShip_FindOnFinish(ShipId, func)
        return __CustomShip_FindFunction("OnFinish", ShipId, func)
    end

    function CustomShip_RemoveOnCreate(ShipId, func)
        if __CheckShipIndex(ShipId) then return end
        index = CustomShip_FindOnCreate(ShipId, func);
        if index == -1 then return end
        count = ships[ShipId].OnCreate.count
        while index < count - 1 do
            ships[ShipId].OnCreate[index] = ships[ShipId].OnCreate[index + 1]
            index = index + 1
        end
        ships[ShipId].OnCreate[index] = nil
        ships[ShipId].OnCreate.count = count - 1
    end

    function CustomShip_RemoveOnUpdate(ShipId, func)
        if __CheckShipIndex(ShipId) then return end
        index = CustomShip_FindOnUpdate(ShipId, func);
        if index == -1 then return end
        count = ships[ShipId].OnUpdate.count
        while index < count - 1 do
            ships[ShipId].OnUpdate[index] = ships[ShipId].OnUpdate[index + 1]
            index = index + 1
        end
        ships[ShipId].OnUpdate[index] = nil
        ships[ShipId].OnUpdate.count = count - 1
    end

    function CustomShip_RemoveOnDestroy(ShipId, func)
        if __CheckShipIndex(ShipId) then return end
        index = CustomShip_FindOnDestroy(ShipId, func);
        if index == -1 then return end
        count = ships[ShipId].OnDestroy.count
        while index < count - 1 do
            ships[ShipId].OnDestroy[index] = ships[ShipId].OnDestroy[index + 1]
            index = index + 1
        end
        ships[ShipId].OnDestroy[index] = nil
        ships[ShipId].OnDestroy.count = count - 1
    end

    function CustomShip_RemoveOnStart(ShipId, func)
        if __CheckShipIndex(ShipId) then return end
        index = CustomShip_FindOnStart(ShipId, func);
        if index == -1 then return end
        count = ships[ShipId].OnStart.count
        while index < count - 1 do
            ships[ShipId].OnStart[index] = ships[ShipId].OnStart[index + 1]
            index = index + 1
        end
        ships[ShipId].OnStart[index] = nil
        ships[ShipId].OnStart.count = count - 1
    end

    function CustomShip_RemoveOnDo(ShipId, func)
        if __CheckShipIndex(ShipId) then return end
        index = CustomShip_FindOnDo(ShipId, func);
        if index == -1 then return end
        count = ships[ShipId].OnDo.count
        while index < count - 1 do
            ships[ShipId].OnDo[index] = ships[ShipId].OnDo[index + 1]
            index = index + 1
        end
        ships[ShipId].OnDo[index] = nil
        ships[ShipId].OnDo.count = count - 1
    end

    function CustomShip_RemoveOnFinish(ShipId, func)
        if __CheckShipIndex(ShipId) then return end
        index = CustomShip_FindOnFinish(ShipId, func);
        if index == -1 then return end
        count = ships[ShipId].OnFinish.count
        while index < count - 1 do
            ships[ShipId].OnFinish[index] = ships[ShipId].OnFinish[index + 1]
            index = index + 1
        end
        ships[ShipId].OnFinish[index] = nil
        ships[ShipId].OnFinish.count = count - 1
    end
end

Usage

In the custom code for the ship you want to add (for example, data:ships/cust_ship/cust_ship.lua):

dofilepath("data:scripts/lib/custom.lua")

function Ship_Create(CustomGroup, PlayerId, ShipId)
    CustomShip_Init(CustomGroup, PlayerId, ShipId)
    
    --CustomShip_AddOnCreate(ShipId, callback)
    --CustomShip_AddOnUpdate(ShipId, callback)
    --CustomShip_AddOnDestroy(ShipId, callback)
    --CustomShip_AddOnStart(ShipId, callback)
    --CustomShip_AddOnDo(ShipId, callback)
    --CustomShip_AddOnFinish(ShipId, callback)
    
    CustomShip_Create(CustomGroup, PlayerId, ShipId)
end

The callback argument is a function with the same signature as the regular custom code functions, i.e. Create_Hgn_Carrier(CustomGroup, playerIndex, shipID).

Make sure you have a unique Ship_Create function name for all the different types of ships that use custom code/commands, to prevent the functionality from one ship overwriting another.


The code to place in the ship file (data:ships/cust_ship/cust_ship.ship):

CustomCommand Ability (for ships that use custom commands):

addAbility(NewShipType,"CustomCommand",1,"Drones",1,0,1000,200,0.35,2.8,0,"data:scripts/lib/custom.lua","CustomShip_Start","CustomShip_Do","CustomShip_Finish","Cust_Ship",1.15,2,1)

addCustomCode (for all ships):

addCustomCode(NewShipType,"data:ship/cust_ship/cust_ship.lua","","Create_DroneFrigate","CustomShip_Update","CustomShip_Destroy","Cust_Ship",1.15)

Here is where the caveat comes in:

If you paid attention above, you’ll notice the emphasis on the note for addCustomCode.

This line of code is required for all ships that use this library. The reason for this is because the library function CustomShip_Init should only be called once. Ever. (Per ShipId). Placing this function in the CustomShip_Start function of CustomCommand would (re-)create and overwrite any persistent data from the previous time the CustomCommand ability was trigged.

If there is no other custom functionality for the ship, you should use the following line of code:

addCustomCode(NewShipType,"data:ship/tai_scout/tai_scout.lua","","Create_Tai_Scout","","","Tai_Scout",1)

It should be noted that leaving CustomShip_Update and CustomShip_Destroy in won’t actually hurt anything (except maybe performance).

3 Likes

Post reserved for Conversion of existing ship Tutorial

1 Like

It took me a while to work out what the purpose of this is, but now I understand it, this is very clever. It allows you to have only one script running instead of lots of custom code scripts (one per ship) all running at the same time and potentially doing the same thing… Right?

2 Likes

If I get what you’re saying right, it would be like creating true custom functions and therefore save a lot of CPU time?

EDIT: so, for example, if I wanted to use the library for the ammunition management system, I would write a general code for it there and call it through the new instruction?

2 Likes

hmmmm:

dofilepath("data:scripts/lib/trp_platform_linking.lua")

function Ship_Create(CustomGroup, PlayerId, ShipId)
	print("+++ Ship_Create() for trp_platform_field, shipId = "..ShipId)
	CustomShip_Init(CustomGroup, PlayerId, ShipId)
	
	CustomShip_AddOnCreate(ShipId, callback)
	CustomShip_AddOnUpdate(ShipId, callback)
	CustomShip_AddOnDestroy(ShipId, callback)
	--CustomShip_AddOnStart(ShipId, callback)
	--CustomShip_AddOnDo(ShipId, callback)
	--CustomShip_AddOnFinish(ShipId, callback)
	
	CustomShip_Create(CustomGroup, PlayerId, ShipId)
end

Result:

+++ Ship_Create() for trp_platform_field, shipId = 544 
 parameter: attempt to call global `CustomShip_Init' (a nil value)
stack traceback:
   1:  function `Ship_Create' at line 5 [string ""] 
 *****---- CommandConstructionComplete : buildingShip=trp_mothership, builtItem=Trp_Platform_Field, buildType=1 
 +++ Ship_Create() for trp_platform_field, shipId = 672 
 parameter: attempt to call global `CustomShip_Init' (a nil value)
stack traceback:
   1:  function `Ship_Create' at line 5 [string ""] 
 *****---- CommandConstructionComplete : buildingShip=trp_mothership, builtItem=Trp_Platform_Field, buildType=1 

My first thought was that my file path to trp_platform_linking.lua (your custom.lua) was wrong, but I stuck a print statement at the top and I see appear it in the log file as the game loads.

Bingo!

Correct!

That’s weird… try adding a print at the beginning of your data:scripts/lib/trp_platform_linking.lua

1 Like

That’s what I did:

I wonder if it could be the Dropbox thing? I’m loading data files from my hard drive and the Dropbox. Also this is using HWRM 2.1.

ok, I have some progress…

  1. Don’t use Ship_Create as a function name - this is already defined somewhere in the game code. Oops.

  2. If I leave the following block, the code never runs. For some reason “CustomShipScripts_Loaded” is always considered true… It is ok though, my print statement at the top of the file only appears once (with 4 ships calling the script). So the lib script is only called once. So I removed this block and it is working.

if CustomShipScripts_Loaded == true then return end
CustomShipScripts_Loaded = true

I could be wrong, but i don’t think true is set to a value… it is like the lines are instead

if CustomShipScripts_Loaded == nil then return end
CustomShipScripts_Loaded = nil

You could try

if CustomShipScripts_Loaded then return end
CustomShipScripts_Loaded = 1

‘true’ is not a keyword in Lua 4. It will be treated as a variable with nil value. Comparisons to it with non-nil values will always be false. Same is true of ‘false’

Thanks. So in this case it compares an undeclared variable with nil. The expected result is that they are equal. Correct?

off the top of my head I think that’d be the case, but I wouldn’t trust it to be so without testing it experimentally.

Which is thankfully pretty easy.

C:\>lua4
Lua 4.0.1  Copyright (C) 1994-2000 TeCGraf, PUC-Rio
> if true == nil then print("hah") else print("boo") end
hah
> if true ~= nil then print("hah") else print("boo") end
boo
> if nonsense == nil then print("hah") else print("boo") end
hah
> if nonsense ~= nil then print("hah") else print("boo") end
boo
> if true == false then print("hah") else print("boo") end
hah
>
2 Likes

I got it working using:

if CustomShipScripts_Loaded ~= nil then
	print("=== trp_platform_linking.lua already loaded")
	return
else
	print("=== loading trp_platform_linking.lua")
end

CustomShipScripts_Loaded = 4

Now to write the actual code! Thanks @radar3301, this is a powerful script.

1 Like

In a recent conversation with @EatThePath, I’ve realized that having more than one CustomCommand ability on a ship (i.e. addAbility(NewShipType,"CustomCommand", ...) gets messy, as in, this library has no idea which CustomCommand ability is the one activated. Therefore, I would recommend not using the CustomCommand Ability code from the example if your ship has more than one CustomCommand ability.

You can still use the addCustomCode code if you have more than one ability.