Author Topic: Toggleable scripted mouse (v. 0.9)  (Read 10710 times)

0 Members and 1 Guest are viewing this topic.

Offline Sushi

  • Art Critic
  • 211
  • Posts: 2,784
Toggleable scripted mouse (v. 0.9)
I figure this probably needs it's own topic.

This is a slightly modified version of the mouse script found here:
http://www.hard-light.net/wiki/index.php/Script_-_Scripted_Mouse

The main difference is that the middle mouse button, instead of recentering the mouse, toggles the "joystick-like" mode the mouse script provides on and off. You can quickly switch between "normal" mouse control and "Freelancer-like" mouse control, which can be handy at times.

The other change is that the "Boundary Limit" configuration attribute was removed, since it was basically redundant. The limit for mouse motion is now more simply defined by the sensitivity setting.

Installing this is the same as installing the normal scripted mouse (see link above). Just use the code below in the file instead of the normal mouse script.

Things it would be nice to improve:
  • Transition between mouse modes can be a bit jarring. Smoothing this out would be a nice addition (my stab at it has so far failed)
  • It would be nice to have more options for how to toggle than middle mouse button (such as using the keyboard)

data/tables/mouse-sct.tbm
Code: [Select]
#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("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 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()
   
   mouse_center_x = scr_width / 2
   mouse_center_y = scr_height / 2

   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
togglescriptmouse = false
old_h = 0
old_p = 0

-- if file exists
if boolmousefileOK then
   -- reset all the variables to nil
   mousesensitivity = nil
   mousesensitivitymode = nil
   mousecontrolmode = nil
   mousedeadzone = nil
   mouseinvert = 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(sensitivity, center_x, center_y)
   f_mouse_x = nil
   f_mouse_y = nil

   -- Do we go outside sensitivity zone
   f_mouse_x = cap_value(center_x - sensitivity, center_x + sensitivity, mouse_x)
   f_mouse_y = cap_value(center_y - sensitivity, center_y + sensitivity, mouse_y)

   -- 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_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

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

      controls = ba.getControlInfo()

      -- if center mouse button hasn't been been pressed in long enough and is being pressed now
      -- reset the mouse
      if mouse_reset_counter > 0.33 and io.isMouseButtonDown(MOUSE_MIDDLE_BUTTON) then
    togglescriptmouse = not togglescriptmouse
         if togglescriptmouse == true then
controls.Heading = old_h
controls.Pitch = old_p
mouse_x = mouse_center_x + (mousesensitivity * old_h)
mouse_y = mouse_center_y + (mousesensitivity * old_p)
          io.forceMousePosition(mouse_x, mouse_y)
         end
         mouse_reset_counter = 0
  else
         mouse_reset_counter = mouse_reset_counter + frametime
      end

  if togglescriptmouse == true then
          ba.setControlMode(LUA_FULL_CONTROLS)
  io.MouseControlStatus = false
  if mousecontrolmode == 1 then

-- make sure we aint gonna go off the boundaries...
do_boundaries_check(mousesensitivity, mouse_center_x, mouse_center_y)
-- 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
drawpos_x = mouse_center_x + (current_h * mousesensitivity)
drawpos_y = mouse_center_y + (current_p * mousesensitivity)

-- draw cursor
draw_cursor()
  end
       else
          ba.setControlMode(NORMAL_CONTROLS)
  io.MouseControlStatus = true
  old_h = cap_value(-1.0, 1.0, controls.Heading)
  old_p = cap_value(-1.0, 1.0, controls.Pitch)
   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

Also, here's a sample configuration file:
data/config/mouse_script.cfg
Code: [Select]
Sensitivity:         250
Sensitivity Curve:   1.0
Control Mode:        1
Deadzone:            2
Mouse Invert:        0
Indicator Color:     255, 255, 0, 50

 

Offline jr2

  • The Mail Man
  • 212
  • Posts: 7,391
  • It's prounounced jayartoo 0x6A7232
    • Steam
Re: Toggleable scripted mouse (v. 0.9)
Nice.  :)

 

Offline z64555

  • 210
  • Posts: 1,748
  • Self-proclaimed controls expert
    • Minecraft
    • Steam
Re: Toggleable scripted mouse (v. 0.9)
Related script: http://www.hard-light.net/forums/index.php/topic,44634.0.html

