Jump to content

Should I Buy Throttle Or Rudder Pedals


91 replies to this topic

#41 evilC

    Member

  • PipPipPipPipPipPipPipPip
  • Legendary Founder
  • Legendary Founder
  • 1,298 posts
  • LocationLondon, UK

Posted 01 March 2016 - 11:26 AM

View Postmclang, on 01 March 2016 - 01:05 AM, said:

Although I am interested how you would code around the throttle issue Posted Image

UCR could do mouse emulation also... You could do way more in UCR than you could in FreePIE, as freepie is limited in scope, whereas AutoHotkey (Which UCR is written in) is a full-blown language that you can basically do anything with.

Given the command GetKeyState() which can be used to read axis states (eg GetKeyState("2JoyX") ), which returns a value from 0 to 100 and SetAxisState(), which is a (simplified, purely for illustration purposes) command which sets the state of the output axis, then the code would look something like this:

out := 50  ; set output axis to start in the middle
Loop {
   ; Get state of input - the Y axis on joystick 2
   ax := GetKeyState("2JoyY")
   ; Subtract 50 from input axis (to convert to -50->+50 scale) and divide by 10 (to convert to -5 to +5 scale)
   ; 10 is an arbitrary divider - you could tweak it to alter the "sensitivity" of the throttle
   delta := (ax - 50) / 10
   ; Change the current value of the output by the amount held in "delta"
   out := out + delta
   ; Ensure output values stay within valid range
   if (out > 100)
	  out := 100
   else if (out < 0)
	  out := 0
   ; Set new state of output axis
   SetAxisState(out)
   ; Sleep a bit to avoid chewing too much CPU - updates every 10ms are enough
   Sleep 10
}

In reality the code would be a little more complex (eg the input reports in the scale 0->100, and the output vJoy stick is in the range of 0->32767, so conversion between the two scales is needed - ie multiply by 327.67), but the core logic would be identical: Hold current value in a variable, then read the input stick, adjust current value of output according to the input, then set the output axis.

Edited by evilC, 01 March 2016 - 11:44 AM.


#42 mclang

    Member

  • PipPipPip
  • The Privateer
  • The Privateer
  • 97 posts

Posted 01 March 2016 - 11:29 PM

FreePIE doesn't seem too limited, but I trust your word for it.

Only thing is I don't know anything about AHK programming, or making plugins for UCR, so in order to do e.g that mouse emulation with AHK/UCR I need to learn a lot. With FreePIE these things seems easier. But if I needed UCR for other purposes also, it would be more sensible to do all things with UCR.

Wait a moment - you are speaking about UCR not UJR Posted Image

#43 evilC

    Member

  • PipPipPipPipPipPipPipPip
  • Legendary Founder
  • Legendary Founder
  • 1,298 posts
  • LocationLondon, UK

Posted 02 March 2016 - 04:29 AM

UCR is the new replacement for UJR.
UCR's functionality can be extended through the use of plugins, whereas UJR was not extendable.
UCR supports all input / output types, whereas UJR only supported joystick.

UCR link

Yes, if you are new to AHK, diving right into writing UCR plugins may be a bit much, especially if you do not know object-orientated programming.

However, you could write the code you wish as a standalone AHK script, then try to write it as a UCR plugin.
In order to do this in vanilla AHK, you would need my vJoy library from here. See the linked GitHub page for download and a wiki containing instructions.

However, for this plugin, it would be fairly simple to take one of the existing plugins (eg the Axis to Axis plugin) and to tweak it slightly to make the plugin you wish.
I just took the Axis to Axis plugin and bodged together a UCR plugin to do what you want - I only had to change ~5 lines of code.
Divider is hard-coded at 10 for the moment, but normally I would make a Gui Control to allow the user to alter this at run-time.
The code is untested at the moment, I am at work and cannot run it.

save this code as AxisToRelative.ahk, place the file in Plugins\User
class AxisToRelative extends _Plugin {
Type := "Remapper (Axis To Relative Axis)"
Description := "Maps an axis input to a virtual axis output and converts to relative mode"
vAxis := 0
vDevice := 0
vState := 0
; Set up the GUI to allow the user to select input and output axes
Init(){
  Gui, Add, Text, % "xm w125 Center", Input Axis
  Gui, Add, Text, % "x+5 yp w100 Center", Input Preview
  Gui, Add, Text, % "x+5 yp w40 Center", Invert
  Gui, Add, Text, % "x+5 yp w40 Center", Deadzone
  Gui, Add, Text, % "x+5 yp w40 Center", Sensitivity
  Gui, Add, Text, % "x+5 yp w125 Center", Output Virtual Axis
  Gui, Add, Text, % "x+5 yp w100 Center", Output Preview
 
  this.AddInputAxis("InputAxis", 0, this.MyInputChangedState.Bind(this), "xm w125")
  Gui, Add, Slider, % "hwndhwnd x+5 yp w100"
  this.hSliderIn := hwnd
  this.AddControl("Invert", 0, "CheckBox", "x+20 yp+3 w30")
  this.AddControl("Deadzone", 0, "Edit", "x+10 yp-3 w30", "0")
  Gui, Add, Text, % "x+0 yp+3", `%
  this.AddControl("Sensitivity", 0, "Edit", "x+10 yp-3 w30", "100")
  Gui, Add, Text, % "x+0 yp+3", `%
  this.AddOutputAxis("OutputAxis", this.MyOutputChangedValue.Bind(this), "x+15 yp-3 w125")
  Gui, Add, Slider, % "hwndhwnd x+5 yp w100"
  this.hSliderOut := hwnd
}
 
; The user changed options - store stick and axis selected for fast retreival
MyOutputChangedValue(value){
  this.vAxis := value.axis
  this.vDevice := value.DeviceID
}
 
; The user moved the selected input axis. Manipulate the output axis accordingly
MyInputChangedState(value){
  static StickOps := UCR.Libraries.StickOps
 
  GuiControl, , % this.hSliderIn, % value
  value := StickOps.AHKToInternal(value)
  if (this.vAxis && this.vDevice){
   if (this.GuiControls.Deadzone.value){
	value := StickOps.Deadzone(value, this.GuiControls.Deadzone.value)
   }
   if (this.GuiControls.Sensitivity.value){
	value := StickOps.Sensitivity(value, this.GuiControls.Sensitivity.value)
   }
   if (this.GuiControls.Invert.value){
	value := StickOps.Invert(value)
   }
   value := value / 10
   value := this.vState + value
   this.vState := value
  
   value := StickOps.InternalToAHK(value)
  
   GuiControl, , % this.hSliderOut, % value
   value := StickOps.AHKToVjoy(value)
   this.OutputAxes.OutputAxis.SetState(value)
  }
}
}

Edited by evilC, 02 March 2016 - 04:31 AM.


#44 mclang

    Member

  • PipPipPip
  • The Privateer
  • The Privateer
  • 97 posts

Posted 02 March 2016 - 10:05 AM

Okay, I have to check UCR out.

I know C, C#, ruby and the kind quite well, so object oriented aspect of the task is not a problem. Although currently I like to use F# where ever I can :)

After I quickly checked how to get started with AHK, it seems that using your UCR is by far the easier way. And I will gladly help in testing UCR when I have time for it. Currently I'm playing Warhammer Dawn of War II with my brother, so MWO is on hold.

By the way, is there Fire Control plugin already for UCR? I'm wondering how to make the jumpjet spam feature work with joystick button...

#45 mclang

    Member

  • PipPipPip
  • The Privateer
  • The Privateer
  • 97 posts

Posted 02 March 2016 - 11:07 AM

What a coincidence Posted Image

I just happened to found your "Relative Throttle" Script:
http://mwomercs.com/...ost__p__3878064

Too bad that that seems so spit various vJoyInterface.dll related errors although I have vjoy installed... Maybe I have too new version or something? In any case, using UCR will probably be better approach in long run because of my need for mouse emulation and Fire Control.

#46 evilC

    Member

  • PipPipPipPipPipPipPipPip
  • Legendary Founder
  • Legendary Founder
  • 1,298 posts
  • LocationLondon, UK

Posted 02 March 2016 - 12:00 PM

Heh, I totally forgot I wrote that :P

Are you perchance using Win8 or Win10?
There have been problems in the past with vJoy installs getting messed up on these OSes, but I thought we nailed this issue a while back.
Latest versions of vJoy are not a problem, I doubt Shaul (the author of vJoy) would ever make compatibility breaking changes to vJoy without consulting me first, as my scripts count for a large proportion of his user-base...
I would recommend trying to unistall vJoy and re-install.

