Hard Light Productions Forums

FreeSpace Releases => Scripting Releases => Topic started by: General Battuta on September 22, 2012, 02:09:48 pm

Title: Highlight or hotkey individual turret
Post by: General Battuta on September 22, 2012, 02:09:48 pm
Can we do this? I feel like someone might've written a script and I missed it.

I want players to be able to press F9 and have a particular turret on a Deimos targeted. Ideally I'd like them to be able to press F9 and cycle through four turrets on this Deimos, but all of them would be highlighted with gray brackets.
Title: Re: Highlight or hotkey individual turret
Post by: MatthTheGeek on September 22, 2012, 02:15:12 pm
This would give a huuuuuuugely useful tool for FREDers. Instead of actually requiring people to go open the pof and look what turret carries what.
Title: Re: Highlight or hotkey individual turret
Post by: Nighteyes on September 22, 2012, 08:16:28 pm
Deimos Basestar
needless to say, something like this would be awesome
Title: Re: Highlight or hotkey individual turret
Post by: mjn.mixael on September 22, 2012, 09:05:17 pm
+1
Title: Re: Highlight or hotkey individual turret
Post by: m!m on September 23, 2012, 06:43:53 am
Here's a script for this:
Code: (turretHotkey-sct.tbm) [Select]
#Conditional Hooks

