mirror of
https://github.com/izzy2lost/Super3.git
synced 2026-07-05 15:18:38 -07:00
Added manual shifter to osd
This commit is contained in:
@@ -57,13 +57,13 @@ InputAccelerator = KEY_W,JOY1_RZAXIS_POS
|
||||
InputBrake = KEY_X,JOY1_ZAXIS_POS
|
||||
|
||||
; Manual transmission (LB/RB + 4-speed on face buttons)
|
||||
InputGearShiftUp = KEY_Y,JOY1_BUTTON6
|
||||
InputGearShiftDown = KEY_H,JOY1_BUTTON5
|
||||
InputGearShift1 = KEY_Q,JOY1_BUTTON3
|
||||
InputGearShift2 = KEY_W,JOY1_BUTTON1
|
||||
InputGearShift3 = KEY_E,JOY1_BUTTON4
|
||||
InputGearShift4 = KEY_R,JOY1_BUTTON2
|
||||
InputGearShiftN = KEY_T
|
||||
InputGearShiftUp = KEY_I,JOY1_BUTTON6
|
||||
InputGearShiftDown = KEY_K,JOY1_BUTTON5
|
||||
InputGearShift1 = KEY_7,JOY1_BUTTON3
|
||||
InputGearShift2 = KEY_8,JOY1_BUTTON1
|
||||
InputGearShift3 = KEY_9,JOY1_BUTTON4
|
||||
InputGearShift4 = KEY_0,JOY1_BUTTON2
|
||||
InputGearShiftN = KEY_6
|
||||
|
||||
; Fighting game buttons (X/A/B/Y)
|
||||
InputPunch = KEY_A,JOY1_BUTTON3
|
||||
|
||||
@@ -59,6 +59,13 @@ void AndroidInputSystem::SetVirtualWheelEnabled(bool enabled)
|
||||
m_virtualJoyX = 0;
|
||||
}
|
||||
|
||||
void AndroidInputSystem::SetVirtualShifterMode(bool shift4, bool shiftUpDown)
|
||||
{
|
||||
m_virtualShifterShift4 = shift4;
|
||||
m_virtualShifterUpDown = shiftUpDown;
|
||||
m_lastVirtualGear = -1;
|
||||
}
|
||||
|
||||
bool AndroidInputSystem::UseVirtualWheel() const
|
||||
{
|
||||
return m_virtualWheelEnabled && m_controllers.empty();
|
||||
@@ -113,6 +120,14 @@ void AndroidInputSystem::ApplyConfig(const Util::Config::Node& config)
|
||||
|
||||
m_touchThrottle.a = keySc(get("InputAccelerator", "KEY_W"), SDL_SCANCODE_W);
|
||||
m_touchBrake.a = keySc(get("InputBrake", "KEY_X"), SDL_SCANCODE_X);
|
||||
|
||||
m_touchShiftUp.a = keySc(get("InputGearShiftUp", "KEY_I"), SDL_SCANCODE_I);
|
||||
m_touchShiftDown.a = keySc(get("InputGearShiftDown", "KEY_K"), SDL_SCANCODE_K);
|
||||
m_touchShift1.a = keySc(get("InputGearShift1", "KEY_7"), SDL_SCANCODE_7);
|
||||
m_touchShift2.a = keySc(get("InputGearShift2", "KEY_8"), SDL_SCANCODE_8);
|
||||
m_touchShift3.a = keySc(get("InputGearShift3", "KEY_9"), SDL_SCANCODE_9);
|
||||
m_touchShift4.a = keySc(get("InputGearShift4", "KEY_0"), SDL_SCANCODE_0);
|
||||
m_touchShiftN.a = keySc(get("InputGearShiftN", "KEY_6"), SDL_SCANCODE_6);
|
||||
}
|
||||
|
||||
bool AndroidInputSystem::InitializeSystem()
|
||||
@@ -131,6 +146,7 @@ bool AndroidInputSystem::InitializeSystem()
|
||||
m_wheelFingerActive = false;
|
||||
m_wheelFinger = 0;
|
||||
m_virtualJoyX = 0;
|
||||
m_lastVirtualGear = -1;
|
||||
|
||||
SDL_GameControllerEventState(SDL_ENABLE);
|
||||
RefreshControllers();
|
||||
@@ -265,6 +281,58 @@ void AndroidInputSystem::HandleTouch(const SDL_TouchFingerEvent& tf, bool down)
|
||||
const float x = tf.x;
|
||||
const float y = tf.y;
|
||||
|
||||
// Virtual manual shifter (racing games): encoded in tf.x/tf.y from Java and keyed by a fixed fingerId.
|
||||
if ((m_virtualShifterShift4 || m_virtualShifterUpDown) && tf.fingerId == 1108)
|
||||
{
|
||||
if (!down)
|
||||
return;
|
||||
|
||||
if (m_virtualShifterShift4)
|
||||
{
|
||||
const float dx = x - 0.5f;
|
||||
const float dy = y - 0.5f;
|
||||
const bool inNeutral = (std::abs(dx) < 0.18f && std::abs(dy) < 0.18f);
|
||||
|
||||
int gear = m_lastVirtualGear;
|
||||
if (inNeutral)
|
||||
{
|
||||
gear = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool left = dx < 0.0f;
|
||||
const bool upper = dy < 0.0f;
|
||||
if (left && upper) gear = 1;
|
||||
else if (left && !upper) gear = 2;
|
||||
else if (!left && upper) gear = 3;
|
||||
else gear = 4;
|
||||
}
|
||||
|
||||
if (gear != m_lastVirtualGear)
|
||||
{
|
||||
switch (gear)
|
||||
{
|
||||
case 0: PulseKeys(m_touchShiftN, 120); break;
|
||||
case 1: PulseKeys(m_touchShift1, 120); break;
|
||||
case 2: PulseKeys(m_touchShift2, 120); break;
|
||||
case 3: PulseKeys(m_touchShift3, 120); break;
|
||||
case 4: PulseKeys(m_touchShift4, 120); break;
|
||||
default: break;
|
||||
}
|
||||
m_lastVirtualGear = gear;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Up/down shifter: tap upper/lower half.
|
||||
if (m_virtualShifterUpDown)
|
||||
{
|
||||
if (y < 0.5f) PulseKeys(m_touchShiftUp, 120);
|
||||
else PulseKeys(m_touchShiftDown, 120);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Virtual steering wheel (racing games): encoded in tf.x from Java and keyed by a fixed fingerId.
|
||||
if (UseVirtualWheel())
|
||||
{
|
||||
@@ -396,6 +464,37 @@ void AndroidInputSystem::HandleTouch(const SDL_TouchFingerEvent& tf, bool down)
|
||||
|
||||
void AndroidInputSystem::HandleTouchMotion(const SDL_TouchFingerEvent& tf)
|
||||
{
|
||||
if (m_virtualShifterShift4 && tf.fingerId == 1108)
|
||||
{
|
||||
// Avoid accidental neutral when passing through center during motion.
|
||||
const float dx = tf.x - 0.5f;
|
||||
const float dy = tf.y - 0.5f;
|
||||
if (std::abs(dx) < 0.18f && std::abs(dy) < 0.18f)
|
||||
return;
|
||||
|
||||
const bool left = dx < 0.0f;
|
||||
const bool upper = dy < 0.0f;
|
||||
int gear;
|
||||
if (left && upper) gear = 1;
|
||||
else if (left && !upper) gear = 2;
|
||||
else if (!left && upper) gear = 3;
|
||||
else gear = 4;
|
||||
|
||||
if (gear != m_lastVirtualGear)
|
||||
{
|
||||
switch (gear)
|
||||
{
|
||||
case 1: PulseKeys(m_touchShift1, 120); break;
|
||||
case 2: PulseKeys(m_touchShift2, 120); break;
|
||||
case 3: PulseKeys(m_touchShift3, 120); break;
|
||||
case 4: PulseKeys(m_touchShift4, 120); break;
|
||||
default: break;
|
||||
}
|
||||
m_lastVirtualGear = gear;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (UseVirtualWheel())
|
||||
{
|
||||
if (m_wheelFingerActive && tf.fingerId == m_wheelFinger)
|
||||
|
||||
@@ -22,6 +22,7 @@ public:
|
||||
|
||||
void SetGunTouchEnabled(bool enabled);
|
||||
void SetVirtualWheelEnabled(bool enabled);
|
||||
void SetVirtualShifterMode(bool shift4, bool shiftUpDown);
|
||||
|
||||
// Called from the SDL event loop (main thread).
|
||||
void HandleEvent(const SDL_Event& ev);
|
||||
@@ -114,6 +115,14 @@ private:
|
||||
DualScancode m_touchThrottle{SDL_SCANCODE_W, SDL_SCANCODE_UNKNOWN};
|
||||
DualScancode m_touchBrake{SDL_SCANCODE_X, SDL_SCANCODE_UNKNOWN};
|
||||
|
||||
DualScancode m_touchShiftUp{SDL_SCANCODE_I, SDL_SCANCODE_UNKNOWN};
|
||||
DualScancode m_touchShiftDown{SDL_SCANCODE_K, SDL_SCANCODE_UNKNOWN};
|
||||
DualScancode m_touchShift1{SDL_SCANCODE_7, SDL_SCANCODE_UNKNOWN};
|
||||
DualScancode m_touchShift2{SDL_SCANCODE_8, SDL_SCANCODE_UNKNOWN};
|
||||
DualScancode m_touchShift3{SDL_SCANCODE_9, SDL_SCANCODE_UNKNOWN};
|
||||
DualScancode m_touchShift4{SDL_SCANCODE_0, SDL_SCANCODE_UNKNOWN};
|
||||
DualScancode m_touchShiftN{SDL_SCANCODE_6, SDL_SCANCODE_UNKNOWN};
|
||||
|
||||
bool m_gunTouchEnabled = false;
|
||||
SDL_FingerID m_gunFinger = 0;
|
||||
bool m_gunFingerActive = false;
|
||||
@@ -124,6 +133,10 @@ private:
|
||||
int m_virtualJoyX = 0;
|
||||
JoyDetails m_virtualJoyDetails{};
|
||||
|
||||
bool m_virtualShifterShift4 = false;
|
||||
bool m_virtualShifterUpDown = false;
|
||||
int m_lastVirtualGear = -1;
|
||||
|
||||
MouseDetails m_mouseDetails{};
|
||||
int m_mouseX = 0;
|
||||
int m_mouseY = 0;
|
||||
|
||||
@@ -319,6 +319,10 @@ struct Super3Host {
|
||||
const bool vehicleGame = (game.inputs & (Game::INPUT_VEHICLE | Game::INPUT_HARLEY)) != 0;
|
||||
inputSystem.SetVirtualWheelEnabled(vehicleGame);
|
||||
|
||||
const bool shift4 = (game.inputs & Game::INPUT_SHIFT4) != 0;
|
||||
const bool shiftUpDown = (game.inputs & Game::INPUT_SHIFTUPDOWN) != 0;
|
||||
inputSystem.SetVirtualShifterMode(shift4, shiftUpDown);
|
||||
|
||||
// Apply Supermodel.ini overrides (Global + [ game ]) after the loader has determined game->name.
|
||||
ApplyIniOverrides(game.name);
|
||||
ApplyAndroidHardOverrides();
|
||||
|
||||
@@ -45,12 +45,24 @@ object AssetInstaller {
|
||||
|
||||
val lines = runCatching { ini.readLines() }.getOrNull() ?: return
|
||||
var changed = false
|
||||
val replacements =
|
||||
mapOf(
|
||||
"InputBrake = KEY_S,JOY1_ZAXIS_POS" to "InputBrake = KEY_X,JOY1_ZAXIS_POS",
|
||||
"InputGearShiftUp = KEY_Y,JOY1_BUTTON6" to "InputGearShiftUp = KEY_I,JOY1_BUTTON6",
|
||||
"InputGearShiftDown = KEY_H,JOY1_BUTTON5" to "InputGearShiftDown = KEY_K,JOY1_BUTTON5",
|
||||
"InputGearShift1 = KEY_Q,JOY1_BUTTON3" to "InputGearShift1 = KEY_7,JOY1_BUTTON3",
|
||||
"InputGearShift2 = KEY_W,JOY1_BUTTON1" to "InputGearShift2 = KEY_8,JOY1_BUTTON1",
|
||||
"InputGearShift3 = KEY_E,JOY1_BUTTON4" to "InputGearShift3 = KEY_9,JOY1_BUTTON4",
|
||||
"InputGearShift4 = KEY_R,JOY1_BUTTON2" to "InputGearShift4 = KEY_0,JOY1_BUTTON2",
|
||||
"InputGearShiftN = KEY_T" to "InputGearShiftN = KEY_6",
|
||||
)
|
||||
val updated =
|
||||
lines.map { line ->
|
||||
val trimmed = line.trim()
|
||||
if (trimmed == "InputBrake = KEY_S,JOY1_ZAXIS_POS") {
|
||||
val repl = replacements[trimmed]
|
||||
if (repl != null) {
|
||||
changed = true
|
||||
"InputBrake = KEY_X,JOY1_ZAXIS_POS"
|
||||
repl
|
||||
} else {
|
||||
line
|
||||
}
|
||||
|
||||
@@ -62,12 +62,27 @@ class Super3Activity : SDLActivity() {
|
||||
gamesXml.isNotBlank() &&
|
||||
GameInputsIndex.hasAnyInputType(gamesXml, game, setOf("vehicle", "harley"))
|
||||
|
||||
val hasShift4 =
|
||||
game.isNotBlank() &&
|
||||
gamesXml.isNotBlank() &&
|
||||
GameInputsIndex.hasAnyInputType(gamesXml, game, setOf("shift4"))
|
||||
|
||||
val hasShiftUpDown =
|
||||
game.isNotBlank() &&
|
||||
gamesXml.isNotBlank() &&
|
||||
GameInputsIndex.hasAnyInputType(gamesXml, game, setOf("shiftupdown"))
|
||||
|
||||
val showShifter = hasShift4 || hasShiftUpDown
|
||||
|
||||
overlay.findViewById<LinearLayout>(R.id.overlay_pedals)?.visibility =
|
||||
if (isRacing) View.VISIBLE else View.GONE
|
||||
|
||||
overlay.findViewById<ImageButton>(R.id.overlay_wheel)?.visibility =
|
||||
if (isRacing) View.VISIBLE else View.GONE
|
||||
|
||||
overlay.findViewById<ImageButton>(R.id.overlay_shifter)?.visibility =
|
||||
if (isRacing && showShifter) View.VISIBLE else View.GONE
|
||||
|
||||
fun nativeTouch(action: Int, fingerId: Int, x: Float, y: Float, p: Float = 1.0f) {
|
||||
SDLActivity.onNativeTouch(0, fingerId, action, x, y, p)
|
||||
}
|
||||
@@ -168,6 +183,52 @@ class Super3Activity : SDLActivity() {
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
val shifter = overlay.findViewById<ImageButton>(R.id.overlay_shifter)
|
||||
shifter?.setOnTouchListener { v, ev ->
|
||||
val w = v.width.toFloat().coerceAtLeast(1f)
|
||||
val h = v.height.toFloat().coerceAtLeast(1f)
|
||||
val cx = w / 2f
|
||||
val cy = h / 2f
|
||||
val dx = ((ev.x - cx) / cx).coerceIn(-1f, 1f)
|
||||
val dy = ((ev.y - cy) / cy).coerceIn(-1f, 1f)
|
||||
|
||||
// Provide a subtle "knob move" feel without changing layout.
|
||||
v.translationX = dx * 10f
|
||||
v.translationY = dy * 10f
|
||||
|
||||
val encodedX = ((dx + 1f) / 2f).coerceIn(0f, 1f)
|
||||
val encodedY = ((dy + 1f) / 2f).coerceIn(0f, 1f)
|
||||
|
||||
when (ev.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
v.alpha = 0.75f
|
||||
nativeTouch(MotionEvent.ACTION_DOWN, 1108, encodedX, encodedY)
|
||||
true
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
if (hasShift4) {
|
||||
nativeTouch(MotionEvent.ACTION_MOVE, 1108, encodedX, encodedY)
|
||||
}
|
||||
true
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
v.alpha = 1.0f
|
||||
v.translationX = 0f
|
||||
v.translationY = 0f
|
||||
nativeTouch(MotionEvent.ACTION_UP, 1108, 0.5f, 0.5f)
|
||||
true
|
||||
}
|
||||
MotionEvent.ACTION_CANCEL -> {
|
||||
v.alpha = 1.0f
|
||||
v.translationX = 0f
|
||||
v.translationY = 0f
|
||||
nativeTouch(MotionEvent.ACTION_UP, 1108, 0.5f, 0.5f)
|
||||
true
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -94,6 +94,21 @@
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/overlay_shifter"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="120dp"
|
||||
android:background="@drawable/overlay_ripple_rounded"
|
||||
android:contentDescription="Shifter"
|
||||
android:padding="8dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/shifter"
|
||||
android:visibility="gone" />
|
||||
|
||||
<Space
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="10dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/overlay_gas"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
Reference in New Issue
Block a user