Most people here are familiar with Capship Command script from BP. It allows control over Karuna's turrets, draws brackets over their targets and comes with a handy rangefinder for long ships. The problem is, it only works for Karuna and, in R2, Custos.
I've wanted to adopt this script for other mods, but found it impractical, difficult and uncomfortable. So, I've asked my father, who's a Delphi programmer (He's not a member of this community, but he usually goes by "Tygrys" (Polish for Tiger), so let's call him that) if he could take a look at this script and modify it. Turned out he didn't knew LUA nor FS-specific scripting, but he did it anyway.
I've helped him with some FS-specific parts, explaining how things works, and handled testing the whole thing. Anyway, here's the result:
Credits:
The_E: The original CSC script. Without him, I wouln't have even thought it's possible.
Tygrys: All modifications to the script. Almost all bits of code that weren't originally there were done by him.
m!m: Final cleanup and some small changes to the code.
Dragon: Testing and teaching Tygrys how FSO works. And the idea.
Usage:
At the very beggining of the script, you define the keys you want it to use.
Just edit the part after "ui_keyToggleFunctions". Defaults are arrow keys and 0.
Up-down cycles turret groups, left-right cycles modes.
Available modes are:
"Auto"-AI handles targetting.
"Track"-Turret fires at the current target.
"Lock"-Turret fires on a target locked by pressing "0" (by default).
"Offline"-Turret doesn't fire.
About 2/3 way down the script, you'll encouncer a section marked with "--config stuff start" comment. That's were you define mod-specific stuff.
There are comments in that section explaining what to do.
Near the end of the script, there are two lines, marked with a comment:
--The Shieldman and Betty script overrides
ShieldmanOverride = true
BettyOverride = true
The first one overrides shield manager script, the second one overrides the Betty (CAWS) script.
Set them to "false" if you don't want them to be overriden. Note, if you chose to set ShieldmanOverride to false, you must edit the key assignments, as they will clash (Shield Manager also uses arrow keys). Also, don't touch two similar lines at the very end of the script, which aren't marked with a comment. Those always need to be false.
The example configuration should be capable of acting as a drop-in replacement for WiH CSC script. I've tested it with The Blade Itself, among other missions.
Now, installation:
Copy this into csc-sct.tbm :
;The CSC script. No longer messy and unreadable. Allows effective control over capital ship turrets.
#Conditional Hooks
$Application: FS2_Open
$On Key Pressed: [[csc_KeyPressed.lua]]
$On Key Released: [[csc_KeyReleased.lua]]
$On Game Init:
[
ui_keyToggleFunctions = {}
InputStates = {}
ui_keyToggleFunctions["Up Arrow"] = function(val) InputStates.up = val end
ui_keyToggleFunctions["Down Arrow"] = function(val) InputStates.down = val end
ui_keyToggleFunctions["Left Arrow"] = function(val) InputStates.left = val end
ui_keyToggleFunctions["Right Arrow"] = function(val) InputStates.right = val end
ui_keyToggleFunctions["0"] = function(val) InputStates.zero = val end
function getTargetName(target)
local targetname
if target then
local targetship = mn.getObjectFromSignature(target:getSignature())
if targetship:isValid() then
if targetship:getBreedName() == "Ship" then
targetname = targetship.Name
end
else
targetname = "None"
end
else
targetname = "None"
end
return targetname
end
function getTargetClass(target)
local targetname
if target then
local targetship = mn.getObjectFromSignature(target:getSignature())
if targetship:isValid() then
if targetship:getBreedName() == "Ship" then
targetname = targetship.Class.Type.Name
end
else
targetname = "None"
end
else
targetname = "None"
end
return targetname
end
function setTurretColor(weapon)
gr.setColor(csc.turretColors[weapon][1], csc.turretColors[weapon][2], csc.turretColors[weapon][3])
end
function drawTurretUI()
local sel = csc.shipdef[csc.shipIdx].turretControl[csc.acnum]
local weapon
gr.drawString("", 5, gr.getScreenHeight() * 0.6)
for i = 1, #csc.shipdef[csc.shipIdx].turretControl, 1 do
weapon = csc.shipdef[csc.shipIdx].turretControl[i]
ba.print("CSC: ui: " .. weapon)
if sel == weapon then
gr.setColor(255, 255, 255)
else
setTurretColor(weapon)
end
gr.drawString(csc.turretDisplay[weapon] .. csc.turretMode[weapon][csc.turretStatus[weapon]])
setTurretColor(weapon)
for i = 1, #csc.turretGroup[weapon], 1 do
local targetname = getTargetName(csc.turretGroup[weapon][i].Target)
if not targetname then targetname = "None" end
if weapon == "pd" then
gr.drawString(" PD Turret: " .. targetname)
else
gr.drawString(" " .. csc.turretGroup[weapon][i].Name .. ": " .. targetname)
end
end
end
gr.setColor(255, 255, 255)
local targetname = getTargetName(csc.CurrentTarget)
local classname = getTargetClass(csc.CurrentTarget)
if not classname then classname = "None" end
if not targetname then targetname = "Invalid" end
gr.drawString("Current Target: " .. targetname .. " Class: " .. classname)
end
function fillturretlist(ship)
local turretnum = 1
ba.print("CSC: Entered fillturretlist\n")
csc.turrets = {}
for i = 1, 60, 1 do
if i < 10 then
turretname = "turret0" .. i
else
turretname = "turret" .. i
end
ba.print("CSC: Looking for turret: " .. turretname .. "\n")
if ship[turretname]:isValid() then
csc.turrets[turretnum] = ship[turretname]
ba.print("CSC: Found Turret.\n")
turretnum = turretnum + 1
end
end
end
function groupTurrets()
ba.print("CSC: Entered GroupTurrets\n")
for i = 1, #csc.turrets, 1 do
if csc.turrets[i].PrimaryBanks[1].WeaponClass:isValid() or csc.turrets[i].SecondaryBanks[1].WeaponClass:isValid() then
for j = 1, #csc.weapondef, 1 do
if string.find(csc.turrets[i].PrimaryBanks[1].WeaponClass.Name, csc.weapondef[j].weapon) then
csc.turretGroup[csc.weapondef[j].turret][#csc.turretGroup[csc.weapondef[j].turret] + 1] = csc.turrets[i]
ba.print("CSC: Found " .. csc.weapondef[j].weapon .. ". Turret Number: " .. i .. " weaponnum: " .. #csc.turretGroup[csc.weapondef[j].turret] .. "\n")
end
end
end
end
csc.initComplete = true
end
function drawTargetBraces(weapon)
setTurretColor(weapon)
for i = 1, #csc.turretGroup[weapon], 1 do
target = csc.turretGroup[weapon][i].Target
if target:isValid() then
gr.drawTargetingBrackets(target, true, csc.turretColors[weapon][4])
end
end
end
function doInput()
MissionTime = mn.getMissionTime()
if OldMissionTime ~= MissionTime and MissionTime ~= 0 then
if InputStates.up and not csc.inputstates.keyup then
csc.acnum = csc.acnum - 1
if csc.acnum == 0 then
csc.acnum = #csc.shipdef[csc.shipIdx].turretControl
end
csc.inputstates.keyup = true
elseif not InputStates.up then
csc.inputstates.keyup = false
end
if InputStates.down and not csc.inputstates.keydown then
csc.acnum = csc.acnum + 1
if csc.acnum == #csc.shipdef[csc.shipIdx].turretControl + 1 then
csc.acnum = 1
end
csc.inputstates.keydown = true
elseif not InputStates.down then
csc.inputstates.keydown = false
end
local weapon = csc.shipdef[csc.shipIdx].turretControl[csc.acnum]
if InputStates.left and not csc.inputstates.keyleft then
if csc.turretStatus[weapon] <= 1 then
csc.turretStatus[weapon] = #csc.turretMode[weapon]
else
csc.turretStatus[weapon] = csc.turretStatus[weapon] - 1
end
csc.inputstates.keyleft = true
elseif not InputStates.left then
csc.inputstates.keyleft = false
end
if InputStates.right and not csc.inputstates.keyright then
if csc.turretStatus[weapon] >= #csc.turretMode[weapon] then
csc.turretStatus[weapon] = 1
else
csc.turretStatus[weapon] = csc.turretStatus[weapon] + 1
end
csc.inputstates.keyright = true
elseif not InputStates.right then
csc.inputstates.keyright = false
end
if InputStates.zero or io.isMouseButtonDown(MOUSE_LEFT_BUTTON) then
csc.CurrentTarget = csc.plrship.Target
csc.inputstates.zero = true
elseif not InputStates.zero or not io.isMouseButtonDown(MOUSE_LEFT_BUTTON) then
csc.inputstates.zero = false
end
OldMissionTime = MissionTime
end
end
function doTurretMode()
local weapon
for j = 1, #csc.shipdef[csc.shipIdx].turretControl, 1 do
weapon = csc.shipdef[csc.shipIdx].turretControl[j]
if csc.turretStatus[weapon] == 1 then
for i = 1, #csc.turretGroup[weapon] do
csc.turretGroup[weapon][i]:targetingOverride(false)
end
elseif csc.turretStatus[weapon] == 2 then
for i = 1, #csc.turretGroup[weapon] do
csc.turretGroup[weapon][i].Target = csc.plrship.Target
end
elseif csc.turretStatus[weapon] == 3 then
if csc.CurrentTarget then
if csc.CurrentTarget:isValid() then
for i = 1, #csc.turretGroup[weapon] do
csc.turretGroup[weapon][i].Target = csc.CurrentTarget
end
end
end
elseif csc.turretStatus[weapon] == 4 then
for i = 1, #csc.turretGroup[weapon] do
csc.turretGroup[weapon][i]:targetingOverride(true)
end
end
end
end
function drawRangeDisplay()
local turposCount = #csc.shipdef[csc.shipIdx].turretRange
if turposCount > 0 then
csc.range = nil
csc.range = {}
csc.range.Shipname = {}
csc.range.Range = {}
local plrship = hv.Player
local turpos = {}
for j = 1, turposCount, 1 do
turpos[j] = (plrship.Position + plrship[csc.shipdef[csc.shipIdx].turretRange[j]].Position)
end
items = 1
for i = 1, #mn.Ships do
local tempship = mn.Ships[i]
local class = getTargetClass(tempship)
if class ~= "Fighter" and class ~= "Bomber" then
if tempship ~= csc.plrship then
local temp = tempship.Position
local tempsum = 0
for j = 1, turposCount, 1 do
tempsum = tempsum + math.sqrt( (temp[1] - turpos[j][1])^2 + (temp[2] - turpos[j][2])^2 + (temp[3] - turpos[j][3])^2 )
end
csc.range.Shipname[items] = tempship.Name
csc.range.Range[items] = math.floor( tempsum / turposCount)
items = items + 1
end
end
end
gr.setColor(255,255,255)
gr.drawString("Range:", gr.getScreenWidth() * 0.17, gr.getScreenHeight() * 0.78)
for i = 1, #csc.range.Shipname do
if csc.range.Range[i] < 1000 then
local c = math.floor((csc.range.Range[i] / 4)) + 5
gr.setColor(255, c, c)
gr.drawString(" " .. csc.range.Shipname[i] .. ": " .. csc.range.Range[i])
gr.setColor(255, 255, 255)
else
gr.drawString(" " .. csc.range.Shipname[i] .. ": " .. csc.range.Range[i])
end
end
end
end
function turretInit(weapon, display, r, g, b, p)
csc.turretGroup[weapon] = {}
csc.turretMode[weapon] = {"Automatic", "Track", "Lock", "Offline"}
csc.turretStatus[weapon] = 1
csc.turretColors[weapon] = {r, g, b, p}
csc.turretDisplay[weapon] = display
ba.print("CSC: Turret init " .. weapon .. "\n")
end;
function weaponInit(weapon, turret)
local i = #csc.weapondef + 1
csc.weapondef[i] = {}
csc.weapondef[i].weapon = weapon
csc.weapondef[i].turret = turret
ba.print("CSC: Weapon init " .. weapon .. "\n")
end
function defineTurrets()
csc.turretGroup = {}
csc.turretStatus = {}
csc.turretColors = {}
csc.turretMode = {}
csc.turretDisplay = {}
-- config stuff start
-- turret group, UI display, R, G, B, Brace padding
turretInit("pd", "point defense turrets: ", 61, 93, 255, 8)
turretInit("flak", "flak turrets: ", 61, 93, 255, 8)
turretInit("md", "mass drivers: ", 255, 93, 48, 11)
turretInit("gauss", "gauss cannons: ", 255, 187, 53, 14)
turretInit("torpedo", "apocalypse torpedoes: ", 255, 255, 53, 17)
turretInit("missile", "missile launchers: ", 255, 255, 53, 17)
turretInit("gattler", "gattler: ", 255, 187, 53, 14)
turretInit("rapier", "rapier: ", 255, 187, 53, 14)
end
function defineWeapons()
csc.weapondef = {}
--weaponClassName -> turret group
weaponInit("Point Defense Turret", "pd")
weaponInit("PD Turret#Player", "pd")
weaponInit("Burst Flak", "flak")
weaponInit("Mass Driver", "md")
weaponInit("Gauss Cannon", "gauss")
weaponInit("Apocalypse", "torpedo")
weaponInit("Warhammer", "missile")
weaponInit("Gattler", "gattler")
weaponInit("Rapier", "rapier")
end
function defineShips()
csc.shipdef = {}
local
--Karuna
--Entry number - don't forget to change while copypasting this one
i = 1
csc.shipdef[i] = {}
csc.shipdef[i].shipClassName = "UEFg Karuna#Player"
--turrets showing target braces
csc.shipdef[i].turretTargets={"pd", "flak", "md", "gauss", "torpedo"}
--turrets aviable in UI
csc.shipdef[i].turretControl={"md", "gauss", "torpedo"}
--turrets to calculate target range
csc.shipdef[i].turretRange={"turret01", "turret02", "turret03", "turret04"}
-- possible turret weapon groups:
-- {"pd", "flak", "md", "gauss", "torpedo", "gattler", "missile"}
--Custos
i = 2
csc.shipdef[i] = {}
csc.shipdef[i].shipClassName = "UEC Custos#Player"
csc.shipdef[i].turretTargets={"pd", "missile"}
csc.shipdef[i].turretControl={"pd", "missile"}
csc.shipdef[i].turretRange={}
end
-- config stuff end
function cscInit()
csc = {}
if not csc.initComplete then
defineShips()
defineWeapons()
ba.print("CSC: init \n")
if not csc.plrship then
csc.plrship = hv.Player
csc.disable = true
if csc.plrship:getBreedName() == "Ship" then
csc.shipIdx = 0
for i = 1, #csc.shipdef, 1 do
if csc.plrship.Class.Name == csc.shipdef[i].shipClassName then
csc.shipIdx = i
csc.disable = false
ba.print("CSC: Ship found " .. csc.shipdef[i].shipClassName)
end
end
end
if not csc.disable and not csc.turrets then
defineTurrets()
csc.active = {"*", " ", " "}
csc.acnum = 1
csc.inputstates = {}
inputlocked = false
fillturretlist(csc.plrship)
groupTurrets()
--The Shieldman and Betty script overrides
ShieldmanOverride = true
BettyOverride = true
end
end
end
end
]
$On Gameplay Start:
[
if (csc) then
ba.warning("There was already a CSC table present when initing the CSC script!")
end
cscInit()
]
$State: GS_STATE_GAME_PLAY
$On Frame:
[
cscMissionTime = mn.getMissionTime()
if cscMissionTime ~= 0 then
if csc then
if not csc.disable and csc.initComplete then
doInput()
doTurretMode()
for j = 1, #csc.shipdef[csc.shipIdx].turretTargets, 1 do
drawTargetBraces(csc.shipdef[csc.shipIdx].turretTargets[j])
end
end
end
end
]
$On HUD Draw:
[
if csc then
if not csc.disable and csc.initComplete then
drawRangeDisplay()
drawTurretUI()
end
end
]
$On Mission End:
[
csc = nil
ShieldmanOverride = false
BettyOverride = false
]
#End
This goes to csc_KeyPressed.lua , in data\scripts folder. Note that the name might be case-sensitive:
:
local key = hv.Key
local func = ui_keyToggleFunctions[key]
if func then
func(true)
end
This goes to csc_KeyReleased.lua , also in data\scripts.
local key = hv.Key
local func = ui_keyToggleFunctions[key]
if func then
func(false)
end
End notes:
This is an alpha release, tested by just one person on a few missions. There might be bugs, odd behavior and so on. It should run a bit faster than the original CSC script, but I can't guarantee that. It might work or not work in multiplayer, as I haven't tested it there either. Also not tested with beams, but it should work OK, as it doesn't tackle actual firing process, just targeting.
Another thing is, while this script is ready for use, it's far from comfortable. I would like to make it to use an external configuration file instead of doing everything in the script, but neither I nor Tygrys know how to do that. I though that it should be simple, but m!m told me it isn't. So, I hope that either somebody will come along with time and knownledge needed to do it, or that people will be OK with editing the script to configure their capships.
Anyway, the script is now free to use for all the HLP, in any mod, anytime. For anything with any sort of turrets.
Enjoy.