$Application: FS2_Open
$On Gameplay Start:
[
turretHotkeys = {}
turretHotkeys.key = "F9"

turretHotkeys.ship = nil
turretHotkeys.subsystemNames = {}
turretHotkeys.lastIndex = 0

function turretHotkeys:keyPressed(key)
    if (key == self.key) then
        if (self.ship ~= nil and self.ship:isValid()) then
            local nextIndex = self.lastIndex + 1

            if (nextIndex > #self.subsystemNames) then
                nextIndex = 1
            end

            local subsys = self.ship[self.subsystemNames[nextIndex]]

            if (subsys:isValid()) then
                hv.Player.Target = self.ship
                hv.Player.TargetSubsystem = subsys
            end

            self.lastIndex = nextIndex
        end
    end
end

function thkKey(key)
    if (type(key) ~= "string") then
        ba.warning("Invalid argument type for thkKey. Need a string, got " .. type(key) .. ".")
        return
    end

    turretHotkeys.key = key
end

function thkShip(name)
    if (name == nil) then
        turretHotkeys.ship = nil
    else
        if (type(name) ~= "string") then
            ba.warning("Invalid argument type for thkShip. Need a string, got " .. type(name) .. ".")
            return
        end

        local ship = mn.Ships[name]

        if (not ship:isValid()) then
            ba.warning("No ship with name \"" .. name .. "\" could be found in the current mission.")
            return
        end

        turretHotkeys.ship = ship
    end
end

function thkAdd(name)
    if (type(name) ~= "string") then
        ba.warning("Invalid argument type for thkAdd. Need a string, got " .. type(name) .. ".")
        return
    end

    if (turretHotkeys.ship == nil or not turretHotkeys.ship:isValid()) then
        ba.warning("Invalid ship in turret hotkey state. You need to specify a ship before adding subsystems")
        return
    end

    if (turretHotkeys.ship[name]:isValid()) then
        table.insert(turretHotkeys.subsystemNames, name)
    end
end

function thkRemove(name)
    if (type(name) ~= "string") then
        ba.warning("Invalid argument type for thkRemove. Need a string, got " .. type(name) .. ".")
        return
    end

    local index = 1
    local done = false

    while (not done) do
        for i = index, #turretHotkeys.subsystemNames do
            if (turretHotkeys.subsystemNames[i] == name) then
                table.remove(turretHotkeys.subsystemNames, i)
                index = i
                break
            end
        end

        done = true
    end
end

function thkClear()
    turretHotkeys.subsystemNames = {}
end
]

$State: GS_STATE_GAME_PLAY
$On Key Pressed:
[
turretHotkeys:keyPressed(hv.Key)
]

#End

To enable it in a mission execute the script-eval SEXP with the argument thkShip('<name>') and add the desired subsystems with thkAdd('<subsys name>').
You can also remove a subsystem with thkRemove('<name>') when it has been added before. If you want to clear this list then call thkClear() and to completely disable it at some point call thkShip() without any name.
You can also change the key that has to be pressed with thkKey('<key>'), the default key if F9.
Title: Re: Highlight or hotkey individual turret
Post by: General Battuta on September 23, 2012, 10:34:36 am
Will you marry me?
Title: Re: Highlight or hotkey individual turret
Post by: General Battuta on September 23, 2012, 07:00:58 pm
m!m this is awesome (once I realized you couldn't add data new arguments to script eval, you had to add new script-evals.) Could I ask for two expanded features, if they're not ridiculously hard:

Allow this to work for more than one ship at a time (I've got two Deimos, and I'd like to cycle between eight turrets, four on each of them)

Draw little escort brackets around every turret on the list, even when it's not actually targeted?

You're the best
Title: Re: Highlight or hotkey individual turret
Post by: m!m on September 24, 2012, 08:42:57 am
(once I realized you couldn't add data new arguments to script eval, you had to add new script-evals.)
I know that that bug exists since some time although I'm not sure why I haven't fixed it :doubt:

Allow this to work for more than one ship at a time (I've got two Deimos, and I'd like to cycle between eight turrets, four on each of them)
I thought about that when I wrote the script but discarded the feature as a proper FRED interface would have been quite work intensive because of the limited space in the SEXP argument but I'll see if I can come up with something...

Draw little escort brackets around every turret on the list, even when it's not actually targeted?
That existed somewhere before this, I'll see if I can still find it...
Title: Re: Highlight or hotkey individual turret
Post by: General Battuta on September 24, 2012, 09:37:13 am
Here's a possibly dumb workaround: could I just copy the script to a second tbm, rename that one 'turretscript2.tbm', and run that in a separate event for the second Deimos?
Title: Re: Highlight or hotkey individual turret
Post by: m!m on September 24, 2012, 09:39:08 am
No, that would not work because it would use the same function names in the lua system and would generally do lots of weird things.
Title: Re: Highlight or hotkey individual turret
Post by: General Battuta on September 24, 2012, 09:43:14 am
yeah I just realized that  :nervous:

(ps i am posting from stats class lab)
Title: Re: Highlight or hotkey individual turret
Post by: Aardwolf on September 25, 2012, 01:10:15 am
This sounds like a job for gr.drawTargetingBrackets!
Title: Re: Highlight or hotkey individual turret
Post by: Admiral MS on September 25, 2012, 02:31:22 am
 gr.drawTargetingBrackets needs a target that is an object. I think subsystems and turrets in scripting aren't objects and it shouldn't be possible to use that function on them.
Instead it's always possible to draw a bunch of lines around the turrets position.
Title: Re: Highlight or hotkey individual turret
Post by: m!m on September 25, 2012, 07:30:04 am
Exactly, I guess I'll just use the radius of the subsystem and try to draw the targeting brackets that way.
Title: Re: Highlight or hotkey individual turret
Post by: Aardwolf on September 25, 2012, 08:36:37 pm
Oh. Right then.

Well, the whole reason gr.drawTargetingBrackets exists is because drawing lines manually from lua was too slow (or seemed to be, anyway)... might be worthwhile to modify it or create a new function that adds support for a target subsystem.
Title: Re: Highlight or hotkey individual turret
Post by: General Battuta on October 02, 2012, 06:17:59 pm
Hey guys, we're holding up a patch for possible developments here. Should I keep waiting or ditch the script for now?
Title: Re: Highlight or hotkey individual turret
Post by: m!m on October 03, 2012, 07:24:43 am
I think it's working correctly now but the drawing of the subsystem brackets need a new scripting function so you will have to wait until that patch is in trunk...
Title: Re: Highlight or hotkey individual turret
Post by: General Battuta on October 03, 2012, 07:29:20 am
I'm okay if it's just hotkeys - all we need is the ability to have the F9 key cycle through turret1 and turret2 on Ship A, then turret1 and turret2 on ship B, then back around again.
Title: Re: Highlight or hotkey individual turret
Post by: m!m on October 03, 2012, 07:36:45 am
Alright then, here you go:
Code: [Select]
#Conditional Hooks

$Application: FS2_Open
$On Gameplay Start:
[
turretHotkeys = {}
turretHotkeys.key = "F9"
turretHotkeys.enabled = false

-- Subsystem indicator settings
turretHotkeys.indicator = {}
turretHotkeys.indicator.width = 20
turretHotkeys.indicator.height = 20
turretHotkeys.indicator.color = {
    r = 255,
    g = 255,
    b = 255,
    a = 200
}

turretHotkeys.subsystems = {}
turretHotkeys.lastIndex = {1, 0}

function turretHotkeys:keyPressed(key)
    if (self:isEnabled() and key == self.key) then
        local tblIndex, subsysIndex = self:getNextIndex()

        if (tblIndex ~= false) then
            -- This is ugly code, don't look at it or it won't work
            local subsys = self.subsystems[tblIndex].ship[self.subsystems[tblIndex][subsysIndex]]

            if (subsys:isValid()) then
                hv.Player.Target = self.subsystems[tblIndex].ship
                hv.Player.TargetSubsystem = subsys
            end

            self.lastIndex = { tblIndex, subsysIndex }
        end
    end
end

function turretHotkeys:checkIndexes(tblIndex, subsysIndex)
    if (tblIndex > 0 and tblIndex <= #self.subsystems) then
        if (subsysIndex > 0 and subsysIndex <= #self.subsystems[tblIndex]) then
            return tblIndex, subsysIndex
        else
            return tblIndex, math.max(1, math.min(subsysIndex, #self.subsystems[tblIndex]))
        end
    else
        -- If we're here it means that we've got an invalid index...
        return math.max(1, math.min(tblIndex, #self.subsystems)), math.max(1, math.min(1, #self.subsystems[tblIndex]))
    end
end

function turretHotkeys:getNextIndex()
    local tblIndex = self.lastIndex[1]

    local tbl = self.subsystems[tblIndex]

    if (tbl == nil) then
        if (tblIndex == 1) then
            return false
        else
            return self:checkIndexes(1, 1)
        end
    end

    local subsystemIndex = self.lastIndex[2] + 1

    if (subsystemIndex > #tbl) then
        subsystemIndex = 1
        tblIndex = tblIndex + 1

        tbl = self.subsystems[tblIndex]
    end

    if (tblIndex > #self.subsystems) then
        tblIndex = 1

        if (tblIndex > #self.subsystems) then
            return self:checkIndexes(tblIndex, 1)
        end
    end

    return self:checkIndexes(tblIndex, subsystemIndex)
end

function turretHotkeys:onFrame()
    if (self:isEnabled()) then
        self:checkShips()

        -- self:drawSubsystems()
    end
end

function turretHotkeys:checkShips()
    local index = 1
    local done = fale

    while (not done) do
        for i = index, #self.subsystems do
            if (not self.subsystems[index].ship:isValid()) then
                table.remove(self.subsystems, index)
                index = i
            end
        end

        done = true
    end
end

function turretHotkeys:drawSubsystems()
    local target = hv.Player.Target
    local subsysName = hv.Player.TargetSubsystem:getModelName()

    for i = 1, #self.subsystems do
        local tbl = self.subsystems[i]
        for j = 1, #tbl do
            local current = tbl.ship[tbl[j]]
            if (tbl.ship ~= target or current:getModelName() ~= subsysName) then
                self:drawSubsystem(current)
            end
        end
    end
end

function turretHotkeys:drawSubsystem(subsys)
    local indicator = self.indicator

    gr.setColor(indicator.color.r, indicator.color.g, indicator.color.b, indicator.color.a)

    gr.drawSubsystemTargetingBrackets(subsys)
end

function turretHotkeys:addShip(ship)
    table.insert(self.subsystems, {
        name = ship.Name,
        ship = ship
    })
end

function turretHotkeys:isValidIndex(index)
    return self.subsystems[index] ~= nil and self.subsystems[index].ship:isValid()
end

function turretHotkeys:addSubsystem(index, name)
    local tbl = self.subsystems[index]

    if (not tbl.ship[name]:isValid()) then
        ba.warning(string.format("Unknown subsystem %q on ship %q of class %q.", name, tbl.ship.name, tbl.ship.class))
        return false
    end

    table.insert(tbl, name)

    return true
end

function turretHotkeys:removeSubsystem(index, name)
    local tbl = self.subsystems[index]

    local newIndex = 1
    local done = false

    while (not done) do
        for i = newIndex, #turretHotkeys.subsystemNames do
            if (turretHotkeys.subsystemNames[i] == name) then
                table.remove(turretHotkeys.subsystemNames, i)
                newIndex = i
                break
            end
        end

        done = true
    end
end

function turretHotkeys:isEnabled()
    return self.enabled and hv.Player:getBreedName() == "Ship"
end

function thkEnable(bool)
    if (bool == nil) then
        bool = true
    end

    if (type(bool) ~= "boolean") then
        ba.warning("Invalid argument type for thkKey. Need a boolean or no argument, got " .. type(bool) .. ".")
        return
    end

    turretHotkeys.enabled = bool
end

function thkKey(key)
    if (type(key) ~= "string") then
        ba.warning("Invalid argument type for thkKey. Need a string, got " .. type(key) .. ".")
        return
    end

    turretHotkeys.key = key
end

function thkShip(...)
    local names = { ... }

    for i = 1, #names do
        if (type(names[i]) ~= "string") then
            ba.warning("Invalid argument type for thkShip. Need a string, got " .. type(names[i]) .. ".")
            return
        end

        local ship = mn.Ships[names[i]]

        if (not ship:isValid()) then
            ba.warning("No ship with name \"" .. names[i] .. "\" could be found in the current mission.")
            return
        end

        turretHotkeys:addShip(ship)
    end
end

function thkAdd(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkAdd. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkAdd. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:addSubsystem(index, name)
end

function thkRemove(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkRemove. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkRemove. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:removeSubsystem(index, name)
end

function thkClear()
    turretHotkeys.subsystems = {}
end
]

$State: GS_STATE_GAME_PLAY
$On Key Pressed:
[
turretHotkeys:keyPressed(hv.Key)
]

$State: GS_STATE_GAME_PLAY
$On Frame:
[
turretHotkeys:onFrame()
]

#End

There are a few modifications to the way the script works now.
First you have to call thkEnable() when you want to enable the script. You can disable it again with thkEnable(false).
To specify the ships you can call thkShip(<...>) with the ship names you want to have subsystems on. Every function call adds the specified ships to the list of ships.
To specify a subsystem you still use thkAdd but you will have to specify which ship to use as the fist argument which should be the number of the ship. That means if you cann thkShip('Ship A', 'Ship B', 'Ship C'), thkAdd(2, 'turret-42') will mark that subsystem on Ship B as being able to be selected using the hotkey.
Title: Re: Highlight or hotkey individual turret
Post by: General Battuta on October 03, 2012, 08:23:42 am
Oh my god you are so cool
Title: Re: Highlight or hotkey individual turret
Post by: General Battuta on October 03, 2012, 07:24:54 pm
Hey, should I do

script-eval thkShip (Deianira)
script-eval thkShip (Iolanthe)

or script-eval thkShip (Deianira, Iolanthe)
Title: Re: Highlight or hotkey individual turret
Post by: General Battuta on October 03, 2012, 09:10:16 pm
The latter seems to work!
Title: Re: Highlight or hotkey individual turret
Post by: m!m on October 04, 2012, 04:51:59 am
Both version do the exact same thing to make it possible to add more ships than the character limit in SEXPs allows.
Title: Re: Highlight or hotkey individual turret
Post by: m!m on October 12, 2012, 11:16:17 am
Minor update to use the newly added drawSubsystemTargetingBrackets to indicate a hotkey-accessible subsystem. This needs a recent trunk build to work.
Code: [Select]
#Conditional Hooks

$Application: FS2_Open
$On Gameplay Start:
[
turretHotkeys = {}
turretHotkeys.key = "F9"
turretHotkeys.enabled = false

-- Subsystem indicator settings
turretHotkeys.indicator = {}
turretHotkeys.indicator.color = {
    r = 255,
    g = 255,
    b = 255,
    a = 200
}

turretHotkeys.subsystems = {}
turretHotkeys.lastIndex = {1, 0}

function turretHotkeys:keyPressed(key)
    if (self:isEnabled() and key == self.key) then
        local tblIndex, subsysIndex = self:getNextIndex()

        if (tblIndex ~= false) then
            -- This is ugly code, don't look at it or it won't work
            local subsys = self.subsystems[tblIndex].ship[self.subsystems[tblIndex][subsysIndex]]

            if (subsys:isValid()) then
                hv.Player.Target = self.subsystems[tblIndex].ship
                hv.Player.TargetSubsystem = subsys
            end

            self.lastIndex = { tblIndex, subsysIndex }
        end
    end
end

function turretHotkeys:checkIndexes(tblIndex, subsysIndex)
    if (tblIndex > 0 and tblIndex <= #self.subsystems) then
        if (subsysIndex > 0 and subsysIndex <= #self.subsystems[tblIndex]) then
            return tblIndex, subsysIndex
        else
            return tblIndex, math.max(1, math.min(subsysIndex, #self.subsystems[tblIndex]))
        end
    else
        -- If we're here it means that we've got an invalid index...
        return math.max(1, math.min(tblIndex, #self.subsystems)), math.max(1, math.min(1, #self.subsystems[tblIndex]))
    end
end

function turretHotkeys:getNextIndex()
    local tblIndex = self.lastIndex[1]

    local tbl = self.subsystems[tblIndex]

    if (tbl == nil) then
        if (tblIndex == 1) then
            return false
        else
            return self:checkIndexes(1, 1)
        end
    end

    local subsystemIndex = self.lastIndex[2] + 1

    if (subsystemIndex > #tbl) then
        subsystemIndex = 1
        tblIndex = tblIndex + 1

        tbl = self.subsystems[tblIndex]
    end

    if (tblIndex > #self.subsystems) then
        tblIndex = 1

        if (tblIndex > #self.subsystems) then
            return self:checkIndexes(tblIndex, 1)
        end
    end

    return self:checkIndexes(tblIndex, subsystemIndex)
end

function turretHotkeys:onFrame()
    if (self:isEnabled()) then
        self:checkShips()

        if (not hu.HUDDrawn) then
            self:drawSubsystems()
        end
    end
end

function turretHotkeys:checkShips()
    local index = 1
    local done = fale

    while (not done) do
        for i = index, #self.subsystems do
            if (not self.subsystems[index].ship:isValid()) then
                table.remove(self.subsystems, index)
                index = i
            end
        end

        done = true
    end
end

function turretHotkeys:drawSubsystems()
    local target = hv.Player.Target
    local subsysName = hv.Player.TargetSubsystem:getModelName()

    for i = 1, #self.subsystems do
        local tbl = self.subsystems[i]
        for j = 1, #tbl do
            local current = tbl.ship[tbl[j]]
            if (tbl.ship ~= target or current:getModelName() ~= subsysName) then
                self:drawSubsystem(current)
            end
        end
    end
end

function turretHotkeys:drawSubsystem(subsys)
    local indicator = self.indicator

    gr.setColor(indicator.color.r, indicator.color.g, indicator.color.b, indicator.color.a)

    gr.drawSubsystemTargetingBrackets(subsys)
end

function turretHotkeys:addShip(ship)
    table.insert(self.subsystems, {
        name = ship.Name,
        ship = ship
    })
end

function turretHotkeys:isValidIndex(index)
    return self.subsystems[index] ~= nil and self.subsystems[index].ship:isValid()
end

function turretHotkeys:addSubsystem(index, name)
    local tbl = self.subsystems[index]

    if (not tbl.ship[name]:isValid()) then
        ba.warning(string.format("Unknown subsystem %q on ship %q of class %q.", name, tbl.ship.name, tbl.ship.class))
        return false
    end

    table.insert(tbl, name)

    return true
end

function turretHotkeys:removeSubsystem(index, name)
    local tbl = self.subsystems[index]

    local newIndex = 1
    local done = false

    while (not done) do
        for i = newIndex, #turretHotkeys.subsystemNames do
            if (turretHotkeys.subsystemNames[i] == name) then
                table.remove(turretHotkeys.subsystemNames, i)
                newIndex = i
                break
            end
        end

        done = true
    end
end

function turretHotkeys:isEnabled()
    return self.enabled and hv.Player:getBreedName() == "Ship"
end

function thkEnable(bool)
    if (bool == nil) then
        bool = true
    end

    if (type(bool) ~= "boolean") then
        ba.warning("Invalid argument type for thkKey. Need a boolean or no argument, got " .. type(bool) .. ".")
        return
    end

    turretHotkeys.enabled = bool
end

function thkKey(key)
    if (type(key) ~= "string") then
        ba.warning("Invalid argument type for thkKey. Need a string, got " .. type(key) .. ".")
        return
    end

    turretHotkeys.key = key
end

function thkShip(...)
    local names = { ... }

    for i = 1, #names do
        if (type(names[i]) ~= "string") then
            ba.warning("Invalid argument type for thkShip. Need a string, got " .. type(names[i]) .. ".")
            return
        end

        local ship = mn.Ships[names[i]]

        if (not ship:isValid()) then
            ba.warning("No ship with name \"" .. names[i] .. "\" could be found in the current mission.")
            return
        end

        turretHotkeys:addShip(ship)
    end
end

function thkAdd(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkAdd. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkAdd. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:addSubsystem(index, name)
end

function thkRemove(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkRemove. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkRemove. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:removeSubsystem(index, name)
end

function thkClear()
    turretHotkeys.subsystems = {}
end
]

$State: GS_STATE_GAME_PLAY
$On Key Pressed:
[
turretHotkeys:keyPressed(hv.Key)
]

$State: GS_STATE_GAME_PLAY
$On Frame:
[
turretHotkeys:onFrame()
]

#End

Title: Re: Highlight or hotkey individual turret
Post by: mjn.mixael on December 11, 2012, 02:59:26 pm
We were using this script in BtA and it was crashing missions due to attempting to target destroyed turrets. Admiral MS fixed it up for us. This doesn't remove destroyed turrets from the hotkey list, but it does prevent them from being targetted and thus crashing the game.

Code: [Select]
#Conditional Hooks

$Application: FS2_Open
$On Gameplay Start:
[
turretHotkeys = {}
turretHotkeys.key = "F9"
turretHotkeys.enabled = false

-- Subsystem indicator settings
turretHotkeys.indicator = {}
turretHotkeys.indicator.color = {
    r = 255,
    g = 255,
    b = 255,
    a = 200
}

turretHotkeys.subsystems = {}
turretHotkeys.lastIndex = {1, 0}

function turretHotkeys:keyPressed(key)
    if (self:isEnabled() and key == self.key) then
        local tblIndex, subsysIndex = self:getNextIndex()

        if (tblIndex ~= false) then
            -- This is ugly code, don't look at it or it won't work
            local subsys = self.subsystems[tblIndex].ship[self.subsystems[tblIndex][subsysIndex]]

            if (subsys:isValid()) then
                hv.Player.Target = self.subsystems[tblIndex].ship
                hv.Player.TargetSubsystem = subsys
            end

            self.lastIndex = { tblIndex, subsysIndex }
        end
    end
end

function turretHotkeys:checkIndexes(tblIndex, subsysIndex)
    if (tblIndex > 0 and tblIndex <= #self.subsystems) then
        if (subsysIndex > 0 and subsysIndex <= #self.subsystems[tblIndex]) then
            return tblIndex, subsysIndex
        else
            return tblIndex, math.max(1, math.min(subsysIndex, #self.subsystems[tblIndex]))
        end
    else
        -- If we're here it means that we've got an invalid index...
        return math.max(1, math.min(tblIndex, #self.subsystems)), math.max(1, math.min(1, #self.subsystems[tblIndex]))
    end
end

function turretHotkeys:getNextIndex()
    local tblIndex = self.lastIndex[1]

    local tbl = self.subsystems[tblIndex]

    if (tbl == nil) then
        if (tblIndex == 1) then
            return false
        else
            return self:checkIndexes(1, 1)
        end
    end

    local subsystemIndex = self.lastIndex[2] + 1

    if (subsystemIndex > #tbl) then
        subsystemIndex = 1
        tblIndex = tblIndex + 1

        tbl = self.subsystems[tblIndex]
    end

    if (tblIndex > #self.subsystems) then
        tblIndex = 1

        if (tblIndex > #self.subsystems) then
            return self:checkIndexes(tblIndex, 1)
        end
    end

    return self:checkIndexes(tblIndex, subsystemIndex)
end

function turretHotkeys:onFrame()
    if (self:isEnabled()) then
        self:checkShips()

        if (not hu.HUDDrawn) then
            self:drawSubsystems()
        end
    end
end

function turretHotkeys:checkShips()
    local index = 1
    local done = false

    while (not done) do
        for i = index, #self.subsystems do
            if (not self.subsystems[index].ship:isValid() ) then
                table.remove(self.subsystems, index)
                index = i
            end
        end

        done = true
    end
end

function turretHotkeys:drawSubsystems()
    local target = hv.Player.Target
    local subsysName = hv.Player.TargetSubsystem:getModelName()

    for i = 1, #self.subsystems do
        local tbl = self.subsystems[i]
        for j = 1, #tbl do
            local current = tbl.ship[tbl[j]]
            if current.HitpointsLeft > 0 and (tbl.ship ~= target or current:getModelName() ~= subsysName) then
                self:drawSubsystem(current)
            end
        end
    end
end

function turretHotkeys:drawSubsystem(subsys)
    local indicator = self.indicator

    gr.setColor(indicator.color.r, indicator.color.g, indicator.color.b, indicator.color.a)

    gr.drawSubsystemTargetingBrackets(subsys)
end

function turretHotkeys:addShip(ship)
    table.insert(self.subsystems, {
        name = ship.Name,
        ship = ship
    })
end

function turretHotkeys:isValidIndex(index)
    return self.subsystems[index] ~= nil and self.subsystems[index].ship:isValid()
end

function turretHotkeys:addSubsystem(index, name)
    local tbl = self.subsystems[index]

    if (not tbl.ship[name]:isValid()) then
        ba.warning(string.format("Unknown subsystem %q on ship %q of class %q.", name, tbl.ship.name, tbl.ship.class))
        return false
    end

    table.insert(tbl, name)

    return true
end

function turretHotkeys:removeSubsystem(index, name)
    local tbl = self.subsystems[index]

    local newIndex = 1
    local done = false

    while (not done) do
        for i = newIndex, #turretHotkeys.subsystemNames do
            if (turretHotkeys.subsystemNames[i] == name) then
                table.remove(turretHotkeys.subsystemNames, i)
                newIndex = i
                break
            end
        end

        done = true
    end
end

function turretHotkeys:isEnabled()
    return self.enabled and hv.Player:getBreedName() == "Ship"
end

function thkEnable(bool)
    if (bool == nil) then
        bool = true
    end

    if (type(bool) ~= "boolean") then
        ba.warning("Invalid argument type for thkKey. Need a boolean or no argument, got " .. type(bool) .. ".")
        return
    end

    turretHotkeys.enabled = bool
end

function thkKey(key)
    if (type(key) ~= "string") then
        ba.warning("Invalid argument type for thkKey. Need a string, got " .. type(key) .. ".")
        return
    end

    turretHotkeys.key = key
end

function thkShip(...)
    local names = { ... }

    for i = 1, #names do
        if (type(names[i]) ~= "string") then
            ba.warning("Invalid argument type for thkShip. Need a string, got " .. type(names[i]) .. ".")
            return
        end

        local ship = mn.Ships[names[i]]

        if (not ship:isValid()) then
            ba.warning("No ship with name \"" .. names[i] .. "\" could be found in the current mission.")
            return
        end

        turretHotkeys:addShip(ship)
    end
end

function thkAdd(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkAdd. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkAdd. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:addSubsystem(index, name)
end

function thkRemove(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkRemove. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkRemove. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:removeSubsystem(index, name)
end

function thkClear()
    turretHotkeys.subsystems = {}
end
]

$State: GS_STATE_GAME_PLAY
$On Key Pressed:
[
turretHotkeys:keyPressed(hv.Key)
]

$State: GS_STATE_GAME_PLAY
$On Frame:
[
turretHotkeys:onFrame()
]

#End
Title: Re: Highlight or hotkey individual turret
Post by: Black Wolf on January 30, 2014, 04:27:03 am
Alright then, here you go:
Code: [Select]
#Conditional Hooks

$Application: FS2_Open
$On Gameplay Start:
[
turretHotkeys = {}
turretHotkeys.key = "F9"
turretHotkeys.enabled = false

-- Subsystem indicator settings
turretHotkeys.indicator = {}
turretHotkeys.indicator.width = 20
turretHotkeys.indicator.height = 20
turretHotkeys.indicator.color = {
    r = 255,
    g = 255,
    b = 255,
    a = 200
}

turretHotkeys.subsystems = {}
turretHotkeys.lastIndex = {1, 0}

function turretHotkeys:keyPressed(key)
    if (self:isEnabled() and key == self.key) then
        local tblIndex, subsysIndex = self:getNextIndex()

        if (tblIndex ~= false) then
            -- This is ugly code, don't look at it or it won't work
            local subsys = self.subsystems[tblIndex].ship[self.subsystems[tblIndex][subsysIndex]]

            if (subsys:isValid()) then
                hv.Player.Target = self.subsystems[tblIndex].ship
                hv.Player.TargetSubsystem = subsys
            end

            self.lastIndex = { tblIndex, subsysIndex }
        end
    end
end

function turretHotkeys:checkIndexes(tblIndex, subsysIndex)
    if (tblIndex > 0 and tblIndex <= #self.subsystems) then
        if (subsysIndex > 0 and subsysIndex <= #self.subsystems[tblIndex]) then
            return tblIndex, subsysIndex
        else
            return tblIndex, math.max(1, math.min(subsysIndex, #self.subsystems[tblIndex]))
        end
    else
        -- If we're here it means that we've got an invalid index...
        return math.max(1, math.min(tblIndex, #self.subsystems)), math.max(1, math.min(1, #self.subsystems[tblIndex]))
    end
end

function turretHotkeys:getNextIndex()
    local tblIndex = self.lastIndex[1]

    local tbl = self.subsystems[tblIndex]

    if (tbl == nil) then
        if (tblIndex == 1) then
            return false
        else
            return self:checkIndexes(1, 1)
        end
    end

    local subsystemIndex = self.lastIndex[2] + 1

    if (subsystemIndex > #tbl) then
        subsystemIndex = 1
        tblIndex = tblIndex + 1

        tbl = self.subsystems[tblIndex]
    end

    if (tblIndex > #self.subsystems) then
        tblIndex = 1

        if (tblIndex > #self.subsystems) then
            return self:checkIndexes(tblIndex, 1)
        end
    end

    return self:checkIndexes(tblIndex, subsystemIndex)
end

function turretHotkeys:onFrame()
    if (self:isEnabled()) then
        self:checkShips()

        -- self:drawSubsystems()
    end
end

function turretHotkeys:checkShips()
    local index = 1
    local done = fale

    while (not done) do
        for i = index, #self.subsystems do
            if (not self.subsystems[index].ship:isValid()) then
                table.remove(self.subsystems, index)
                index = i
            end
        end

        done = true
    end
end

function turretHotkeys:drawSubsystems()
    local target = hv.Player.Target
    local subsysName = hv.Player.TargetSubsystem:getModelName()

    for i = 1, #self.subsystems do
        local tbl = self.subsystems[i]
        for j = 1, #tbl do
            local current = tbl.ship[tbl[j]]
            if (tbl.ship ~= target or current:getModelName() ~= subsysName) then
                self:drawSubsystem(current)
            end
        end
    end
end

function turretHotkeys:drawSubsystem(subsys)
    local indicator = self.indicator

    gr.setColor(indicator.color.r, indicator.color.g, indicator.color.b, indicator.color.a)

    gr.drawSubsystemTargetingBrackets(subsys)
end

function turretHotkeys:addShip(ship)
    table.insert(self.subsystems, {
        name = ship.Name,
        ship = ship
    })
end

function turretHotkeys:isValidIndex(index)
    return self.subsystems[index] ~= nil and self.subsystems[index].ship:isValid()
end

function turretHotkeys:addSubsystem(index, name)
    local tbl = self.subsystems[index]

    if (not tbl.ship[name]:isValid()) then
        ba.warning(string.format("Unknown subsystem %q on ship %q of class %q.", name, tbl.ship.name, tbl.ship.class))
        return false
    end

    table.insert(tbl, name)

    return true
end

function turretHotkeys:removeSubsystem(index, name)
    local tbl = self.subsystems[index]

    local newIndex = 1
    local done = false

    while (not done) do
        for i = newIndex, #turretHotkeys.subsystemNames do
            if (turretHotkeys.subsystemNames[i] == name) then
                table.remove(turretHotkeys.subsystemNames, i)
                newIndex = i
                break
            end
        end

        done = true
    end
end

function turretHotkeys:isEnabled()
    return self.enabled and hv.Player:getBreedName() == "Ship"
end

function thkEnable(bool)
    if (bool == nil) then
        bool = true
    end

    if (type(bool) ~= "boolean") then
        ba.warning("Invalid argument type for thkKey. Need a boolean or no argument, got " .. type(bool) .. ".")
        return
    end

    turretHotkeys.enabled = bool
end

function thkKey(key)
    if (type(key) ~= "string") then
        ba.warning("Invalid argument type for thkKey. Need a string, got " .. type(key) .. ".")
        return
    end

    turretHotkeys.key = key
end

function thkShip(...)
    local names = { ... }

    for i = 1, #names do
        if (type(names[i]) ~= "string") then
            ba.warning("Invalid argument type for thkShip. Need a string, got " .. type(names[i]) .. ".")
            return
        end

        local ship = mn.Ships[names[i]]

        if (not ship:isValid()) then
            ba.warning("No ship with name \"" .. names[i] .. "\" could be found in the current mission.")
            return
        end

        turretHotkeys:addShip(ship)
    end
end

function thkAdd(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkAdd. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkAdd. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:addSubsystem(index, name)
end

function thkRemove(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkRemove. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkRemove. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:removeSubsystem(index, name)
end

function thkClear()
    turretHotkeys.subsystems = {}
end
]

$State: GS_STATE_GAME_PLAY
$On Key Pressed:
[
turretHotkeys:keyPressed(hv.Key)
]

$State: GS_STATE_GAME_PLAY
$On Frame:
[
turretHotkeys:onFrame()
]

#End

There are a few modifications to the way the script works now.
First you have to call thkEnable() when you want to enable the script. You can disable it again with thkEnable(false).
To specify the ships you can call thkShip(<...>) with the ship names you want to have subsystems on. Every function call adds the specified ships to the list of ships.
To specify a subsystem you still use thkAdd but you will have to specify which ship to use as the fist argument which should be the number of the ship. That means if you cann thkShip('Ship A', 'Ship B', 'Ship C'), thkAdd(2, 'turret-42') will mark that subsystem on Ship B as being able to be selected using the hotkey.

:bump:

Hey, can someone who's made use of this sexp give me the exact syntax and mission setup I need to use to make it put brackets around a specific turret?

I have 4 chained script-evals associated with the proper event sequence, with this written in them:

thkEnable()
thkShip('PVI Aswan')
thkAdd(1, 'Turret13') (I've also tried just plain thkAdd('Turret13') with no success)
thkEnable(false)

Ship names are right, subsystem name is right, and I copied mjn's latest version of the script directly into turretHotkey-sct.tbm. Really not sure what's wrong, so I'm hoping it's a syntax issue that I've missed somewhere. Any suggestions?
Title: Re: Highlight or hotkey individual turret
Post by: m!m on January 30, 2014, 04:42:16 am
thkEnable(false) disables all rendering of the brackets so if you execute that directly after thkAdd you will not see the brackets as they aren't rendered at all. So you could try removing thkEnable(false) and see if that changes it.
Title: Re: Highlight or hotkey individual turret
Post by: Black Wolf on January 30, 2014, 05:23:59 am
thkEnable(false) disables all rendering of the brackets so if you execute that directly after thkAdd you will not see the brackets as they aren't rendered at all. So you could try removing thkEnable(false) and see if that changes it.

That only fires once the turret has been destroyed, due to the conditions of the chain, but I'll try it.
Title: Re: Highlight or hotkey individual turret
Post by: m!m on January 30, 2014, 05:25:45 am
If it doesn't fire immediately then it should work but that was my best guess, maybe mjn.mixael can help you with this...
Title: Re: Highlight or hotkey individual turret
Post by: mjn.mixael on January 30, 2014, 07:55:33 am
Here's how we did it.

(http://i282.photobucket.com/albums/kk264/mjnmixael/Private/ImageBin_zps45cdd2af.jpg~original)
Title: Re: Highlight or hotkey individual turret
Post by: Black Wolf on January 30, 2014, 09:13:36 am
http://imagebin.org/289952

As far as I can tell, mine is identical, so I'm not sure what's going on. It does assign the F9 hotkey to the appropriate Turret, which is something, but I understood it would draw brackets around it as well, even when the hotkey isn't pressed, to aid identification?
Title: Re: Highlight or hotkey individual turret
Post by: mjn.mixael on January 30, 2014, 09:45:45 am
Oh, I don't think it does the latter. I use this script (http://www.hard-light.net/forums/index.php?topic=67711.0) for that on ships, though I don't think it will work on subsystems.
Title: Re: Highlight or hotkey individual turret
Post by: mjn.mixael on September 26, 2014, 05:47:58 pm
:necro:

So BtA is using this version of the script and I just noticed that I now crashes FSO.

Code: [Select]
#Conditional Hooks

$Application: FS2_Open
$On Gameplay Start:
[
turretHotkeys = {}
turretHotkeys.key = "F9"
turretHotkeys.enabled = false

-- Subsystem indicator settings
turretHotkeys.indicator = {}
turretHotkeys.indicator.color = {
    r = 255,
    g = 255,
    b = 255,
    a = 200
}

turretHotkeys.subsystems = {}
turretHotkeys.lastIndex = {1, 0}

function turretHotkeys:keyPressed(key)
    if (self:isEnabled() and key == self.key) then
        local tblIndex, subsysIndex = self:getNextIndex()

        if (tblIndex ~= false) then
            -- This is ugly code, don't look at it or it won't work
            local subsys = self.subsystems[tblIndex].ship[self.subsystems[tblIndex][subsysIndex]]

            if (subsys:isValid()) then
                hv.Player.Target = self.subsystems[tblIndex].ship
                hv.Player.TargetSubsystem = subsys
            end

            self.lastIndex = { tblIndex, subsysIndex }
        end
    end
end

function turretHotkeys:checkIndexes(tblIndex, subsysIndex)
    if (tblIndex > 0 and tblIndex <= #self.subsystems) then
        if (subsysIndex > 0 and subsysIndex <= #self.subsystems[tblIndex]) then
            return tblIndex, subsysIndex
        else
            return tblIndex, math.max(1, math.min(subsysIndex, #self.subsystems[tblIndex]))
        end
    else
        -- If we're here it means that we've got an invalid index...
        return math.max(1, math.min(tblIndex, #self.subsystems)), math.max(1, math.min(1, #self.subsystems[tblIndex]))
    end
end

function turretHotkeys:getNextIndex()
    local tblIndex = self.lastIndex[1]

    local tbl = self.subsystems[tblIndex]

    if (tbl == nil) then
        if (tblIndex == 1) then
            return false
        else
            return self:checkIndexes(1, 1)
        end
    end

    local subsystemIndex = self.lastIndex[2] + 1

    if (subsystemIndex > #tbl) then
        subsystemIndex = 1
        tblIndex = tblIndex + 1

        tbl = self.subsystems[tblIndex]
    end

    if (tblIndex > #self.subsystems) then
        tblIndex = 1

        if (tblIndex > #self.subsystems) then
            return self:checkIndexes(tblIndex, 1)
        end
    end

    return self:checkIndexes(tblIndex, subsystemIndex)
end

function turretHotkeys:onFrame()
    if (self:isEnabled()) then
        self:checkShips()

        if (not hu.HUDDrawn) then
            self:drawSubsystems()
        end
    end
end

function turretHotkeys:checkShips()
    local index = 1
    local done = false

    while (not done) do
        for i = index, #self.subsystems do
            if (not self.subsystems[index].ship:isValid() ) then
                table.remove(self.subsystems, index)
                index = i
            end
        end

        done = true
    end
end

function turretHotkeys:drawSubsystems()
    local target = hv.Player.Target
    local subsysName = hv.Player.TargetSubsystem:getModelName()

    for i = 1, #self.subsystems do
        local tbl = self.subsystems[i]
        for j = 1, #tbl do
            local current = tbl.ship[tbl[j]]
            if current.HitpointsLeft > 0 and (tbl.ship ~= target or current:getModelName() ~= subsysName) then
                self:drawSubsystem(current)
            end
        end
    end
end

function turretHotkeys:drawSubsystem(subsys)
    local indicator = self.indicator

    gr.setColor(indicator.color.r, indicator.color.g, indicator.color.b, indicator.color.a)

    gr.drawSubsystemTargetingBrackets(subsys)
end

function turretHotkeys:addShip(ship)
    table.insert(self.subsystems, {
        name = ship.Name,
        ship = ship
    })
end

function turretHotkeys:isValidIndex(index)
    return self.subsystems[index] ~= nil and self.subsystems[index].ship:isValid()
end

function turretHotkeys:addSubsystem(index, name)
    local tbl = self.subsystems[index]

    if (not tbl.ship[name]:isValid()) then
        ba.warning(string.format("Unknown subsystem %q on ship %q of class %q.", name, tbl.ship.name, tbl.ship.class))
        return false
    end

    table.insert(tbl, name)

    return true
end

function turretHotkeys:removeSubsystem(index, name)
    local tbl = self.subsystems[index]

    local newIndex = 1
    local done = false

    while (not done) do
        for i = newIndex, #turretHotkeys.subsystemNames do
            if (turretHotkeys.subsystemNames[i] == name) then
                table.remove(turretHotkeys.subsystemNames, i)
                newIndex = i
                break
            end
        end

        done = true
    end
end

function turretHotkeys:isEnabled()
    return self.enabled and hv.Player:getBreedName() == "Ship"
end

function thkEnable(bool)
    if (bool == nil) then
        bool = true
    end

    if (type(bool) ~= "boolean") then
        ba.warning("Invalid argument type for thkKey. Need a boolean or no argument, got " .. type(bool) .. ".")
        return
    end

    turretHotkeys.enabled = bool
end

function thkKey(key)
    if (type(key) ~= "string") then
        ba.warning("Invalid argument type for thkKey. Need a string, got " .. type(key) .. ".")
        return
    end

    turretHotkeys.key = key
end

function thkShip(...)
    local names = { ... }

    for i = 1, #names do
        if (type(names[i]) ~= "string") then
            ba.warning("Invalid argument type for thkShip. Need a string, got " .. type(names[i]) .. ".")
            return
        end

        local ship = mn.Ships[names[i]]

        if (not ship:isValid()) then
            ba.warning("No ship with name \"" .. names[i] .. "\" could be found in the current mission.")
            return
        end

        turretHotkeys:addShip(ship)
    end
end

function thkAdd(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkAdd. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkAdd. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:addSubsystem(index, name)
end

function thkRemove(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkRemove. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkRemove. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:removeSubsystem(index, name)
end

function thkClear()
    turretHotkeys.subsystems = {}
end
]

$State: GS_STATE_GAME_PLAY
$On Key Pressed:
[
turretHotkeys:keyPressed(hv.Key)
]

$State: GS_STATE_GAME_PLAY
$On Frame:
[
turretHotkeys:onFrame()
]

#End

I'll post a test mission soon.. but halp!

Scratch that.. It's apparently somehow mission specific. BLAH.
Title: Re: Highlight or hotkey individual turret
Post by: mjn.mixael on September 27, 2014, 12:44:14 am
Figured it out and posting it here so that it's documented and might be fixed. The crash is somehow caused by additional turret data added by FRED.

I was trying to hotkey missile launchers which would be listed like so.

Code: [Select]
+Subsystem: turret01
+Subsystem: turret02
+Subsystem: turret03

But if FRED decides for some reason (haven't figured out why yet) to add Ammo amount.. bam crash.
Code: [Select]
+Subsystem: turret01
+Sbank Ammo: ( 0 )
+Subsystem: turret02
+Sbank Ammo: ( 0 )
+Subsystem: turret03
+Sbank Ammo: ( 0 )

Note that the latter is perfectly acceptable for FRED and FSO, but it definitely caused the crash somehow.
Title: Re: Highlight or hotkey individual turret
Post by: AdmiralRalwood on December 08, 2014, 04:32:39 pm
I was unable to reproduce the crash, but I did find that there was a logic error preventing the brackets from being drawn and thkRemove() wouldn't work.

Code: [Select]
#Conditional Hooks

$Application: FS2_Open
$On Gameplay Start:
[
turretHotkeys = {}
turretHotkeys.key = "F9"
turretHotkeys.enabled = false

-- Subsystem indicator settings
turretHotkeys.indicator = {}
turretHotkeys.indicator.color = {
    r = 255,
    g = 255,
    b = 255,
    a = 200
}

turretHotkeys.subsystems = {}
turretHotkeys.lastIndex = {1, 0}

function turretHotkeys:keyPressed(key)
    if (self:isEnabled() and key == self.key) then
        local tblIndex, subsysIndex = self:getNextIndex()

        if (tblIndex ~= false) then
            -- This is ugly code, don't look at it or it won't work
            local subsys = self.subsystems[tblIndex].ship[self.subsystems[tblIndex][subsysIndex]]

            if (subsys:isValid()) then
                hv.Player.Target = self.subsystems[tblIndex].ship
                hv.Player.TargetSubsystem = subsys
            end

            self.lastIndex = { tblIndex, subsysIndex }
        end
    end
end

function turretHotkeys:checkIndexes(tblIndex, subsysIndex)
    if (tblIndex > 0 and tblIndex <= #self.subsystems) then
        if (subsysIndex > 0 and subsysIndex <= #self.subsystems[tblIndex]) then
            return tblIndex, subsysIndex
        else
            return tblIndex, math.max(1, math.min(subsysIndex, #self.subsystems[tblIndex]))
        end
    else
        -- If we're here it means that we've got an invalid index...
        return math.max(1, math.min(tblIndex, #self.subsystems)), math.max(1, math.min(1, #self.subsystems[tblIndex]))
    end
end

function turretHotkeys:getNextIndex()
    local tblIndex = self.lastIndex[1]

    local tbl = self.subsystems[tblIndex]

    if (tbl == nil) then
        if (tblIndex == 1) then
            return false
        else
            return self:checkIndexes(1, 1)
        end
    end

    local subsystemIndex = self.lastIndex[2] + 1

    if (subsystemIndex > #tbl) then
        subsystemIndex = 1
        tblIndex = tblIndex + 1

        tbl = self.subsystems[tblIndex]
    end

    if (tblIndex > #self.subsystems) then
        tblIndex = 1

        if (tblIndex > #self.subsystems) then
            return self:checkIndexes(tblIndex, 1)
        end
    end

    return self:checkIndexes(tblIndex, subsystemIndex)
end

function turretHotkeys:onFrame()
    if (self:isEnabled()) then
        self:checkShips()

        if (hu.HUDDrawn) then
            self:drawSubsystems()
        end
    end
end

function turretHotkeys:checkShips()
    local index = 1
    local done = false

    while (not done) do
        for i = index, #self.subsystems do
            if (not self.subsystems[index].ship:isValid() ) then
                table.remove(self.subsystems, index)
                index = i
            end
        end

        done = true
    end
end

function turretHotkeys:drawSubsystems()
    local target = hv.Player.Target
    local subsysName = hv.Player.TargetSubsystem:getModelName()

    for i = 1, #self.subsystems do
        local tbl = self.subsystems[i]
        for j = 1, #tbl do
            local current = tbl.ship[tbl[j]]
            if current.HitpointsLeft > 0 and (tbl.ship ~= target or current:getModelName() ~= subsysName) then
                self:drawSubsystem(current)
            end
        end
    end
end

function turretHotkeys:drawSubsystem(subsys)
    local indicator = self.indicator

    gr.setColor(indicator.color.r, indicator.color.g, indicator.color.b, indicator.color.a)

    gr.drawSubsystemTargetingBrackets(subsys)
end

function turretHotkeys:addShip(ship)
    table.insert(self.subsystems, {
        name = ship.Name,
        ship = ship
    })
end

function turretHotkeys:isValidIndex(index)
    return self.subsystems[index] ~= nil and self.subsystems[index].ship:isValid()
end

function turretHotkeys:addSubsystem(index, name)
    local tbl = self.subsystems[index]

    if (not tbl.ship[name]:isValid()) then
        ba.warning(string.format("Unknown subsystem %q on ship %q of class %q.", name, tbl.ship.name, tbl.ship.class))
        return false
    end

    table.insert(tbl, name)

    return true
end

function turretHotkeys:removeSubsystem(index, name)
    local tbl = self.subsystems[index]

    local newIndex = 1
    local done = false

    while (not done) do
        for i = newIndex, #tbl do
            if (tbl[i] == name) then
                table.remove(tbl, i)
                newIndex = i
                break
            end
        end

        done = true
    end
end

function turretHotkeys:isEnabled()
    return self.enabled and hv.Player:getBreedName() == "Ship"
end

function thkEnable(bool)
    if (bool == nil) then
        bool = true
    end

    if (type(bool) ~= "boolean") then
        ba.warning("Invalid argument type for thkKey. Need a boolean or no argument, got " .. type(bool) .. ".")
        return
    end

    turretHotkeys.enabled = bool
end

function thkKey(key)
    if (type(key) ~= "string") then
        ba.warning("Invalid argument type for thkKey. Need a string, got " .. type(key) .. ".")
        return
    end

    turretHotkeys.key = key
end

function thkShip(...)
    local names = { ... }

    for i = 1, #names do
        if (type(names[i]) ~= "string") then
            ba.warning("Invalid argument type for thkShip. Need a string, got " .. type(names[i]) .. ".")
            return
        end

        local ship = mn.Ships[names[i]]

        if (not ship:isValid()) then
            ba.warning("No ship with name \"" .. names[i] .. "\" could be found in the current mission.")
            return
        end

        turretHotkeys:addShip(ship)
    end
end

function thkAdd(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkAdd. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkAdd. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:addSubsystem(index, name)
end

function thkRemove(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkRemove. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkRemove. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:removeSubsystem(index, name)
end

function thkClear()
    turretHotkeys.subsystems = {}
end
]

$State: GS_STATE_GAME_PLAY
$On Key Pressed:
[
turretHotkeys:keyPressed(hv.Key)
]

$State: GS_STATE_GAME_PLAY
$On Frame:
[
turretHotkeys:onFrame()
]

#End
Title: Re: Highlight or hotkey individual turret
Post by: Rheyah on December 30, 2014, 01:24:59 pm
I needed the brackets thing fixing.  Thanks, Admiral!
Title: Re: Highlight or hotkey individual turret
Post by: Lepanto on June 15, 2015, 07:58:51 pm
Found a weird bug (oh, joy, I know.) Using AdmiralRalwood's version of the script.

When I add a ship(s) and their turrets to the list, then have any of those ships depart, then have new ships jump in and add THEIR turrets, it throws an error or CTDs. Even if I use thkClear to clear the script before tagging the new ships and turrets.

When playing one of these missions in debug mode, it gave me a couple lua errors when one of the thkAdded ships jumped out. Then, when the other thkAdded ships jumped in, I tried to F9-target their turrets and got a CTD. When I F9-target the new ships just after they jump in, anyway; one time, I waited a little while after they jumped in, and F9 worked just fine despite having thrown the same lua errors earlier.

What's up? How do I prevent ships jumping out from screwing the script up?

Lua errors when the thkAdded ship jumped out:
Code: [Select]
Assert: model_instance_num < (int)Polygon_model_instances.size()
File: modelread.cpp
Line: 2990

fs2_open_3_7_2_RC5-DEBUG.exe! @ILT+83290(__RTC_CheckEsp)
fs2_open_3_7_2_RC5-DEBUG.exe! WinAssert + 194 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! model_get_instance + 106 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! find_submodel_instance_point + 144 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! find_submodel_instance_world_point + 47 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! get_subsystem_pos + 162 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! get_subsystem_world_pos + 47 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! draw_subsys_brackets + 205 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! l_Graphics_drawSubsystemTargetingBrackets_f + 238 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! luaD_precall + 785 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! luaV_execute + 5766 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! luaD_call + 161 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! f_call + 58 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! luaD_rawrunprotected + 86 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! luaD_pcall + 100 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! lua_pcall + 134 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! script_state::RunBytecodeSub + 317 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! script_state::RunBytecode + 66 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! ConditionedHook::Run + 169 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! script_state::RunCondition + 167 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! gr_flip + 80 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! gr_opengl_cleanup + 90 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! gr_close + 84 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! doexit + 266 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! exit + 18 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! LuaError + 928 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! script_state::EvalString + 665 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! sexp_script_eval + 643 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! eval_sexp + 11523 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! eval_when_do_one_exp + 417 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! eval_when + 792 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! eval_sexp + 3659 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! mission_process_event + 547 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! mission_eval_goals + 517 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! game_simulation_frame + 1153 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! game_frame + 497 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! game_do_frame + 239 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! game_do_state + 379 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! gameseq_process_events + 237 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! game_main + 782 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! WinMain + 330 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! __tmainCRTStartup + 358 bytes
fs2_open_3_7_2_RC5-DEBUG.exe! WinMainCRTStartup + 15 bytes
KERNEL32.DLL! BaseThreadInitThunk + 36 bytes
ntdll.dll! RtlInitializeExceptionChain + 143 bytes
ntdll.dll! RtlInitializeExceptionChain + 90 bytes

Code: [Select]
---------------------------
Error!
---------------------------
LUA ERROR: [string "turretHotkey-sct.tbm - On Gameplay Start"]:97: attempt to index field '?' (a nil value)



------------------------------------------------------------------
ADE Debug:

------------------------------------------------------------------
Name: checkShips

Name of: method

Function type: Lua

Defined on: 91

Upvalues: 0



Source: turretHotkey-sct.tbm - On Gameplay Start

Short source: [string "turretHotkey-sct.tbm - On Gameplay Start"]

Current line: 98

- Function line: 8

------------------------------------------------------------------




------------------------------------------------------------------


stack traceback:
[C]: ?
[string "turretHotkey-sct.tbm - On Gameplay Start"]:97: in function 'checkShips'
[string "turretHotkey-sct.tbm - On Gameplay Start"]:83: in function 'onFrame'
[string "turretHotkey-sct.tbm - On Frame"]:1: in main chunk

------------------------------------------------------------------


------------------------------------------------------------------


[ This info is in the clipboard so you can paste it somewhere now ]





Use Yes to break into Debugger, No to continue.

and Cancel to Quit
---------------------------
Yes   No   Cancel   
---------------------------

Debug log for the CTD in attachment.

Also, whenever I replay a mission with the turret script in it, it throws the following error.
Code: [Select]
LUA ERROR: [string "?"]:1: '=' expected near '<eof>'

------------------------------------------------------------------
ADE Debug:
------------------------------------------------------------------
Name: (null)
Name of: (null)
Function type: (null)
Defined on: 0
Upvalues: 0

Source: (null)
Short source:
Current line: 0
- Function line: 0
------------------------------------------------------------------


------------------------------------------------------------------

stack traceback:
------------------------------------------------------------------

------------------------------------------------------------------

[attachment deleted by nobody]
Title: Re: Highlight or hotkey individual turret
Post by: AdmiralRalwood on June 16, 2015, 12:28:57 pm
Found a weird bug (oh, joy, I know.) Using AdmiralRalwood's version of the script.

When I add a ship(s) and their turrets to the list, then have any of those ships depart, then have new ships jump in and add THEIR turrets, it throws an error or CTDs. Even if I use thkClear to clear the script before tagging the new ships and turrets.

When playing one of these missions in debug mode, it gave me a couple lua errors when one of the thkAdded ships jumped out. Then, when the other thkAdded ships jumped in, I tried to F9-target their turrets and got a CTD. When I F9-target the new ships just after they jump in, anyway; one time, I waited a little while after they jumped in, and F9 worked just fine despite having thrown the same lua errors earlier.

What's up? How do I prevent ships jumping out from screwing the script up?
You should remove a ship before it jumps out. As for other problems, I'm going to need to see the exact SEXPs you're using.

...Actually, I just saw a couple logic errors with bits of the script I didn't modify last time (including in the function that's supposed to make sure the ships are still valid), so maybe give it a shot with this version:

Code: [Select]
#Conditional Hooks

$Application: FS2_Open
$On Gameplay Start:
[
turretHotkeys = {}
turretHotkeys.key = "F9"
turretHotkeys.enabled = false

-- Subsystem indicator settings
turretHotkeys.indicator = {}
turretHotkeys.indicator.color = {
    r = 255,
    g = 255,
    b = 255,
    a = 200
}

turretHotkeys.subsystems = {}
turretHotkeys.lastIndex = {1, 0}

function turretHotkeys:keyPressed(key)
    if (self:isEnabled() and key == self.key) then
        local tblIndex, subsysIndex = self:getNextIndex()

        if (tblIndex ~= false) then
            -- This is ugly code, don't look at it or it won't work
            local subsys = self.subsystems[tblIndex].ship[self.subsystems[tblIndex][subsysIndex]]

            if (subsys:isValid()) then
                hv.Player.Target = self.subsystems[tblIndex].ship
                hv.Player.TargetSubsystem = subsys
            end

            self.lastIndex = { tblIndex, subsysIndex }
        end
    end
end

function turretHotkeys:checkIndexes(tblIndex, subsysIndex)
    if (tblIndex > 0 and tblIndex <= #self.subsystems) then
        if (subsysIndex > 0 and subsysIndex <= #self.subsystems[tblIndex]) then
            return tblIndex, subsysIndex
        else
            return tblIndex, math.max(1, math.min(subsysIndex, #self.subsystems[tblIndex]))
        end
    else
        -- If we're here it means that we've got an invalid index...
        return math.max(1, math.min(tblIndex, #self.subsystems)), math.max(1, math.min(1, #self.subsystems[tblIndex]))
    end
end

function turretHotkeys:getNextIndex()
    local tblIndex = self.lastIndex[1]

    local tbl = self.subsystems[tblIndex]

    if (tbl == nil) then
        if (tblIndex == 1) then
            return false
        else
            return self:checkIndexes(1, 1)
        end
    end

    local subsystemIndex = self.lastIndex[2] + 1

    if (subsystemIndex > #tbl) then
        subsystemIndex = 1
        tblIndex = tblIndex + 1

        tbl = self.subsystems[tblIndex]
    end

    if (tblIndex > #self.subsystems) then
        tblIndex = 1

        if (tblIndex > #self.subsystems) then
            return self:checkIndexes(tblIndex, 1)
        end
    end

    return self:checkIndexes(tblIndex, subsystemIndex)
end

function turretHotkeys:onFrame()
    if (self:isEnabled()) then
        self:checkShips()

        if (hu.HUDDrawn) then
            self:drawSubsystems()
        end
    end
end

function turretHotkeys:checkShips()
    local index = 1
    local done = false

    while (not done) do
        done = true
        for i = index, #self.subsystems do
            if (i ~= #self.subsystems) do
                done = false
            end
            if (not self.subsystems[i].ship:isValid() ) then
                table.remove(self.subsystems, i)
                index = i
                break
            end
        end
    end
end

function turretHotkeys:drawSubsystems()
    local target = hv.Player.Target
    local subsysName = hv.Player.TargetSubsystem:getModelName()

    for i = 1, #self.subsystems do
        local tbl = self.subsystems[i]
        for j = 1, #tbl do
            local current = tbl.ship[tbl[j]]
            if current.HitpointsLeft > 0 and (tbl.ship ~= target or current:getModelName() ~= subsysName) then
                self:drawSubsystem(current)
            end
        end
    end
end

function turretHotkeys:drawSubsystem(subsys)
    local indicator = self.indicator

    gr.setColor(indicator.color.r, indicator.color.g, indicator.color.b, indicator.color.a)

    gr.drawSubsystemTargetingBrackets(subsys)
end

function turretHotkeys:addShip(ship)
    table.insert(self.subsystems, {
        name = ship.Name,
        ship = ship
    })
end

function turretHotkeys:isValidIndex(index)
    return self.subsystems[index] ~= nil and self.subsystems[index].ship:isValid()
end

function turretHotkeys:addSubsystem(index, name)
    local tbl = self.subsystems[index]

    if (not tbl.ship[name]:isValid()) then
        ba.warning(string.format("Unknown subsystem %q on ship %q of class %q.", name, tbl.ship.name, tbl.ship.class))
        return false
    end

    table.insert(tbl, name)

    return true
end

function turretHotkeys:removeSubsystem(index, name)
    local tbl = self.subsystems[index]

    local newIndex = 1
    local done = false

    while (not done) do
        done = true
        for i = newIndex, #tbl do
            if (i ~= #tbl) do
                done = false
            end
            if (tbl[i] == name) then
                table.remove(tbl, i)
                newIndex = i
                break
            end
        end
    end
end

function turretHotkeys:isEnabled()
    return self.enabled and hv.Player:getBreedName() == "Ship"
end

function thkEnable(bool)
    if (bool == nil) then
        bool = true
    end

    if (type(bool) ~= "boolean") then
        ba.warning("Invalid argument type for thkKey. Need a boolean or no argument, got " .. type(bool) .. ".")
        return
    end

    turretHotkeys.enabled = bool
end

function thkKey(key)
    if (type(key) ~= "string") then
        ba.warning("Invalid argument type for thkKey. Need a string, got " .. type(key) .. ".")
        return
    end

    turretHotkeys.key = key
end

function thkShip(...)
    local names = { ... }

    for i = 1, #names do
        if (type(names[i]) ~= "string") then
            ba.warning("Invalid argument type for thkShip. Need a string, got " .. type(names[i]) .. ".")
            return
        end

        local ship = mn.Ships[names[i]]

        if (not ship:isValid()) then
            ba.warning("No ship with name \"" .. names[i] .. "\" could be found in the current mission.")
            return
        end

        turretHotkeys:addShip(ship)
    end
end

function thkAdd(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkAdd. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkAdd. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:addSubsystem(index, name)
end

function thkRemove(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkRemove. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkRemove. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:removeSubsystem(index, name)
end

function thkClear()
    turretHotkeys.subsystems = {}
end
]

$State: GS_STATE_GAME_PLAY
$On Key Pressed:
[
turretHotkeys:keyPressed(hv.Key)
]

$State: GS_STATE_GAME_PLAY
$On Frame:
[
turretHotkeys:onFrame()
]

#End
Title: Re: Highlight or hotkey individual turret
Post by: Lepanto on June 16, 2015, 09:21:51 pm
Thanks a lot; this is rather important for Inferno.

But still ran into this error when starting FSO with the new version:
Code: [Select]
LUA ERROR: [string "turretHotkey-sct.tbm - On Gameplay Start"]:98: 'then' expected near 'do'

------------------------------------------------------------------
ADE Debug:
------------------------------------------------------------------
Name: (null)
Name of: (null)
Function type: (null)
Defined on: 0
Upvalues: 0

Source: (null)
Short source:
Current line: 0
- Function line: 0
------------------------------------------------------------------


------------------------------------------------------------------

stack traceback:
------------------------------------------------------------------

------------------------------------------------------------------
Title: Re: Highlight or hotkey individual turret
Post by: AdmiralRalwood on June 18, 2015, 02:18:06 am
Thanks a lot; this is rather important for Inferno.

But still ran into this error when starting FSO with the new version:
Code: [Select]
LUA ERROR: [string "turretHotkey-sct.tbm - On Gameplay Start"]:98: 'then' expected near 'do'
Whoops; accidentally used "do" instead of "then" for an if statement (I'm not a Lua coder, but I play one on HLP...).

Code: [Select]
#Conditional Hooks

$Application: FS2_Open
$On Gameplay Start:
[
turretHotkeys = {}
turretHotkeys.key = "F9"
turretHotkeys.enabled = false

-- Subsystem indicator settings
turretHotkeys.indicator = {}
turretHotkeys.indicator.color = {
    r = 255,
    g = 255,
    b = 255,
    a = 200
}

turretHotkeys.subsystems = {}
turretHotkeys.lastIndex = {1, 0}

function turretHotkeys:keyPressed(key)
    if (self:isEnabled() and key == self.key) then
        local tblIndex, subsysIndex = self:getNextIndex()

        if (tblIndex ~= false) then
            -- This is ugly code, don't look at it or it won't work
            local subsys = self.subsystems[tblIndex].ship[self.subsystems[tblIndex][subsysIndex]]

            if (subsys:isValid()) then
                hv.Player.Target = self.subsystems[tblIndex].ship
                hv.Player.TargetSubsystem = subsys
            end

            self.lastIndex = { tblIndex, subsysIndex }
        end
    end
end

function turretHotkeys:checkIndexes(tblIndex, subsysIndex)
    if (tblIndex > 0 and tblIndex <= #self.subsystems) then
        if (subsysIndex > 0 and subsysIndex <= #self.subsystems[tblIndex]) then
            return tblIndex, subsysIndex
        else
            return tblIndex, math.max(1, math.min(subsysIndex, #self.subsystems[tblIndex]))
        end
    else
        -- If we're here it means that we've got an invalid index...
        return math.max(1, math.min(tblIndex, #self.subsystems)), math.max(1, math.min(1, #self.subsystems[tblIndex]))
    end
end

function turretHotkeys:getNextIndex()
    local tblIndex = self.lastIndex[1]

    local tbl = self.subsystems[tblIndex]

    if (tbl == nil) then
        if (tblIndex == 1) then
            return false
        else
            return self:checkIndexes(1, 1)
        end
    end

    local subsystemIndex = self.lastIndex[2] + 1

    if (subsystemIndex > #tbl) then
        subsystemIndex = 1
        tblIndex = tblIndex + 1

        tbl = self.subsystems[tblIndex]
    end

    if (tblIndex > #self.subsystems) then
        tblIndex = 1

        if (tblIndex > #self.subsystems) then
            return self:checkIndexes(tblIndex, 1)
        end
    end

    return self:checkIndexes(tblIndex, subsystemIndex)
end

function turretHotkeys:onFrame()
    if (self:isEnabled()) then
        self:checkShips()

        if (hu.HUDDrawn) then
            self:drawSubsystems()
        end
    end
end

function turretHotkeys:checkShips()
    local index = 1
    local done = false

    while (not done) do
        done = true
        for i = index, #self.subsystems do
            if (i ~= #self.subsystems) then
                done = false
            end
            if (not self.subsystems[i].ship:isValid() ) then
                table.remove(self.subsystems, i)
                index = i
                break
            end
        end
    end
end

function turretHotkeys:drawSubsystems()
    local target = hv.Player.Target
    local subsysName = hv.Player.TargetSubsystem:getModelName()

    for i = 1, #self.subsystems do
        local tbl = self.subsystems[i]
        for j = 1, #tbl do
            local current = tbl.ship[tbl[j]]
            if current.HitpointsLeft > 0 and (tbl.ship ~= target or current:getModelName() ~= subsysName) then
                self:drawSubsystem(current)
            end
        end
    end
end

function turretHotkeys:drawSubsystem(subsys)
    local indicator = self.indicator

    gr.setColor(indicator.color.r, indicator.color.g, indicator.color.b, indicator.color.a)

    gr.drawSubsystemTargetingBrackets(subsys)
end

function turretHotkeys:addShip(ship)
    table.insert(self.subsystems, {
        name = ship.Name,
        ship = ship
    })
end

function turretHotkeys:isValidIndex(index)
    return self.subsystems[index] ~= nil and self.subsystems[index].ship:isValid()
end

function turretHotkeys:addSubsystem(index, name)
    local tbl = self.subsystems[index]

    if (not tbl.ship[name]:isValid()) then
        ba.warning(string.format("Unknown subsystem %q on ship %q of class %q.", name, tbl.ship.name, tbl.ship.class))
        return false
    end

    table.insert(tbl, name)

    return true
end

function turretHotkeys:removeSubsystem(index, name)
    local tbl = self.subsystems[index]

    local newIndex = 1
    local done = false

    while (not done) do
        done = true
        for i = newIndex, #tbl do
            if (i ~= #tbl) do
                done = false
            end
            if (tbl[i] == name) then
                table.remove(tbl, i)
                newIndex = i
                break
            end
        end
    end
end

function turretHotkeys:isEnabled()
    return self.enabled and hv.Player:getBreedName() == "Ship"
end

function thkEnable(bool)
    if (bool == nil) then
        bool = true
    end

    if (type(bool) ~= "boolean") then
        ba.warning("Invalid argument type for thkKey. Need a boolean or no argument, got " .. type(bool) .. ".")
        return
    end

    turretHotkeys.enabled = bool
end

function thkKey(key)
    if (type(key) ~= "string") then
        ba.warning("Invalid argument type for thkKey. Need a string, got " .. type(key) .. ".")
        return
    end

    turretHotkeys.key = key
end

function thkShip(...)
    local names = { ... }

    for i = 1, #names do
        if (type(names[i]) ~= "string") then
            ba.warning("Invalid argument type for thkShip. Need a string, got " .. type(names[i]) .. ".")
            return
        end

        local ship = mn.Ships[names[i]]

        if (not ship:isValid()) then
            ba.warning("No ship with name \"" .. names[i] .. "\" could be found in the current mission.")
            return
        end

        turretHotkeys:addShip(ship)
    end
end

function thkAdd(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkAdd. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkAdd. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:addSubsystem(index, name)
end

function thkRemove(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkRemove. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkRemove. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:removeSubsystem(index, name)
end

function thkClear()
    turretHotkeys.subsystems = {}
end
]

$State: GS_STATE_GAME_PLAY
$On Key Pressed:
[
turretHotkeys:keyPressed(hv.Key)
]

$State: GS_STATE_GAME_PLAY
$On Frame:
[
turretHotkeys:onFrame()
]

#End
Title: Re: Highlight or hotkey individual turret
Post by: Lepanto on June 19, 2015, 06:56:11 pm
No dice, sorry. Got the following errors:

Code: [Select]
LUA ERROR: [string "turretHotkey-sct.tbm - On Gameplay Start"]:166: 'then' expected near 'do'

------------------------------------------------------------------
ADE Debug:
------------------------------------------------------------------
Name: (null)
Name of: (null)
Function type: (null)
Defined on: 0
Upvalues: 0

Source: (null)
Short source:
Current line: 0
- Function line: 0
------------------------------------------------------------------


------------------------------------------------------------------

stack traceback:
------------------------------------------------------------------

------------------------------------------------------------------

Code: [Select]
turretHotkey-sct.tbm(line 272):
Error: Missing required token: [#End]. Found [$State: GS_STATE_GAME_PLAY] instead.

ntdll.dll! ZwWaitForSingleObject + 12 bytes
KERNELBASE.dll! WaitForSingleObject + 18 bytes
fs2_open_3_7_2_RC5.exe! <no symbol>
fs2_open_3_7_2_RC5.exe! <no symbol>
Title: Re: Highlight or hotkey individual turret
Post by: AdmiralRalwood on June 20, 2015, 12:33:24 am
Same problem, different line.

Code: [Select]
#Conditional Hooks

$Application: FS2_Open
$On Gameplay Start:
[
turretHotkeys = {}
turretHotkeys.key = "F9"
turretHotkeys.enabled = false

-- Subsystem indicator settings
turretHotkeys.indicator = {}
turretHotkeys.indicator.color = {
    r = 255,
    g = 255,
    b = 255,
    a = 200
}

turretHotkeys.subsystems = {}
turretHotkeys.lastIndex = {1, 0}

function turretHotkeys:keyPressed(key)
    if (self:isEnabled() and key == self.key) then
        local tblIndex, subsysIndex = self:getNextIndex()

        if (tblIndex ~= false) then
            -- This is ugly code, don't look at it or it won't work
            local subsys = self.subsystems[tblIndex].ship[self.subsystems[tblIndex][subsysIndex]]

            if (subsys:isValid()) then
                hv.Player.Target = self.subsystems[tblIndex].ship
                hv.Player.TargetSubsystem = subsys
            end

            self.lastIndex = { tblIndex, subsysIndex }
        end
    end
end

function turretHotkeys:checkIndexes(tblIndex, subsysIndex)
    if (tblIndex > 0 and tblIndex <= #self.subsystems) then
        if (subsysIndex > 0 and subsysIndex <= #self.subsystems[tblIndex]) then
            return tblIndex, subsysIndex
        else
            return tblIndex, math.max(1, math.min(subsysIndex, #self.subsystems[tblIndex]))
        end
    else
        -- If we're here it means that we've got an invalid index...
        return math.max(1, math.min(tblIndex, #self.subsystems)), math.max(1, math.min(1, #self.subsystems[tblIndex]))
    end
end

function turretHotkeys:getNextIndex()
    local tblIndex = self.lastIndex[1]

    local tbl = self.subsystems[tblIndex]

    if (tbl == nil) then
        if (tblIndex == 1) then
            return false
        else
            return self:checkIndexes(1, 1)
        end
    end

    local subsystemIndex = self.lastIndex[2] + 1

    if (subsystemIndex > #tbl) then
        subsystemIndex = 1
        tblIndex = tblIndex + 1

        tbl = self.subsystems[tblIndex]
    end

    if (tblIndex > #self.subsystems) then
        tblIndex = 1

        if (tblIndex > #self.subsystems) then
            return self:checkIndexes(tblIndex, 1)
        end
    end

    return self:checkIndexes(tblIndex, subsystemIndex)
end

function turretHotkeys:onFrame()
    if (self:isEnabled()) then
        self:checkShips()

        if (hu.HUDDrawn) then
            self:drawSubsystems()
        end
    end
end

function turretHotkeys:checkShips()
    local index = 1
    local done = false

    while (not done) do
        done = true
        for i = index, #self.subsystems do
            if (i ~= #self.subsystems) then
                done = false
            end
            if (not self.subsystems[i].ship:isValid() ) then
                table.remove(self.subsystems, i)
                index = i
                break
            end
        end
    end
end

function turretHotkeys:drawSubsystems()
    local target = hv.Player.Target
    local subsysName = hv.Player.TargetSubsystem:getModelName()

    for i = 1, #self.subsystems do
        local tbl = self.subsystems[i]
        for j = 1, #tbl do
            local current = tbl.ship[tbl[j]]
            if current.HitpointsLeft > 0 and (tbl.ship ~= target or current:getModelName() ~= subsysName) then
                self:drawSubsystem(current)
            end
        end
    end
end

function turretHotkeys:drawSubsystem(subsys)
    local indicator = self.indicator

    gr.setColor(indicator.color.r, indicator.color.g, indicator.color.b, indicator.color.a)

    gr.drawSubsystemTargetingBrackets(subsys)
end

function turretHotkeys:addShip(ship)
    table.insert(self.subsystems, {
        name = ship.Name,
        ship = ship
    })
end

function turretHotkeys:isValidIndex(index)
    return self.subsystems[index] ~= nil and self.subsystems[index].ship:isValid()
end

function turretHotkeys:addSubsystem(index, name)
    local tbl = self.subsystems[index]

    if (not tbl.ship[name]:isValid()) then
        ba.warning(string.format("Unknown subsystem %q on ship %q of class %q.", name, tbl.ship.name, tbl.ship.class))
        return false
    end

    table.insert(tbl, name)

    return true
end

function turretHotkeys:removeSubsystem(index, name)
    local tbl = self.subsystems[index]

    local newIndex = 1
    local done = false

    while (not done) do
        done = true
        for i = newIndex, #tbl do
            if (i ~= #tbl) then
                done = false
            end
            if (tbl[i] == name) then
                table.remove(tbl, i)
                newIndex = i
                break
            end
        end
    end
end

function turretHotkeys:isEnabled()
    return self.enabled and hv.Player:getBreedName() == "Ship"
end

function thkEnable(bool)
    if (bool == nil) then
        bool = true
    end

    if (type(bool) ~= "boolean") then
        ba.warning("Invalid argument type for thkKey. Need a boolean or no argument, got " .. type(bool) .. ".")
        return
    end

    turretHotkeys.enabled = bool
end

function thkKey(key)
    if (type(key) ~= "string") then
        ba.warning("Invalid argument type for thkKey. Need a string, got " .. type(key) .. ".")
        return
    end

    turretHotkeys.key = key
end

function thkShip(...)
    local names = { ... }

    for i = 1, #names do
        if (type(names[i]) ~= "string") then
            ba.warning("Invalid argument type for thkShip. Need a string, got " .. type(names[i]) .. ".")
            return
        end

        local ship = mn.Ships[names[i]]

        if (not ship:isValid()) then
            ba.warning("No ship with name \"" .. names[i] .. "\" could be found in the current mission.")
            return
        end

        turretHotkeys:addShip(ship)
    end
end

function thkAdd(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkAdd. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkAdd. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:addSubsystem(index, name)
end

function thkRemove(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkRemove. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkRemove. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:removeSubsystem(index, name)
end

function thkClear()
    turretHotkeys.subsystems = {}
end
]

$State: GS_STATE_GAME_PLAY
$On Key Pressed:
[
turretHotkeys:keyPressed(hv.Key)
]

$State: GS_STATE_GAME_PLAY
$On Frame:
[
turretHotkeys:onFrame()
]

#End
Title: Re: Highlight or hotkey individual turret
Post by: Lepanto on July 19, 2015, 08:58:17 pm
Tried that version. CTD when I try to F9-select the second ship's weapons after it jumps in. Works just fine on the debug build, weirdly enough.
Title: Re: Highlight or hotkey individual turret
Post by: Axem on August 22, 2015, 04:39:41 pm
Just as a heads up, I made a new version that should hopefully be a bit more robust and user friendly.

Turret Hotkey Script v2 (http://pastebin.com/LaD6pY6q)

Please note that this is not a drop in replacement! You will need to alter your syntax a bit, but it should make things more readable.

The way to call it is now (with script-eval-block so you have room for all of this!):
TurretHotkey:Add('ship name','subobject name,'label','hotkey')
Where ship name is the ship's name, subobject name is what you refer to it in the subsystems part of the table or PCS2, label is an optional text label you can give it (like Beam Cannon, Weakpoint etc) and hotkey is F5 though F12. If hotkey isn't entered, it'll use F9.

You can remove a turret from the list with:
TurretHotkey:Rem('ship name','subobject name')
If you don't put the subobject name it will clear everything tied to the ship.

See the pic below for some real power user stuff.

(http://lazymodders.fsmods.net/files/turrethotkey2.png)

And there are a a few new features. Like I said before, you can specify labels for giving the player more clarity, and also it'll show the health of any subsystem while the list is active, and... AND! When you destroy subsystems that are on a hotkey list, it will jump to the next one in line for easy disarming!

Let me know if this crashes for you!

(Also if you use this right away, the text drawing will be a little goofy until the new nightly goes out tomorrow)
Title: Re: Highlight or hotkey individual turret
Post by: Lepanto on August 22, 2015, 09:42:04 pm
Thanks a ton, Mr. Make Everyone Else Feel Inferior! :p

I'll give it a try tomorrow, then.
Title: Re: Highlight or hotkey individual turret
Post by: Lepanto on September 19, 2015, 10:45:13 pm
Sorry, but it's giving me grief again. Believe I tested it on 2 separate missions.

The first set of TurretHotkey:Add-ed turrets shows up on SEXP call, with damage percentages and everything. But for some reason, when targeting a certain set of 3 turrets I can't target the 3rd turret using F9 (when I destroy the other 2 turrets in the group, it does switch target to turret 3 as intended, though.)

No errors from the script when the first set of ships with tagged turrets warps out, or when the second ship/group jumps in. But when I try to F9-target the turrets on the second ship/group of ships, it immediately CTDs.

Would you please do this lowly LUA-illiterate peasant (and the Inferno project) a favor? Please kindly find out why the script gets so cranky when having to load two separate sets of turrets on two separate ships/groups of ships?

[attachment deleted by nobody]
Title: Re: Highlight or hotkey individual turret
Post by: Axem on September 20, 2015, 04:54:10 pm
Can you make me a retail usable mission to try that out on? And which ship from the debug log is it that it's not cycling correctly on.
Title: Re: Highlight or hotkey individual turret
Post by: procdrone on September 20, 2015, 08:37:13 pm
Sorry, but it's giving me grief again. Believe I tested it on 2 separate missions.

The first set of TurretHotkey:Add-ed turrets shows up on SEXP call, with damage percentages and everything. But for some reason, when targeting a certain set of 3 turrets I can't target the 3rd turret using F9 (when I destroy the other 2 turrets in the group, it does switch target to turret 3 as intended, though.)

No errors from the script when the first set of ships with tagged turrets warps out, or when the second ship/group jumps in. But when I try to F9-target the turrets on the second ship/group of ships, it immediately CTDs.

Would you please do this lowly LUA-illiterate peasant (and the Inferno project) a favor? Please kindly find out why the script gets so cranky when having to load two separate sets of turrets on two separate ships/groups of ships?

Do you remember to delete the turret from the script when the ship/turret is destroyed? Otherwise, it might just bug the hell out of it. Had many issues with that in the past as well. Script also had issues when the ship you are setting up is not on the mission from the start, but arriving at some point.
Title: Re: Highlight or hotkey individual turret
Post by: Axem on September 20, 2015, 08:58:19 pm
My new script has a "auto-prune" function that will get rid the hotkey list of departed and destroyed ships. But that's not to say it might not be tripping on something I didn't take in account.
Title: Re: Highlight or hotkey individual turret
Post by: procdrone on September 21, 2015, 08:46:11 am
I was not using your version 2 so dunno
Title: Re: Highlight or hotkey individual turret
Post by: Lepanto on September 28, 2015, 10:20:55 pm
2 bugs CONFIRMED, when only using hotkey script + MediaVPs 2014.

-When you try to F9-target a second group of tagged ships' turrets after the first group of tagged ships has departed, immediate CTD.

-When some of the tagged turrets are destroyed, the F9 key no longer works to switch between the remaining turrets.

Here is a provided test mod, containing Axem's turret hotkey script and a single mission (in the techroom.)

[attachment deleted by nobody]
Title: Re: Highlight or hotkey individual turret
Post by: Axem on September 28, 2015, 10:21:48 pm
Great! I will take a look at this tomorrow.
Title: Re: Highlight or hotkey individual turret
Post by: Lepanto on September 28, 2015, 10:22:32 pm
Wow that was fast thanks.
Title: Re: Highlight or hotkey individual turret
Post by: Axem on September 29, 2015, 07:39:36 pm
Okay, I can reproduce the issue, and I am working on it!
Title: Re: Highlight or hotkey individual turret
Post by: AdmiralRalwood on July 02, 2018, 05:16:36 pm
Happy, uh, 2nd of July?

I was working with Battuta to fix up the bugs with the turret hotkey script and figured I should post it here, too, so here you go:
Code: [Select]
#Conditional Hooks

$Application: FS2_Open
$On Gameplay Start:
[
turretHotkeys = {}
turretHotkeys.key = "F9"
turretHotkeys.enabled = false

-- Subsystem indicator settings
turretHotkeys.indicator = {}
turretHotkeys.indicator.color = {
    r = 255,
    g = 255,
    b = 255,
    a = 200
}

turretHotkeys.subsystems = {}
turretHotkeys.lastIndex = {1, 0}

function turretHotkeys:keyPressed(key)
    if (self:isEnabled() and key == self.key) then
        local done = false
        local startindex = nil
        local startsubsys = nil
        while (not done) do
            local tblIndex, subsysIndex = self:getNextIndex()
            if (startindex == nil) then
                startindex = tblIndex
                startsubsys = subsysIndex
            else
                if (startindex == tblIndex and startsubsys == subsysIndex) then
                    done = true
                    break
                end
            end

            if (tblIndex ~= false) then
                -- This is ugly code, don't look at it or it won't work
                local tbl = self.subsystems[tblIndex]
                if (tbl ~= nil and tbl[subsysIndex] ~= nil) then
                    local subsys = tbl.ship[tbl[subsysIndex]]

                    if (subsys:isValid() and subsys.HitpointsLeft > 0) then
                        hv.Player.Target = self.subsystems[tblIndex].ship
                        hv.Player.TargetSubsystem = subsys
                        done = true
                    end

                    self.lastIndex = { tblIndex, subsysIndex }
                end
            else
                done = true
            end
        end
    end
end

function turretHotkeys:checkIndexes(tblIndex, subsysIndex)
    if (tblIndex > 0 and tblIndex <= #self.subsystems) then
        if (subsysIndex > 0 and subsysIndex <= #self.subsystems[tblIndex]) then
            return tblIndex, subsysIndex
        else
            return tblIndex, math.max(1, math.min(subsysIndex, #self.subsystems[tblIndex]))
        end
    else
        -- If we're here it means that we've got an invalid index...
        return math.max(1, math.min(tblIndex, #self.subsystems)), math.max(1, math.min(1, #self.subsystems[tblIndex]))
    end
end

function turretHotkeys:getNextIndex()
    local tblIndex = self.lastIndex[1]

    local tbl = self.subsystems[tblIndex]

    if (tbl == nil) then
        if (tblIndex == 1) then
            return false
        else
            return self:checkIndexes(1, 1)
        end
    end

    local subsystemIndex = self.lastIndex[2] + 1

    if (subsystemIndex > #tbl) then
        subsystemIndex = 1
        tblIndex = tblIndex + 1

        tbl = self.subsystems[tblIndex]
    end

    if (tblIndex > #self.subsystems) then
        tblIndex = 1

        if (tblIndex > #self.subsystems) then
            return self:checkIndexes(tblIndex, 1)
        end
    end

    return self:checkIndexes(tblIndex, subsystemIndex)
end

function turretHotkeys:onFrame()
    if (self:isEnabled()) then
        self:checkShips()

        if (hu.HUDDrawn) then
            self:drawSubsystems()
        end
    end
end

function turretHotkeys:checkShips()
    local i = 1

    while (i <= #self.subsystems) do
        if (self.subsystems[i].ship:isValid() and self.subsystems[i].ship.HitpointsLeft > 0 and self.subsystems[i].ship:getBreedName() == "Ship") then
            i = i + 1
        else
            table.remove(self.subsystems, i)
        end
    end
end

function turretHotkeys:drawSubsystems()
    local target = hv.Player.Target
    local subsysName = hv.Player.TargetSubsystem:getModelName()

    for i = 1, #self.subsystems do
        local tbl = self.subsystems[i]
        for j = 1, #tbl do
            local current = tbl.ship[tbl[j]]
            if current.HitpointsLeft > 0 and (tbl.ship ~= target or current:getModelName() ~= subsysName) then
                self:drawSubsystem(current)
            end
        end
    end
end

function turretHotkeys:drawSubsystem(subsys)
    local indicator = self.indicator

    gr.setColor(indicator.color.r, indicator.color.g, indicator.color.b, indicator.color.a)

    gr.drawSubsystemTargetingBrackets(subsys)
end

function turretHotkeys:addShip(ship)
    table.insert(self.subsystems, {
        name = ship.Name,
        ship = ship
    })
end

function turretHotkeys:isValidIndex(index)
    return self.subsystems[index] ~= nil and self.subsystems[index].ship:isValid()
end

function turretHotkeys:addSubsystem(index, name)
    local tbl = self.subsystems[index]

    if (not tbl.ship[name]:isValid()) then
        ba.warning(string.format("Unknown subsystem %q on ship %q of class %q.", name, tbl.ship.name, tbl.ship.class))
        return false
    end

    table.insert(tbl, name)

    return true
end

function turretHotkeys:removeSubsystem(index, name)
    local tbl = self.subsystems[index]

    local newIndex = 1
    local done = false

    while (not done) do
        done = true
        for i = newIndex, #tbl do
            if (i ~= #tbl) then
                done = false
            end
            if (tbl[i] == name) then
                table.remove(tbl, i)
                newIndex = i
                break
            end
        end
    end
end

function turretHotkeys:isEnabled()
    return self.enabled and hv.Player:getBreedName() == "Ship"
end

function thkEnable(bool)
    if (bool == nil) then
        bool = true
    end

    if (type(bool) ~= "boolean") then
        ba.warning("Invalid argument type for thkKey. Need a boolean or no argument, got " .. type(bool) .. ".")
        return
    end

    turretHotkeys.enabled = bool
end

function thkKey(key)
    if (type(key) ~= "string") then
        ba.warning("Invalid argument type for thkKey. Need a string, got " .. type(key) .. ".")
        return
    end

    turretHotkeys.key = key
end

function thkShip(...)
    local names = { ... }

    for i = 1, #names do
        if (type(names[i]) ~= "string") then
            ba.warning("Invalid argument type for thkShip. Need a string, got " .. type(names[i]) .. ".")
            return
        end

        local ship = mn.Ships[names[i]]

        if (not ship:isValid()) then
            ba.warning("No ship with name \"" .. names[i] .. "\" could be found in the current mission.")
            return
        end

        turretHotkeys:addShip(ship)
    end
end

function thkAdd(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkAdd. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkAdd. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:addSubsystem(index, name)
end

function thkRemove(index, name)
    if (type(index) ~= "number") then
        ba.warning("Invalid first argument type for thkRemove. Need a string, got " .. type(index) .. ".")
        return
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument type for thkRemove. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:removeSubsystem(index, name)
end

function thkClear()
    turretHotkeys.subsystems = {}
end
]

$State: GS_STATE_GAME_PLAY
$On Key Pressed:
[
turretHotkeys:keyPressed(hv.Key)
]

$State: GS_STATE_GAME_PLAY
$On Frame:
[
turretHotkeys:onFrame()
]

#End
No real new features, but plenty of bugfixes, and the hotkey will now skip over destroyed subsystems, plus destroyed/departed ships be automatically pruned.

EDIT: Updated again; pruned ships are now marked as inactive but left in the table, so indices won't rearrange themselves mid-mission without thkClear() getting called. In addition, thkAdd() and thkRemove() now accept a ship name for an index so you don't actually have to keep track of what order ships were added to the script (helpful if a ship is only added conditionally). Use script-eval-block to execute longer pieces of Lua from SEXPs.
Code: [Select]
#Conditional Hooks

$Application: FS2_Open
$On Gameplay Start:
[
turretHotkeys = {}
turretHotkeys.key = "F9"
turretHotkeys.enabled = false

-- Subsystem indicator settings
turretHotkeys.indicator = {}
turretHotkeys.indicator.color = {
    r = 255,
    g = 255,
    b = 255,
    a = 200
}

turretHotkeys.subsystems = {}
turretHotkeys.lastIndex = {1, 0}

function turretHotkeys:keyPressed(key)
    if (self:isEnabled() and key == self.key) then
        local done = false
        local startindex = nil
        local startsubsys = nil
        while (not done) do
            local tblIndex, subsysIndex = self:getNextIndex()
            if (startindex == nil) then
                startindex = tblIndex
                startsubsys = subsysIndex
            else
                if (startindex == tblIndex and startsubsys == subsysIndex) then
                    done = true
                    break
                end
            end

            if (tblIndex ~= false) then
                -- This is ugly code, don't look at it or it won't work
                local tbl = self.subsystems[tblIndex]
                if (tbl ~= nil and tbl.active and tbl[subsysIndex] ~= nil) then
                    local subsys = tbl.ship[tbl[subsysIndex]]

                    if (subsys:isValid() and subsys.HitpointsLeft > 0) then
                        hv.Player.Target = self.subsystems[tblIndex].ship
                        hv.Player.TargetSubsystem = subsys
                        done = true
                    end
                end
                self.lastIndex = { tblIndex, subsysIndex }
            else
                done = true
            end
        end
    end
end

function turretHotkeys:checkIndexes(tblIndex, subsysIndex)
    if (tblIndex > 0 and tblIndex <= #self.subsystems) then
        if (subsysIndex > 0 and subsysIndex <= #self.subsystems[tblIndex]) then
            return tblIndex, subsysIndex
        else
            return tblIndex, math.max(1, math.min(subsysIndex, #self.subsystems[tblIndex]))
        end
    else
        -- If we're here it means that we've got an invalid index...
        return math.max(1, math.min(tblIndex, #self.subsystems)), math.max(1, math.min(1, #self.subsystems[tblIndex]))
    end
end

function turretHotkeys:getNextIndex()
    local tblIndex = self.lastIndex[1]

    local tbl = self.subsystems[tblIndex]

    if (tbl == nil) then
        if (tblIndex == 1) then
            return false
        else
            return self:checkIndexes(1, 1)
        end
    end

    local subsystemIndex = self.lastIndex[2] + 1

    if (subsystemIndex > #tbl) then
        subsystemIndex = 1
        tblIndex = tblIndex + 1

        tbl = self.subsystems[tblIndex]
    end

    if (tblIndex > #self.subsystems) then
        tblIndex = 1

        if (tblIndex > #self.subsystems) then
            return self:checkIndexes(tblIndex, 1)
        end
    end

    return self:checkIndexes(tblIndex, subsystemIndex)
end

function turretHotkeys:onFrame()
    if (self:isEnabled()) then
        self:checkShips()

        if (hu.HUDDrawn) then
            self:drawSubsystems()
        end
    end
end

function turretHotkeys:checkShips()
    for i = 1, #self.subsystems do
    if (self.subsystems[i].active) then
        if (not (self.subsystems[i].ship:isValid() and self.subsystems[i].ship.HitpointsLeft > 0 and self.subsystems[i].ship:getBreedName() == "Ship")) then
            self.subsystems[i].active = false
        end
    end
    end
end

function turretHotkeys:drawSubsystems()
    local target = hv.Player.Target
    local subsysName = hv.Player.TargetSubsystem:getModelName()

    for i = 1, #self.subsystems do
        local tbl = self.subsystems[i]
        if (tbl.active) then
        for j = 1, #tbl do
            local current = tbl.ship[tbl[j]]
            if current.HitpointsLeft > 0 and (tbl.ship ~= target or current:getModelName() ~= subsysName) then
                self:drawSubsystem(current)
            end
        end
    end
    end
end

function turretHotkeys:drawSubsystem(subsys)
    local indicator = self.indicator

    gr.setColor(indicator.color.r, indicator.color.g, indicator.color.b, indicator.color.a)

    gr.drawSubsystemTargetingBrackets(subsys)
end

function turretHotkeys:addShip(ship)
    table.insert(self.subsystems, {
        name = ship.Name,
        ship = ship,
        active = true
    })
end

function turretHotkeys:isValidIndex(index)
    return self.subsystems[index] ~= nil and self.subsystems[index].ship:isValid()
end

function turretHotkeys:addSubsystem(index, name)
    local tbl = self.subsystems[index]

    if (not tbl.ship[name]:isValid()) then
        ba.warning(string.format("Unknown subsystem %q on ship %q of class %q.", name, tbl.ship.name, tbl.ship.class))
        return false
    end

    table.insert(tbl, name)

    return true
end

function turretHotkeys:removeSubsystem(index, name)
    local tbl = self.subsystems[index]

    local newIndex = 1
    local done = false

    while (not done) do
        done = true
        for i = newIndex, #tbl do
            if (i ~= #tbl) then
                done = false
            end
            if (tbl[i] == name) then
                table.remove(tbl, i)
                newIndex = i
                break
            end
        end
    end
end

function turretHotkeys:isEnabled()
    return self.enabled and hv.Player:getBreedName() == "Ship"
end

function turretHotkeys:findIndex(name)
for i = 1, #self.subsystems do
if (self.subsystems[i].name == name) then
return i
end
end
return nil
end

function thkEnable(bool)
    if (bool == nil) then
        bool = true
    end

    if (type(bool) ~= "boolean") then
        ba.warning("Invalid argument type for thkEnable. Need a boolean or no argument, got " .. type(bool) .. ".")
        return
    end

    turretHotkeys.enabled = bool
end

function thkKey(key)
    if (type(key) ~= "string") then
        ba.warning("Invalid argument type for thkKey. Need a string, got " .. type(key) .. ".")
        return
    end

    turretHotkeys.key = key
end

function thkShip(...)
    local names = { ... }

    for i = 1, #names do
        if (type(names[i]) ~= "string") then
            ba.warning("Invalid argument type for thkShip. Need a string, got " .. type(names[i]) .. ".")
            return
        end

        local ship = mn.Ships[names[i]]

        if (not ship:isValid()) then
            ba.warning("No ship with name \"" .. names[i] .. "\" could be found in the current mission.")
            return
        end

        turretHotkeys:addShip(ship)
    end
end

function thkAdd(index, name)
    if (type(index) ~= "number") then
    if (type(index) == "string") then
    local temp = turretHotkeys:findIndex(index)
    if (index == nil) then
    ba.warning("Ship name \"" .. index .. "\" not found in list of registered ship names.")
    return
    end
    index = temp
    else
        ba.warning("Invalid first argument (index) type for thkAdd. Need a number or string, got " .. type(index) .. ".")
        return
    end
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument (subsys name) type for thkAdd. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:addSubsystem(index, name)
end

function thkRemove(index, name)
    if (type(index) ~= "number") then
    if (type(index) == "string") then
    local temp = turretHotkeys:findIndex(index)
    if (temp == nil) then
    ba.warning("Ship name \"" .. index .. "\" not found in list of registered ship names.")
    return
    end
    index = temp
    else
        ba.warning("Invalid first argument (index) type for thkRemove. Need a number or string, got " .. type(index) .. ".")
        return
    end
    end

    if (type(name) ~= "string") then
        ba.warning("Invalid second argument (subsys name) type for thkRemove. Need a string, got " .. type(name) .. ".")
        return
    end

    if (not turretHotkeys:isValidIndex(index)) then
        ba.warning("Invalid ship index \"" .. tostring(index) .. "\"!")
        return
    end

    turretHotkeys:removeSubsystem(index, name)
end

function thkClear()
    turretHotkeys.subsystems = {}
end
]

$State: GS_STATE_GAME_PLAY
$On Key Pressed:
[
turretHotkeys:keyPressed(hv.Key)
]

$State: GS_STATE_GAME_PLAY
$On Frame:
[
turretHotkeys:onFrame()
]

#End

EDIT2: Couple more bugfixes in the previous version of the script.