Note that the script would only work if you have WLCoolman's Pre-commit Build, at least as far as I know.

Has anybody used this script before? Is there any significant differences between Nuke's code and Sushi's code?

EveningTea: Time to go Freeman on this cultist..
* EveningTea pulls crowbar off his shoulderstrap and charges screaming incoherently across the marsh *

z64555: bro. do you even salad

 

Offline Nuke

  • Ka-Boom!
  • 212
  • Posts: 6,769
  • Mutants Worship Me
Re: Toggleable scripted mouse (v. 0.9)
my code kinda dates back to when scripting was kinda new and not quite set in its ways. im actually somewhat supprised that it still works. it also raped physics. wanderer did the second incarnation of the script which had much better features than what i had in mine. it also took advantages to new c code implemented by wanderer to give better access to input variables.  sushi's script is the third incarnation, which should be the most up to date of all of the above. i havent used it yet, since i usually fly stick. so im not quite sure how it compares to the first two.
I can no longer sit back and allow communist infiltration, communist indoctrination, communist subversion, and the international communist conspiracy to sap and impurify all of our precious bodily fluids.

Nuke's Scripting SVN

 

Offline Sushi

  • Art Critic
  • 211
  • Posts: 2,784
Re: Toggleable scripted mouse (v. 0.9)
my code kinda dates back to when scripting was kinda new and not quite set in its ways. im actually somewhat supprised that it still works. it also raped physics. wanderer did the second incarnation of the script which had much better features than what i had in mine. it also took advantages to new c code implemented by wanderer to give better access to input variables.  sushi's script is the third incarnation, which should be the most up to date of all of the above. i havent used it yet, since i usually fly stick. so im not quite sure how it compares to the first two.

The new feature is the in-mission toggle. It's basically the same as Wanderer's incarnation (the one on the wiki, linked in the first post) but the middle mouse button toggles the "freelancer-like-mouse" mode on and off instead of just re centering it. So (for example) you can fly with normal mouse control, then when you get into a turning fight where you would normally be dragging the mouse all over to keep turning, you could switch to joy-mouse mode and turn continuously without having to pick up and reset the mouse.

It was JR2's idea, and I thought it was a good one, so I went ahead and implemented it.

 

Offline potatochief

  • 22
  • Posts: 6
