Difference between revisions of "Script - Zoom"

From FreeSpace Wiki
Jump to: navigation, search
m (A bit of cleanup.)
(New Config options and FRED interface)
Line 1: Line 1:
 
This script enables you to zoom your view so precise aiming is easier.
 
This script enables you to zoom your view so precise aiming is easier.
 +
It includes a config file that specifies the values the script will use. It also features a FRED SEXP interface so A FREDer can force a zoom-in (or -out) as needed.
 +
For more documentation check out [http://www.hard-light.net/forums/index.php?topic=70411.0 this thread].
 
== Table Entry ==
 
== Table Entry ==
* To use create an file called '''zoom-sct.tbm''' in your date/tables directory and paste the following code into it:
+
* To use create an file called '''zoom-sct.tbm''' in your ''date/tables'' directory and paste the following code into it:
 
<pre>
 
<pre>
 
#Conditional Hooks
 
#Conditional Hooks
  
 
$Application: FS2_Open
 
$Application: FS2_Open
$On Mission Start:  
+
$On Game Init:  
 
[
 
[
-- Some things to tell the script to do something and some default values
+
-------------------------------------------------------------
runZoomScript = true
+
--------              utility functions              --------
zoomValue = 0.1
+
-------------------------------------------------------------
normalFOV = 0.75
+
function trim(s)
transitionTime = 2
+
  return (s:gsub("^%s*(.-)%s*$", "%1"))
zooming = false
+
end
defaultZoomSet = false
+
 
cameraSet = false
+
function isAllIdent(check)
 +
check = check:lower()
 +
if check == "**all**" then
 +
return true
 +
else
 +
return false
 +
end
 +
end
 +
 
 +
function indexOf(t,val)
 +
    for k,v in ipairs(t) do
 +
        if v == val then
 +
return k
 +
end
 +
    end
 +
end
 +
-------------------------------------------------------------
 +
---- defining functions used for parsing the config file ----
 +
-------------------------------------------------------------
 +
 
 +
-- gets the next line out of the file
 +
local function getNextLine(file)
 +
if file:isValid() then
 +
local line = file:read("*l")
 +
return line
 +
else
 +
ba.warning("Invalid file handle pased to readLine function")
 +
return nil
 +
end
 +
end
 +
 
 +
-- looks up the set function which is connected to the specified keyword
 +
local function getSetFunc(keyword)
 +
keyword = keyword:lower()
 +
for i,v in pairs(setTable) do
 +
index = i:lower()
 +
if index == keyword then
 +
return v
 +
end
 +
end
 +
return nil
 +
end
 +
 
 +
local function addAllowedShipClass(value)
 +
if isAllIdent(value) then
 +
allowedShips = {}
 +
allowedShips[1] = value
 +
return false
 +
end
 +
if allowedShips[1] == nil or not isAllIdent(allowedShips[1]) then
 +
 +
if indexOf(allowedShips,value) ~= nil then
 +
ba.warning("Ship " .. value .. " was specified more than once. Skipping...")
 +
return false
 +
end
 +
 +
local ship = tb.ShipClasses[value]
 +
if ship ~= nil and ship:isValid() then
 +
table.insert(allowedShips, value)
 +
return true
 +
else
 +
ba.warning("Specified ship class '" .. value .. "' does not exist.")
 +
return false
 +
end
 +
end
 +
end
 +
 
 +
local function addRestrictedShipClass(value)
 +
if isAllIdent(value) then
 +
restrictedShips = {}
 +
restrictedShips[1] = value
 +
return
 +
end
 +
if restrictedShips[1] == nil or not isAllIdent(restrictedShips[1]) then
 +
 +
if indexOf(restrictedShips,value) ~= nil then
 +
ba.warning("Ship " .. value .. " was specified more than once. Skipping...")
 +
return false
 +
end
 +
 +
local ship = tb.ShipClasses[value]
 +
if ship ~= nil and ship:isValid() then
 +
table.insert(restrictedShips, value)
 +
return true
 +
else
 +
ba.warning("Specified ship class '" .. value .. "' does not exist.")
 +
return false
 +
end
 +
end
 +
end
 +
 
 +
local function addAllowedWeaponClass(value)
 +
if isAllIdent(value) then
 +
allowedWeapons = {}
 +
allowedWeapons[1] = value
 +
return false
 +
end
 +
 +
if allowedWeapons[1] == nil or not isAllIdent(allowedWeapons[1]) then
 +
 +
if indexOf(allowedWeapons,value) ~= nil then
 +
ba.warning("Weapon " .. value .. " was specified more than once. Skipping...")
 +
return false
 +
end
 +
 +
local weapon = tb.WeaponClasses[value]
 +
if weapon ~= nil and weapon:isValid() then
 +
table.insert(allowedWeapons, value)
 +
return true
 +
else
 +
ba.warning("Specified weapon class '" .. value .. "' does not exist.")
 +
return false
 +
end
 +
end
 +
end
 +
 
 +
local function addRestrictedWeaponClass(value)
 +
if isAllIdent(value) then
 +
restrictedWeapons = {}
 +
restrictedWeapons[1] = value
 +
return false
 +
end
 +
if restrictedWeapons[1] == nil or not isAllIdent(restrictedWeapons[1]) then
 +
 +
if indexOf(restrictedWeapons,value) ~= nil then
 +
ba.warning("Weapon " .. value .. " was specified more than once. Skipping...")
 +
return false
 +
end
 +
 +
local weapon = tb.WeaponClasses[value]
 +
if weapon ~= nil and weapon:isValid() then
 +
table.insert(restrictedWeapons, value)
 +
return true
 +
else
 +
ba.warning("Specified weapon class '" .. value .. "' does not exist.")
 +
return false
 +
end
 +
end
 +
end
 +
 
 +
local function parseLine(line)
 +
if line == nil or line == "" then -- Check if this line needs to be parsed
 +
return
 +
end
 +
 +
local comm_s, comm_e = line:find("--",1,true)
 +
if comm_s ~= nil then
 +
line = line:sub(1,(comm_s - 1))
 +
end
 +
if line == "" then -- was the line fully commented away?
 +
return -- Nothing to be done...
 +
end
 +
local key_s, key_e = line:find(":",1,true)
 +
local val_s, val_e = line:find("%a",key_e)
 +
 +
if key_s == nil then -- malformatted line
 +
ba.warning("Malformatted line: " .. line .. "\nSkipping...")
 +
return
 +
end
 +
 +
local keyword = line:sub(1,(key_e - 1))
 +
 +
if val_s == nil then -- Maybe we_ve got a digit value
 +
val_s, val_e = line:find("%d",key_e) -- search for them
 +
if val_s == nil  then
 +
-- We have no value
 +
ba.warning("Keyword '" .. keyword .. "' hasn't specified a value. Skipping...")
 +
return
 +
end
 +
end
 +
 +
keyword = keyword:lower() -- make the keyword lowercase
 +
 +
local val = line:sub(val_s, line:len())
 +
 +
if val ~= nil then
 +
val = trim(val)
 +
local setFunc = getSetFunc(keyword)
 +
if setFunc == nil then
 +
ba.warning("Unknown keyword: " .. keyword)
 +
return
 +
end
 +
setFunc(val)
 +
else
 +
return
 +
end
 +
end
 +
 
 +
local function initSetTable()
 +
setTable = {}
 +
setTable["Zoom Factor"]=
 +
function(value)
 +
local val = tonumber(value)
 +
if val == nil then
 +
return
 +
ba.warning("Non numeric value given to 'Zoom Factor'. Skipping...")
 +
end
 +
zoomValue = val
 +
end
 +
 +
setTable["Transition Time"]=
 +
function(value)
 +
local val = tonumber(value)
 +
if val == nil then
 +
ba.warning("Non numeric value given to 'Transition Time'. Skipping...")
 +
return
 +
end
 +
transitionTime = val
 +
end
 +
 +
setTable["Sensitivity"]=
 +
function(value)
 +
local val = tonumber(value)
 +
if val == nil then
 +
ba.warning("Non numeric value given to 'Sensitivity'. Skipping...")
 +
return
 +
end
 +
sensitivity = val
 +
end
 +
 +
setTable["Allowed Ship Class"]=addAllowedShipClass
 +
 +
setTable["Restricted Ship Class"]=addRestrictedShipClass
 +
 +
setTable["Allowed Weapon Class"]=addWeaponClass
 +
 +
setTable["Restricted Weapon Class"]=addRestrictedWeaponClass
 +
 +
setTable["Linked Zoom"]=
 +
function(value)
 +
local yesKey = "yes"
 +
value = value:lower()
 +
if value == yesKey then
 +
linkedZoom = true
 +
else
 +
linkedZoom = false
 +
end
 +
end
 +
 +
setTable["Key"]=
 +
function(key)
 +
if key == nil then
 +
ba.warning("Invalid value given for 'Key'. Skipping...")
 +
return
 +
end
 +
zoomKey=key
 +
end
 +
 +
setTable["Zoom Lock Key"]=
 +
function(key)
 +
if key == nil then
 +
ba.warning("Invalid value given for 'Zoom Lock Key'. Skipping...")
 +
return
 +
end
 +
zoomLockKey=key
 +
end
 +
end
  
-- Setting the values to be used in the $On Frame: hook
+
local function readConfig()
currentProgr = 0
+
if cf.fileExists(configFile) then
zoomEndProgress = 100
+
local config = cf.openFile(configFile)
 +
if config:isValid() then
 +
initSetTable()
 +
local parse = true
 +
while parse do
 +
local line = getNextLine(config)
 +
if line == nil then
 +
parse = false
 +
break
 +
end
 +
parseLine(line) -- parse the found line
 +
end
 +
else
 +
ba.warning("Handle to config file is invalid. Is the file readable?")
 +
end
 +
else
 +
ba.warning("No config file found. Returning to default")
 +
end
 +
end
  
zoomingIn = false
 
zoomedIn = false
 
  
zoomingOut = false
+
-------------------------------------------------------------
zoomedOut = true
+
------these functions will be use for the FRED interface-----
 +
-------------------------------------------------------------
  
-- Constants for drawing the progress
+
function zoom_zoomIn()
offset_x = 30
+
if defaultZoomSet and cam:isValid() then
offset_y = 70
+
cam:setFOV(normalFOV) -- Resetting the FOV in case we have not zoomed out completely
heigth = 8
+
end
width = 80
+
fredOverride = true
 +
zoomIn()
 +
end
  
stepWidth = width / 100
+
function zoom_zoomOut()
 +
fredOverride = false
 +
zoomOut()
 +
end
 +
 
 +
function zoom_alS(ship)
 +
if addAllowedShipClass(ship) then
 +
table.insert(tempAllowedShip, ship)
 +
end
 +
end
  
-- Settings for the sensitivity
+
function zoom_alW(weapon)
sensitivity = 0.25
+
if addAllowedWeaponClass(weapon) then
 +
table.insert(tempAllowedWeapon, weapon)
 +
end
 +
end
  
-- The camera which is used for zooming (is initialized in the first $Key Pressed: hook)
+
function zoom_reS(ship)
cam = nil
+
if addRestrictedShipClass(ship) then
 +
table.insert(tempRestrictedShip, ship)
 +
end
 +
end
 +
 
 +
function zoom_reW(weapon)
 +
if addRestrictedWeaponClass(weapon) then
 +
table.insert(tempRestrictedWeapon, weapon)
 +
end
 +
end
 +
 
 +
 
 +
-------------------------------------------------------------
 +
------      defining functions used in the script      ------
 +
-------------------------------------------------------------
 +
function removeTemps()
 +
for i,v in ipairs(tempAllowedShip) do
 +
index = indexOf(allowedShips,v)
 +
if index ~= nil then
 +
allowedShips[index]=nil
 +
end
 +
end
 +
 +
for i,v in ipairs(tempRestrictedShip) do
 +
index = indexOf(restrictedShips,v)
 +
if index ~= nil then
 +
restrictedShips[index]=nil
 +
end
 +
end
 +
 +
for i,v in ipairs(tempAllowedWeapon) do
 +
index = indexOf(allowedWeapons,v)
 +
if index ~= nil then
 +
allowedWeapons[index]=nil
 +
end
 +
end
 +
 +
for i,v in ipairs(tempRestrictedWeapon) do
 +
index = indexOf(restrictedWeapons,v)
 +
if index ~= nil then
 +
restrictedWeapons[index]=nil
 +
end
 +
end
 +
end
 +
 
 +
local function checkTables(allTbl,restrTbl, checkHandle)
 +
if checkHandle == nil then
 +
return false
 +
end
 +
if allTbl ~= nil and restrTbl ~= nil then
 +
if #allTbl > 0 then
 +
if isAllIdent(allTbl[1]) then
 +
if #restrTbl > 0 then
 +
if isAllIdent(restrTbl[1]) then
 +
return true
 +
end
 +
end
 +
for i,v in ipairs(restrTbl) do
 +
if checkHandle.Name == v then
 +
return false
 +
end
 +
end
 +
return true
 +
elseif isAllIdent(restrTbl[1]) then
 +
if #allTbl > 0 then
 +
if isAllIdent(allTbl[1]) then
 +
return true
 +
end
 +
end
 +
for i,v in ipairs(allTbl) do
 +
if checkHandle.Name == v then
 +
return true
 +
end
 +
end
 +
return false
 +
else
 +
local allowed = false
 +
for i,v in ipairs(allTbl) do
 +
if checkHandle.Name == v then
 +
allowed = true
 +
break
 +
end
 +
end
 +
for i,v in ipairs(restrTbl) do
 +
if checkHandle.Name == v then
 +
allowed = false
 +
break
 +
end
 +
end
 +
return allowed
 +
end
 +
end
 +
end
 +
return true
 +
end
 +
 
 +
function isAllowedShip(shipClass)
 +
return checkTables(allowedShips,restrictedShips,shipClass)
 +
end
 +
 
 +
function isAllowedWeapon(weaponClass)
 +
return checkTables(allowedWeapons,restrictedWeapons,weaponClass)
 +
end
  
 
function getProgressString()  
 
function getProgressString()  
Line 59: Line 450:
 
function handleControls()
 
function handleControls()
 
if zoomingIn or zoomedIn then
 
if zoomingIn or zoomedIn then
local ci = ba.getControlInfo()
+
if currentProgr > 0 then
ci.Pitch = ci.Pitch * sensitivity
+
local tempSensValue = sensitivity * 100 / currentProgr
ci.Heading = ci.Heading * sensitivity
+
ci.Bank = ci.Bank * sensitivity
+
if tempSensValue <= 1 then
 +
local ci = ba.getControlInfo()
 +
ci.Pitch = ci.Pitch * tempSensValue
 +
ci.Heading = ci.Heading * tempSensValue
 +
ci.Bank = ci.Bank * tempSensValue
 +
end
 +
end
 +
end
 +
end
 +
 
 +
function isAllowedToZoom()
 +
local allowedShip = isAllowedShip(hv.Player.Class)
 +
 +
local allowedWeapon = true
 +
local primWeaponBank = hv.Player.PrimaryBanks
 +
if not linkedZoom and primWeaponBank.Linked then
 +
allowedWeapon = false
 +
else
 +
for i=0, #primWeaponBank do
 +
local v = primWeaponBank[i]
 +
if v.Armed then
 +
if isAllowedWeapon(tb.WeaponClasses[v.WeaponClass.Name]) then
 +
allowedWeapon = true
 +
break
 +
else
 +
allowedWeapon = false
 +
end
 +
end
 +
end
 +
if allowedWeapon then
 +
local secWeaponBank = hv.Player.SecondaryBanks
 +
for i=0, #secWeaponBank do
 +
local v = secWeaponBank[i]
 +
if v.Armed then
 +
if isAllowedWeapon(tb.WeaponClasses[v.WeaponClass.Name]) then
 +
allowedWeapon = true
 +
break
 +
else
 +
allowedWeapon = false
 +
end
 +
end
 +
end
 +
end
 +
end
 +
return allowedShip and allowedWeapon
 +
end
 +
 
 +
function zoomIn()
 +
if not cameraSet then
 +
if #gr.Cameras > 0 then
 +
cam = gr.Cameras[1]
 +
end
 +
end
 +
if not zooming then
 +
if hv.Player:isValid() then
 +
if cam:isValid() then
 +
 +
if isAllowedToZoom() then
 +
if currentProgr > 0 and currentProgr < 100 then
 +
stepWidth = width / currentProgr -- Setting the stepWidth in case the zoom may not have been completed
 +
end
 +
 +
zoomEndProgress = 100
 +
 +
zooming = true
 +
 +
zoomingIn = true
 +
zoomedIn = false
 +
zoomedOut = false
 +
zoomingOut = false
 +
 +
if not defaultZoomSet then
 +
normalFOV = cam.FOV
 +
defaultZoomSet = true
 +
end
 +
zoom = normalFOV * zoomValue
 +
 +
ba.setControlMode(LUA_FULL_CONTROLS)
 +
 +
cam:setFOV(zoom,transitionTime,transitionTime / 2,transitionTime / 4)
 +
end
 +
end
 +
end
 +
end
 +
end
 +
 
 +
function zoomOut()
 +
if zooming and not zoomOutOverride then
 +
if hv.Player:isValid() then
 +
if cam:isValid() then
 +
 +
stepWidth = width / 100
 +
 +
zoomingIn = false
 +
zoomingOut = true
 +
zoomedIn = false
 +
zoomedOut = false
 +
 +
if currentProgr > 0 then
 +
zoomEndProgress = currentProgr -- Save the progress we made as we begin to zoom back
 +
end
 +
 +
cam:setFOV(normalFOV,transitionTime,transitionTime / 2,transitionTime / 4)
 +
 +
ba.setControlMode(NORMAL_CONTROLS)
 +
 +
fredOverride = false
 +
end
 +
end
 
end
 
end
 
end
 
end
Line 77: Line 576:
 
local progressLineOffset_x = realOffset_x + stepWidth * currentProgr
 
local progressLineOffset_x = realOffset_x + stepWidth * currentProgr
 
gr.drawLine(progressLineOffset_x, offset_y, progressLineOffset_x, offset_y + heigth)
 
gr.drawLine(progressLineOffset_x, offset_y, progressLineOffset_x, offset_y + heigth)
 +
 +
if not isAllowedToZoom() and not zoomOutOverride then
 +
if zoomingIn or zoomedIn then
 +
zoomOut()
 +
zoomOutOverride = true
 +
end
 +
end
 
 
 
local thisFrameProg = (frameTime / transitionTime) * zoomEndProgress
 
local thisFrameProg = (frameTime / transitionTime) * zoomEndProgress
Line 83: Line 589:
 
if currentProgr >= 100 then
 
if currentProgr >= 100 then
 
currentProgr = 100
 
currentProgr = 100
zoomingIn = false
+
zoomingIn = false
zoomedIn = true
+
zoomedIn = true
 
end
 
end
 
elseif zoomingOut then
 
elseif zoomingOut then
Line 90: Line 596:
 
if currentProgr <= 0 then
 
if currentProgr <= 0 then
 
currentProgr = 0
 
currentProgr = 0
 +
zoomOutOverride = false
 
zoomedOut = true
 
zoomedOut = true
 
zoomingOut = false
 
zoomingOut = false
Line 95: Line 602:
 
end
 
end
 
end
 
end
 +
end
 +
 +
local function init()
 +
if useConfig then
 +
readConfig() -- read the config file if exists
 +
end
 +
end
 +
 +
local function initVars()
 +
-- Some things to tell the script to do something and some default values
 +
runZoomScript = false
 +
useConfig = true
 +
 +
zoomValue = 0.1
 +
normalFOV = 0.75
 +
transitionTime = 2
 +
zooming = false
 +
defaultZoomSet = false
 +
cameraSet = false
 +
configFile = "data/config/zoom_config.cfg"
 +
zoomOutOverride = false
 +
linkedZoom = true
 +
zoomKey = "d"
 +
zoomLockKey = nil
 +
 +
fredOverride=false
 +
 +
lockedZoom = false
 +
 +
-- Setting the values to be used in the $On Frame: hook
 +
currentProgr = 0
 +
zoomEndProgress = 100
 +
 +
zoomingIn = false
 +
zoomedIn = false
 +
 +
zoomingOut = false
 +
zoomedOut = true
 +
 +
-- Constants for drawing the progress
 +
offset_x = 30
 +
offset_y = 70
 +
heigth = 8
 +
width = 80
 +
 +
stepWidth = width / 100
 +
 +
-- Settings for the sensitivity
 +
sensitivity = 0.25
 +
 +
-- The camera which is used for zooming (is initialized in the first $Key Pressed: hook)
 +
cam = nil
 +
 +
-- Tables that hold the allowe/restricted informations
 +
-- Ships
 +
allowedShips = {"**all**"}
 +
tempAllowedShip = {}
 +
 +
restrictedShips = {}
 +
tempRestrictedShip = {}
 +
 +
-- Weapons
 +
allowedWeapons = {"**all**"}
 +
tempAllowedWeapon = {}
 +
 +
restrictedWeapons = {}
 +
tempRestrictedWeapon = {}
 +
end
 +
 +
-- Initialize the global variables
 +
initVars()
 +
 +
-- Initialize the rest
 +
init()
 +
]
 +
 +
$Application: FS2_Open
 +
$On Mission Start:
 +
[
 +
if not runZoomScript then
 +
runZoomScript = true
 
end
 
end
 
]
 
]
Line 102: Line 690:
 
if runZoomScript then
 
if runZoomScript then
 
runZoomScript = false
 
runZoomScript = false
if defaultZoomSet then
+
if defaultZoomSet and cam:isValid() then
 
cam:setFOV(normalFOV) -- Resetting the FOV in case we have not zoomed out completely
 
cam:setFOV(normalFOV) -- Resetting the FOV in case we have not zoomed out completely
 
end
 
end
 +
removeTemps()
 
end
 
end
 
]
 
]
Line 119: Line 708:
 
]
 
]
  
$State: GS_STATE_GAME_PLAY
+
$On Key Pressed: [[zoom_KeyDn.lua]]
$KeyPress: d
+
$On Key Released: [[zoom_KeyUp.lua]]
$On Key Pressed:  
+
 
[
+
 
if runZoomScript then
+
#End
if not cameraSet then
+
</pre>
if #gr.Cameras > 0 then
+
 
cam = gr.Cameras[1]
+
Then create the file '''zoom_KeyDn.lua''' and '''zoom_KeyUp.lua''' in your ''data/scripts'' directory and paste the following into then:
end
+
 
end
+
'''zoom_KeyDn.lua:'''
if not zooming then
+
<pre>
plr = hv.Player
+
if runZoomScript and not fredOverride then
if plr:isValid() then
+
if hv.Key:lower() == zoomLockKey:lower() then
if cam:isValid() then
+
if lockedZoom then
+
lockedZoom = false
if currentProgr > 0 and currentProgr < 100 then
+
zoomOut()
stepWidth = width / currentProgr -- Setting the stepWidth in case the zoom may not have been completed
+
else
end
+
lockedZoom = true
+
zoomIn()
zoomEndProgress = 100
 
 
zooming = true
 
 
zoomingIn = true
 
zoomedIn = false
 
zoomedOut = false
 
zoomingOut = false
 
 
if not defaultZoomSet then
 
normalFOV = cam.FOV
 
defaultZoomSet = true
 
end
 
zoom = normalFOV * zoomValue
 
 
ba.setControlMode(LUA_FULL_CONTROLS)
 
 
cam:setFOV(zoom,transitionTime,transitionTime / 2,transitionTime / 4)
 
end
 
 
end
 
end
 +
elseif hv.Key:lower() == zoomKey:lower() then
 +
zoomIn()
 
end
 
end
 
end
 
end
]
+
</pre>
  
$On Key Released:
+
'''zoom_KeyUp.lua:'''
[
+
<pre>
if runZoomScript then  
+
if runZoomScript and not fredOverride then
if zooming then
+
if not lockedZoom then
plr = hv.Player
+
if hv.Key:lower() == zoomKey:lower() then
if plr:isValid() then
+
zoomOut()
if cam:isValid() then
 
 
stepWidth = width / 100
 
 
zoomingIn = false
 
zoomingOut = true
 
zoomedIn = false
 
zoomedOut = false
 
 
if currentProgr > 0 then
 
zoomEndProgress = currentProgr -- Save the progress we made as we begin to zoom back
 
end
 
 
cam:setFOV(normalFOV,transitionTime,transitionTime / 2,transitionTime / 4)
 
 
ba.setControlMode(NORMAL_CONTROLS)
 
end
 
 
end
 
end
 
end
 
end
 
end
 
end
]
+
</pre>
 +
 
 +
After this create a file named '''zoom_config.cfg''' in your ''data/config'' directory and paste the following text into it:
 +
<pre>
 +
-- Setting standart values
 +
Zoom Factor: 0.1
 +
Transition Time: 2
 +
Sensitivity: 0.25
 +
Linked Zoom: YES
 +
Key: d
 +
Zoom Lock Key: Alt-d
 +
 
 +
-- Setting ship class options
 +
Allowed Ship Class: **all**
  
#End
+
-- Setting weapon class options
 +
Allowed Weapon Class: **all**
 
</pre>
 
</pre>
  
 
[[Category:Scripting Examples|Zoom]]
 
[[Category:Scripting Examples|Zoom]]

Revision as of 13:46, 12 September 2010

This script enables you to zoom your view so precise aiming is easier. It includes a config file that specifies the values the script will use. It also features a FRED SEXP interface so A FREDer can force a zoom-in (or -out) as needed. For more documentation check out this thread.

Table Entry

  • To use create an file called zoom-sct.tbm in your date/tables directory and paste the following code into it:
#Conditional Hooks

$Application: FS2_Open
$On Game Init: 
[
-------------------------------------------------------------
--------              utility functions              --------
-------------------------------------------------------------
function trim(s)
  return (s:gsub("^%s*(.-)%s*$", "%1"))
end

function isAllIdent(check)
	check = check:lower()
	if check == "**all**" then
		return true
	else
		return false
	end
end

function indexOf(t,val)
    for k,v in ipairs(t) do 
        if v == val then 
			return k 
		end
    end
end
-------------------------------------------------------------
---- defining functions used for parsing the config file ----
-------------------------------------------------------------

-- gets the next line out of the file
local function getNextLine(file)
	if file:isValid() then
		local line = file:read("*l")
		return line
	else
		ba.warning("Invalid file handle pased to readLine function")
		return nil
	end
end

-- looks up the set function which is connected to the specified keyword
local function getSetFunc(keyword)
	keyword = keyword:lower()
	for i,v in pairs(setTable) do
		index = i:lower()
		if index == keyword then
			return v
		end
	end
	return nil
end

local function addAllowedShipClass(value)
	if isAllIdent(value) then
		allowedShips = {}
		allowedShips[1] = value
		return false
	end
	if allowedShips[1] == nil or not isAllIdent(allowedShips[1]) then
		
		if indexOf(allowedShips,value) ~= nil then
			ba.warning("Ship " .. value .. " was specified more than once. Skipping...")
			return false
		end
		
		local ship = tb.ShipClasses[value]
		if ship ~= nil and ship:isValid() then
			table.insert(allowedShips, value)
			return true
		else
			ba.warning("Specified ship class '" .. value .. "' does not exist.")
			return false
		end
	end
end

local function addRestrictedShipClass(value)
	if isAllIdent(value) then
		restrictedShips = {}
		restrictedShips[1] = value
		return
	end
	if restrictedShips[1] == nil or not isAllIdent(restrictedShips[1]) then
		
		if indexOf(restrictedShips,value) ~= nil then
			ba.warning("Ship " .. value .. " was specified more than once. Skipping...")
			return false
		end
		
		local ship = tb.ShipClasses[value]
		if ship ~= nil and ship:isValid() then
			table.insert(restrictedShips, value)
			return true
		else
			ba.warning("Specified ship class '" .. value .. "' does not exist.")
			return false
		end
	end
end

local function addAllowedWeaponClass(value)
	if isAllIdent(value) then
		allowedWeapons = {}
		allowedWeapons[1] = value
		return false
	end
	
	if allowedWeapons[1] == nil or not isAllIdent(allowedWeapons[1]) then
	
		if indexOf(allowedWeapons,value) ~= nil then
			ba.warning("Weapon " .. value .. " was specified more than once. Skipping...")
			return false
		end
	
		local weapon = tb.WeaponClasses[value]
		if weapon ~= nil and weapon:isValid() then
			table.insert(allowedWeapons, value)
			return true
		else
			ba.warning("Specified weapon class '" .. value .. "' does not exist.")
			return false
		end
	end
end

local function addRestrictedWeaponClass(value)
	if isAllIdent(value) then
		restrictedWeapons = {}
		restrictedWeapons[1] = value
		return false
	end
	if restrictedWeapons[1] == nil or not isAllIdent(restrictedWeapons[1]) then
		
		if indexOf(restrictedWeapons,value) ~= nil then
			ba.warning("Weapon " .. value .. " was specified more than once. Skipping...")
			return false
		end
		
		local weapon = tb.WeaponClasses[value]
		if weapon ~= nil and weapon:isValid() then
			table.insert(restrictedWeapons, value)
			return true
		else
			ba.warning("Specified weapon class '" .. value .. "' does not exist.")
			return false
		end
	end
end

local function parseLine(line)
	if line == nil or line == "" then -- Check if this line needs to be parsed
		return
	end
		
	local comm_s, comm_e = line:find("--",1,true) 
	if comm_s ~= nil then
		line = line:sub(1,(comm_s - 1))
	end
	if line == "" then -- was the line fully commented away?
		return -- Nothing to be done...
	end
	local key_s, key_e = line:find(":",1,true)
	local val_s, val_e = line:find("%a",key_e)
	
	if key_s == nil then -- malformatted line
		ba.warning("Malformatted line: " .. line .. "\nSkipping...")
		return
	end
	
	local keyword = line:sub(1,(key_e - 1))
	
	if val_s == nil then -- Maybe we_ve got a digit value
		val_s, val_e = line:find("%d",key_e) -- search for them
		if val_s == nil  then
			-- We have no value
			ba.warning("Keyword '" .. keyword .. "' hasn't specified a value. Skipping...")
			return
		end
	end
	
	keyword = keyword:lower() -- make the keyword lowercase
	
	local val = line:sub(val_s, line:len())
	
	if val ~= nil then
		val = trim(val)
		local setFunc = getSetFunc(keyword)
		if setFunc == nil then
			ba.warning("Unknown keyword: " .. keyword)
			return
		end
		setFunc(val)
	else
		return
	end
end

local function initSetTable()
	setTable = {}
	setTable["Zoom Factor"]=
	function(value) 
		local val = tonumber(value)
		if val == nil then
			return
			ba.warning("Non numeric value given to 'Zoom Factor'. Skipping...")
		end
		zoomValue = val
	end
	
	setTable["Transition Time"]=
	function(value)
		local val = tonumber(value)
		if val == nil then
			ba.warning("Non numeric value given to 'Transition Time'. Skipping...")
			return
		end
		transitionTime = val
	end
	
	setTable["Sensitivity"]=
	function(value)
		local val = tonumber(value)
		if val == nil then
			ba.warning("Non numeric value given to 'Sensitivity'. Skipping...")
			return
		end
		sensitivity = val
	end
	
	setTable["Allowed Ship Class"]=addAllowedShipClass
	
	setTable["Restricted Ship Class"]=addRestrictedShipClass
	
	setTable["Allowed Weapon Class"]=addWeaponClass
	
	setTable["Restricted Weapon Class"]=addRestrictedWeaponClass
	
	setTable["Linked Zoom"]=
	function(value)
		local yesKey = "yes"
		value = value:lower()
		if value == yesKey then
			linkedZoom = true
		else
			linkedZoom = false
		end
	end
	
	setTable["Key"]=
	function(key)
		if key == nil then
			ba.warning("Invalid value given for 'Key'. Skipping...")
			return
		end
		zoomKey=key
	end
	
	setTable["Zoom Lock Key"]=
	function(key)
		if key == nil then
			ba.warning("Invalid value given for 'Zoom Lock Key'. Skipping...")
			return
		end
		zoomLockKey=key
	end
end

local function readConfig()
	if cf.fileExists(configFile) then
		local config = cf.openFile(configFile)
		if config:isValid() then
			initSetTable()
			local parse = true
			while parse do
				local line = getNextLine(config)
				if line == nil then
					parse = false
					break
				end
				parseLine(line) -- parse the found line
			end
		else
			ba.warning("Handle to config file is invalid. Is the file readable?")
		end
	else
		ba.warning("No config file found. Returning to default")
	end
end


-------------------------------------------------------------
------these functions will be use for the FRED interface-----
-------------------------------------------------------------

function zoom_zoomIn()
	if defaultZoomSet and cam:isValid() then
		cam:setFOV(normalFOV) -- Resetting the FOV in case we have not zoomed out completely
	end
	fredOverride = true
	zoomIn()
end

function zoom_zoomOut()
	fredOverride = false
	zoomOut()
end

function zoom_alS(ship)
	if addAllowedShipClass(ship) then
		table.insert(tempAllowedShip, ship)
	end
end

function zoom_alW(weapon)
	if addAllowedWeaponClass(weapon) then
		table.insert(tempAllowedWeapon, weapon)
	end
end

function zoom_reS(ship)
	if addRestrictedShipClass(ship) then
		table.insert(tempRestrictedShip, ship)
	end
end

function zoom_reW(weapon)
	if addRestrictedWeaponClass(weapon) then
		table.insert(tempRestrictedWeapon, weapon)
	end
end


-------------------------------------------------------------
------      defining functions used in the script      ------
-------------------------------------------------------------
function removeTemps()
	for i,v in ipairs(tempAllowedShip) do
		index = indexOf(allowedShips,v)
		if index ~= nil then
			allowedShips[index]=nil
		end
	end
	
	for i,v in ipairs(tempRestrictedShip) do
		index = indexOf(restrictedShips,v)
		if index ~= nil then
			restrictedShips[index]=nil
		end
	end
	
	for i,v in ipairs(tempAllowedWeapon) do
		index = indexOf(allowedWeapons,v)
		if index ~= nil then
			allowedWeapons[index]=nil
		end
	end
	
	for i,v in ipairs(tempRestrictedWeapon) do
		index = indexOf(restrictedWeapons,v)
		if index ~= nil then
			restrictedWeapons[index]=nil
		end
	end
end

local function checkTables(allTbl,restrTbl, checkHandle)
	if checkHandle == nil then
		return false
	end
	if allTbl ~= nil and restrTbl ~= nil then
		if #allTbl > 0 then
			if isAllIdent(allTbl[1]) then
				if #restrTbl > 0 then
					if isAllIdent(restrTbl[1]) then
						return true
					end
				end
				for i,v in ipairs(restrTbl) do
				if checkHandle.Name == v then
						return false
					end
				end
				return true
			elseif isAllIdent(restrTbl[1]) then
				if #allTbl > 0 then
					if isAllIdent(allTbl[1]) then
						return true
					end
				end
				for i,v in ipairs(allTbl) do
					if checkHandle.Name == v then
						return true
					end
				end
				return false
			else
				local allowed = false
				for i,v in ipairs(allTbl) do
					if checkHandle.Name == v then
						allowed = true
						break
					end
				end
				for i,v in ipairs(restrTbl) do
					if checkHandle.Name == v then
						allowed = false
						break
					end
				end
				return allowed
			end
		end
	end
	return true
end

function isAllowedShip(shipClass)
	return checkTables(allowedShips,restrictedShips,shipClass)
end

function isAllowedWeapon(weaponClass)
	return checkTables(allowedWeapons,restrictedWeapons,weaponClass)
end

function getProgressString() 
	local progressString = "Zooming"
	if zoomingIn then
		progressString = progressString .. " in:"
	elseif zoomingOut then
		progressString = progressString .. " out:"
	elseif zoomedIn then
		progressString = "Zoomed in:"
	elseif zoomedOut then
		progressString = "Zoomed out:"
	else
		progressString = "Zoomed:"
	end
	return progressString
end

function handleControls()
	if zoomingIn or zoomedIn then
		if currentProgr > 0 then
			local tempSensValue = sensitivity * 100 / currentProgr
			
			if tempSensValue <= 1 then
				local ci = ba.getControlInfo()
				ci.Pitch = ci.Pitch * tempSensValue
				ci.Heading = ci.Heading * tempSensValue
				ci.Bank = ci.Bank * tempSensValue
			end
		end
	end
end

function isAllowedToZoom()
	local allowedShip = isAllowedShip(hv.Player.Class)
	
	local allowedWeapon = true
	local primWeaponBank = hv.Player.PrimaryBanks
	if not linkedZoom and primWeaponBank.Linked then
		allowedWeapon = false
	else
		for i=0, #primWeaponBank do
			local v = primWeaponBank[i]
			if v.Armed then
				if isAllowedWeapon(tb.WeaponClasses[v.WeaponClass.Name]) then
					allowedWeapon = true
					break
				else
					allowedWeapon = false
				end
			end
		end
		if allowedWeapon then
			local secWeaponBank = hv.Player.SecondaryBanks
			for i=0, #secWeaponBank do
				local v = secWeaponBank[i]
				if v.Armed then
					if isAllowedWeapon(tb.WeaponClasses[v.WeaponClass.Name]) then
						allowedWeapon = true
						break
					else
						allowedWeapon = false
					end
				end
			end
		end
	end
	return allowedShip and allowedWeapon
end

function zoomIn()
	if not cameraSet then
		if #gr.Cameras > 0 then
			cam = gr.Cameras[1]
		end
	end
	if not zooming then
		if hv.Player:isValid() then
			if cam:isValid() then
				
				if isAllowedToZoom() then
					if currentProgr > 0 and currentProgr < 100 then
						stepWidth = width / currentProgr -- Setting the stepWidth in case the zoom may not have been completed
					end
					
					zoomEndProgress = 100
				
					zooming = true
					
					zoomingIn = true
					zoomedIn = false
					zoomedOut = false
					zoomingOut = false
					
					if not defaultZoomSet then
						normalFOV = cam.FOV
						defaultZoomSet = true
					end
					zoom = normalFOV * zoomValue
					
					ba.setControlMode(LUA_FULL_CONTROLS)
					
					cam:setFOV(zoom,transitionTime,transitionTime / 2,transitionTime / 4)
				end
			end
		end
	end
end

function zoomOut()
	if zooming and not zoomOutOverride then
		if hv.Player:isValid() then
			if cam:isValid() then
			
				stepWidth = width / 100
			
				zoomingIn = false
				zoomingOut = true
				zoomedIn = false
				zoomedOut = false
				
				if currentProgr > 0 then
					zoomEndProgress = currentProgr -- Save the progress we made as we begin to zoom back
				end
				
				cam:setFOV(normalFOV,transitionTime,transitionTime / 2,transitionTime / 4)
				
				ba.setControlMode(NORMAL_CONTROLS)
				
				fredOverride = false
			end
		end
	end
end

function drawProgress()
	progressString = getProgressString()
	
	local stringWidth = gr.getStringWidth(progressString);
	gr.drawString(progressString,30,70)
	local realOffset_x = offset_x + stringWidth + 10
	gr.drawRectangle(realOffset_x, offset_y,realOffset_x + width, offset_y + heigth, zoomedIn)
	local frameTime = ba.getFrametime()
	
	local progressLineOffset_x = realOffset_x + stepWidth * currentProgr
	gr.drawLine(progressLineOffset_x, offset_y, progressLineOffset_x, offset_y + heigth)
	
	if not isAllowedToZoom() and not zoomOutOverride then
		if zoomingIn or zoomedIn then
			zoomOut()
			zoomOutOverride = true
		end
	end
	
	local thisFrameProg = (frameTime / transitionTime) * zoomEndProgress
	if zoomingIn then
		currentProgr = currentProgr + thisFrameProg
		if currentProgr >= 100 then
			currentProgr = 100
		zoomingIn = false
		zoomedIn = true
		end
	elseif zoomingOut then
		currentProgr = currentProgr - thisFrameProg
		if currentProgr <= 0 then
			currentProgr = 0
			zoomOutOverride = false
			zoomedOut = true
			zoomingOut = false
			zooming = false
		end
	end
end

local function init() 
	if useConfig then
		readConfig() -- read the config file if exists
	end
end

local function initVars() 
	-- Some things to tell the script to do something and some default values
	runZoomScript = false
	useConfig = true
	
	zoomValue = 0.1
	normalFOV = 0.75
	transitionTime = 2
	zooming = false
	defaultZoomSet = false
	cameraSet = false
	configFile = "data/config/zoom_config.cfg"
	zoomOutOverride = false
	linkedZoom = true
	zoomKey = "d"
	zoomLockKey = nil
	
	fredOverride=false
	
	lockedZoom = false

	-- Setting the values to be used in the $On Frame: hook
	currentProgr = 0
	zoomEndProgress = 100

	zoomingIn = false
	zoomedIn = false

	zoomingOut = false
	zoomedOut = true

	-- Constants for drawing the progress
	offset_x = 30
	offset_y = 70
	heigth = 8
	width = 80

	stepWidth = width / 100

	-- Settings for the sensitivity
	sensitivity = 0.25

	-- The camera which is used for zooming (is initialized in the first $Key Pressed: hook)
	cam = nil
	
	-- Tables that hold the allowe/restricted informations
	-- Ships
	allowedShips = {"**all**"}
	tempAllowedShip = {}
	
	restrictedShips = {}
	tempRestrictedShip = {}
	
	-- Weapons
	allowedWeapons = {"**all**"}
	tempAllowedWeapon = {}	
	
	restrictedWeapons = {}
	tempRestrictedWeapon = {}
end

-- Initialize the global variables
initVars()

-- Initialize the rest
init()
]