If you can code OOP, then you should have no problem coding UCR plugins - at the moment it is still only me writing plugins for UCR, so I could really do with other people trying to write plugins and feeding back what is good and bad about the system from a plugin author's point of view. It's still extremely early days for the project, so now is the time to make major changes if they need to be made.

Regarding porting FC to a UCR plugin, that is definitely something I want to do, but I just don't have the time at the moment. The JJ spam part would be trivial though, and would be very suitable as a first plugin - it would be a simple case of deriving from the button to button plugin and tweaking it slightly.

#47 mclang

    Member

  • PipPipPip
  • The Privateer
  • The Privateer
  • 97 posts

Posted 02 March 2016 - 10:37 PM

Yes, I'm using Windows 10, Developer Priview to be exact.

I will reinstall vJoy and try again... But wouldn't using UJR fail also if vJoy installation is one to blame? Does UCR work without vJoy by any change?

#48 evilC

    Member

  • PipPipPipPipPipPipPipPip
  • Legendary Founder
  • Legendary Founder
  • 1,298 posts
  • LocationLondon, UK

Posted 03 March 2016 - 04:39 AM

The code UJR uses to detect the location of the vJoy DLL and the code that UCR uses are slightly different.

Yes, UCR should work without vJoy, but I haven't really tested that code path yet.

#49 mclang

    Member

  • PipPipPip
  • The Privateer
  • The Privateer
  • 97 posts

Posted 05 March 2016 - 10:07 AM

Hello.

I have been sick and unable to try or test anything. And I'm on a trip nexy week so it will be at least next weekend before I have time to get back to these matters.

#50 evilC

    Member

  • PipPipPipPipPipPipPipPip
  • Legendary Founder
  • Legendary Founder
  • 1,298 posts
  • LocationLondon, UK

Posted 06 March 2016 - 04:16 AM

No worries.
I don't get notifications for this forum, so if you want to grab my attention, post on the AHK forum thread where you download UCR.

#51 mclang

    Member

  • PipPipPip
  • The Privateer
  • The Privateer
  • 97 posts

Posted 14 March 2016 - 01:16 PM

Wasn't easy, but I got something working:
class ButtonSpam extends _Plugin {
Type := "Remapper (Spams a Button)"
Description := "Maps button to other button so that continuously pressing the first spams the second"
ButtonState := 0
Init() {
  this.SpamButtonFn := this.SpamButton.Bind(this) ; Needed for 'Settimer' ?
  Gui, Add, Text, xm y+10, % "Press"
  this.AddInputButton("IB1", 0, this.InputChangedState.Bind(this, "IB1"), "x+5 yp-2 w150")
  Gui, Add, Text, x+5 yp+2, % "to spam"
  this.AddOutputButton("OB1", 0, "x+5 yp-2 w150")
  Gui, Add, Text, x+5 yp+2, % "in every"
  this.AddControl("SpamInterval", 0, "Edit", "x+5 yp w30", "250")
  Gui, Add, Text, x+5 yp+2, % "ms"
}
SpamButton() {
  if (this.ButtonState == 0)
   this.ButtonState := 1
  else
   this.ButtonState := 0
  this.OutputButtons.OB1.SetState(this.ButtonState)
}
InputChangedState(Name, e) {
  fn := this.SpamButtonFn
  if (e) {
   Settimer, % fn, 250 ; this.GuiControls.SpamInterval.value
  } else {
   Settimer, % fn, Off
  }
}
}

Biggest problem was making the timer to work. Documentation at AHK site was quite different than the code needed here, and I still don't know how to use "SpamInterval" value with the timer. Another remaining problem is that I can't get "in every" and "ms" text fields to be visible together with "OB1" listbox. If I remove "OB1" both fields become visible.

How should I time the spamming functionality, is another value like off time needed so that JJ spam works properly?

#52 evilC

    Member

  • PipPipPipPipPipPipPipPip
  • Legendary Founder
  • Legendary Founder
  • 1,298 posts
  • LocationLondon, UK

Posted 15 March 2016 - 12:21 PM

OK, so your script basically works, it's just you probably weren't aware of a feature of AHK - it observes "key repeats".
When you hold an input key, by default windows repeats it. This causes InputChangedState to repeatedly get fired, so things werent quite working right.
In the drop-down list for the input, set "Suppress Repeats" to ON.

