Difference between revisions of "Script - Scripted Mouse"

From FreeSpace Wiki
Jump to: navigation, search
m (fixing bug)
(edited the parser, moved config file to data/config dir, changed it to .cfg to improve scripts portability)
Line 11: Line 11:
 
[
 
[
  
-- couple of functions to make things go easier
+
--NEW PARSER
function find_after_keyword(line_to_parse, keyword)
+
----------------------
 +
-- parser functions --
 +
----------------------
  
  -- make sure both strings are in lower case
+
--- get newline and make sure its lowercase
   line_to_parse = line_to_parse:lower()
+
function get_next_line(nfile)
   keyword = keyword:lower()
+
  -- read the line
 +
   nline = nfile:read("*l")
 +
   -- change to lowercase
 +
  if nline ~= nil then
 +
      nline = nline:lower()
 +
  end
 +
  return nline
 +
end
  
 +
--- find keyword and return the place where it ends
 +
function find_keyword(line_to_parse, keyword)
 
   -- find any instances of the keyword
 
   -- find any instances of the keyword
   key_s, key_e = line_to_parse:find(keyword)
+
   keyword = keyword:lower()
 +
  local key_s, key_e = line_to_parse:find(keyword)
  
 
   -- if we cant find a thing
 
   -- if we cant find a thing
Line 26: Line 38:
 
   end
 
   end
  
   result = line_to_parse:match("%d+", e_key)
+
   -- check if the line has been commented away
  return tonumber(result)
+
  local comment_s, comment_e = line_to_parse:find("--")
end
 
  
function find_4_numbers_after_keyword(line_to_parse, keyword)
+
  if comment_s == nil then
 +
      return key_e
 +
  elseif comment_s < key_s then
 +
      return nil
 +
  end
  
   -- make sure both strings are in lower case
+
   return key_e
  line_to_parse = line_to_parse:lower()
+
end
  keyword = keyword:lower()
 
  
   -- find any instances of the keyword
+
--- specific parsing funcs to make things easier to read ---
   key_s, key_e = line_to_parse:find(keyword)
+
--- string or rather substring parser
 +
function parse_string(start_key, line_to_parse)
 +
  local substring = line_to_parse:sub(start_key)
 +
   -- remove empty spaces
 +
  local substring_start = substring:find("%a")
 +
   substring = substring:sub(substring_start)
 +
  return substring
 +
end
  
  -- if we cant find a thing
+
--- function to parse numbers
   if key_s == nil then
+
function parse_number(start_key, line_to_parse)
      return nil
+
   local result = line_to_parse:sub(start_key)
   end
+
   local r_value = result:match('[0-9%.]+')
 
+
   r_value = tonumber(r_value)
  -- make sure r is an array
+
   return r_value
   r = {}
+
end
   i = 1
 
  
 +
--- function to parse arrays of numbers
 +
function parse_number_array(start_key, line_to_parse)
 
   -- stuff the array
 
   -- stuff the array
   for p in line_to_parse:gmatch("%d+") do
+
  local r = { }
       r[i] = p
+
  local p
      r[i] = tonumber(r[i])
+
   for p in line_to_parse:gmatch('[0-9%.]+') do
       if r[i] > 255 then
+
       p = tonumber(p)
        r[i] = 255
+
       p = math.floor(p)
       elseif r[i] < 0 then
+
       table.insert(r, p)
        r[i] = 0
 
      end
 
      i = i + 1
 
 
   end
 
   end
 
   return r
 
   return r
 
end
 
end
  
function parse_mousefile_line(line_to_parse, mousefile)
+
--- function to parse things
   -- check for sensitivity
+
function parse_entry(keyword, mousefile, type, use_same_line)
   if mousesensitivity == nil then
+
   local new_entry = nil
       mousesensitivity = find_after_keyword(line_to_parse, "Sensitivity:")
+
  local return_val
       if mousesensitivity ~= nil then
+
  local return_array = {}
         if mousesensitivity < 100 then
+
  local c_line
            mousesensitivity = 100
+
   if use_same_line == false then
         elseif mousesensitivity > 600 then
+
       c_line = get_next_line(mousefile)
             mousesensitivity = 600
+
       if c_line == nil then
 +
         -- end of file
 +
        return -2, false, return_array
 +
      end
 +
      while c_line:len() == 0 do
 +
         c_line = get_next_line(mousefile)
 +
        if c_line == nil then
 +
             -- end of file
 +
            return -2, false, return_array
 
         end
 
         end
 
       end
 
       end
   -- if we already have sensitivity, check for sensitivity curve
+
      current_start_line = c_line
  elseif mousesensitivitymode == nil then
+
  else
      mousesensitivitymode = find_after_keyword(line_to_parse, "Sensitivity Curve:")
+
      c_line = use_same_line
      if mousesensitivitymode ~= nil then
+
  end
        if mousesensitivitymode < 0 then
+
 
             mousesensitivitymode = 1
+
  local c_key = nil
         elseif mousesensitivitymode > 6 then
+
   while c_key == nil do
             mousesensitivitymode = 6
+
      -- we didn't find the thing...
 +
      c_key = find_keyword(c_line, keyword)
 +
      if c_key == nil then
 +
        local comment_s = nil
 +
        comment_s = c_line:find("//")
 +
        if comment_s ~= nil then
 +
            c_line = get_next_line(mousefile)
 +
            if c_line == nil then
 +
              -- end of file
 +
              return -2, false, return_array
 +
            end
 +
            while c_line:len() == 0 do
 +
              c_line = get_next_line(mousefile)
 +
              if c_line == nil then
 +
                  return -2, false, return_array
 +
              end
 +
             end
 +
         else
 +
            -- try next entry
 +
             return -1, c_line, return_array
 
         end
 
         end
 
       end
 
       end
   -- if we have all the above, go for control mode
+
   end
   elseif mousecontrolmode == nil then
+
  if type == "n" then
       mousecontrolmode = find_after_keyword(line_to_parse, "Control Mode:")
+
      -- soo... parse a number
  -- if we have all the above, go for deadzone
+
      return_array[1] = parse_number(c_key, c_line)
  elseif mousedeadzone == nil then
+
      return_val = #return_array
      mousedeadzone = find_after_keyword(line_to_parse, "Deadzone:")
+
      return return_val, false, return_array
      if mousedeadzone ~= nil then
+
  elseif type == "array_n" then
        if mousedeadzone < 0 then
+
      -- soo... parse an array of numbers
             mousedeadzone = 0
+
      return_array = parse_number_array(c_key, c_line)
 +
      return_val = #return_array
 +
      return return_val, false, return_array
 +
  end
 +
  return -1, c_line, return_array
 +
end
 +
 
 +
function cap_value(min_val, max_val, value)
 +
  if min_val > value then
 +
      value = min_val
 +
   elseif max_val < value then
 +
      value = max_val
 +
  end
 +
  return value
 +
end
 +
 
 +
-- actual parsing function
 +
function parse_mousefile(mousefile)
 +
  use_same_line = false
 +
  local current_start_line = use_same_line
 +
  local not_new_entry = false
 +
  local entry_start = 1
 +
  local still_parsing = true
 +
 
 +
  while still_parsing == true do
 +
 
 +
      if use_same_line == false then
 +
        use_same_line = get_next_line(mousefile)
 +
      end
 +
 
 +
      if use_same_line == nil then
 +
        -- end of file
 +
        still_parsing = false
 +
        break
 +
      end
 +
 
 +
       if use_same_line:len() == 0 then
 +
        use_same_line = false
 +
      else
 +
        current_start_line = use_same_line
 +
        if entry_start ~= nil then
 +
            while entry_start ~= nil do
 +
              if not_new_entry == false then
 +
                  ba.print("\nScripted Mouse Loading\n")
 +
              end
 +
 
 +
              local temp_val = nil
 +
              local temp_arr = nil
 +
              entry_start = nil
 +
 
 +
              temp_val, use_same_line, temp_arr = parse_entry("Sensitivity:", mousefile, "n", use_same_line)
 +
              if temp_val == -2 then
 +
                  break
 +
              elseif temp_val ~= -1 then
 +
                  temp_arr[1] = cap_value(100, 600, temp_arr[1])
 +
                  ba.print("\tSensitivity: " .. temp_arr[1] .. "\n")
 +
                  mousesensitivity = temp_arr[1]
 +
              end
 +
 
 +
              temp_val, use_same_line, temp_arr = parse_entry("Sensitivity Curve:", mousefile, "n", use_same_line)
 +
              if temp_val == -2 then
 +
                  break
 +
              elseif temp_val ~= -1 then
 +
                  temp_arr[1] = cap_value(0, 6, temp_arr[1])
 +
                  ba.print("\tSensitivity Curve: " .. temp_arr[1] .. "\n")
 +
                  mousesensitivitymode = temp_arr[1]
 +
              end
 +
 
 +
              temp_val, use_same_line, temp_arr = parse_entry("Control Mode:", mousefile, "n", use_same_line)
 +
              if temp_val == -2 then
 +
                  break
 +
              elseif temp_val ~= -1 then
 +
                  ba.print("\tControl Mode: " .. temp_arr[1] .. "\n")
 +
                  mousecontrolmode = temp_arr[1]
 +
              end
 +
 
 +
              temp_val, use_same_line, temp_arr = parse_entry("Deadzone:", mousefile, "n", use_same_line)
 +
              if temp_val == -2 then
 +
                  break
 +
              elseif temp_val ~= -1 then
 +
                  temp_arr[1] = cap_value(0, 300, temp_arr[1])
 +
                  ba.print("\tDeadzone: " .. temp_arr[1] .. "\n")
 +
                  mousedeadzone = temp_arr[1]
 +
              end
 +
 
 +
              temp_val, use_same_line, temp_arr = parse_entry("Mouse Invert:", mousefile, "n", use_same_line)
 +
              if temp_val == -2 then
 +
                  break
 +
              elseif temp_val ~= -1 then
 +
                  if temp_arr[1] ~= 0 then
 +
                    temp_arr[1] = 1
 +
                  end
 +
                  ba.print("\tMouse Invert: " .. temp_arr[1] .. "\n")
 +
                  mouseinvert = temp_arr[1]
 +
              end
 +
 
 +
              temp_val, use_same_line, temp_arr = parse_entry("Boundary Limit:", mousefile, "n", use_same_line)
 +
              if temp_val == -2 then
 +
                  break
 +
              elseif temp_val ~= -1 then
 +
                  ba.print("\tBoundary Limit: " .. temp_arr[1] .. "\n")
 +
                  mouseboundaries = temp_arr[1]
 +
              end
 +
 
 +
              temp_val, use_same_line, temp_arr = parse_entry("Indicator Color:", mousefile, "array_n", use_same_line)
 +
              if temp_val == -2 then
 +
                  break
 +
              elseif temp_val ~= -1 then
 +
                  ba.print("\tIndicator Color: " .. temp_val .. "\n")
 +
                  mousecolors = temp_arr
 +
              end
 +
 
 +
              if use_same_line ~= false then
 +
                  ba.warning("Bogus string in mouse config file: " .. use_same_line)
 +
              end
 +
             end
 +
        else
 +
            use_same_line = false
 
         end
 
         end
 
       end
 
       end
   -- if we have all the above, go for mouse invert
+
  end
   elseif mouseinvert == nil then
+
end
       mouseinvert = find_after_keyword(line_to_parse, "Mouse Invert:")
+
--NEW PARSER
      if mouseinvert ~= 1 then
+
 
        mouseinvert = 0
+
function verify_mouse_data()
       end
+
   -- make sure we have a number in deadzone var and not nil
   -- if we have all the above, go for mouse boundaries
+
   if mousedeadzone == nil then
   elseif mouseboundaries == nil then
+
       mousedeadzone = 0
       mouseboundaries = find_after_keyword(line_to_parse, "Boundary Limit:")
+
  end
   -- if we have all the above, go for mouse colors
+
 
   elseif mousecolor == nil then
+
  -- define some variables
       mousecolors = find_4_numbers_after_keyword(line_to_parse, "Indicator Color:")
+
  mouse_reset_counter = nil
   -- all found - set the position to end of file
+
  missiontime_old = 0
 +
  missiontime = nil
 +
 
 +
  -- if mouse invert was set to true, then invert the mouse (set it to '-1')
 +
  -- otherwise define the multiplier as '1'
 +
  if mouseinvert == 1 then
 +
      mouseinvert = -1
 +
  else
 +
       mouseinvert = 1
 +
  end
 +
 
 +
   -- check if boundaries are valid
 +
   if mouseboundaries == nil then
 +
       mouseboundaries = 1
 +
  end
 +
 
 +
   -- check that defined control area is not larger than the actual area which can be controlled
 +
  local max_limits
 +
   max_limits = mousesensitivity * 2 + mousedeadzone * 2 + 10
 +
  scr_width = gr.getScreenWidth()
 +
  scr_height = gr.getScreenHeight()
 +
  if max_limits > scr_width or max_limits > scr_height then
 +
       ba.warning("Mouse control area defined to be larger than the window size, please resize")
 +
  end
 +
 
 +
   --setup mouse bitmap
 +
  mouse_bm = gr.loadTexture("mouse_ret_2.dds", true)
 +
  if mouse_bm:isValid() then
 +
      no_bitmap = false
 +
      mouse_bm_w = mouse_bm:getWidth() / 2
 +
      mouse_bm_h = mouse_bm:getHeight() / 2
 
   else
 
   else
       mousefile:seek("end")
+
       no_bitmap = true
 
   end
 
   end
 
end
 
end
Line 115: Line 308:
 
-- check if the file containing mouse references exists
 
-- check if the file containing mouse references exists
 
-----------------------------------------------------------------------
 
-----------------------------------------------------------------------
filename_mouse = "mouse_script.txt"
+
filename_mouse = "mouse_script.cfg"
boolmousefileOK = cf.fileExists(filename_mouse, "data/text/", true)
+
boolmousefileOK = cf.fileExists(filename_mouse, "data/config/", true)
 
boolscriptmouse = false
 
boolscriptmouse = false
  
Line 133: Line 326:
 
   -- open the file in read only mode
 
   -- open the file in read only mode
 
   mousefile = cf.openFile(filename_mouse, "r")
 
   mousefile = cf.openFile(filename_mouse, "r")
 
+
   -- do the parsing thing
   -- pick first line
+
  parse_mousefile(mousefile)
  new_line = mousefile:read("*l")
 
 
 
  while new_line ~= nil do
 
      -- parse line and then jump on to the next line until end of file
 
      parse_mousefile_line(new_line,mousefile)
 
      new_line = mousefile:read("*l")
 
  end
 
 
 
 
   -- all data read.. time to close the file
 
   -- all data read.. time to close the file
 
   mousefile:close()
 
   mousefile:close()
Line 152: Line 337:
 
       boolscriptmouse = true
 
       boolscriptmouse = true
 
       ba.setControlMode(LUA_FULL_CONTROLS)
 
       ba.setControlMode(LUA_FULL_CONTROLS)
 +
      verify_mouse_data()
  
      -- make sure we have a number in deadzone var and not nil
 
      if mousedeadzone == nil then
 
        mousedeadzone = 0
 
      end
 
 
      -- define some variables
 
      mouse_reset_counter = nil
 
      missiontime_old = 0
 
      missiontime = nil
 
 
      -- if mouse invert was set to true, then invert the mouse (set it to '-1')
 
      -- otherwise define the multiplier as '1'
 
      if mouseinvert == 1 then
 
        mouseinvert = -1
 
      else
 
        mouseinvert = 1
 
      end
 
 
      -- check if boundaries are valid
 
      if mouseboundaries == nil then
 
        mouseboundaries = 1
 
      end
 
 
      -- check that defined control area is not larger than the actual area which can be controlled
 
      local max_limits
 
      max_limits = mousesensitivity * 2 + mousedeadzone * 2 + 10
 
      scr_width = gr.getScreenWidth()
 
      scr_height = gr.getScreenHeight()
 
      if max_limits > scr_width or max_limits > scr_height then
 
        ba.warning("Mouse control area defined to be larger than the window size, please resize")
 
      end
 
 
      --setup mouse bitmap
 
      mouse_bm = gr.loadTexture("mouse_ret_2.dds", true)
 
      if mouse_bm:isValid() then
 
        no_bitmap = false
 
        mouse_bm_w = mouse_bm:getWidth() / 2
 
        mouse_bm_h = mouse_bm:getHeight() / 2
 
      else
 
        no_bitmap = true
 
      end
 
 
   else
 
   else
 
       ba.warning("Scripted mouse's init failed")
 
       ba.warning("Scripted mouse's init failed")
Line 200: Line 345:
 
   ba.warning("File '" .. filename_mouse .."' not found")
 
   ba.warning("File '" .. filename_mouse .."' not found")
 
end
 
end
 
]
 
 
$Application: FS2_Open
 
$State: GS_STATE_GAME_PLAY
 
$On Frame:
 
 
[
 
  
 
------------------------
 
------------------------
Line 277: Line 414:
 
         io.forceMousePosition(mouse_x, f_mouse_y)
 
         io.forceMousePosition(mouse_x, f_mouse_y)
 
       end
 
       end
 +
  end
 +
end
 +
 +
function draw_cursor()
 +
  if no_bitmap == true then
 +
      -- no bitmap defined, so use normal lines
 +
      gr.drawGradientLine(drawpos_x, drawpos_y, drawpos_x + 20, drawpos_y - 20)
 +
      gr.drawGradientLine(drawpos_x, drawpos_y, drawpos_x - 20, drawpos_y + 20)
 +
      gr.drawGradientLine(drawpos_x, drawpos_y, drawpos_x + 20, drawpos_y + 20)
 +
      gr.drawGradientLine(drawpos_x, drawpos_y, drawpos_x - 20, drawpos_y - 20)
 +
  else
 +
      -- draw the bitmap centered on the spot
 +
      gr.drawMonochromeImage(mouse_bm, drawpos_x - mouse_bm_w, drawpos_y - mouse_bm_h, drawpos_x + mouse_bm_w, drawpos_y + mouse_bm_h)
 +
  end
 +
end
 +
 +
function check_mouse_reset()
 +
  if mouse_reset_counter == nil then
 +
      mouse_center_x = io.getMouseX()
 +
      mouse_center_y = io.getMouseY()
 +
      mouse_reset_counter = 0
 +
  end
 +
end
 +
 +
function force_mouse_control_status()
 +
  if io.MouseControlStatus == true then
 +
      mouse_reset_on_end = true
 +
      io.MouseControlStatus = false
 +
  end
 +
end
 +
 +
function set_mouse_colors()
 +
  if mousecolors[1] == nil then
 +
      gr.setColor(0,64,220,196)
 +
  else
 +
      gr.setColor(mousecolors[1],mousecolors[2],mousecolors[3],mousecolors[4])
 
   end
 
   end
 
end
 
end
Line 282: Line 455:
 
--- end of functions ---
 
--- end of functions ---
 
------------------------
 
------------------------
 +
 +
]
 +
 +
$Application: FS2_Open
 +
$State: GS_STATE_GAME_PLAY
 +
$On Frame:
 +
 +
[
  
 
if boolscriptmouse == true then
 
if boolscriptmouse == true then
 
   -- check for center button reset...
 
   -- check for center button reset...
   if mouse_reset_counter == nil then
+
   check_mouse_reset()
      mouse_center_x = io.getMouseX()
 
      mouse_center_y = io.getMouseY()
 
      mouse_reset_counter = 0
 
  end
 
  
 
   -- get frametime (used to increment the timer)
 
   -- get frametime (used to increment the timer)
Line 301: Line 478:
  
 
   -- if the setting changes make sure to reset it to false
 
   -- if the setting changes make sure to reset it to false
   if io.MouseControlStatus == true then
+
   force_mouse_control_status()
      mouse_reset_on_end = true
 
      io.MouseControlStatus = false
 
  end
 
  
 
   -- check if missiontime is actually running or not
 
   -- check if missiontime is actually running or not
Line 342: Line 516:
  
 
         -- define the color of the cursor
 
         -- define the color of the cursor
         if mousecolors == nil then
+
         -- could use color inheritance!!!
            gr.setColor(0,64,220,196)
+
         set_mouse_colors()
         else
 
            gr.setColor(mousecolors[1],mousecolors[2],mousecolors[3],mousecolors[4])
 
        end
 
  
 
         -- get the actual control values
 
         -- get the actual control values
Line 363: Line 534:
  
 
         -- draw cursor
 
         -- draw cursor
         if no_bitmap == true then
+
         draw_cursor()
            gr.drawGradientLine(drawpos_x, drawpos_y, drawpos_x + 20, drawpos_y - 20)
 
            gr.drawGradientLine(drawpos_x, drawpos_y, drawpos_x - 20, drawpos_y + 20)
 
            gr.drawGradientLine(drawpos_x, drawpos_y, drawpos_x + 20, drawpos_y + 20)
 
            gr.drawGradientLine(drawpos_x, drawpos_y, drawpos_x - 20, drawpos_y - 20)
 
        else
 
            gr.drawMonochromeImage(mouse_bm, drawpos_x - mouse_bm_w, drawpos_y - mouse_bm_h, drawpos_x + mouse_bm_w, drawpos_y + mouse_bm_h)
 
        end
 
 
       end
 
       end
 
   else
 
   else
Line 395: Line 559:
  
 
==Configuration file==
 
==Configuration file==
Filename '''mouse_script.txt''', placed into '''.../data/text/''' directory
+
Filename '''mouse_script.cfg''', placed into '''.../data/config/''' directory
 
<pre>Sensitivity:        350
 
<pre>Sensitivity:        350
 
Sensitivity Curve:  1.5
 
Sensitivity Curve:  1.5

Revision as of 16:43, 20 February 2010

Function

Draws mouse flight icon and modifies the mouse controls

Table entry

For example script_mouse-sct.tbm

#Conditional Hooks

$Application: FS2_Open
$On Mission Start:

[

--NEW PARSER
----------------------
-- parser functions --
----------------------

--- get newline and make sure its lowercase
function get_next_line(nfile)
   -- read the line
   nline = nfile:read("*l")
   -- change to lowercase
   if nline ~= nil then
      nline = nline:lower()
   end
   return nline
end

--- find keyword and return the place where it ends
function find_keyword(line_to_parse, keyword)
   -- find any instances of the keyword
   keyword = keyword:lower()
   local key_s, key_e = line_to_parse:find(keyword)

   -- if we cant find a thing
   if key_s == nil then
      return nil
   end

   -- check if the line has been commented away
   local comment_s, comment_e = line_to_parse:find("--")

   if comment_s == nil then
      return key_e
   elseif comment_s < key_s then
      return nil
   end

   return key_e
end

--- specific parsing funcs to make things easier to read ---
--- string or rather substring parser
function parse_string(start_key, line_to_parse)
   local substring = line_to_parse:sub(start_key)
   -- remove empty spaces
   local substring_start = substring:find("%a")
   substring = substring:sub(substring_start)
   return substring
end

--- function to parse numbers
function parse_number(start_key, line_to_parse)
   local result = line_to_parse:sub(start_key)
   local r_value = result:match('[0-9%.]+')
   r_value = tonumber(r_value)
   return r_value
end

--- function to parse arrays of numbers
function parse_number_array(start_key, line_to_parse)
   -- stuff the array
   local r = { }
   local p
   for p in line_to_parse:gmatch('[0-9%.]+') do
      p = tonumber(p)
      p = math.floor(p)
      table.insert(r, p)
   end
   return r
end

--- function to parse things
function parse_entry(keyword, mousefile, type, use_same_line)
   local new_entry = nil
   local return_val
   local return_array = {}
   local c_line
   if use_same_line == false then
      c_line = get_next_line(mousefile)
      if c_line == nil then
         -- end of file
         return -2, false, return_array
      end
      while c_line:len() == 0 do
         c_line = get_next_line(mousefile)
         if c_line == nil then
            -- end of file
            return -2, false, return_array
         end
      end
      current_start_line = c_line
   else
      c_line = use_same_line
   end

   local c_key = nil
   while c_key == nil do
      -- we didn't find the thing...
      c_key = find_keyword(c_line, keyword)
      if c_key == nil then
         local comment_s = nil
         comment_s = c_line:find("//")
         if comment_s ~= nil then
            c_line = get_next_line(mousefile)
            if c_line == nil then
               -- end of file
               return -2, false, return_array
            end
            while c_line:len() == 0 do
               c_line = get_next_line(mousefile)
               if c_line == nil then
                  return -2, false, return_array
               end
            end
         else
            -- try next entry
            return -1, c_line, return_array
         end
      end
   end
   if type == "n" then
      -- soo... parse a number
      return_array[1] = parse_number(c_key, c_line)
      return_val = #return_array
      return return_val, false, return_array
   elseif type == "array_n" then
      -- soo... parse an array of numbers
      return_array = parse_number_array(c_key, c_line)
      return_val = #return_array
      return return_val, false, return_array
   end
   return -1, c_line, return_array
end

function cap_value(min_val, max_val, value)
   if min_val > value then
      value = min_val
   elseif max_val < value then
      value = max_val
   end
   return value
end

-- actual parsing function
function parse_mousefile(mousefile)
   use_same_line = false
   local current_start_line = use_same_line
   local not_new_entry = false
   local entry_start = 1
   local still_parsing = true

   while still_parsing == true do

      if use_same_line == false then
         use_same_line = get_next_line(mousefile)
      end

      if use_same_line == nil then
         -- end of file
         still_parsing = false
         break
      end

      if use_same_line:len() == 0 then
         use_same_line = false
      else
         current_start_line = use_same_line
         if entry_start ~= nil then
            while entry_start ~= nil do
               if not_new_entry == false then
                  ba.print("\nScripted Mouse Loading\n")
               end

               local temp_val = nil
               local temp_arr = nil
               entry_start = nil

               temp_val, use_same_line, temp_arr = parse_entry("Sensitivity:", mousefile, "n", use_same_line)
               if temp_val == -2 then
                  break
               elseif temp_val ~= -1 then
                  temp_arr[1] = cap_value(100, 600, temp_arr[1])
                  ba.print("\tSensitivity: " .. temp_arr[1] .. "\n")
                  mousesensitivity = temp_arr[1]
               end

               temp_val, use_same_line, temp_arr = parse_entry("Sensitivity Curve:", mousefile, "n", use_same_line)
               if temp_val == -2 then
                  break
               elseif temp_val ~= -1 then
                  temp_arr[1] = cap_value(0, 6, temp_arr[1])
                  ba.print("\tSensitivity Curve: " .. temp_arr[1] .. "\n")
                  mousesensitivitymode = temp_arr[1]
               end

               temp_val, use_same_line, temp_arr = parse_entry("Control Mode:", mousefile, "n", use_same_line)
               if temp_val == -2 then
                  break
               elseif temp_val ~= -1 then
                  ba.print("\tControl Mode: " .. temp_arr[1] .. "\n")
                  mousecontrolmode = temp_arr[1]
               end

               temp_val, use_same_line, temp_arr = parse_entry("Deadzone:", mousefile, "n", use_same_line)
               if temp_val == -2 then
                  break
               elseif temp_val ~= -1 then
                  temp_arr[1] = cap_value(0, 300, temp_arr[1])
                  ba.print("\tDeadzone: " .. temp_arr[1] .. "\n")
                  mousedeadzone = temp_arr[1]
               end

               temp_val, use_same_line, temp_arr = parse_entry("Mouse Invert:", mousefile, "n", use_same_line)
               if temp_val == -2 then
                  break
               elseif temp_val ~= -1 then
                  if temp_arr[1] ~= 0 then
                     temp_arr[1] = 1
                  end
                  ba.print("\tMouse Invert: " .. temp_arr[1] .. "\n")
                  mouseinvert = temp_arr[1]
               end

               temp_val, use_same_line, temp_arr = parse_entry("Boundary Limit:", mousefile, "n", use_same_line)
               if temp_val == -2 then
                  break
               elseif temp_val ~= -1 then
                  ba.print("\tBoundary Limit: " .. temp_arr[1] .. "\n")
                  mouseboundaries = temp_arr[1]
               end

               temp_val, use_same_line, temp_arr = parse_entry("Indicator Color:", mousefile, "array_n", use_same_line)
               if temp_val == -2 then
                  break
               elseif temp_val ~= -1 then
                  ba.print("\tIndicator Color: " .. temp_val .. "\n")
                  mousecolors = temp_arr
               end

               if use_same_line ~= false then
                  ba.warning("Bogus string in mouse config file: " .. use_same_line)
               end
            end
         else
            use_same_line = false
         end
      end
   end
end
--NEW PARSER

function verify_mouse_data()
   -- make sure we have a number in deadzone var and not nil
   if mousedeadzone == nil then
      mousedeadzone = 0
   end

   -- define some variables
   mouse_reset_counter = nil
   missiontime_old = 0
   missiontime = nil

   -- if mouse invert was set to true, then invert the mouse (set it to '-1')
   -- otherwise define the multiplier as '1'
   if mouseinvert == 1 then
      mouseinvert = -1
   else
      mouseinvert = 1
   end

   -- check if boundaries are valid
   if mouseboundaries == nil then
      mouseboundaries = 1
   end

   -- check that defined control area is not larger than the actual area which can be controlled
   local max_limits
   max_limits = mousesensitivity * 2 + mousedeadzone * 2 + 10
   scr_width = gr.getScreenWidth()
   scr_height = gr.getScreenHeight()
   if max_limits > scr_width or max_limits > scr_height then
      ba.warning("Mouse control area defined to be larger than the window size, please resize")
   end

   --setup mouse bitmap
   mouse_bm = gr.loadTexture("mouse_ret_2.dds", true)
   if mouse_bm:isValid() then
      no_bitmap = false
      mouse_bm_w = mouse_bm:getWidth() / 2
      mouse_bm_h = mouse_bm:getHeight() / 2
   else
      no_bitmap = true
   end
end

-----------------------------------------------------------------------
-- check if the file containing mouse references exists
-----------------------------------------------------------------------
filename_mouse = "mouse_script.cfg"
boolmousefileOK = cf.fileExists(filename_mouse, "data/config/", true)
boolscriptmouse = false

-- if file exists
if boolmousefileOK then
   -- reset all the variables to nil
   mousesensitivity = nil
   mousesensitivitymode = nil
   mousecontrolmode = nil
   mousedeadzone = nil
   mouseinvert = nil
   mouseboundaries = nil
   mousecolor = nil
   mousecolors = {}

   -- open the file in read only mode
   mousefile = cf.openFile(filename_mouse, "r")
   -- do the parsing thing
   parse_mousefile(mousefile)
   -- all data read.. time to close the file
   mousefile:close()

   -- if these three were found from the cfg file...
   if mousesensitivity and mousesensitivitymode and mousecontrolmode then

      -- set script to actually run, allow lua to override c coded controls
      boolscriptmouse = true
      ba.setControlMode(LUA_FULL_CONTROLS)
      verify_mouse_data()

   else
      ba.warning("Scripted mouse's init failed")
   end
else
   ba.warning("File '" .. filename_mouse .."' not found")
end

------------------------
------ functions -------
------------------------
function mouse_control_A(value, centervalue, axis)
   -- get the actual difference from centerpoint
   delta = value - centervalue

   -- default multiplier to +1
   multiplier = 1

   -- if we are handling negative values set multiplier to -1
   -- and make sure we deal only with positive values
   if delta < 0 then
      multiplier = -1
      delta = math.abs(delta)
   end

   -- deduct deadzone from the delta
   delta = delta - mousedeadzone
   if delta < 0 then
      delta = 0
   end

   -- scale delta from 0 to 1 according to defined sensitivity
   delta = delta / mousesensitivity
   if delta > 1 then
      delta = 1
   end

   -- if we do not have extreme values
   -- apply the defined sensitivity curve
   if (delta > 0) and (delta < 1) then
      delta = math.pow(delta, mousesensitivitymode)
   end

   -- apply the multiplier
   delta = delta * multiplier

   return delta
end
------------------------
function do_boundaries_check(limit)
   f_mouse_x = nil
   f_mouse_y = nil

   -- do we go over width limits
   if mouse_x <= limit then
      f_mouse_x = limit
   elseif mouse_x >= (scr_width - limit) then
      f_mouse_x = scr_width - limit
   end

   -- do we go over height limits
   if mouse_y <= limit then
      f_mouse_y = limit
   elseif mouse_y >= (scr_height - limit) then
      f_mouse_y = scr_height - limit
   end

   -- reset the cursor to the nearest boundary
   if f_mouse_x ~= nil or f_mouse_y ~= nil then
      if f_mouse_x and f_mouse_y then
         io.forceMousePosition(f_mouse_x, f_mouse_y)
      elseif f_mouse_x then
         io.forceMousePosition(f_mouse_x, mouse_y)
      else
         io.forceMousePosition(mouse_x, f_mouse_y)
      end
   end
end

function draw_cursor()
   if no_bitmap == true then
      -- no bitmap defined, so use normal lines
      gr.drawGradientLine(drawpos_x, drawpos_y, drawpos_x + 20, drawpos_y - 20)
      gr.drawGradientLine(drawpos_x, drawpos_y, drawpos_x - 20, drawpos_y + 20)
      gr.drawGradientLine(drawpos_x, drawpos_y, drawpos_x + 20, drawpos_y + 20)
      gr.drawGradientLine(drawpos_x, drawpos_y, drawpos_x - 20, drawpos_y - 20)
   else
      -- draw the bitmap centered on the spot
      gr.drawMonochromeImage(mouse_bm, drawpos_x - mouse_bm_w, drawpos_y - mouse_bm_h, drawpos_x + mouse_bm_w, drawpos_y + mouse_bm_h)
   end
end

function check_mouse_reset()
   if mouse_reset_counter == nil then
      mouse_center_x = io.getMouseX()
      mouse_center_y = io.getMouseY()
      mouse_reset_counter = 0
   end
end

function force_mouse_control_status()
   if io.MouseControlStatus == true then
      mouse_reset_on_end = true
      io.MouseControlStatus = false
   end
end

function set_mouse_colors()
   if mousecolors[1] == nil then
      gr.setColor(0,64,220,196)
   else
      gr.setColor(mousecolors[1],mousecolors[2],mousecolors[3],mousecolors[4])
   end
end
------------------------
--- end of functions ---
------------------------

]

$Application: FS2_Open
$State: GS_STATE_GAME_PLAY
$On Frame:

[

if boolscriptmouse == true then
   -- check for center button reset...
   check_mouse_reset()

   -- get frametime (used to increment the timer)
   frametime = ba.getFrametime()

   -- make sure missiontime and old missiontime exist
   if missiontime ~= nil then
      missiontime_old = missiontime
   end
   missiontime = mn.getMissionTime()

   -- if the setting changes make sure to reset it to false
   force_mouse_control_status()

   -- check if missiontime is actually running or not
   if missiontime ~= missiontime_old then

      -- after pause ends (ie. missiontime starts running again)
      -- reset the mouse to the center
      if end_of_pause == true then
         io.forceMousePosition(mouse_center_x, mouse_center_y)
         end_of_pause = nil
      end

      -- increment the center mouse button down counter
      if io.isMouseButtonDown(MOUSE_MIDDLE_BUTTON) then
         mouse_reset_counter = mouse_reset_counter + frametime
      else
         mouse_reset_counter = 0
      end

      -- if center mouse button has been pressed long enough
      -- reset the mouse
      if mouse_reset_counter > 0.1 then
         io.forceMousePosition(mouse_center_x, mouse_center_y)
         mouse_reset_counter = 0
      end

      -- get control values
      mouse_x = io.getMouseX()
      mouse_y = io.getMouseY()
      controls = ba.getControlInfo()

      if mousecontrolmode == 1 then

         -- make sure we aint gonna go off the boundaries...
         do_boundaries_check(mouseboundaries)
         -- end of boundaries check

         -- define the color of the cursor
         -- could use color inheritance!!!
         set_mouse_colors()

         -- get the actual control values
         current_h = mouse_control_A(mouse_x, mouse_center_x, "x")
         current_p = mouseinvert * mouse_control_A(mouse_y, mouse_center_y, "y")

         -- increment, not replace the existing values
         controls.Heading = current_h + controls.Heading
         controls.Pitch = current_p + controls.Pitch

         -- get draw position
         center_x = scr_width / 2
         center_y = scr_height / 2
         drawpos_x = center_x + (current_h * 300)
         drawpos_y = center_y + (current_p * 300)

         -- draw cursor
         draw_cursor()
      end
   else
      end_of_pause = true
   end
end

]

$Application: FS2_Open
$On Mission End:

[

boolscriptmouse = false
ba.setControlMode(NORMAL_CONTROLS)
if mouse_reset_on_end == true then
   io.MouseControlStatus = true
end

]

#End

Configuration file

Filename mouse_script.cfg, placed into .../data/config/ directory

Sensitivity:         350
Sensitivity Curve:   1.5
Control Mode:        1
Deadzone:            2
Mouse Invert:        0
Boundary Limit:      20
Indicator Color:     255, 255, 0, 64
  • Sensitivity is basically defines the amount of pixels from the center of the screen that the script will read (limited from 100 to 600)
  • Sensitivity Curve is exponent for the value
  • Control Mode is just option to toggle the - possible, not yet implemented - alternate control modes on
  • Deadzone defines the deadzone for the control input
  • Mouse Invert allows mouse controls to be inverted
  • Boundary Limit defines the limit for the cursor from the edge of the screen.
  • Indicator Color defines the used draw color of the mouse flight indicator

Recommendations... (sensitivity + deadzone + boundary limit) * 2 should be greater than the smaller dimension of the screen.

Additional files

Script will accept mouse_ret_2.dds as the mouse indicator but should the effect be missing the script should revert to simple 2d draw icon (icon is automatically centered).