$Application: FS2_Open
$On Mission Start: 
[
if not runZoomScript then
	runZoomScript = true
end
]

$On Mission End: 
[
if runZoomScript then
	runZoomScript = false
	if defaultZoomSet and cam:isValid() then
		cam:setFOV(normalFOV) -- Resetting the FOV in case we have not zoomed out completely
	end
	removeTemps()
end
]

$On Frame: 
[
if runZoomScript then	
	if zooming then
		handleControls()
	
		drawProgress()
	end
end
]

$On Key Pressed: [[zoom_KeyDn.lua]]
$On Key Released: [[zoom_KeyUp.lua]]


#End

Then create the file zoom_KeyDn.lua and zoom_KeyUp.lua in your data/scripts directory and paste the following into then:

zoom_KeyDn.lua:

if runZoomScript and not fredOverride then
	if hv.Key:lower() == zoomLockKey:lower() then
		if lockedZoom then
			lockedZoom = false
			zoomOut()
		else
			lockedZoom = true
			zoomIn()
		end
	elseif hv.Key:lower() == zoomKey:lower() then
		zoomIn()
	end
end

zoom_KeyUp.lua:

if runZoomScript and not fredOverride then
	if not lockedZoom then
		if hv.Key:lower() == zoomKey:lower() then
			zoomOut()
		end
	end
end

After this create a file named zoom_config.cfg in your data/config directory and paste the following text into it:

-- Setting standart values
Zoom Factor: 0.1
Transition Time: 2
Sensitivity: 0.25
Linked Zoom: YES
Key: d
Zoom Lock Key: Alt-d

-- Setting ship class options
Allowed Ship Class: **all**

-- Setting weapon class options
Allowed Weapon Class: **all**