RE the timers:

Commands - ie commandname, param, param are "old style" %var% syntax by default. You cannot do %obj.prop% so just force "expression syntax" using percent space

Also note that bound functions (ie % fn) cannot be passed to commands like % this.SpamButtonFn, this is the reason for storing it on this.SpamButtonFn but using it as % fn.

Also note that SetTimer does not fire straight away, so you want to call the function just after you set the timer.

Settimer, % fn, % this.GuiControls.SpamInterval.value
fn.Call() ; SetTimer does nothing straight away, so call now


Regarding the buttonstate toggle:
You could have just done
this.OutputButtons.OB1.SetState(!this.OutputButtons.OB1.State)


I guess you were trying to write a sort of Fire Control in UCR? If so, your code would only "fire" at half the rate in the interval box because it toggled each time.
Also, the main reason I have not ported FC to UCR yet is because of "Key Delay". This is a setting for AHK scripts that determines how long to wait between key events, and can be critical to the operation of a script (eg MWO needs ~50ms between the down and up event of a key to recognize it). As each plugin is actually all running in the same "script", they need to share this value, or there needs to be an option to specify these values for any given output.
Seeing as I have not written or even designed this feature yet, I didn't really want to have to address it for an FC re-write.
The other thing UCR does not have is a way to define a sequence of keys, ie the "Fire Sequence" box in FC.

So, with that in mind, here is my interpretation of your script- basically a rapid fire script for MWO.

class ButtonSpam extends _Plugin {
Type := "Remapper (Spams a Button)"
Description := "Maps button to other button so that continuously pressing the first spams the second"
ButtonState := 0
Init() {
   this.SpamButtonFn := this.SpamButton.Bind(this) ; Needed for 'Settimer' ?
   Gui, Add, Text, xm y+10, % "Press"
   this.AddInputButton("IB1", 0, this.InputChangedState.Bind(this, "IB1"), "x+5 yp-2 w150")
   Gui, Add, Text, x+5 yp+2, % "to spam"
   this.AddOutputButton("OB1", 0, "x+5 yp-2 w150")
   Gui, Add, Text, x+5 yp+2, % "in every"
   this.AddControl("SpamInterval", 0, "Edit", "x+5 yp w30", "250")
   Gui, Add, Text, x+5 yp+2, % "ms"
}

SpamButton() {
  SetKeyDelay, 0, 0 ; Remove all delays before or after sending key events
  this.OutputButtons.OB1.SetState(1) ; Press button
  Sleep 50 ; MWO likes 50ms between down and up event. Hard-code this for now.
  this.OutputButtons.OB1.SetState(0) ; Release button
}

InputChangedState(Name, e) {
  fn := this.SpamButtonFn
  if (e) {
   ;~ if (this.InputButtons.IB1.State == 1)
    ;~ return ; force "no repeat" mode, even if user has it off
   Settimer, % fn, % this.GuiControls.SpamInterval.value
   fn.Call()   ; SetTimer does nothing straight away, so call now
  } else {
   Settimer, % fn, Off
  }
}
}


#53 mclang

    Member

  • PipPipPip
  • The Privateer
  • The Privateer
  • 97 posts

Posted 15 March 2016 - 10:52 PM

Yes, basically I don't know anything about AHK, so it's like stabbing in the dark.

I noticed that my spam buttons script wasn't firing as fast as it should, so I knew there was something amiss. And I mean something in addition to that the timer function doesn't fire right away which I knew. I tried also SpamButtons function like yours with "sleep", but it didn't work, probably because I didn't know about "Key Delay".

Should I fork UCR, add this "Jumjet Spam" plugin and make a pull request, or do I keep it myself? Should I call it "Fire Control" so that it can later be used to do similar things like chain fire? Is key sequence support coming?

I'm also trying to do mouse emulation by using two input axises (joystick x and y) that fire same function, where mouse is moved using "Mousemove" like this:
CoordMode, Mouse, Screen
MouseMove, mouseX, mouseY, 0

Problem is the joystick X and Y values are small, so most of the warthog resolution is lost, which makes cursor movement jerky and thus unusable in aiming. I have 1920x1200 monitor and to get full range of motion I have to use multipliers like 20 and 13, so I think the axis ranges AHK uses are something like 0-100.

Is there way to use full range of the Warthog axis values in AHK/UCR? This is currently the most important thing to me, jumpjet spam and proper chain fire come distant second.

#54 evilC

    Member

  • PipPipPipPipPipPipPipPip
  • Legendary Founder
  • Legendary Founder
  • 1,298 posts
  • LocationLondon, UK

Posted 16 March 2016 - 05:16 AM

Hold on to that plugin for now. If you make tweaks to the "Core" plugins then that would warrant a pull request, and at some point I would imagine that fire timing / button spamming etc would make it in as a core plugin, but for now, until I address the issues I mentioned previously, it's probably best to hang fire.

When AHK reads joystick values, it is in the range 0..100, however, this is a floating point number, so there should be no loss of resolution - ie there are way more than 100 "units".
If trying to control the mouse in such a way that MWO would recognize it for torso twist input, I think MouseMove is going to be no use.
FPS games in general ignore the mouse cursor and are solely concerned with the "delta" information coming from the mouse - this is why in most FPS games you can turn endlessly in one direction. If it used mouse cursor position, it would stop turning once the mouse cursor hit the edge of the screen. Some games to read the cursor position and reset it to center, but in general they do not.
You probably need the mouse_event WinAPI call - I made some posts about this recently here.

If you are trying to implement a proper "Absolute" stick aim mode, this will not be trivial, as there are lots of variables.
For example, different mechs would have different ranges of torso twist, and if the mouse moves outside of the twist range, that extra movement is "thrown away" and the new position becomes the limit.
To clarify:
Torso is centered. Stick moves to 100% deflection left, so you move the mouse "all the way" left - lets say you move the mouse 1000 units left. But what if the limit of the torso twist is 900 units? The extra 100 will be thrown away.
So then you release the stick, and it moves the mouse 1000 units to the right - this should result in the torso being centered, but in fact you end up 100 units to the right of center.
This issue is compounded if you have arm lock on. With arm lock on, if you move the mouse 100 units left in 1 second, but the twist rate of the torso can only handle 90 units / sec, then it also throws that excess movement away. I think PGI made some improvements in this area recently, but AFAIK arm lock on is still different to arm lock off in this regard.

#55 mclang

    Member

  • PipPipPip
  • The Privateer
  • The Privateer
  • 97 posts

Posted 16 March 2016 - 09:09 AM

Okay, so it's back to FreePIE for the mouse emulation.

I have read quite a lot about how Loc Nar does things, so I know how different torso twist ranges complicate matters. And then there is the arm movement also... I have a list of the movement ranges of my mechs and I'm planning to make the ratio between joystick and mouse switchable. And to counter the centering issue I have logic that centers torso to the legs whenever joystick is middle.

If that autocentering makes it difficult to aim, I can always throw that logic away and use keyboard C key.

What can go wrong =)

#56 mclang

    Member

  • PipPipPip
  • The Privateer
  • The Privateer
  • 97 posts

Posted 16 March 2016 - 09:31 AM

Checked the link.

It seems that the moving part is not so difficult after all, all you have to do is keep track of the current positions and move mouse according to the differences. But how is the performance of AHK scripts, can it pose any problems?

And still, basic mouse emulation is easier with FreePIE, couple lines only.

#57 evilC

    Member

  • PipPipPipPipPipPipPipPip
  • Legendary Founder
  • Legendary Founder
  • 1,298 posts
  • LocationLondon, UK

Posted 16 March 2016 - 12:43 PM

For a subject like absolute aim for mwo, I would say that it would be best to start off with a simple stand-alone AHK script, and if you can get that to work, then make it a UCR plugin. The main purpose of UCR is to make scripts shareable, configurable, and to be able to easily enable or disable groups of scripts.

So, with that said, here is a quick stab I had at an absolute aim script:
#SingleInstance force
; Configure these
INPUT_AXIS := "4JoyX" ; Joystick axis to use as input
MAX_WIDTH := 1500  ; Mouse units of twist range. Varies with mech
; non-configurables
CONV_RATIO := MAX_WIDTH / 101
curr_input_x := 0
curr_output_x := 0
Loop {
x := GetKeyState(INPUT_AXIS) - 50 ; Shift scale to zero-centered
if (x != curr_input_x){
  output_x := round(x * CONV_RATIO)
  diff := output_x - curr_output_x
  dllCall("mouse_event","Int",0x0001,"Int",diff,"Int",0,"Int",0,"UPtr",0)
  curr_input_x := x
  curr_output_x := output_x
}
Sleep 10
}

It only works for the X axis, but I have had a play with it (just in windows, cant play mwo on this pc really) and it seems to not drift from centre.
Obviously though, in MWO, if your mech hits its twist limit but the script doesnt realize this, then it will drift out and only hitting C will help you.
What AHK has over FreePie in this regard is the ability to do something about these issues. For example, AHK has pixel detection, so you can potentially have the script look at the screen to detect if the mech hits it's twist limit, and also potentially to help calibrate centering. Arent there those new icons that show up at the bottom of the HUD when you hit the twist limit?

Also with AHK you can potentially write a calibration routine. Drop into testing grounds with a mech, hit a button and it twists the torso until it detects it has hit the limit, then tells you the value you need or even saves it to a settings file - quicker than trial and error and potentially even do-able on the fly, say at the start of a match.

#58 mclang

    Member

  • PipPipPip
  • The Privateer
  • The Privateer
  • 97 posts

Posted 17 March 2016 - 10:21 AM

Thanks, I will add Y axis and try that AHK script.

But why is this completely different looking that the code UCR needs although UCR is based on AHK? And is there some reason why I shouldn't just learn UCR way of doing things instead of messing with AHK also? I'm asking this because if/when I need the jump jet spam with absolute aim I'd rather run just one additional program instead of two.

Is there some limit in UCR how often InputAxisChanged event can fire, or is it around the 10ms value that you have put in the above loop?

That calibration routine would be really cool, but I remember reading that someone (you?) had some kind of problems doing similar things in one occasion... I may remember wrong though.

#59 evilC

    Member

  • PipPipPipPipPipPipPipPip
  • Legendary Founder
  • Legendary Founder
  • 1,298 posts
  • LocationLondon, UK

Posted 17 March 2016 - 11:34 AM

That script does not use classes or even functions, so everything is a lot simpler.
eg settimer doesn't need a bound function (because the label called does not need "this" to be in scope)

UCR plugins are classes for a multitude of reasons (Mainly because each plugin script could exist many times, so instantiating a class is the simplest way to do that).

When it comes to inputs and outputs, in UCR these are defined by the end-user via the custom guicontrols, whereas the script above's inputs and outputs are hard-coded. I/O can be slightly different in vanilla AHK tho, eg you cannot subscribe to a joystick axis and have a function called whenever it moves (Like you can in UCR) - you have to do an endless loop and poll state continuously. UCR does this for you behind the scenes, and only fires the function in your plugin when the axis actually changes.

So all the syntax in UCR plugins (Apart from I/O) is completely "normal" class-based AHK.

If writing UCR plugins, you are probably best mocking up the plugin in normal AHK, as you can be sure that if it doesn't work it is due to your script, not some problem due to UCR. Also you could ask for help in the normal AHK forum and get better answers with a normal AHK script.

Regarding timing in UCR, for joystick axes, these are polled every 10ms. 10ms is the shortest amount of time you can use with the Sleep command, and I have not yet implemented a more accurate / higher granularity method such as using the QPX API.

Regarding pixel detection - yeah I had some problems in the past, but I was trying something way more tricky. The twist limit indicators are bloody great icons that should be easy to detect presence of - the big question is whether they only appear when you reach the absolute limit of twist, or do they appear when you are near the limit? If the former, we should be laughing.

#60 mclang

    Member

  • PipPipPip
  • The Privateer
  • The Privateer
  • 97 posts

Posted 17 March 2016 - 12:48 PM

I have to check those icons because I don't know what you are talking about.

Before even thinking about calibration though, I have to make usable absolute aim script and test it with my extended joystick. After making the basics work after your example, calibrating the X and Y ratios e.g for my Highlander shouldn't be too big a problem.

But tonight went testing that rapid fire plugin. I got it working as per your example, except I still cannot see ANY text field after the output button selection box. Edit boxes to hold down time and fire rate are visible though. If I take out the output button selection box, the text fields become visible... So do you know what gives?

By the way I thing I had the Settimer trigger time and Sleep time wrong way... I mean for jumpjet spam the button needs to be pressed down for 250ms and help up 50ms between presses, right?





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users