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:
#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.
#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.