Re: Toggleable scripted mouse (v. 0.9)
im trying to run your mouse codes... cant seem to get them running? im a coding idiot :(
tried PPjoy awhile ago, it worked but left me dizzy and nauseated since the movement isn't as smooth say as freelancer
(to my memory)
How to I make this work? lay man's please :)

 

Offline Sushi

  • Art Critic
  • 211
  • Posts: 2,784
Re: Toggleable scripted mouse (v. 0.9)
im trying to run your mouse codes... cant seem to get them running? im a coding idiot :(
tried PPjoy awhile ago, it worked but left me dizzy and nauseated since the movement isn't as smooth say as freelancer
(to my memory)
How to I make this work? lay man's please :)

1. Open notepad. Paste in the first file (highlighted in green). Save the file as "mouse-sct.tbm"
2. Do the same thing for the second file, saving it as "mouse_script.cfg"
3. Go to your Freespace2 directory. Open the "data" folder, then the "tables" folder (create it if it does not exist). Put mouse-sct.tbm inside of data/tables.
4. Open or create a Freespace2/data/config directory and put mouse_script.cfg there.
5. Run the game with any reasonably recent build (including 3.6.12).

That's it! You should be able to toggle between regular mouse control and "freelancer-like" mouse control by clicking the middle mouse button.

 

Offline potatochief

  • 22
  • Posts: 6
Re: Toggleable scripted mouse (v. 0.9)
Thanks sushiman!
Got the controls to work... will have the new controls "reviewed" by my younger brothers and cousins that i coerced to play freespace over 8 years ago!
I'll also give it a good run and see if i can adapt to using it as well. thanks!
« Last Edit: August 09, 2011, 02:34:23 am by potatochief »

 

Offline technopredator

  • Does not compute
  • 27
  • Posts: 132
  • Don't get mad, consequences are revenging you.
    • World Community Grid
Re: Toggleable scripted mouse (v. 0.9)
I'm all for the joystick improvement but I think the activation would be better with a keyboard key. I have to break the bound of the middle mouse button (button 3) and restart the launcher to make it work. 'Shields Manager' script uses '0' to enable/disable itself and it works great.

 Anyway this script needs work, its dynamic acceleration blows, the faster I move the mouse on the pad the slower the ship moves, it's backwards, but it has a future because I managed to get it close to what I want with this configuration:

Code: [Select]
Sensitivity:         362
Sensitivity Curve:   3.9
Control Mode:        1
Deadzone:            2
Mouse Invert:        0
Boundary Limit:      0
Indicator Color:     255, 255, 0, 64

but why I can't see the cursor? is that option removed? I can see the cursor in 'joystick mode' but is not what I expected, I like the concept but the functionality IMO should be the same as 'mouse mode' in the center, the approximation to a real joystick I don't think is useful at all, it requires to move the mouse like crazy. A Merge between both modes & with a reverse dynamic acceleration fix, would be IMO the perfect mouse driver, good enough to incorporate it into the next release of FS2O.
« Last Edit: January 11, 2012, 12:57:40 am by technopredator »

 

Offline jr2

  • The Mail Man
  • 212
  • Posts: 7,391
  • It's prounounced jayartoo 0x6A7232
    • Steam
Re: Toggleable scripted mouse (v. 0.9)
If it ever makes a release as an official part of FS2O, I think there are some who will commit mass homicide as they are afraid that the mouse's accuracy plus the joystick's ability to not have to stop turning every 2 seconds will put them at a disadvantage.

 

Offline technopredator

  • Does not compute
  • 27
  • Posts: 132
  • Don't get mad, consequences are revenging you.
    • World Community Grid
Re: Toggleable scripted mouse (v. 0.9)
Those would the "5 guys" that can do goood with keyboard and only a little mouse. Old-school people, I used to be one of them but I grew up and I'm progress-wise oriented now.

 

Offline Sushi

  • Art Critic
  • 211
  • Posts: 2,784
Re: Toggleable scripted mouse (v. 0.9)
The option to toggle via keyboard (probably just specify the key in the config) would be a nice feature. I don't have time to implement it right now though, so someone else will have to.

Can't speak to the dynamic acceleration bit, I didn't write that (it was part of the existing script).


 

Offline jr2

  • The Mail Man
  • 212
  • Posts: 7,391
  • It's prounounced jayartoo 0x6A7232
    • Steam
Re: Toggleable scripted mouse (v. 0.9)
Nuke did, right?

 

Offline Sushi

  • Art Critic
  • 211
  • Posts: 2,784
Re: Toggleable scripted mouse (v. 0.9)
Nuke did, right?

I think the script originates with Nuke, although I think Wanderer may have worked on it too at some point.

 

Offline iVoid

  • 26
  • Posts: 71
Re: Toggleable scripted mouse (v. 0.9)
Can somebody please give a quick "status report" on the implementation of this in FS2O?

 

Offline General Battuta

  • Poe's Law In Action
  • 214
  • Posts: 22,880
  • i wonder when my postcount will exceed my iq
Re: Toggleable scripted mouse (v. 0.9)
Can somebody please give a quick "status report" on the implementation of this in FS2O?

It's working and you can use it right now?

 

Offline iVoid

  • 26
  • Posts: 71
Re: Toggleable scripted mouse (v. 0.9)
Can somebody please give a quick "status report" on the implementation of this in FS2O?

It's working and you can use it right now?

You know what I meant.

 

Offline General Battuta

  • Poe's Law In Action
  • 214
  • Posts: 22,880
  • i wonder when my postcount will exceed my iq
Re: Toggleable scripted mouse (v. 0.9)
No, I don't. What are you asking?

 

Offline iVoid

  • 26
  • Posts: 71
Re: Toggleable scripted mouse (v. 0.9)
Ok, sorry then.

I know this already works with the scripting system (I use it). What I'd like to know is if it will actually be integrated into SCP as an option as it was voted.

 

Offline jr2

  • The Mail Man
  • 212
  • Posts: 7,391
  • It's prounounced jayartoo 0x6A7232
    • Steam
Re: Toggleable scripted mouse (v. 0.9)
...over the cold, bloodied bodies of the joystick elitists.  :doubt: