forked from Mirrors/openclonk
Merge branch 'sdl-gamecontroller' (pull request GH-17)
commit
19caa65b7b
|
@ -304,7 +304,7 @@ endif()
|
|||
# SDL
|
||||
if(USE_SDL_MAINLOOP)
|
||||
find_package(SDL2 REQUIRED)
|
||||
elseif(NOT WIN32)
|
||||
else()
|
||||
# for gamepads
|
||||
find_package(SDL2)
|
||||
endif()
|
||||
|
@ -908,8 +908,6 @@ elseif(USE_WIN32_WINDOWS)
|
|||
list(APPEND OC_GUI_SOURCES
|
||||
src/editor/C4ConsoleWin32.cpp
|
||||
src/platform/C4WindowWin32.cpp
|
||||
src/platform/StdJoystick.cpp
|
||||
src/platform/StdJoystick.h
|
||||
)
|
||||
elseif(USE_COCOA)
|
||||
list(APPEND OC_GUI_SOURCES
|
||||
|
@ -1131,6 +1129,7 @@ target_link_libraries(openclonk-server
|
|||
${PNG_LIBRARIES}
|
||||
${JPEG_LIBRARIES}
|
||||
${EXECINFO_LIBRARY}
|
||||
${SDL2_LIBRARIES}
|
||||
${READLINE_LIBRARIES}
|
||||
${Audio_LIBRARIES}
|
||||
${GETOPT_LIBRARIES}
|
||||
|
|
|
@ -17,9 +17,19 @@
|
|||
# SDL2_LIBRARIES - a list of libraries to link against to use SDL2
|
||||
# SDL2_FOUND - if false, SDL2 cannot be used
|
||||
|
||||
find_path(SDL2_INCLUDE_DIR SDL.h PATH_SUFFIXES SDL2 HINTS ENV SDL2DIR)
|
||||
find_path(SDL2_INCLUDE_DIR SDL.h
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
PATH_SUFFIXES SDL2 include
|
||||
)
|
||||
mark_as_advanced(SDL2_INCLUDE_DIR)
|
||||
find_library(SDL2_LIBRARY SDL2 HINTS ENV SDL2DIR)
|
||||
|
||||
find_library(SDL2_LIBRARY
|
||||
SDL2
|
||||
HINTS
|
||||
$ENV{SDL2DIR}
|
||||
PATH_SUFFIXES lib
|
||||
)
|
||||
mark_as_advanced(SDL2_LIBRARY)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
|
|
@ -273,7 +273,7 @@
|
|||
return true;
|
||||
}</code>
|
||||
<text>most commands (except for asynchronous commands in the player menu) call a global script function:</text>
|
||||
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release)</code>
|
||||
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, int state)</code>
|
||||
<text>For an explanation of the parameters see <funclink>PlayerControl</funclink>. Amongst others, the function receives the calling player in player as well as the command to be executed in control.</text>
|
||||
<text>As a simple example let's assume that in the global <em>PlayerControls.txt</em> the following command has been defined:</text>
|
||||
<code>[ControlDefs]
|
||||
|
@ -293,11 +293,11 @@
|
|||
Control=Jump
|
||||
Priority=50</code>
|
||||
<text>This defines a Jump key and the corresponding standard mapping on the keyboard for the first player. The following script is used to handle the control:</text>
|
||||
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release)
|
||||
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, int state)
|
||||
{
|
||||
// Which command has been issued?
|
||||
// The constant CON_Jump has been declared automatically through the definition in PlayerControls.txt
|
||||
if (control == CON_Jump && !release)
|
||||
if (control == CON_Jump && state == CONS_Down)
|
||||
{
|
||||
// pressed the jump button. The clonk selected by the player shall jump
|
||||
var player_clonk = GetCursor(player);
|
||||
|
@ -319,17 +319,17 @@
|
|||
GUIDesc=Going underground
|
||||
ExtraData=Shovel</code>
|
||||
<text>Let shovel be the ID of a shovel object. In the global script there could be the following, generic handling for unknown commands, for example:</text>
|
||||
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release)
|
||||
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, int state)
|
||||
{
|
||||
// Handling of known controls
|
||||
// [...]
|
||||
// control with own handling
|
||||
if (control_extra) return control_extra->PlayerControl(player, control, x, y, strength, repeat, release);
|
||||
if (control_extra) return control_extra->PlayerControl(player, control, x, y, strength, repeat, state);
|
||||
// unkown control
|
||||
return false;
|
||||
}</code>
|
||||
<text>And in the script of the shovel:</text>
|
||||
<code>func PlayerControl(int player, int control, int x, int y, int strength, bool repeated, bool release)
|
||||
<code>func PlayerControl(int player, int control, int x, int y, int strength, bool repeated, int state)
|
||||
{
|
||||
// Handling of known controls
|
||||
// Control dig directly in the shovel
|
||||
|
@ -353,6 +353,7 @@
|
|||
<li>Mappings can emulate permanent key presses using the <em>Hold</em>/<em>Release</em> flags.</li>
|
||||
<li><emlink href="playercontrols.xml#Repeat">Key repeats</emlink> are generated.</li>
|
||||
<li>The held state of the key can be queried in the script via <funclink>GetPlayerControlState</funclink>.</li>
|
||||
<li>If the command is bound to an analog stick or trigger on a controller, every change in position causes in a call to PlayerControl() with state = CONS_Moved.</li>
|
||||
</ul>
|
||||
</text>
|
||||
<text>A good example for this functionality is a directional command:</text>
|
||||
|
@ -362,7 +363,7 @@
|
|||
GUIDesc=Walk left
|
||||
Hold=1</code>
|
||||
<text>In the script the direction is transferred to the Clonk:</text>
|
||||
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release)
|
||||
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, int state)
|
||||
{
|
||||
if (control == CON_Left) return UpdateControlDir(player);
|
||||
// ...
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<func>
|
||||
<title>GetPlayerControlState</title>
|
||||
<category>Player</category>
|
||||
<version>5.1 OC</version>
|
||||
<version>5.1 OC (extended in 8.0 OC)</version>
|
||||
<syntax>
|
||||
<rtype>int</rtype>
|
||||
<params>
|
||||
|
@ -20,9 +20,15 @@
|
|||
<name>control</name>
|
||||
<desc>Control to query. A CON_* constant should be used here.</desc>
|
||||
</param>
|
||||
<param>
|
||||
<type>bool</type>
|
||||
<name>analog_strength</name>
|
||||
<desc>If true: Query current state of an analog control on a gamepad instead of the emulated button state.</desc>
|
||||
<optional />
|
||||
</param>
|
||||
</params>
|
||||
</syntax>
|
||||
<desc>Returns the current state of a control for a certain player. The return value is the strength of the control (e.g. for gamepad joysticks). If the control is assigned to a key, a value not equal to 0 means that the key is currently held down by the player.</desc>
|
||||
<desc>Returns the current state of a control for a certain player. If the control is assigned to a key, a value not equal to 0 means that the key is currently held down by the player. For analog controls on gamepads, the function either queries the current emulated button state (analog_strength = false), or the current position of the stick or trigger (analog_strength = true).</desc>
|
||||
<examples>
|
||||
<example>
|
||||
<code>
|
||||
|
@ -36,4 +42,5 @@ if (GetPlayerControlState(GetOwner(), CON_Left) != 0)
|
|||
</related>
|
||||
</func>
|
||||
<author>Zapper</author><date>2015-10</date>
|
||||
<author>Luchs</author><date>2016-02</date>
|
||||
</funcs>
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!DOCTYPE funcs
|
||||
SYSTEM '../../../clonk.dtd'>
|
||||
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
|
||||
<funcs>
|
||||
<func>
|
||||
<title>PlayRumble</title>
|
||||
<category>Player</category>
|
||||
<version>8.0 OC</version>
|
||||
<syntax>
|
||||
<rtype>bool</rtype>
|
||||
<params>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>player</name>
|
||||
<desc>Number of the player whose controller should rumble. Can be NO_OWNER to make all controllers rumble.</desc>
|
||||
</param>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>strength</name>
|
||||
<desc>Strength of the rumble, between 0 and 1000.</desc>
|
||||
</param>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>length</name>
|
||||
<desc>Duration of the rumble in milliseconds.</desc>
|
||||
</param>
|
||||
</params>
|
||||
</syntax>
|
||||
<desc>Plays a haptic effect on the given player's gamepad. Returns true if all parameters are valid; there is no way to know whether the rumble was actually played.</desc>
|
||||
<examples>
|
||||
<example>
|
||||
<code>
|
||||
<funclink>ShakeObjects</funclink>(<funclink>LandscapeWidth</funclink>()/2, <funclink>LandscapeHeight</funclink>()/2, <funclink>Distance</funclink>(<funclink>LandscapeWidth</funclink>(), <funclink>LandscapeHeight</funclink>())/2);
|
||||
PlayRumble(NO_OWNER, 1000, 2000);
|
||||
</code>
|
||||
<text>Earthquake: Shakes all Clonks and rumbles all controllers at full strength for two seconds.</text>
|
||||
</example>
|
||||
</examples>
|
||||
<related>
|
||||
<funclink>StopRumble</funclink>
|
||||
</related>
|
||||
</func>
|
||||
<author>Luchs</author><date>2016-02</date>
|
||||
</funcs>
|
|
@ -0,0 +1,80 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!DOCTYPE funcs
|
||||
SYSTEM '../../../clonk.dtd'>
|
||||
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
|
||||
<funcs>
|
||||
<func>
|
||||
<title>PlayerControl</title>
|
||||
<category>Callbacks</category>
|
||||
<version>5.1 OC</version>
|
||||
<syntax>
|
||||
<rtype>bool</rtype>
|
||||
<params>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>player</name>
|
||||
<desc>Number of the player who pressed the control.</desc>
|
||||
</param>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>control</name>
|
||||
<desc>Number of the pressed control, defined as a CON_ constant via PlayerControls.txt.</desc>
|
||||
</param>
|
||||
<param>
|
||||
<type>id</type>
|
||||
<name>control_extra</name>
|
||||
<desc>Optional id defined with ExtraData in PlayerControls.txt.</desc>
|
||||
</param>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>x</name>
|
||||
<desc>X coordinate for mouse controls.</desc>
|
||||
</param>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>y</name>
|
||||
<desc>Y coordinate for mouse controls.</desc>
|
||||
</param>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>strength</name>
|
||||
<desc>Current strength of the control. For key presses: 0 or 100. For analog stick or trigger movement (state = CONS_Moved): 0 to <code>PLRCON_MaxStrength</code>.</desc>
|
||||
</param>
|
||||
<param>
|
||||
<type>bool</type>
|
||||
<name>repeated</name>
|
||||
<desc>Whether the call is generated because of a held button.</desc>
|
||||
</param>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>state</name>
|
||||
<desc>
|
||||
State of the key press. Possible values:
|
||||
<table>
|
||||
<rowh>
|
||||
<col>Constant</col>
|
||||
<col>Description</col>
|
||||
</rowh>
|
||||
<row>
|
||||
<literal_col>CONS_Down</literal_col>
|
||||
<col>Key has been pressed down.</col>
|
||||
</row>
|
||||
<row>
|
||||
<literal_col>CONS_Up</literal_col>
|
||||
<col>Key has been released. Only generated for held keys.</col>
|
||||
</row>
|
||||
<row>
|
||||
<literal_col>CONS_Moved</literal_col>
|
||||
<col>An analog control on a gamepad has been moved. Only generated for held keys.</col>
|
||||
</row>
|
||||
</table>
|
||||
</desc>
|
||||
</param>
|
||||
</params>
|
||||
</syntax>
|
||||
<desc>Called globally for each control command by players. See <emlink href="script/playercontrols.html">Player Controls</emlink>.</desc>
|
||||
<related><funclink>GetPlayerControlState</funclink></related>
|
||||
<related><emlink href="playercontrols.html">Player Controls</emlink></related>
|
||||
</func>
|
||||
<author>Luchs</author><date>2016-02</date>
|
||||
</funcs>
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<!DOCTYPE funcs
|
||||
SYSTEM '../../../clonk.dtd'>
|
||||
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
|
||||
<funcs>
|
||||
<func>
|
||||
<title>StopRumble</title>
|
||||
<category>Player</category>
|
||||
<version>8.0 OC</version>
|
||||
<syntax>
|
||||
<rtype>bool</rtype>
|
||||
<params>
|
||||
<param>
|
||||
<type>int</type>
|
||||
<name>player</name>
|
||||
<desc>Number of the player whose controller should stop rumbling. Can be NO_OWNER to make all controllers stop.</desc>
|
||||
</param>
|
||||
</params>
|
||||
</syntax>
|
||||
<desc>Stops a rumble effect that was started with <funclink>PlayRumble</funclink>. Returns true if the given player is valid; there is no way to know whether there was actually a playing rumble effect.</desc>
|
||||
<related>
|
||||
<funclink>PlayRumble</funclink>
|
||||
</related>
|
||||
</func>
|
||||
<author>Luchs</author><date>2016-02</date>
|
||||
</funcs>
|
|
@ -13,3 +13,6 @@ StartupPlrSelBG.jpg
|
|||
StartupScenSelBG.jpg)
|
||||
|
||||
Loader1.jpg - Nachtfalter
|
||||
|
||||
ControllerIcons.png - Nicolae Berbece (http://opengameart.org/content/free-keyboard-and-controllers-prompts-pack)
|
||||
License: CC0
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
|
@ -14,9 +14,11 @@ static g_player_cursor_pos; // array of [x,y] pos arrays; indexed by player. las
|
|||
// Called by engine whenever a control is issued
|
||||
// Forwards control to special handler or cursor
|
||||
// Return whether handled
|
||||
global func PlayerControl(int plr, int ctrl, id spec_id, int x, int y, int strength, bool repeat, bool release)
|
||||
global func PlayerControl(int plr, int ctrl, id spec_id, int x, int y, int strength, bool repeat, int status)
|
||||
{
|
||||
//Log("%d, %s, %i, %d, %d, %d, %v, %v", plr, GetPlayerControlName(ctrl), spec_id, x,y,strength, repeat, release);
|
||||
var release = status == CONS_Up;
|
||||
//Log("%d, %s, %i, %d, %d, %d, %v, %v", plr, GetPlayerControlName(ctrl), spec_id, x,y,strength, repeat, status);
|
||||
if (status == CONS_Moved) return false;
|
||||
// Control handled by definition? Forward
|
||||
if (spec_id) return spec_id->PlayerControl(plr, ctrl, x, y, strength, repeat, release);
|
||||
|
||||
|
@ -550,4 +552,4 @@ global func Library_ClonkInventoryControl_Sort_Priority(int x_position)
|
|||
var priority_x = GetX() - x_position;
|
||||
if (priority_x < 0) priority_x += 1000;
|
||||
return priority_x;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -836,31 +836,41 @@
|
|||
# Contents Menu
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1F
|
||||
Key=ControllerButtonB
|
||||
GUIGroup=50
|
||||
Control=Contents
|
||||
|
||||
[Assignment]
|
||||
Key=ControllerButtonLeftShoulder
|
||||
GUIGroup=50
|
||||
Control=InventoryShiftBackward
|
||||
|
||||
[Assignment]
|
||||
Key=ControllerButtonRightShoulder
|
||||
GUIGroup=50
|
||||
Control=InventoryShiftForward
|
||||
|
||||
# Menu
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1J
|
||||
Key=ControllerButtonY
|
||||
GUIGroup=50
|
||||
Control=PlayerMenu
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1C
|
||||
Key=ControllerButtonA
|
||||
GUIGroup=50
|
||||
Control=MenuOK
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1B
|
||||
Key=ControllerButtonB
|
||||
GUIGroup=50
|
||||
Control=MenuCancel
|
||||
|
||||
# Movement
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1Left
|
||||
Key=ControllerLeftStickLeft
|
||||
Priority=50
|
||||
GUIName=$KEY_Left$
|
||||
GUIDesc=$KEY_Left_Desc$
|
||||
|
@ -868,7 +878,7 @@
|
|||
Control=Left
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1Right
|
||||
Key=ControllerLeftStickRight
|
||||
Priority=50
|
||||
GUIName=$KEY_Right$
|
||||
GUIDesc=$KEY_Right_Desc$
|
||||
|
@ -876,7 +886,7 @@
|
|||
Control=Right
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1Down
|
||||
Key=ControllerLeftStickDown
|
||||
Priority=50
|
||||
GUIName=$KEY_Down$
|
||||
GUIDesc=$KEY_Down_Desc$
|
||||
|
@ -884,7 +894,7 @@
|
|||
Control=Down
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1Up
|
||||
Key=ControllerLeftStickUp
|
||||
Priority=50
|
||||
GUIName=$KEY_Up$
|
||||
GUIDesc=$KEY_Up_Desc$
|
||||
|
@ -892,34 +902,40 @@
|
|||
Control=Up
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1C
|
||||
Key=ControllerButtonRightStick
|
||||
Priority=10
|
||||
GUIGroup=10
|
||||
Control=Jump
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1Axis1Min
|
||||
Key=ControllerButtonDpadDown
|
||||
Priority=10
|
||||
GUIGroup=10
|
||||
Control=FallThrough
|
||||
|
||||
[Assignment]
|
||||
Key=ControllerRightStickLeft
|
||||
GUIDesc=$KEY_AimAxis_Desc$
|
||||
Priority=80
|
||||
GUIGroup=30
|
||||
Control=AimAxisLeft
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1Axis1Max
|
||||
Key=ControllerRightStickRight
|
||||
GUIDesc=$KEY_AimAxis_Desc$
|
||||
Priority=80
|
||||
GUIGroup=30
|
||||
Control=AimAxisRight
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1Axis2Max
|
||||
Key=ControllerRightStickDown
|
||||
GUIDesc=$KEY_AimAxis_Desc$
|
||||
Priority=80
|
||||
GUIGroup=30
|
||||
Control=AimAxisDown
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1Axis2Min
|
||||
Key=ControllerRightStickUp
|
||||
GUIDesc=$KEY_AimAxis_Desc$
|
||||
Priority=80
|
||||
GUIGroup=30
|
||||
|
@ -928,41 +944,57 @@
|
|||
# Object interaction
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1B
|
||||
Key=ControllerButtonA
|
||||
Priority=35
|
||||
GUIGroup=40
|
||||
Control=Interact
|
||||
|
||||
[Assignment]
|
||||
Key=ControllerButtonX
|
||||
Control=PickUp
|
||||
GUIGroup=50
|
||||
|
||||
# Crew
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1I
|
||||
Key=ControllerButtonDpadRight
|
||||
Control=NextCrew
|
||||
GUIGroup=70
|
||||
|
||||
[Assignment]
|
||||
Key=ControllerButtonDpadLeft
|
||||
Control=PreviousCrew
|
||||
GUIGroup=70
|
||||
|
||||
# Use, Throw, Drop
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1A
|
||||
Key=ControllerRightTrigger
|
||||
GUIName=$KEY_GamepadUse$
|
||||
GUIDesc=$KEY_GamepadUse_Desc$
|
||||
GUIGroup=20
|
||||
Priority=100
|
||||
Control=UseDelayed
|
||||
|
||||
# Zoom
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1H
|
||||
Key=ControllerLeftTrigger
|
||||
GUIGroup=20
|
||||
Priority=100
|
||||
GUIGroup=60
|
||||
Control=ZoomIn
|
||||
Control=ThrowDelayed
|
||||
|
||||
[Assignment]
|
||||
Key=Joy1G
|
||||
Priority=100
|
||||
GUIGroup=60
|
||||
Control=ZoomOut
|
||||
# TODO: Zoom
|
||||
|
||||
#[Assignment]
|
||||
#Key=Joy1H
|
||||
#Priority=100
|
||||
#GUIGroup=60
|
||||
#Control=ZoomIn
|
||||
|
||||
#[Assignment]
|
||||
#Key=Joy1G
|
||||
#Priority=100
|
||||
#GUIGroup=60
|
||||
#Control=ZoomOut
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -423,7 +423,7 @@ void C4ControlPlayerControl::ControlItem::CompileFunc(StdCompiler *pComp)
|
|||
void C4ControlPlayerControl::CompileFunc(StdCompiler *pComp)
|
||||
{
|
||||
pComp->Value(mkNamingAdapt(mkIntPackAdapt(iPlr), "Player", -1));
|
||||
pComp->Value(mkNamingAdapt(fRelease, "Release", false));
|
||||
pComp->Value(mkNamingAdapt(mkIntAdapt(state), "State", 0));
|
||||
pComp->Value(mkNamingAdapt(ExtraData, "ExtraData", C4KeyEventData()));
|
||||
pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(ControlItems), "Controls", ControlItemVec()));
|
||||
C4ControlPacket::CompileFunc(pComp);
|
||||
|
|
|
@ -203,11 +203,11 @@ public:
|
|||
class C4ControlPlayerControl : public C4ControlPacket // sync
|
||||
{
|
||||
public:
|
||||
C4ControlPlayerControl() : iPlr(-1), fRelease(false) {}
|
||||
C4ControlPlayerControl(int32_t iPlr, bool fRelease, const C4KeyEventData &rExtraData)
|
||||
: iPlr(iPlr), fRelease(fRelease), ExtraData(rExtraData) { }
|
||||
C4ControlPlayerControl() : iPlr(-1), state(C4PlayerControl::CONS_Down) {}
|
||||
C4ControlPlayerControl(int32_t iPlr, C4PlayerControl::ControlState state, const C4KeyEventData &rExtraData)
|
||||
: iPlr(iPlr), state(state), ExtraData(rExtraData) { }
|
||||
C4ControlPlayerControl(int32_t iPlr, int32_t iControl, int32_t iExtraData) // old-style menu com emulation
|
||||
: iPlr(iPlr), fRelease(false), ExtraData(iExtraData,0,0,0,0) { AddControl(iControl,0); }
|
||||
: iPlr(iPlr), state(C4PlayerControl::CONS_Down), ExtraData(iExtraData,0,0,0,0) { AddControl(iControl,0); }
|
||||
|
||||
struct ControlItem
|
||||
{
|
||||
|
@ -221,7 +221,7 @@ public:
|
|||
typedef std::vector<ControlItem> ControlItemVec;
|
||||
protected:
|
||||
int32_t iPlr;
|
||||
bool fRelease;
|
||||
int32_t state;
|
||||
C4KeyEventData ExtraData;
|
||||
ControlItemVec ControlItems;
|
||||
public:
|
||||
|
@ -229,7 +229,7 @@ public:
|
|||
void AddControl(int32_t iControl, int32_t iTriggerMode)
|
||||
{ ControlItems.push_back(ControlItem(iControl, iTriggerMode)); }
|
||||
const ControlItemVec &GetControlItems() const { return ControlItems; }
|
||||
bool IsReleaseControl() const { return fRelease; }
|
||||
C4PlayerControl::ControlState GetState() const { return static_cast<C4PlayerControl::ControlState>(state); }
|
||||
const C4KeyEventData &GetExtraData() const { return ExtraData; }
|
||||
void SetExtraData(const C4KeyEventData &new_extra_data) { ExtraData = new_extra_data; }
|
||||
};
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "C4PlayerList.h"
|
||||
#include "C4Control.h"
|
||||
#include "C4Game.h"
|
||||
#include "C4GamePadCon.h"
|
||||
#include "C4Log.h"
|
||||
#include "C4GraphicsResource.h"
|
||||
#include "C4MouseControl.h"
|
||||
|
@ -706,7 +707,7 @@ C4Facet C4PlayerControlAssignmentSet::GetPicture() const
|
|||
{
|
||||
// get image to be drawn to represent this control set
|
||||
// picture per set not implemented yet. So just default to out standard images
|
||||
if (HasGamepad()) return ::GraphicsResource.fctGamepad.GetPhase(GetGamepadIndex());
|
||||
if (HasGamepad()) return ::GraphicsResource.fctGamepad.GetPhase(0);
|
||||
// if (HasMouse()) return ::GraphicsResource.fctMouse; // might be useful again with changing control sets
|
||||
if (HasKeyboard()) return ::GraphicsResource.fctKeyboard.GetPhase(Game.PlayerControlUserAssignmentSets.GetSetIndex(this));
|
||||
return C4Facet();
|
||||
|
@ -734,21 +735,6 @@ void C4PlayerControlAssignmentSets::CompileFunc(StdCompiler *pComp)
|
|||
}
|
||||
pComp->Value(mkNamingAdapt(clear_previous, "ClearPrevious", false));
|
||||
pComp->Value(mkSTLContainerAdapt(Sets, StdCompiler::SEP_NONE));
|
||||
|
||||
// Remove all sets that have gamepad controls, since gamepad
|
||||
// support is broken at the moment. Disable this once we have gamepad
|
||||
// support again!
|
||||
if (pComp->isCompiler())
|
||||
{
|
||||
AssignmentSetList::iterator iter = Sets.begin();
|
||||
for (AssignmentSetList::iterator iter = Sets.begin(); iter != Sets.end(); )
|
||||
{
|
||||
if (iter->HasGamepad())
|
||||
iter = Sets.erase(iter);
|
||||
else
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool C4PlayerControlAssignmentSets::operator ==(const C4PlayerControlAssignmentSets &cmp) const
|
||||
|
@ -908,14 +894,18 @@ void C4PlayerControl::CSync::ControlDownState::CompileFunc(StdCompiler *pComp)
|
|||
{
|
||||
pComp->Value(DownState);
|
||||
pComp->Separator();
|
||||
pComp->Value(MovedState);
|
||||
pComp->Separator();
|
||||
pComp->Value(iDownFrame);
|
||||
pComp->Separator();
|
||||
pComp->Value(iMovedFrame);
|
||||
pComp->Separator();
|
||||
pComp->Value(fDownByUser);
|
||||
}
|
||||
|
||||
bool C4PlayerControl::CSync::ControlDownState::operator ==(const ControlDownState &cmp) const
|
||||
{
|
||||
return DownState == cmp.DownState && iDownFrame == cmp.iDownFrame && fDownByUser == cmp.fDownByUser;
|
||||
return DownState == cmp.DownState && MovedState == cmp.MovedState && iDownFrame == cmp.iDownFrame && iMovedFrame == cmp.iMovedFrame && fDownByUser == cmp.fDownByUser;
|
||||
}
|
||||
|
||||
const C4PlayerControl::CSync::ControlDownState *C4PlayerControl::CSync::GetControlDownState(int32_t iControl) const
|
||||
|
@ -943,6 +933,16 @@ void C4PlayerControl::CSync::SetControlDownState(int32_t iControl, const C4KeyEv
|
|||
rState.fDownByUser = fDownByUser;
|
||||
}
|
||||
|
||||
void C4PlayerControl::CSync::SetControlMovedState(int32_t iControl, const C4KeyEventData &rMovedState, int32_t iMovedFrame)
|
||||
{
|
||||
// update state
|
||||
if (iControl < 0) return;
|
||||
if (iControl >= int32_t(ControlDownStates.size())) ControlDownStates.resize(iControl+1);
|
||||
ControlDownState &rState = ControlDownStates[iControl];
|
||||
rState.MovedState = rMovedState;
|
||||
rState.iMovedFrame = iMovedFrame;
|
||||
}
|
||||
|
||||
bool C4PlayerControl::CSync::SetControlDisabled(int32_t iControl, int32_t iVal)
|
||||
{
|
||||
// disable control
|
||||
|
@ -963,6 +963,7 @@ void C4PlayerControl::CSync::ResetControlDownState(int32_t iControl)
|
|||
C4KeyEventData KeyDownState = pDownState->DownState;
|
||||
KeyDownState.iStrength = 0;
|
||||
SetControlDownState(iControl, KeyDownState, 0, false);
|
||||
SetControlMovedState(iControl, KeyDownState, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1009,12 +1010,19 @@ void C4PlayerControl::CompileFunc(StdCompiler *pComp)
|
|||
pComp->Value(mkNamingAdapt(Sync, "PlayerControl", DefaultSync));
|
||||
}
|
||||
|
||||
bool C4PlayerControl::ProcessKeyEvent(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key, bool fUp, const C4KeyEventData &rKeyExtraData, bool reset_down_states_only, bool *clear_recent_keys)
|
||||
bool C4PlayerControl::ProcessKeyEvent(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key, ControlState state, const C4KeyEventData &rKeyExtraData, bool reset_down_states_only, bool *clear_recent_keys)
|
||||
{
|
||||
if (Key_IsGamepad(pressed_key.Key))
|
||||
{
|
||||
// We have to filter gamepad events here.
|
||||
C4Player *plr = ::Players.Get(iPlr);
|
||||
if (!plr || !plr->pGamepad || plr->pGamepad->GetID() != pressed_key.deviceId)
|
||||
return false;
|
||||
}
|
||||
// collect all matching keys
|
||||
C4PlayerControlAssignmentPVec Matches;
|
||||
assert(pControlSet); // shouldn't get this callback for players without control set
|
||||
pControlSet->GetAssignmentsByKey(ControlDefs, matched_key, fUp, &Matches, DownKeys, RecentKeys);
|
||||
pControlSet->GetAssignmentsByKey(ControlDefs, matched_key, state != CONS_Down, &Matches, DownKeys, RecentKeys);
|
||||
// process async controls
|
||||
C4ControlPlayerControl *pControlPacket = NULL;
|
||||
for (C4PlayerControlAssignmentPVec::const_iterator i = Matches.begin(); i != Matches.end(); ++i)
|
||||
|
@ -1023,7 +1031,7 @@ bool C4PlayerControl::ProcessKeyEvent(const C4KeyCodeEx &pressed_key, const C4Ke
|
|||
assert(pAssignment);
|
||||
int32_t iControlIndex = pAssignment->GetControl();
|
||||
const C4PlayerControlDef *pControlDef = ControlDefs.GetControlByIndex(iControlIndex);
|
||||
if (pControlDef && pControlDef->IsValid() && !Sync.IsControlDisabled(iControlIndex) && (!fUp || pControlDef->IsHoldKey()))
|
||||
if (pControlDef && pControlDef->IsValid() && !Sync.IsControlDisabled(iControlIndex) && (state == CONS_Down || pControlDef->IsHoldKey()))
|
||||
{
|
||||
// clear RecentKeys if requested by this assignment. Must be done before sync queue, so multiple combos can be issued in a single control frame.
|
||||
if (clear_recent_keys && (pAssignment->GetTriggerMode() & C4PlayerControlAssignment::CTM_ClearRecentKeys)) *clear_recent_keys = true;
|
||||
|
@ -1031,7 +1039,7 @@ bool C4PlayerControl::ProcessKeyEvent(const C4KeyCodeEx &pressed_key, const C4Ke
|
|||
if (pControlDef->IsAsync() && !pControlPacket)
|
||||
{
|
||||
if (pControlDef->IsSendCursorPos()) IsCursorPosRequested = true; // async cursor pos request - doesn't really make sense to set this flag for async controls
|
||||
if (ExecuteControl(iControlIndex, fUp, rKeyExtraData, pAssignment->GetTriggerMode(), pressed_key.IsRepeated(), reset_down_states_only))
|
||||
if (ExecuteControl(iControlIndex, state, rKeyExtraData, pAssignment->GetTriggerMode(), pressed_key.IsRepeated(), reset_down_states_only))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
@ -1041,7 +1049,7 @@ bool C4PlayerControl::ProcessKeyEvent(const C4KeyCodeEx &pressed_key, const C4Ke
|
|||
if (pressed_key.IsRepeated()) return false;
|
||||
// sync control has higher priority - no more async execution then
|
||||
// build a control packet and add control data instead. even for async controls later in chain, as they may be blocked by a sync handler
|
||||
if (!pControlPacket) pControlPacket = new C4ControlPlayerControl(iPlr, fUp, rKeyExtraData);
|
||||
if (!pControlPacket) pControlPacket = new C4ControlPlayerControl(iPlr, state, rKeyExtraData);
|
||||
int32_t extra_trigger_mode = 0;
|
||||
if (reset_down_states_only) extra_trigger_mode |= C4PlayerControlAssignment::CTM_HandleDownStatesOnly;
|
||||
pControlPacket->AddControl(iControlIndex, pAssignment->GetTriggerMode() | extra_trigger_mode);
|
||||
|
@ -1071,7 +1079,7 @@ bool C4PlayerControl::ProcessKeyDown(const C4KeyCodeEx &pressed_key, const C4Key
|
|||
}
|
||||
// process!
|
||||
bool clear_recent_keys = false;
|
||||
bool fResult = ProcessKeyEvent(pressed_key, matched_key, false, Game.KeyboardInput.GetLastKeyExtraData(), false, &clear_recent_keys);
|
||||
bool fResult = ProcessKeyEvent(pressed_key, matched_key, CONS_Down, Game.KeyboardInput.GetLastKeyExtraData(), false, &clear_recent_keys);
|
||||
// unless assignment requests a clear, always add keys to recent list even if not handled
|
||||
if (clear_recent_keys)
|
||||
RecentKeys.clear();
|
||||
|
@ -1090,7 +1098,13 @@ bool C4PlayerControl::ProcessKeyUp(const C4KeyCodeEx &pressed_key, const C4KeyCo
|
|||
if (i != DownKeys.end()) DownKeys.erase(i);
|
||||
}
|
||||
// process!
|
||||
return ProcessKeyEvent(pressed_key, matched_key, true, Game.KeyboardInput.GetLastKeyExtraData());
|
||||
return ProcessKeyEvent(pressed_key, matched_key, CONS_Up, Game.KeyboardInput.GetLastKeyExtraData());
|
||||
}
|
||||
|
||||
bool C4PlayerControl::ProcessKeyMoved(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key)
|
||||
{
|
||||
// process!
|
||||
return ProcessKeyEvent(pressed_key, matched_key, CONS_Moved, Game.KeyboardInput.GetLastKeyExtraData());
|
||||
}
|
||||
|
||||
void C4PlayerControl::ExecuteControlPacket(const class C4ControlPlayerControl *pCtrl)
|
||||
|
@ -1111,10 +1125,10 @@ void C4PlayerControl::ExecuteControlPacket(const class C4ControlPlayerControl *p
|
|||
AddDbgRec(RCT_PlrCom, &rItem.iControl, sizeof(rItem.iControl));
|
||||
}
|
||||
}
|
||||
if (ExecuteControl(rItem.iControl, pCtrl->IsReleaseControl(), pCtrl->GetExtraData(), rItem.iTriggerMode, false, fHandleDownStateOnly))
|
||||
if (ExecuteControl(rItem.iControl, pCtrl->GetState(), pCtrl->GetExtraData(), rItem.iTriggerMode, false, fHandleDownStateOnly))
|
||||
if (pCtrlDef->IsSync())
|
||||
{
|
||||
if (pCtrl->IsReleaseControl())
|
||||
if (pCtrl->GetState() == CONS_Up)
|
||||
{
|
||||
// control processed. however, for key releases, overriden keys are released silently so following down events aren't handled as key repeats
|
||||
// note this does not affect CTM_Hold/CTM_Release, because they ignore release controls anyway
|
||||
|
@ -1129,7 +1143,7 @@ void C4PlayerControl::ExecuteControlPacket(const class C4ControlPlayerControl *p
|
|||
}
|
||||
}
|
||||
|
||||
bool C4PlayerControl::ExecuteControl(int32_t iControl, bool fUp, const C4KeyEventData &rKeyExtraData, int32_t iTriggerMode, bool fRepeated, bool fHandleDownStateOnly)
|
||||
bool C4PlayerControl::ExecuteControl(int32_t iControl, ControlState state, const C4KeyEventData &rKeyExtraData, int32_t iTriggerMode, bool fRepeated, bool fHandleDownStateOnly)
|
||||
{
|
||||
// execute single control. return if handled
|
||||
const C4PlayerControlDef *pControlDef = ControlDefs.GetControlByIndex(iControl);
|
||||
|
@ -1147,7 +1161,7 @@ bool C4PlayerControl::ExecuteControl(int32_t iControl, bool fUp, const C4KeyEven
|
|||
{
|
||||
if (eAction != C4PlayerControlDef::CDA_Script) return false;
|
||||
if (!pControlDef->IsHoldKey()) return false;
|
||||
if (fUp) return false; // hold triggers have no "up"-event
|
||||
if (state == CONS_Up) return false; // hold triggers have no "up"-event
|
||||
// perform hold/release
|
||||
if (fWasDown)
|
||||
{
|
||||
|
@ -1157,7 +1171,7 @@ bool C4PlayerControl::ExecuteControl(int32_t iControl, bool fUp, const C4KeyEven
|
|||
KeyExtraData.iStrength = 0;
|
||||
Sync.SetControlDownState(iControl, KeyExtraData, Game.FrameCounter, false);
|
||||
// now process as a regular "Up" event
|
||||
fUp = true;
|
||||
state = CONS_Up;
|
||||
fRepeated = false;
|
||||
}
|
||||
else
|
||||
|
@ -1186,7 +1200,7 @@ bool C4PlayerControl::ExecuteControl(int32_t iControl, bool fUp, const C4KeyEven
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (fUp)
|
||||
else if (state == CONS_Up)
|
||||
{
|
||||
// regular ControlUp: Only valid if that control was down
|
||||
if (!fWasDown) return false;
|
||||
|
@ -1194,14 +1208,22 @@ bool C4PlayerControl::ExecuteControl(int32_t iControl, bool fUp, const C4KeyEven
|
|||
}
|
||||
else if (pControlDef->IsHoldKey())
|
||||
{
|
||||
// regular ControlDown on Hold Key: Set in down list
|
||||
Sync.SetControlDownState(iControl, KeyExtraData, Game.FrameCounter, true);
|
||||
fRepeated = fWasDown;
|
||||
if (state == CONS_Moved)
|
||||
{
|
||||
Sync.SetControlMovedState(iControl, KeyExtraData, Game.FrameCounter);
|
||||
fRepeated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// regular ControlDown on Hold Key: Set in down list
|
||||
Sync.SetControlDownState(iControl, KeyExtraData, Game.FrameCounter, true);
|
||||
fRepeated = fWasDown;
|
||||
}
|
||||
}
|
||||
// down state handling done
|
||||
if (fHandleDownStateOnly) return false;
|
||||
// perform action for this control
|
||||
bool fHandled = ExecuteControlAction(iControl, eAction, pControlDef->GetExtraData(), fUp, KeyExtraData, fRepeated);
|
||||
bool fHandled = ExecuteControlAction(iControl, eAction, pControlDef->GetExtraData(), state, KeyExtraData, fRepeated);
|
||||
// handled controls hide control display
|
||||
C4Player *pPlr;
|
||||
if ((pPlr = ::Players.Get(iPlr))) if (pPlr->ShowStartup) pPlr->ShowStartup = false;
|
||||
|
@ -1209,8 +1231,10 @@ bool C4PlayerControl::ExecuteControl(int32_t iControl, bool fUp, const C4KeyEven
|
|||
return fHandled && !(iTriggerMode & C4PlayerControlAssignment::CTM_AlwaysUnhandled);
|
||||
}
|
||||
|
||||
bool C4PlayerControl::ExecuteControlAction(int32_t iControl, C4PlayerControlDef::Actions eAction, C4ID idControlExtraData, bool fUp, const C4KeyEventData &rKeyExtraData, bool fRepeated)
|
||||
bool C4PlayerControl::ExecuteControlAction(int32_t iControl, C4PlayerControlDef::Actions eAction, C4ID idControlExtraData, ControlState state, const C4KeyEventData &rKeyExtraData, bool fRepeated)
|
||||
{
|
||||
// moved events don't make sense for menus and are only handled by script
|
||||
if (state == CONS_Moved && eAction != C4PlayerControlDef::CDA_Script) return false;
|
||||
// get affected player
|
||||
C4Player *pPlr = NULL;
|
||||
C4Viewport *pVP;
|
||||
|
@ -1223,12 +1247,13 @@ bool C4PlayerControl::ExecuteControlAction(int32_t iControl, C4PlayerControlDef:
|
|||
pCursor = pPlr->Cursor;
|
||||
if (pCursor && pCursor->Menu && pCursor->Menu->IsActive()) pCursorMenu = pCursor->Menu;
|
||||
}
|
||||
bool fUp = state == CONS_Up;
|
||||
// exec action (on player)
|
||||
switch (eAction)
|
||||
{
|
||||
// scripted player control
|
||||
case C4PlayerControlDef::CDA_Script:
|
||||
return ExecuteControlScript(iControl, idControlExtraData, fUp, rKeyExtraData, fRepeated);
|
||||
return ExecuteControlScript(iControl, idControlExtraData, state, rKeyExtraData, fRepeated);
|
||||
|
||||
// menu controls
|
||||
case C4PlayerControlDef::CDA_Menu: if (!pPlr || fUp) return false; if (pPlr->Menu.IsActive()) pPlr->Menu.TryClose(false, true); else pPlr->ActivateMenuMain(); return true; // toggle
|
||||
|
@ -1256,15 +1281,16 @@ bool C4PlayerControl::ExecuteControlAction(int32_t iControl, C4PlayerControlDef:
|
|||
}
|
||||
}
|
||||
|
||||
bool C4PlayerControl::ExecuteControlScript(int32_t iControl, C4ID idControlExtraData, bool fUp, const C4KeyEventData &rKeyExtraData, bool fRepeated)
|
||||
bool C4PlayerControl::ExecuteControlScript(int32_t iControl, C4ID idControlExtraData, ControlState state, const C4KeyEventData &rKeyExtraData, bool fRepeated)
|
||||
{
|
||||
C4Player *pPlr = ::Players.Get(iPlr);
|
||||
if (pPlr)
|
||||
{
|
||||
// Not for eliminated (checked again in DirectCom, but make sure no control is generated for eliminated players!)
|
||||
if (pPlr->Eliminated) return false;
|
||||
// control count for statistics
|
||||
pPlr->CountControl(C4Player::PCID_DirectCom, iControl*2+fUp);
|
||||
// control count for statistics (but don't count analog stick wiggles)
|
||||
if (state != CONS_Moved)
|
||||
pPlr->CountControl(C4Player::PCID_DirectCom, iControl*2+state);
|
||||
}
|
||||
else if (iPlr > -1)
|
||||
{
|
||||
|
@ -1285,7 +1311,7 @@ bool C4PlayerControl::ExecuteControlScript(int32_t iControl, C4ID idControlExtra
|
|||
C4Value vx = (x == C4KeyEventData::KeyPos_None) ? C4VNull : C4VInt(x);
|
||||
C4Value vy = (y == C4KeyEventData::KeyPos_None) ? C4VNull : C4VInt(y);
|
||||
// exec control function
|
||||
C4AulParSet Pars(iPlr, iControl, C4Id2Def(idControlExtraData), vx, vy, rKeyExtraData.iStrength, fRepeated, fUp);
|
||||
C4AulParSet Pars(iPlr, iControl, C4Id2Def(idControlExtraData), vx, vy, rKeyExtraData.iStrength, fRepeated, C4VInt(state));
|
||||
return ::ScriptEngine.GetPropList()->Call(PSF_PlayerControl, &Pars).getBool();
|
||||
}
|
||||
|
||||
|
@ -1310,7 +1336,7 @@ void C4PlayerControl::Execute()
|
|||
if (!((iFrameDiff-iCtrlInitialRepeatDelay) % iCtrlRepeatDelay))
|
||||
{
|
||||
// it's RepeatTime for this key!
|
||||
ExecuteControlAction(i, pCtrlDef->GetAction(), pCtrlDef->GetExtraData(), false, pControlDownState->DownState, true);
|
||||
ExecuteControlAction(i, pCtrlDef->GetAction(), pCtrlDef->GetExtraData(), CONS_Down, pControlDownState->DownState, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1363,7 +1389,7 @@ void C4PlayerControl::AddKeyBinding(const C4KeyCodeEx &key, bool fHoldKey, int32
|
|||
{
|
||||
KeyBindings.push_back(new C4KeyBinding(
|
||||
key, FormatString("PlrKey%02d", idx).getData(), KEYSCOPE_Control,
|
||||
new C4KeyCBExPassKey<C4PlayerControl, C4KeyCodeEx>(*this, key, &C4PlayerControl::ProcessKeyDown, fHoldKey ? &C4PlayerControl::ProcessKeyUp : NULL),
|
||||
new C4KeyCBExPassKey<C4PlayerControl, C4KeyCodeEx>(*this, key, &C4PlayerControl::ProcessKeyDown, fHoldKey ? &C4PlayerControl::ProcessKeyUp : NULL, NULL, fHoldKey ? &C4PlayerControl::ProcessKeyMoved : NULL),
|
||||
C4CustomKey::PRIO_PlrControl));
|
||||
}
|
||||
|
||||
|
@ -1455,7 +1481,7 @@ void C4PlayerControl::PrepareInput()
|
|||
ev.iStrength = 0;
|
||||
ev.vp_x = x; ev.vp_y = y;
|
||||
ev.game_x = game_x; ev.game_y = game_y;
|
||||
C4ControlPlayerControl *pControlPacket = new C4ControlPlayerControl(iPlr, false, ev);
|
||||
C4ControlPlayerControl *pControlPacket = new C4ControlPlayerControl(iPlr, CONS_Down, ev);
|
||||
pControlPacket->AddControl(ControlDefs.InternalCons.CON_CursorPos, C4PlayerControlAssignment::CTM_Default);
|
||||
// make sure it's added at head, because controls that have SendCursorPos=1 set will follow, which will rely
|
||||
// on the cursor pos being known
|
||||
|
|
|
@ -277,7 +277,6 @@ public:
|
|||
bool HasMouse() const { return has_mouse; }
|
||||
bool HasGamepad() const { return has_gamepad; }
|
||||
int32_t GetLayoutOrder() const { return 0; } // returns position on keyboard (increasing from left to right) for viewport sorting
|
||||
int32_t GetGamepadIndex() const { return 0; }
|
||||
bool IsMouseControlAssigned(int32_t mouseevent) const;
|
||||
};
|
||||
|
||||
|
@ -333,6 +332,12 @@ class C4PlayerControl
|
|||
public:
|
||||
enum { MaxRecentKeyLookback = 3000, MaxSequenceKeyDelay = 800 }; // milliseconds: Time to press key combos
|
||||
|
||||
enum ControlState {
|
||||
CONS_Down = 0,
|
||||
CONS_Up,
|
||||
CONS_Moved,
|
||||
};
|
||||
|
||||
private:
|
||||
C4PlayerControlDefs &ControlDefs; // shortcut
|
||||
|
||||
|
@ -353,8 +358,8 @@ public:
|
|||
{
|
||||
struct ControlDownState
|
||||
{
|
||||
C4KeyEventData DownState; // control is down if DownState.iStrength>0
|
||||
int32_t iDownFrame; // frame when control was pressed
|
||||
C4KeyEventData DownState, MovedState; // control is down if DownState.iStrength>0
|
||||
int32_t iDownFrame, iMovedFrame; // frame when control was pressed
|
||||
bool fDownByUser; // if true, the key is actually pressed. Otherwise, it's triggered as down by another key
|
||||
ControlDownState(const C4KeyEventData &rDownState, int32_t iDownFrame, bool fDownByUser)
|
||||
: DownState(rDownState), iDownFrame(iDownFrame), fDownByUser(fDownByUser) {}
|
||||
|
@ -373,6 +378,7 @@ public:
|
|||
int32_t GetControlDisabled(int32_t iControl) const;
|
||||
bool IsControlDisabled(int32_t iControl) const { return GetControlDisabled(iControl)>0; }
|
||||
void SetControlDownState(int32_t iControl, const C4KeyEventData &rDownState, int32_t iDownFrame, bool fDownByUser);
|
||||
void SetControlMovedState(int32_t iControl, const C4KeyEventData &rMovedState, int32_t iMovedFrame);
|
||||
void ResetControlDownState(int32_t iControl);
|
||||
bool SetControlDisabled(int32_t iControl, int32_t iVal);
|
||||
|
||||
|
@ -386,14 +392,15 @@ private:
|
|||
CSync Sync;
|
||||
|
||||
// callbacks from Game.KeyboardInput
|
||||
bool ProcessKeyEvent(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key, bool fUp, const C4KeyEventData &rKeyExtraData, bool reset_down_states_only=false, bool *clear_recent_keys=NULL);
|
||||
bool ProcessKeyEvent(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key, ControlState state, const C4KeyEventData &rKeyExtraData, bool reset_down_states_only=false, bool *clear_recent_keys=NULL);
|
||||
bool ProcessKeyDown(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key);
|
||||
bool ProcessKeyUp(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key);
|
||||
bool ProcessKeyMoved(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key);
|
||||
|
||||
// execute single control. return if handled.
|
||||
bool ExecuteControl(int32_t iControl, bool fUp, const C4KeyEventData &rKeyExtraData, int32_t iTriggerMode, bool fRepeated, bool fHandleDownStateOnly);
|
||||
bool ExecuteControlAction(int32_t iControl, C4PlayerControlDef::Actions eAction, C4ID idControlExtraData, bool fUp, const C4KeyEventData &rKeyExtraData, bool fRepeated);
|
||||
bool ExecuteControlScript(int32_t iControl, C4ID idControlExtraData, bool fUp, const C4KeyEventData &rKeyExtraData, bool fRepeated);
|
||||
bool ExecuteControl(int32_t iControl, ControlState state, const C4KeyEventData &rKeyExtraData, int32_t iTriggerMode, bool fRepeated, bool fHandleDownStateOnly);
|
||||
bool ExecuteControlAction(int32_t iControl, C4PlayerControlDef::Actions eAction, C4ID idControlExtraData, ControlState state, const C4KeyEventData &rKeyExtraData, bool fRepeated);
|
||||
bool ExecuteControlScript(int32_t iControl, C4ID idControlExtraData, ControlState state, const C4KeyEventData &rKeyExtraData, bool fRepeated);
|
||||
|
||||
// init
|
||||
void AddKeyBinding(const C4KeyCodeEx &key, bool fHoldKey, int32_t idx);
|
||||
|
|
|
@ -677,6 +677,7 @@ void C4Application::GameTick()
|
|||
case C4AS_Startup:
|
||||
SoundSystem.Execute();
|
||||
MusicSystem.Execute();
|
||||
if (pGamePadControl) pGamePadControl->Execute();
|
||||
// wait for the user to start a game
|
||||
break;
|
||||
case C4AS_StartGame:
|
||||
|
@ -697,6 +698,10 @@ void C4Application::GameTick()
|
|||
Application.SetVideoMode(GetConfigWidth(), GetConfigHeight(), Config.Graphics.RefreshRate, Config.Graphics.Monitor, true);
|
||||
if (!isEditor)
|
||||
pWindow->GrabMouse(true);
|
||||
// Gamepad events have to be polled here so that the controller
|
||||
// connection state is always up-to-date before players are
|
||||
// joining.
|
||||
if (pGamePadControl) pGamePadControl->Execute();
|
||||
break;
|
||||
case C4AS_AfterGame:
|
||||
// stop game
|
||||
|
|
|
@ -75,6 +75,8 @@
|
|||
#include <C4SolidMask.h>
|
||||
#include <C4FoW.h>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
class C4GameSec1Timer : public C4ApplicationSec1Timer
|
||||
{
|
||||
public:
|
||||
|
@ -1889,6 +1891,12 @@ bool C4Game::DoKeyboardInput(C4KeyCode vk_code, C4KeyEventType eEventType, bool
|
|||
#endif
|
||||
// compose key
|
||||
C4KeyCodeEx Key(vk_code, C4KeyShiftState(fAlt*KEYS_Alt + fCtrl*KEYS_Control + fShift*KEYS_Shift), fRepeated);
|
||||
return DoKeyboardInput(Key, eEventType, pForDialog, fPlrCtrlOnly, iStrength);
|
||||
}
|
||||
|
||||
|
||||
bool C4Game::DoKeyboardInput(C4KeyCodeEx Key, C4KeyEventType eEventType, class C4GUI::Dialog *pForDialog, bool fPlrCtrlOnly, int32_t iStrength)
|
||||
{
|
||||
Key.FixShiftKeys();
|
||||
// compose keyboard scope
|
||||
DWORD InScope = 0;
|
||||
|
@ -2966,25 +2974,25 @@ bool C4Game::InitKeyboard()
|
|||
|
||||
// fullscreen menu
|
||||
Keys.clear(); Keys.push_back(C4KeyCodeEx(K_LEFT));
|
||||
if (Config.Controls.GamepadGuiControl) Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Left)));
|
||||
if (Config.Controls.GamepadGuiControl) ControllerKeys::Left(Keys);
|
||||
KeyboardInput.RegisterKey(new C4CustomKey(Keys, "FullscreenMenuLeft", KEYSCOPE_FullSMenu, new C4KeyCBEx<C4FullScreen, BYTE> (FullScreen, COM_MenuLeft, &C4FullScreen::MenuKeyControl)));
|
||||
Keys.clear(); Keys.push_back(C4KeyCodeEx(K_RIGHT));
|
||||
if (Config.Controls.GamepadGuiControl) Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Right)));
|
||||
if (Config.Controls.GamepadGuiControl) ControllerKeys::Right(Keys);
|
||||
KeyboardInput.RegisterKey(new C4CustomKey(Keys, "FullscreenMenuRight", KEYSCOPE_FullSMenu, new C4KeyCBEx<C4FullScreen, BYTE> (FullScreen, COM_MenuRight, &C4FullScreen::MenuKeyControl)));
|
||||
Keys.clear(); Keys.push_back(C4KeyCodeEx(K_UP));
|
||||
if (Config.Controls.GamepadGuiControl) Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Up)));
|
||||
if (Config.Controls.GamepadGuiControl) ControllerKeys::Up(Keys);
|
||||
KeyboardInput.RegisterKey(new C4CustomKey(Keys, "FullscreenMenuUp", KEYSCOPE_FullSMenu, new C4KeyCBEx<C4FullScreen, BYTE> (FullScreen, COM_MenuUp, &C4FullScreen::MenuKeyControl)));
|
||||
Keys.clear(); Keys.push_back(C4KeyCodeEx(K_DOWN));
|
||||
if (Config.Controls.GamepadGuiControl) Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Down)));
|
||||
if (Config.Controls.GamepadGuiControl) ControllerKeys::Down(Keys);
|
||||
KeyboardInput.RegisterKey(new C4CustomKey(Keys, "FullscreenMenuDown", KEYSCOPE_FullSMenu, new C4KeyCBEx<C4FullScreen, BYTE> (FullScreen, COM_MenuDown, &C4FullScreen::MenuKeyControl)));
|
||||
Keys.clear(); Keys.push_back(C4KeyCodeEx(K_SPACE)); Keys.push_back(C4KeyCodeEx(K_RETURN));
|
||||
if (Config.Controls.GamepadGuiControl) Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyLowButton)));
|
||||
if (Config.Controls.GamepadGuiControl) ControllerKeys::Ok(Keys);
|
||||
KeyboardInput.RegisterKey(new C4CustomKey(Keys, "FullscreenMenuOK", KEYSCOPE_FullSMenu, new C4KeyCBEx<C4FullScreen, BYTE> (FullScreen, COM_MenuEnter, &C4FullScreen::MenuKeyControl))); // name used by PlrControlKeyName
|
||||
Keys.clear(); Keys.push_back(C4KeyCodeEx(K_ESCAPE));
|
||||
if (Config.Controls.GamepadGuiControl) Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyHighButton)));
|
||||
if (Config.Controls.GamepadGuiControl) ControllerKeys::Cancel(Keys);
|
||||
KeyboardInput.RegisterKey(new C4CustomKey(Keys, "FullscreenMenuCancel", KEYSCOPE_FullSMenu, new C4KeyCBEx<C4FullScreen, BYTE> (FullScreen, COM_MenuClose, &C4FullScreen::MenuKeyControl))); // name used by PlrControlKeyName
|
||||
Keys.clear(); Keys.push_back(C4KeyCodeEx(K_SPACE));
|
||||
if (Config.Controls.GamepadGuiControl) Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyButton)));
|
||||
if (Config.Controls.GamepadGuiControl) ControllerKeys::Any(Keys);
|
||||
KeyboardInput.RegisterKey(new C4CustomKey(Keys, "FullscreenMenuOpen", KEYSCOPE_FreeView, new C4KeyCB <C4FullScreen> (FullScreen, &C4FullScreen::ActivateMenuMain))); // name used by C4MainMenu!
|
||||
KeyboardInput.RegisterKey(new C4CustomKey(C4KeyCodeEx(K_RIGHT ), "FilmNextPlayer", KEYSCOPE_FilmView, new C4KeyCB <C4ViewportList>(::Viewports, &C4ViewportList::ViewportNextPlayer)));
|
||||
|
||||
|
@ -3609,6 +3617,33 @@ void C4Game::Abort(bool fApproved)
|
|||
Application.QuitGame();
|
||||
}
|
||||
|
||||
static const std::unordered_map<std::string, C4GUI::Icons> str_to_icon =
|
||||
{
|
||||
{ "Locked", C4GUI::Ico_Ex_LockedFrontal },
|
||||
{ "League", C4GUI::Ico_Ex_League },
|
||||
{ "GameRunning", C4GUI::Ico_GameRunning },
|
||||
{ "Lobby", C4GUI::Ico_Lobby },
|
||||
{ "RuntimeJoin", C4GUI::Ico_RuntimeJoin },
|
||||
|
||||
{ "A", C4GUI::Ico_Controller_A },
|
||||
{ "B", C4GUI::Ico_Controller_B },
|
||||
{ "X", C4GUI::Ico_Controller_X },
|
||||
{ "Y", C4GUI::Ico_Controller_Y },
|
||||
{ "Back", C4GUI::Ico_Controller_Back },
|
||||
{ "Start", C4GUI::Ico_Controller_Start },
|
||||
{ "Dpad", C4GUI::Ico_Controller_Dpad },
|
||||
{ "DpadLeft", C4GUI::Ico_Controller_DpadLeft },
|
||||
{ "DpadRight", C4GUI::Ico_Controller_DpadRight },
|
||||
{ "DpadDown", C4GUI::Ico_Controller_DpadDown },
|
||||
{ "DpadUp", C4GUI::Ico_Controller_DpadUp },
|
||||
{ "LeftShoulder", C4GUI::Ico_Controller_LeftShoulder },
|
||||
{ "RightShoulder", C4GUI::Ico_Controller_RightShoulder },
|
||||
{ "LeftTrigger", C4GUI::Ico_Controller_LeftTrigger },
|
||||
{ "RightTrigger", C4GUI::Ico_Controller_RightTrigger },
|
||||
{ "LeftStick", C4GUI::Ico_Controller_LeftStick },
|
||||
{ "RightStick", C4GUI::Ico_Controller_RightStick },
|
||||
};
|
||||
|
||||
bool GetTextSpecFacet(const char* szSpec, C4Facet& fct)
|
||||
{
|
||||
// safety
|
||||
|
@ -3618,19 +3653,12 @@ bool GetTextSpecFacet(const char* szSpec, C4Facet& fct)
|
|||
if (SEqual2(szSpec, "@Ico:"))
|
||||
{
|
||||
szSpec += 5;
|
||||
if (SEqual2(szSpec, "Locked"))
|
||||
fct = C4GUI::Icon::GetIconFacet(C4GUI::Ico_Ex_LockedFrontal);
|
||||
else if (SEqual2(szSpec, "League"))
|
||||
fct = C4GUI::Icon::GetIconFacet(C4GUI::Ico_Ex_League);
|
||||
else if (SEqual2(szSpec, "GameRunning"))
|
||||
fct = C4GUI::Icon::GetIconFacet(C4GUI::Ico_GameRunning);
|
||||
else if (SEqual2(szSpec, "Lobby"))
|
||||
fct = C4GUI::Icon::GetIconFacet(C4GUI::Ico_Lobby);
|
||||
else if (SEqual2(szSpec, "RuntimeJoin"))
|
||||
fct = C4GUI::Icon::GetIconFacet(C4GUI::Ico_RuntimeJoin);
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
auto it = str_to_icon.find(szSpec);
|
||||
if (it != str_to_icon.end())
|
||||
{
|
||||
fct = C4GUI::Icon::GetIconFacet(it->second);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -146,6 +146,7 @@ public:
|
|||
void Evaluate();
|
||||
void ShowGameOverDlg();
|
||||
bool DoKeyboardInput(C4KeyCode vk_code, C4KeyEventType eEventType, bool fAlt, bool fCtrl, bool fShift, bool fRepeated, class C4GUI::Dialog *pForDialog=NULL, bool fPlrCtrlOnly=false, int32_t iStrength=-1);
|
||||
bool DoKeyboardInput(C4KeyCodeEx Key, C4KeyEventType eEventType, class C4GUI::Dialog *pForDialog=NULL, bool fPlrCtrlOnly=false, int32_t iStrength=-1);
|
||||
void DrawCrewOverheadText(C4TargetFacet &cgo, int32_t iPlayer);
|
||||
void FixRandom(int32_t iSeed);
|
||||
bool Init();
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <C4GameObjects.h>
|
||||
#include <C4GameControl.h>
|
||||
#include <C4GameMessage.h>
|
||||
#include <C4GamePadCon.h>
|
||||
#include <C4GraphicsSystem.h>
|
||||
#include <C4Log.h>
|
||||
#include <C4MessageInput.h>
|
||||
|
@ -2599,7 +2600,7 @@ static bool FnSetNextMission(C4PropList * _this, C4String *szNextMission, C4Stri
|
|||
return true;
|
||||
}
|
||||
|
||||
static long FnGetPlayerControlState(C4PropList * _this, long iPlr, long iControl)
|
||||
static long FnGetPlayerControlState(C4PropList * _this, long iPlr, long iControl, bool fMovedState)
|
||||
{
|
||||
// get control set to check
|
||||
C4PlayerControl *pCheckCtrl = NULL;
|
||||
|
@ -2617,8 +2618,8 @@ static long FnGetPlayerControlState(C4PropList * _this, long iPlr, long iControl
|
|||
const C4PlayerControl::CSync::ControlDownState *pControlState = pCheckCtrl->GetControlDownState(iControl);
|
||||
// no state means not down
|
||||
if (!pControlState) return 0;
|
||||
// otherwise take down-value
|
||||
return pControlState->DownState.iStrength;
|
||||
// otherwise take either down-value or moved-value
|
||||
return fMovedState ? pControlState->MovedState.iStrength : pControlState->DownState.iStrength;
|
||||
}
|
||||
|
||||
// undocumented!
|
||||
|
@ -2675,6 +2676,46 @@ static C4String *FnGetPlayerControlAssignment(C4PropList * _this, long player, l
|
|||
return String(assignment->GetKeysAsString(human_readable, short_name).getData());
|
||||
}
|
||||
|
||||
// strength: 0-1000, length: milliseconds
|
||||
static bool FnPlayRumble(C4PropList * _this, long player, long strength, long length)
|
||||
{
|
||||
// Check parameters.
|
||||
if (strength <= 0 || strength > 1000) return false;
|
||||
if (length <= 0) return false;
|
||||
// NO_OWNER: play rumble for all players (e.g. earthquakes)
|
||||
if (player == NO_OWNER)
|
||||
{
|
||||
for (C4Player *plr = ::Players.First; plr; plr=plr->Next)
|
||||
if (plr->Number != NO_OWNER) // can't happen, but would be a crash if it did...
|
||||
FnPlayRumble(_this, plr->Number, strength, length);
|
||||
return true;
|
||||
}
|
||||
C4Player *plr = ::Players.Get(player);
|
||||
if (!plr) return false;
|
||||
if (plr->pGamepad)
|
||||
plr->pGamepad->PlayRumble(strength / 1000.f, length);
|
||||
// We can't return whether the rumble was actually played.
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool FnStopRumble(C4PropList * _this, long player)
|
||||
{
|
||||
// NO_OWNER: stop rumble for all players
|
||||
// Not sure whether this makes sense to do - mainly provided for symmetry with PlayRumble().
|
||||
if (player == NO_OWNER)
|
||||
{
|
||||
for (C4Player *plr = ::Players.First; plr; plr=plr->Next)
|
||||
if (plr->Number != NO_OWNER) // can't happen, but would be a crash if it did...
|
||||
FnStopRumble(_this, plr->Number);
|
||||
return true;
|
||||
}
|
||||
C4Player *plr = ::Players.Get(player);
|
||||
if (!plr) return false;
|
||||
if (plr->pGamepad)
|
||||
plr->pGamepad->StopRumble();
|
||||
return true;
|
||||
}
|
||||
|
||||
static int32_t FnGetStartupPlayerCount(C4PropList * _this)
|
||||
{
|
||||
// returns number of players when game was initially started
|
||||
|
@ -2902,6 +2943,8 @@ void InitGameFunctionMap(C4AulScriptEngine *pEngine)
|
|||
F(SetPlayerControlEnabled);
|
||||
F(GetPlayerControlEnabled);
|
||||
F(GetPlayerControlAssignment);
|
||||
F(PlayRumble);
|
||||
F(StopRumble);
|
||||
F(GetStartupPlayerCount);
|
||||
F(GetStartupTeamCount);
|
||||
F(EditCursor);
|
||||
|
@ -3091,6 +3134,14 @@ C4ScriptConstDef C4ScriptGameConstMap[]=
|
|||
{ "GUI_Multiple" ,C4V_Int, C4ScriptGuiWindowStyleFlag::Multiple },
|
||||
{ "GUI_IgnoreMouse" ,C4V_Int, C4ScriptGuiWindowStyleFlag::IgnoreMouse },
|
||||
{ "GUI_NoCrop" ,C4V_Int, C4ScriptGuiWindowStyleFlag::NoCrop },
|
||||
|
||||
// control states
|
||||
{ "CONS_Down" ,C4V_Int, C4PlayerControl::CONS_Down },
|
||||
{ "CONS_Up" ,C4V_Int, C4PlayerControl::CONS_Up },
|
||||
{ "CONS_Moved" ,C4V_Int, C4PlayerControl::CONS_Moved },
|
||||
|
||||
{ "PLRCON_MaxStrength" ,C4V_Int, C4GamePadControl::MaxStrength },
|
||||
|
||||
{ NULL, C4V_Nil, 0}
|
||||
};
|
||||
|
||||
|
|
|
@ -128,6 +128,7 @@ void C4GraphicsResource::Clear()
|
|||
idSfcCaption = idSfcButton = idSfcButtonD = idSfcScroll = idSfcContext = 0;
|
||||
barCaption.Clear(); barButton.Clear(); barButtonD.Clear();
|
||||
fctButtonHighlight.Clear(); fctIcons.Clear(); fctIconsEx.Clear();
|
||||
fctControllerIcons.Clear();
|
||||
fctButtonHighlightRound.Clear();
|
||||
fctSubmenu.Clear();
|
||||
fctCheckbox.Clear();
|
||||
|
@ -218,6 +219,8 @@ bool C4GraphicsResource::Init()
|
|||
fctIcons.Set(fctIcons.Surface,0,0,C4GUI_IconWdt,C4GUI_IconHgt);
|
||||
if (!LoadFile(fctIconsEx, "GUIIcons2", Files, C4FCT_Full, C4FCT_Full, false, 0)) return false;
|
||||
fctIconsEx.Set(fctIconsEx.Surface,0,0,C4GUI_IconExWdt,C4GUI_IconExHgt);
|
||||
if (!LoadFile(fctControllerIcons, "ControllerIcons", Files, C4FCT_Full, C4FCT_Full, false, 0)) return false;
|
||||
fctControllerIcons.Set(fctControllerIcons.Surface,0,0,C4GUI_ControllerIconWdt,C4GUI_ControllerIconHgt);
|
||||
if (!LoadFile(sfcScroll, "GUIScroll", Files, idSfcScroll, 0)) return false;
|
||||
sfctScroll.Set(C4Facet(&sfcScroll,0,0,32,32));
|
||||
if (!LoadFile(sfcContext, "GUIContext", Files, idSfcContext, 0)) return false;
|
||||
|
|
|
@ -83,6 +83,7 @@ public:
|
|||
C4FacetID fctButtonHighlight;
|
||||
C4FacetID fctButtonHighlightRound;
|
||||
C4FacetID fctIcons, fctIconsEx;
|
||||
C4FacetID fctControllerIcons;
|
||||
C4FacetID fctSubmenu;
|
||||
C4FacetID fctCheckbox;
|
||||
C4FacetID fctBigArrows;
|
||||
|
|
|
@ -569,7 +569,7 @@ namespace C4GUI
|
|||
}
|
||||
}
|
||||
|
||||
Screen::Screen() : Window(), Mouse(0, 0), pContext(NULL), fExclusive(true), pGamePadOpener(NULL), fZoom(1.0f)
|
||||
Screen::Screen() : Window(), Mouse(0, 0), pContext(NULL), fExclusive(true), fZoom(1.0f)
|
||||
{
|
||||
// no dialog active
|
||||
pActiveDlg = NULL;
|
||||
|
@ -585,9 +585,6 @@ namespace C4GUI
|
|||
// set size - calcs client area as well
|
||||
SetBounds(C4Rect(tx,ty,twdt,thgt));
|
||||
SetPreferredDlgRect(C4Rect(0,0,twdt,thgt));
|
||||
// GamePad
|
||||
if (Application.pGamePadControl && Config.Controls.GamepadGuiControl)
|
||||
pGamePadOpener = new C4GamePadOpener(0);
|
||||
}
|
||||
|
||||
void Screen::Clear()
|
||||
|
@ -595,8 +592,6 @@ namespace C4GUI
|
|||
Container::Clear();
|
||||
// dtor: Close context menu
|
||||
AbortContext(false);
|
||||
// GamePad
|
||||
if (pGamePadOpener) delete pGamePadOpener;
|
||||
// fields reset
|
||||
fExclusive = true;
|
||||
fZoom = 1.0f;
|
||||
|
@ -1055,15 +1050,7 @@ namespace C4GUI
|
|||
|
||||
void Screen::UpdateGamepadGUIControlEnabled()
|
||||
{
|
||||
// update pGamePadOpener to config value
|
||||
if (pGamePadOpener && (!Config.Controls.GamepadGuiControl || !Application.pGamePadControl))
|
||||
{
|
||||
delete pGamePadOpener; pGamePadOpener = NULL;
|
||||
}
|
||||
else if (!pGamePadOpener && (Config.Controls.GamepadGuiControl && Application.pGamePadControl))
|
||||
{
|
||||
pGamePadOpener = new C4GamePadOpener(0);
|
||||
}
|
||||
// Gamepad is always kept open now.
|
||||
}
|
||||
|
||||
Screen TheScreen;
|
||||
|
|
|
@ -94,6 +94,8 @@
|
|||
#define C4GUI_IconHgt 40
|
||||
#define C4GUI_IconExWdt 64
|
||||
#define C4GUI_IconExHgt 64
|
||||
#define C4GUI_ControllerIconWdt 100
|
||||
#define C4GUI_ControllerIconHgt 100
|
||||
|
||||
#define C4GUI_IconLabelSpacing 2 // space between an icon and its text
|
||||
|
||||
|
@ -627,7 +629,11 @@ namespace C4GUI
|
|||
};
|
||||
|
||||
// icon indices
|
||||
enum { Ico_Extended = 0x100 }; // icon index offset for extended icons
|
||||
enum
|
||||
{
|
||||
Ico_Extended = 0x100, // icon index offset for extended icons
|
||||
Ico_Controller = 0x200,
|
||||
};
|
||||
enum Icons
|
||||
{
|
||||
Ico_Empty = -2, // for context menus only
|
||||
|
@ -705,7 +711,25 @@ namespace C4GUI
|
|||
Ico_Ex_Update = Ico_Extended + 14,
|
||||
Ico_Ex_Chat = Ico_Extended + 15,
|
||||
Ico_Ex_GameList = Ico_Extended + 16,
|
||||
Ico_Ex_Comment = Ico_Extended + 17
|
||||
Ico_Ex_Comment = Ico_Extended + 17,
|
||||
|
||||
Ico_Controller_A = Ico_Controller + 0,
|
||||
Ico_Controller_B = Ico_Controller + 3,
|
||||
Ico_Controller_X = Ico_Controller + 17,
|
||||
Ico_Controller_Y = Ico_Controller + 18,
|
||||
Ico_Controller_Back = Ico_Controller + 1,
|
||||
Ico_Controller_Start = Ico_Controller + 16,
|
||||
Ico_Controller_Dpad = Ico_Controller + 6,
|
||||
Ico_Controller_DpadLeft = Ico_Controller + 5,
|
||||
Ico_Controller_DpadRight = Ico_Controller + 7,
|
||||
Ico_Controller_DpadDown = Ico_Controller + 4,
|
||||
Ico_Controller_DpadUp = Ico_Controller + 8,
|
||||
Ico_Controller_LeftShoulder = Ico_Controller + 9,
|
||||
Ico_Controller_RightShoulder = Ico_Controller + 12,
|
||||
Ico_Controller_LeftTrigger = Ico_Controller + 11,
|
||||
Ico_Controller_RightTrigger = Ico_Controller + 14,
|
||||
Ico_Controller_LeftStick = Ico_Controller + 10,
|
||||
Ico_Controller_RightStick = Ico_Controller + 13,
|
||||
};
|
||||
|
||||
// cute, litte, useless thingy
|
||||
|
@ -2566,7 +2590,6 @@ namespace C4GUI
|
|||
ContextMenu *pContext; // currently opened context menu (lowest submenu)
|
||||
bool fExclusive; // default true. if false, input is shared with the game
|
||||
C4Rect PreferredDlgRect; // rectangle in which dialogs should be placed
|
||||
C4GamePadOpener * pGamePadOpener;
|
||||
float fZoom;
|
||||
|
||||
static Screen *pScreen; // static singleton var
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace C4GUI
|
|||
keys.push_back(C4KeyCodeEx(K_SPACE));
|
||||
keys.push_back(C4KeyCodeEx(K_RETURN));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyLowButton)));
|
||||
ControllerKeys::Ok(keys);
|
||||
pKeyButton = new C4KeyBinding(keys, "GUIButtonPress", KEYSCOPE_Gui,
|
||||
new ControlKeyCB<Button>(*this, &Button::KeyButtonDown, &Button::KeyButtonUp), C4CustomKey::PRIO_Ctrl);
|
||||
sText = "";
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace C4GUI
|
|||
Keys.push_back(C4KeyCodeEx(K_SPACE));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyLowButton)));
|
||||
ControllerKeys::Ok(Keys);
|
||||
}
|
||||
pKeyCheck = new C4KeyBinding(Keys, "GUICheckboxToggle", KEYSCOPE_Gui,
|
||||
new ControlKeyCB<CheckBox>(*this, &CheckBox::KeyCheck), C4CustomKey::PRIO_Ctrl);
|
||||
|
|
|
@ -76,8 +76,8 @@ namespace C4GUI
|
|||
cbKeys.push_back(C4KeyCodeEx(K_SPACE, KEYS_Alt));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
cbKeys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyLowButton)));
|
||||
cbKeys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Down)));
|
||||
ControllerKeys::Ok(cbKeys);
|
||||
ControllerKeys::Down(cbKeys);
|
||||
}
|
||||
pKeyOpenCombo = new C4KeyBinding(cbKeys, "GUIComboOpen", KEYSCOPE_Gui,
|
||||
new ControlKeyCB<ComboBox>(*this, &ComboBox::KeyDropDown), C4CustomKey::PRIO_Ctrl);
|
||||
|
@ -85,7 +85,7 @@ namespace C4GUI
|
|||
cbKeys.push_back(C4KeyCodeEx(K_ESCAPE));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
cbKeys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyHighButton)));
|
||||
ControllerKeys::Cancel(cbKeys);
|
||||
}
|
||||
pKeyCloseCombo = new C4KeyBinding(cbKeys, "GUIComboClose", KEYSCOPE_Gui,
|
||||
new ControlKeyCB<ComboBox>(*this, &ComboBox::KeyAbortDropDown), C4CustomKey::PRIO_Ctrl);
|
||||
|
|
|
@ -281,7 +281,7 @@ namespace C4GUI
|
|||
Keys.push_back(C4KeyCodeEx(K_TAB));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Right)));
|
||||
ControllerKeys::Right(Keys);
|
||||
}
|
||||
pKeyAdvanceControl = new C4KeyBinding(Keys, "GUIAdvanceFocus", KEYSCOPE_Gui,
|
||||
new DlgKeyCBEx<Dialog, bool>(*this, false, &Dialog::KeyAdvanceFocus), C4CustomKey::PRIO_Dlg);
|
||||
|
@ -289,7 +289,7 @@ namespace C4GUI
|
|||
Keys.push_back(C4KeyCodeEx(K_TAB, KEYS_Shift));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Left)));
|
||||
ControllerKeys::Left(Keys);
|
||||
}
|
||||
pKeyAdvanceControlB = new C4KeyBinding(Keys, "GUIAdvanceFocusBack", KEYSCOPE_Gui,
|
||||
new DlgKeyCBEx<Dialog, bool>(*this, true, &Dialog::KeyAdvanceFocus), C4CustomKey::PRIO_Dlg);
|
||||
|
@ -302,7 +302,7 @@ namespace C4GUI
|
|||
Keys.push_back(C4KeyCodeEx(K_RETURN));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyLowButton)));
|
||||
ControllerKeys::Ok(Keys);
|
||||
}
|
||||
pKeyEnter = new C4KeyBinding(Keys, "GUIDialogOkay", KEYSCOPE_Gui,
|
||||
new DlgKeyCB<Dialog>(*this, &Dialog::KeyEnter), C4CustomKey::PRIO_Dlg);
|
||||
|
@ -310,7 +310,7 @@ namespace C4GUI
|
|||
Keys.push_back(C4KeyCodeEx(K_ESCAPE));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyHighButton)));
|
||||
ControllerKeys::Cancel(Keys);
|
||||
}
|
||||
pKeyEscape = new C4KeyBinding(Keys, "GUIDialogAbort", KEYSCOPE_Gui,
|
||||
new DlgKeyCB<Dialog>(*this, &Dialog::KeyEscape), C4CustomKey::PRIO_Dlg);
|
||||
|
|
|
@ -710,7 +710,7 @@ namespace C4GUI
|
|||
keys.push_back(C4KeyCodeEx(K_ESCAPE));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyHighButton)));
|
||||
ControllerKeys::Cancel(keys);
|
||||
}
|
||||
pKeyAbort = new C4KeyBinding(keys, "GUIRenameEditAbort", KEYSCOPE_Gui,
|
||||
new ControlKeyCB<RenameEdit>(*this, &RenameEdit::KeyAbort), C4CustomKey::PRIO_FocusCtrl);
|
||||
|
|
|
@ -428,12 +428,18 @@ namespace C4GUI
|
|||
C4Facet Icon::GetIconFacet(Icons icoIconIndex)
|
||||
{
|
||||
if (icoIconIndex == Ico_None) return C4Facet();
|
||||
C4Facet &rFacet = (icoIconIndex & Ico_Extended) ? ::GraphicsResource.fctIconsEx : ::GraphicsResource.fctIcons;
|
||||
icoIconIndex = Icons(icoIconIndex & (Ico_Extended-1));
|
||||
C4Facet *rFacet;
|
||||
switch (icoIconIndex & ~0xff)
|
||||
{
|
||||
case Ico_Extended: rFacet = &::GraphicsResource.fctIconsEx; break;
|
||||
case Ico_Controller: rFacet = &::GraphicsResource.fctControllerIcons; break;
|
||||
default: rFacet = &::GraphicsResource.fctIcons;
|
||||
}
|
||||
icoIconIndex = Icons(icoIconIndex & 0xff);
|
||||
int32_t iXMax, iYMax;
|
||||
rFacet.GetPhaseNum(iXMax, iYMax);
|
||||
rFacet->GetPhaseNum(iXMax, iYMax);
|
||||
if (!iXMax) iXMax = 6;
|
||||
return rFacet.GetPhase(icoIconIndex % iXMax, icoIconIndex / iXMax);
|
||||
return rFacet->GetPhase(icoIconIndex % iXMax, icoIconIndex / iXMax);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -43,22 +43,22 @@ namespace C4GUI
|
|||
new ControlKeyCB<ListBox>(*this, &ListBox::KeyContext), C4CustomKey::PRIO_Ctrl);
|
||||
C4CustomKey::CodeList keys;
|
||||
keys.push_back(C4KeyCodeEx(K_UP));
|
||||
if (Config.Controls.GamepadGuiControl) keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Up)));
|
||||
if (Config.Controls.GamepadGuiControl) ControllerKeys::Up(keys);
|
||||
pKeyUp = new C4KeyBinding(keys, "GUIListBoxUp", KEYSCOPE_Gui,
|
||||
new ControlKeyCB<ListBox>(*this, &ListBox::KeyUp), C4CustomKey::PRIO_Ctrl);
|
||||
keys.clear();
|
||||
keys.push_back(C4KeyCodeEx(K_DOWN));
|
||||
if (Config.Controls.GamepadGuiControl) keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Down)));
|
||||
if (Config.Controls.GamepadGuiControl) ControllerKeys::Down(keys);
|
||||
pKeyDown = new C4KeyBinding(keys, "GUIListBoxDown", KEYSCOPE_Gui,
|
||||
new ControlKeyCB<ListBox>(*this, &ListBox::KeyDown), C4CustomKey::PRIO_Ctrl);
|
||||
keys.clear();
|
||||
keys.push_back(C4KeyCodeEx(K_LEFT));
|
||||
if (Config.Controls.GamepadGuiControl) keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Left)));
|
||||
if (Config.Controls.GamepadGuiControl) ControllerKeys::Left(keys);
|
||||
pKeyLeft = new C4KeyBinding(keys, "GUIListBoxLeft", KEYSCOPE_Gui,
|
||||
new ControlKeyCB<ListBox>(*this, &ListBox::KeyLeft), C4CustomKey::PRIO_Ctrl);
|
||||
keys.clear();
|
||||
keys.push_back(C4KeyCodeEx(K_RIGHT));
|
||||
if (Config.Controls.GamepadGuiControl) keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Right)));
|
||||
if (Config.Controls.GamepadGuiControl) ControllerKeys::Right(keys);
|
||||
pKeyRight = new C4KeyBinding(keys, "GUIListBoxRight", KEYSCOPE_Gui,
|
||||
new ControlKeyCB<ListBox>(*this, &ListBox::KeyRight), C4CustomKey::PRIO_Ctrl);
|
||||
pKeyPageUp = new C4KeyBinding(C4KeyCodeEx(K_PAGEUP), "GUIListBoxPageUp", KEYSCOPE_Gui,
|
||||
|
@ -75,7 +75,7 @@ namespace C4GUI
|
|||
keys.push_back(C4KeyCodeEx(K_RETURN, KEYS_Alt));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyLowButton)));
|
||||
ControllerKeys::Ok(keys);
|
||||
}
|
||||
pKeyActivate = new C4KeyBinding(keys, "GUIListActivate", KEYSCOPE_Gui,
|
||||
new ControlKeyCB<ListBox>(*this, &ListBox::KeyActivate), C4CustomKey::PRIO_Ctrl);
|
||||
|
|
|
@ -94,7 +94,7 @@ namespace C4GUI
|
|||
Keys.push_back(C4KeyCodeEx(K_UP));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Up)));
|
||||
ControllerKeys::Up(Keys);
|
||||
}
|
||||
pKeySelUp = new C4KeyBinding(Keys, "GUIContextSelUp", KEYSCOPE_Gui,
|
||||
new C4KeyCB<ContextMenu>(*this, &ContextMenu::KeySelUp), C4CustomKey::PRIO_Context);
|
||||
|
@ -103,7 +103,7 @@ namespace C4GUI
|
|||
Keys.push_back(C4KeyCodeEx(K_DOWN));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Down)));
|
||||
ControllerKeys::Down(Keys);
|
||||
}
|
||||
pKeySelDown = new C4KeyBinding(Keys, "GUIContextSelDown", KEYSCOPE_Gui,
|
||||
new C4KeyCB<ContextMenu>(*this, &ContextMenu::KeySelDown), C4CustomKey::PRIO_Context);
|
||||
|
@ -112,7 +112,7 @@ namespace C4GUI
|
|||
Keys.push_back(C4KeyCodeEx(K_RIGHT));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Right)));
|
||||
ControllerKeys::Right(Keys);
|
||||
}
|
||||
pKeySubmenu = new C4KeyBinding(Keys, "GUIContextSubmenu", KEYSCOPE_Gui,
|
||||
new C4KeyCB<ContextMenu>(*this, &ContextMenu::KeySubmenu), C4CustomKey::PRIO_Context);
|
||||
|
@ -121,7 +121,7 @@ namespace C4GUI
|
|||
Keys.push_back(C4KeyCodeEx(K_LEFT));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Left)));
|
||||
ControllerKeys::Left(Keys);
|
||||
}
|
||||
pKeyBack = new C4KeyBinding(Keys, "GUIContextBack", KEYSCOPE_Gui,
|
||||
new C4KeyCB<ContextMenu>(*this, &ContextMenu::KeyBack), C4CustomKey::PRIO_Context);
|
||||
|
@ -130,7 +130,7 @@ namespace C4GUI
|
|||
Keys.push_back(C4KeyCodeEx(K_ESCAPE));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyHighButton)));
|
||||
ControllerKeys::Cancel(Keys);
|
||||
}
|
||||
pKeyAbort = new C4KeyBinding(Keys, "GUIContextAbort", KEYSCOPE_Gui,
|
||||
new C4KeyCB<ContextMenu>(*this, &ContextMenu::KeyAbort), C4CustomKey::PRIO_Context);
|
||||
|
@ -139,7 +139,7 @@ namespace C4GUI
|
|||
Keys.push_back(C4KeyCodeEx(K_RETURN));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyLowButton)));
|
||||
ControllerKeys::Ok(Keys);
|
||||
}
|
||||
pKeyConfirm = new C4KeyBinding(Keys, "GUIContextConfirm", KEYSCOPE_Gui,
|
||||
new C4KeyCB<ContextMenu>(*this, &ContextMenu::KeyConfirm), C4CustomKey::PRIO_Context);
|
||||
|
|
|
@ -181,7 +181,7 @@ namespace C4GUI
|
|||
Keys.push_back(C4KeyCodeEx(K_UP));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Up)));
|
||||
ControllerKeys::Up(Keys);
|
||||
}
|
||||
pKeySelUp = new C4KeyBinding(Keys, "GUITabularSelUp", KEYSCOPE_Gui,
|
||||
new ControlKeyCB<Tabular>(*this, &Tabular::KeySelUp), C4CustomKey::PRIO_Ctrl);
|
||||
|
@ -190,7 +190,7 @@ namespace C4GUI
|
|||
Keys.push_back(C4KeyCodeEx(K_DOWN));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
Keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Down)));
|
||||
ControllerKeys::Down(Keys);
|
||||
}
|
||||
pKeySelDown = new C4KeyBinding(Keys, "GUITabularSelDown", KEYSCOPE_Gui,
|
||||
new ControlKeyCB<Tabular>(*this, &Tabular::KeySelDown), C4CustomKey::PRIO_Ctrl);
|
||||
|
|
|
@ -31,8 +31,11 @@
|
|||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef USE_SDL_MAINLOOP
|
||||
#ifdef HAVE_SDL
|
||||
#include <SDL.h>
|
||||
#endif
|
||||
|
||||
|
@ -190,6 +193,11 @@ const C4KeyCodeMapEntry KeyCodeMap[] = {
|
|||
};
|
||||
#endif
|
||||
|
||||
C4KeyCodeEx::C4KeyCodeEx(C4KeyCode key, C4KeyShiftState Shift, bool fIsRepeated, int32_t deviceId)
|
||||
: Key(key), dwShift(Shift), fRepeated(fIsRepeated), deviceId(deviceId)
|
||||
{
|
||||
}
|
||||
|
||||
void C4KeyCodeEx::FixShiftKeys()
|
||||
{
|
||||
// reduce stuff like Ctrl+RightCtrl to simply RightCtrl
|
||||
|
@ -205,6 +213,36 @@ C4KeyCode C4KeyCodeEx::GetKeyByScanCode(const char *scan_code)
|
|||
return scan_code_int;
|
||||
}
|
||||
|
||||
static const std::unordered_map<std::string, C4KeyCode> controllercodes =
|
||||
{
|
||||
{ "ButtonA", KEY_CONTROLLER_ButtonA },
|
||||
{ "ButtonB", KEY_CONTROLLER_ButtonB },
|
||||
{ "ButtonX", KEY_CONTROLLER_ButtonX },
|
||||
{ "ButtonY", KEY_CONTROLLER_ButtonY },
|
||||
{ "ButtonBack", KEY_CONTROLLER_ButtonBack },
|
||||
{ "ButtonGuide", KEY_CONTROLLER_ButtonGuide },
|
||||
{ "ButtonStart", KEY_CONTROLLER_ButtonStart },
|
||||
{ "ButtonLeftStick", KEY_CONTROLLER_ButtonLeftStick },
|
||||
{ "ButtonRightStick", KEY_CONTROLLER_ButtonRightStick },
|
||||
{ "ButtonLeftShoulder", KEY_CONTROLLER_ButtonLeftShoulder },
|
||||
{ "ButtonRightShoulder", KEY_CONTROLLER_ButtonRightShoulder },
|
||||
{ "ButtonDpadUp", KEY_CONTROLLER_ButtonDpadUp },
|
||||
{ "ButtonDpadDown", KEY_CONTROLLER_ButtonDpadDown },
|
||||
{ "ButtonDpadLeft", KEY_CONTROLLER_ButtonDpadLeft },
|
||||
{ "ButtonDpadRight", KEY_CONTROLLER_ButtonDpadRight },
|
||||
{ "AnyButton", KEY_CONTROLLER_AnyButton },
|
||||
{ "LeftStickLeft", KEY_CONTROLLER_AxisLeftXLeft },
|
||||
{ "LeftStickRight", KEY_CONTROLLER_AxisLeftXRight },
|
||||
{ "LeftStickUp", KEY_CONTROLLER_AxisLeftYUp },
|
||||
{ "LeftStickDown", KEY_CONTROLLER_AxisLeftYDown },
|
||||
{ "RightStickLeft", KEY_CONTROLLER_AxisRightXLeft },
|
||||
{ "RightStickRight", KEY_CONTROLLER_AxisRightXRight },
|
||||
{ "RightStickUp", KEY_CONTROLLER_AxisRightYUp },
|
||||
{ "RightStickDown", KEY_CONTROLLER_AxisRightYDown },
|
||||
{ "LeftTrigger", KEY_CONTROLLER_AxisTriggerLeft },
|
||||
{ "RightTrigger", KEY_CONTROLLER_AxisTriggerRight },
|
||||
};
|
||||
|
||||
C4KeyCode C4KeyCodeEx::String2KeyCode(const StdStrBuf &sName)
|
||||
{
|
||||
// direct key code?
|
||||
|
@ -215,47 +253,16 @@ C4KeyCode C4KeyCodeEx::String2KeyCode(const StdStrBuf &sName)
|
|||
// scan code
|
||||
if (*sName.getData() == '$') return GetKeyByScanCode(sName.getData());
|
||||
// direct gamepad code
|
||||
#ifdef _WIN32
|
||||
if (!strnicmp(sName.getData(), "Joy", 3))
|
||||
#else
|
||||
if (!strncasecmp(sName.getData(), "Joy", 3))
|
||||
#endif
|
||||
std::regex controller_re(R"/(^Controller(\w+)$)/");
|
||||
std::cmatch matches;
|
||||
if (std::regex_match(sName.getData(), matches, controller_re))
|
||||
{
|
||||
int iGamepad;
|
||||
if (sscanf(sName.getData(), "Joy%d", &iGamepad) == 1)
|
||||
{
|
||||
// skip Joy[number]
|
||||
const char *key_str = sName.getData()+4;
|
||||
while (isdigit(*key_str)) ++key_str;
|
||||
// check for button (single, uppercase letter) (e.g. Joy1A)
|
||||
if (*key_str && !key_str[1])
|
||||
{
|
||||
char cGamepadButton = toupper(*key_str);
|
||||
if (Inside(cGamepadButton, 'A', 'Z'))
|
||||
{
|
||||
cGamepadButton = cGamepadButton - 'A';
|
||||
return KEY_Gamepad(iGamepad-1, KEY_JOY_Button(cGamepadButton));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// check for standard axis (e.g. Joy1Left)
|
||||
if (!stricmp(key_str, "Left")) return KEY_Gamepad(iGamepad-1, KEY_JOY_Left);
|
||||
if (!stricmp(key_str, "Up")) return KEY_Gamepad(iGamepad-1, KEY_JOY_Up);
|
||||
if (!stricmp(key_str, "Down")) return KEY_Gamepad(iGamepad-1, KEY_JOY_Down);
|
||||
if (!stricmp(key_str, "Right")) return KEY_Gamepad(iGamepad-1, KEY_JOY_Right);
|
||||
// check for specific axis (e.g. Joy1Axis1Min)
|
||||
int iAxis;
|
||||
if (sscanf(key_str, "Axis%d", &iAxis) == 1 && iAxis>0)
|
||||
{
|
||||
--iAxis; // axis is 0-based internally but written 1-based in config
|
||||
key_str += 5;
|
||||
while (isdigit(*key_str)) ++key_str;
|
||||
if (!stricmp(key_str, "Min")) return KEY_Gamepad(iGamepad-1, KEY_JOY_Axis(iAxis, false));
|
||||
if (!stricmp(key_str, "Max")) return KEY_Gamepad(iGamepad-1, KEY_JOY_Axis(iAxis, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
auto keycode_it = controllercodes.find(matches[1].str());
|
||||
if (keycode_it != controllercodes.end())
|
||||
return KEY_Gamepad(keycode_it->second);
|
||||
else
|
||||
return KEY_Undefined;
|
||||
|
||||
}
|
||||
bool is_mouse_key;
|
||||
#ifdef _WIN32
|
||||
|
@ -326,34 +333,51 @@ StdStrBuf C4KeyCodeEx::KeyCode2String(C4KeyCode wCode, bool fHumanReadable, bool
|
|||
// Gamepad keys
|
||||
if (Key_IsGamepad(wCode))
|
||||
{
|
||||
int iGamepad = Key_GetGamepad(wCode);
|
||||
int gamepad_event = Key_GetGamepadEvent(wCode);
|
||||
switch (gamepad_event)
|
||||
if (fHumanReadable)
|
||||
{
|
||||
case KEY_JOY_Left: return FormatString("Joy%dLeft", iGamepad+1);
|
||||
case KEY_JOY_Up: return FormatString("Joy%dUp", iGamepad+1);
|
||||
case KEY_JOY_Down: return FormatString("Joy%dDown", iGamepad+1);
|
||||
case KEY_JOY_Right: return FormatString("Joy%dRight", iGamepad+1);
|
||||
default:
|
||||
if (Key_IsGamepadAxis(wCode))
|
||||
switch (Key_GetGamepadEvent(wCode))
|
||||
{
|
||||
if (fHumanReadable)
|
||||
// This is still not great, but it is not really possible to assign unknown axes to "left/right" "up/down"...
|
||||
return FormatString("[%d] %s", int(1 + Key_GetGamepadAxisIndex(wCode)), Key_IsGamepadAxisHigh(wCode) ? "Max" : "Min");
|
||||
else
|
||||
return FormatString("Joy%dAxis%d%s", iGamepad+1, static_cast<int>(Key_GetGamepadAxisIndex(wCode)+1), Key_IsGamepadAxisHigh(wCode) ? "Max" : "Min");
|
||||
}
|
||||
else
|
||||
{
|
||||
// button
|
||||
if (fHumanReadable)
|
||||
// If there should be gamepads around with A B C D... on the buttons, we might create a display option to show letters instead...
|
||||
return FormatString("< %d >", int(1 + Key_GetGamepadButtonIndex(wCode)));
|
||||
else
|
||||
return FormatString("Joy%d%c", iGamepad+1, static_cast<char>(Key_GetGamepadButtonIndex(wCode) + 'A'));
|
||||
case KEY_CONTROLLER_ButtonA : return StdStrBuf("{{@Ico:A}}");
|
||||
case KEY_CONTROLLER_ButtonB : return StdStrBuf("{{@Ico:B}}");
|
||||
case KEY_CONTROLLER_ButtonX : return StdStrBuf("{{@Ico:X}}");
|
||||
case KEY_CONTROLLER_ButtonY : return StdStrBuf("{{@Ico:Y}}");
|
||||
case KEY_CONTROLLER_ButtonBack : return StdStrBuf("{{@Ico:Back}}");
|
||||
case KEY_CONTROLLER_ButtonGuide : return StdStrBuf("Guide");
|
||||
case KEY_CONTROLLER_ButtonStart : return StdStrBuf("{{@Ico:Start}}");
|
||||
case KEY_CONTROLLER_ButtonLeftStick : return StdStrBuf("{{@Ico:LeftStick}}");
|
||||
case KEY_CONTROLLER_ButtonRightStick : return StdStrBuf("{{@Ico:RightStick}}");
|
||||
case KEY_CONTROLLER_ButtonLeftShoulder : return StdStrBuf("{{@Ico:LeftShoulder}}");
|
||||
case KEY_CONTROLLER_ButtonRightShoulder : return StdStrBuf("{{@Ico:RightShoulder}}");
|
||||
case KEY_CONTROLLER_ButtonDpadUp : return StdStrBuf("{{@Ico:DpadUp}}");
|
||||
case KEY_CONTROLLER_ButtonDpadDown : return StdStrBuf("{{@Ico:DpadDown}}");
|
||||
case KEY_CONTROLLER_ButtonDpadLeft : return StdStrBuf("{{@Ico:DpadLeft}}");
|
||||
case KEY_CONTROLLER_ButtonDpadRight : return StdStrBuf("{{@Ico:DpadRight}}");
|
||||
case KEY_CONTROLLER_AnyButton : return StdStrBuf("Any Button");
|
||||
case KEY_CONTROLLER_AxisLeftXLeft : return StdStrBuf("{{@Ico:LeftStick}} Left");
|
||||
case KEY_CONTROLLER_AxisLeftXRight : return StdStrBuf("{{@Ico:LeftStick}} Right");
|
||||
case KEY_CONTROLLER_AxisLeftYUp : return StdStrBuf("{{@Ico:LeftStick}} Up");
|
||||
case KEY_CONTROLLER_AxisLeftYDown : return StdStrBuf("{{@Ico:LeftStick}} Down");
|
||||
case KEY_CONTROLLER_AxisRightXLeft : return StdStrBuf("{{@Ico:RightStick}} Left");
|
||||
case KEY_CONTROLLER_AxisRightXRight : return StdStrBuf("{{@Ico:RightStick}} Right");
|
||||
case KEY_CONTROLLER_AxisRightYUp : return StdStrBuf("{{@Ico:RightStick}} Up");
|
||||
case KEY_CONTROLLER_AxisRightYDown : return StdStrBuf("{{@Ico:RightStick}} Down");
|
||||
case KEY_CONTROLLER_AxisTriggerLeft : return StdStrBuf("{{@Ico:LeftTrigger}}");
|
||||
case KEY_CONTROLLER_AxisTriggerRight : return StdStrBuf("{{@Ico:RightTrigger}}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// A linear search in our small map is probably fast enough.
|
||||
auto it = std::find_if(controllercodes.begin(), controllercodes.end(), [wCode](const auto &p)
|
||||
{
|
||||
return p.second == Key_GetGamepadEvent(wCode);
|
||||
});
|
||||
if (it != controllercodes.end())
|
||||
return FormatString("Controller%s", it->first.c_str());
|
||||
}
|
||||
return StdStrBuf("Unknown");
|
||||
}
|
||||
|
||||
// Mouse keys
|
||||
if (Key_IsMouse(wCode))
|
||||
{
|
||||
|
@ -602,6 +626,15 @@ C4CustomKey::~C4CustomKey()
|
|||
(*i)->Deref();
|
||||
}
|
||||
|
||||
bool C4CustomKey::IsCodeMatched(const C4KeyCodeEx &key) const
|
||||
{
|
||||
const CodeList &codes = GetCodes();
|
||||
for (const auto &code : codes)
|
||||
if (code == key)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void C4CustomKey::Update(const C4CustomKey *pByKey)
|
||||
{
|
||||
assert(pByKey);
|
||||
|
@ -812,28 +845,12 @@ bool C4KeyboardInput::DoInput(const C4KeyCodeEx &InKey, C4KeyEventType InEvent,
|
|||
FallbackKeys[iKeyRangeCnt++] = InKey.Key;
|
||||
if (Key_IsGamepadButton(InKey.Key))
|
||||
{
|
||||
uint8_t byGamepad = Key_GetGamepad(InKey.Key);
|
||||
uint8_t byBtnIndex = Key_GetGamepadButtonIndex(InKey.Key);
|
||||
// even/odd button events: Add even button indices as odd events, because byBtnIndex is zero-based and the event naming scheme is for one-based button indices
|
||||
if (byBtnIndex % 2) FallbackKeys[iKeyRangeCnt++] = KEY_Gamepad(byGamepad, KEY_JOY_AnyEvenButton);
|
||||
else FallbackKeys[iKeyRangeCnt++] = KEY_Gamepad(byGamepad, KEY_JOY_AnyOddButton);
|
||||
// high/low button events
|
||||
if (byBtnIndex < 4) FallbackKeys[iKeyRangeCnt++] = KEY_Gamepad(byGamepad, KEY_JOY_AnyLowButton);
|
||||
else FallbackKeys[iKeyRangeCnt++] = KEY_Gamepad(byGamepad, KEY_JOY_AnyHighButton);
|
||||
// "any gamepad button"-event
|
||||
FallbackKeys[iKeyRangeCnt++] = KEY_Gamepad(byGamepad, KEY_JOY_AnyButton);
|
||||
FallbackKeys[iKeyRangeCnt++] = KEY_Gamepad(KEY_CONTROLLER_AnyButton);
|
||||
}
|
||||
else if (Key_IsGamepadAxis(InKey.Key))
|
||||
{
|
||||
// xy-axis-events for all even/odd axises
|
||||
uint8_t byGamepad = Key_GetGamepad(InKey.Key);
|
||||
uint8_t byAxis = Key_GetGamepadAxisIndex(InKey.Key);
|
||||
bool fHigh = Key_IsGamepadAxisHigh(InKey.Key);
|
||||
C4KeyCode keyAxisDir;
|
||||
if (byAxis % 2)
|
||||
if (fHigh) keyAxisDir = KEY_JOY_Down; else keyAxisDir = KEY_JOY_Up;
|
||||
else if (fHigh) keyAxisDir = KEY_JOY_Right; else keyAxisDir = KEY_JOY_Left;
|
||||
FallbackKeys[iKeyRangeCnt++] = KEY_Gamepad(byGamepad, (uint8_t)keyAxisDir);
|
||||
// TODO: do we need "any axis" events?
|
||||
}
|
||||
if (InKey.Key != KEY_Any) FallbackKeys[iKeyRangeCnt++] = KEY_Any;
|
||||
// now get key ranges for fallback chain
|
||||
|
|
|
@ -46,14 +46,15 @@ enum C4KeyEventType
|
|||
KEYEV_None = 0, // no event
|
||||
KEYEV_Down = 1, // in response to WM_KEYDOWN or joypad button pressed
|
||||
KEYEV_Up = 2, // in response to WM_KEYUP or joypad button released
|
||||
KEYEV_Pressed = 3 // in response to WM_KEYPRESSED
|
||||
KEYEV_Pressed = 3, // in response to WM_KEYPRESSED
|
||||
KEYEV_Moved = 4, // when moving a gamepad stick
|
||||
};
|
||||
|
||||
// keyboard code
|
||||
typedef unsigned long C4KeyCode;
|
||||
|
||||
// Gamepad codes (KEY_JOY_*): Masked as 0x420000; bit 8-15 used for gamepad index
|
||||
const C4KeyCode KEY_JOY_Mask = 0x420000;
|
||||
// Gamepad codes (KEY_CONTROLLER_*): Masked as 0x420000; bit 8-15 used for gamepad index
|
||||
const C4KeyCode KEY_CONTROLLER_Mask = 0x420000;
|
||||
|
||||
// Mouse codes (KEY_MOUSE_*): Masked as 0x430000; bit 8-15 used for mouse index
|
||||
const C4KeyCode KEY_MOUSE_Mask = 0x430000;
|
||||
|
@ -62,20 +63,36 @@ const C4KeyCode
|
|||
KEY_Default = 0, // no key
|
||||
KEY_Any = ~0, // used for default key processing
|
||||
KEY_Undefined = (~0)^1, // used to indicate an unknown key
|
||||
KEY_JOY_Left = 1, // joypad axis control: Any x axis min
|
||||
KEY_JOY_Up = 2, // joypad axis control: Any y axis min
|
||||
KEY_JOY_Right = 3, // joypad axis control: Any x axis max
|
||||
KEY_JOY_Down = 4, // joypad axis control: Any y axis max
|
||||
KEY_JOY_Button1 = 0x10, // key index of joypad buttons + button index for more buttons
|
||||
KEY_JOY_ButtonMax = KEY_JOY_Button1+0x1f, // maximum number of supported buttons on a gamepad
|
||||
KEY_JOY_Axis1Min = 0x30,
|
||||
KEY_JOY_Axis1Max = 0x31,
|
||||
KEY_JOY_AxisMax = KEY_JOY_Axis1Min + 0x20,
|
||||
KEY_JOY_AnyButton = 0xff, // any joypad button (not axis)
|
||||
KEY_JOY_AnyOddButton = 0xfe, // joypad buttons 1, 3, 5, etc.
|
||||
KEY_JOY_AnyEvenButton = 0xfd, // joypad buttons 2, 4, 6, etc.
|
||||
KEY_JOY_AnyLowButton = 0xfc, // joypad buttons 1 - 4
|
||||
KEY_JOY_AnyHighButton = 0xfb, // joypad buttons > 4
|
||||
KEY_CONTROLLER_ButtonMin = 0x10, // first button
|
||||
KEY_CONTROLLER_ButtonA = 0x10,
|
||||
KEY_CONTROLLER_ButtonB = 0x11,
|
||||
KEY_CONTROLLER_ButtonX = 0x12,
|
||||
KEY_CONTROLLER_ButtonY = 0x13,
|
||||
KEY_CONTROLLER_ButtonBack = 0x14,
|
||||
KEY_CONTROLLER_ButtonGuide = 0x15,
|
||||
KEY_CONTROLLER_ButtonStart = 0x16,
|
||||
KEY_CONTROLLER_ButtonLeftStick = 0x17,
|
||||
KEY_CONTROLLER_ButtonRightStick = 0x18,
|
||||
KEY_CONTROLLER_ButtonLeftShoulder = 0x19,
|
||||
KEY_CONTROLLER_ButtonRightShoulder = 0x1a,
|
||||
KEY_CONTROLLER_ButtonDpadUp = 0x1b,
|
||||
KEY_CONTROLLER_ButtonDpadDown = 0x1c,
|
||||
KEY_CONTROLLER_ButtonDpadLeft = 0x1d,
|
||||
KEY_CONTROLLER_ButtonDpadRight = 0x1e,
|
||||
KEY_CONTROLLER_ButtonMax = 0x1e, // last button
|
||||
KEY_CONTROLLER_AnyButton = 0xff, // any of the buttons above
|
||||
KEY_CONTROLLER_AxisMin = 0x30, // first axis
|
||||
KEY_CONTROLLER_AxisLeftXLeft = 0x30,
|
||||
KEY_CONTROLLER_AxisLeftXRight = 0x31,
|
||||
KEY_CONTROLLER_AxisLeftYUp = 0x32,
|
||||
KEY_CONTROLLER_AxisLeftYDown = 0x33,
|
||||
KEY_CONTROLLER_AxisRightXLeft = 0x34,
|
||||
KEY_CONTROLLER_AxisRightXRight = 0x35,
|
||||
KEY_CONTROLLER_AxisRightYUp = 0x36,
|
||||
KEY_CONTROLLER_AxisRightYDown = 0x37,
|
||||
KEY_CONTROLLER_AxisTriggerLeft = 0x39, // triggers are only positive
|
||||
KEY_CONTROLLER_AxisTriggerRight = 0x3b,
|
||||
KEY_CONTROLLER_AxisMax = 0x3b, // last axis
|
||||
KEY_MOUSE_Move = 1, // mouse control: mouse movement
|
||||
KEY_MOUSE_Button1 = 0x10, // key index of mouse buttons + button index for more buttons
|
||||
KEY_MOUSE_ButtonLeft = KEY_MOUSE_Button1 + 0,
|
||||
|
@ -90,18 +107,18 @@ const C4KeyCode
|
|||
KEY_MOUSE_Wheel1Up = 0x40, // mouse control: wheel up
|
||||
KEY_MOUSE_Wheel1Down = 0x41; // mouse control: wheel down
|
||||
|
||||
inline uint8_t KEY_JOY_Button(uint8_t idx) { return KEY_JOY_Button1+idx; }
|
||||
inline uint8_t KEY_JOY_Axis(uint8_t idx, bool fMax) { return KEY_JOY_Axis1Min+2*idx+fMax; }
|
||||
inline uint8_t KEY_CONTROLLER_Button(uint8_t idx) { return KEY_CONTROLLER_ButtonMin+idx; }
|
||||
inline uint8_t KEY_CONTROLLER_Axis(uint8_t idx, bool fMax) { return KEY_CONTROLLER_AxisMin+2*idx+fMax; }
|
||||
|
||||
inline C4KeyCode KEY_Gamepad(uint8_t idGamepad, uint8_t idButton) // convert gamepad key to Clonk-gamepad-keycode
|
||||
inline C4KeyCode KEY_Gamepad(uint8_t idButton) // convert gamepad key to Clonk-gamepad-keycode
|
||||
{
|
||||
// mask key as 0x0042ggbb, where gg is gamepad ID and bb is button ID.
|
||||
return KEY_JOY_Mask + (idGamepad<<8) + idButton;
|
||||
// mask key as 0x004200bb, where 00 used to be the gamepad ID and bb is button ID.
|
||||
return KEY_CONTROLLER_Mask + idButton;
|
||||
}
|
||||
|
||||
inline bool Key_IsGamepad(C4KeyCode key)
|
||||
{
|
||||
return (0xff0000 & key) == KEY_JOY_Mask;
|
||||
return (0xff0000 & key) == KEY_CONTROLLER_Mask;
|
||||
}
|
||||
|
||||
inline uint8_t Key_GetGamepad(C4KeyCode key)
|
||||
|
@ -117,25 +134,25 @@ inline uint8_t Key_GetGamepadEvent(C4KeyCode key)
|
|||
inline bool Key_IsGamepadButton(C4KeyCode key)
|
||||
{
|
||||
// whether this is a unique button event (AnyButton not included)
|
||||
return Key_IsGamepad(key) && Inside<uint8_t>(Key_GetGamepadEvent(key), KEY_JOY_Button1, KEY_JOY_ButtonMax);
|
||||
return Key_IsGamepad(key) && Inside<uint8_t>(Key_GetGamepadEvent(key), KEY_CONTROLLER_ButtonMin, KEY_CONTROLLER_ButtonMax);
|
||||
}
|
||||
|
||||
inline bool Key_IsGamepadAxis(C4KeyCode key)
|
||||
{
|
||||
// whether this is a unique button event (AnyButton not included)
|
||||
return Key_IsGamepad(key) && Inside<uint8_t>(Key_GetGamepadEvent(key), KEY_JOY_Axis1Min, KEY_JOY_AxisMax);
|
||||
return Key_IsGamepad(key) && Inside<uint8_t>(Key_GetGamepadEvent(key), KEY_CONTROLLER_AxisMin, KEY_CONTROLLER_AxisMax);
|
||||
}
|
||||
|
||||
inline uint8_t Key_GetGamepadButtonIndex(C4KeyCode key)
|
||||
{
|
||||
// get zero-based button index
|
||||
return Key_GetGamepadEvent(key) - KEY_JOY_Button1;
|
||||
return Key_GetGamepadEvent(key) - KEY_CONTROLLER_ButtonMin;
|
||||
}
|
||||
|
||||
inline uint8_t Key_GetGamepadAxisIndex(C4KeyCode key)
|
||||
{
|
||||
// get zero-based axis index
|
||||
return (Key_GetGamepadEvent(key) - KEY_JOY_Axis1Min) / 2;
|
||||
return (Key_GetGamepadEvent(key) - KEY_CONTROLLER_AxisMin) / 2;
|
||||
}
|
||||
|
||||
inline bool Key_IsGamepadAxisHigh(C4KeyCode key)
|
||||
|
@ -188,6 +205,8 @@ struct C4KeyCodeEx
|
|||
C4KeyCode Key; // the key
|
||||
DWORD dwShift; // the status of Alt, Shift, Control
|
||||
|
||||
int32_t deviceId;
|
||||
|
||||
// if set, the keycode was generated by a key that has been held down
|
||||
// this flag is ignored in comparison operations
|
||||
bool fRepeated;
|
||||
|
@ -212,8 +231,7 @@ struct C4KeyCodeEx
|
|||
|
||||
void CompileFunc(StdCompiler *pComp, StdStrBuf *pOutBuf=NULL);
|
||||
|
||||
C4KeyCodeEx(C4KeyCode Key = KEY_Default, C4KeyShiftState Shift = KEYS_None, bool fIsRepeated = false)
|
||||
: Key(Key), dwShift(Shift), fRepeated(fIsRepeated) {}
|
||||
C4KeyCodeEx(C4KeyCode Key = KEY_Default, C4KeyShiftState Shift = KEYS_None, bool fIsRepeated = false, int32_t deviceId = -1);
|
||||
|
||||
bool IsRepeated() const { return fRepeated; }
|
||||
|
||||
|
@ -234,6 +252,21 @@ struct C4KeyEventData
|
|||
bool operator ==(const struct C4KeyEventData &cmp) const;
|
||||
};
|
||||
|
||||
// Helper functions for high-level GUI control mappings.
|
||||
namespace ControllerKeys {
|
||||
template<class T> void Any(T &keys) { keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_AnyButton))); }
|
||||
template<class T> void Cancel(T &keys) { keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_ButtonB))); }
|
||||
template<class T> void Ok(T &keys) { keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_ButtonA))); }
|
||||
template<class T> void Left(T &keys) { keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_AxisLeftXLeft)));
|
||||
keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_ButtonDpadLeft))); }
|
||||
template<class T> void Right(T &keys) { keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_AxisLeftXRight)));
|
||||
keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_ButtonDpadRight))); }
|
||||
template<class T> void Up(T &keys) { keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_AxisLeftYUp)));
|
||||
keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_ButtonDpadUp))); }
|
||||
template<class T> void Down(T &keys) { keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_AxisLeftYDown)));
|
||||
keys.push_back(C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_ButtonDpadDown))); }
|
||||
}
|
||||
|
||||
// callback interface
|
||||
class C4KeyboardCallbackInterface
|
||||
{
|
||||
|
@ -265,7 +298,7 @@ public:
|
|||
|
||||
protected:
|
||||
TargetClass &rTarget;
|
||||
CallbackFunc pFuncDown, pFuncUp, pFuncPressed;
|
||||
CallbackFunc pFuncDown, pFuncUp, pFuncPressed, pFuncMoved;
|
||||
|
||||
protected:
|
||||
virtual bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv)
|
||||
|
@ -276,6 +309,7 @@ protected:
|
|||
case KEYEV_Down: return pFuncDown ? (rTarget.*pFuncDown)() : false;
|
||||
case KEYEV_Up: return pFuncUp ? (rTarget.*pFuncUp)() : false;
|
||||
case KEYEV_Pressed: return pFuncPressed ? (rTarget.*pFuncPressed)() : false;
|
||||
case KEYEV_Moved: return pFuncMoved ? (rTarget.*pFuncMoved)() : false;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
@ -283,8 +317,8 @@ protected:
|
|||
virtual bool CheckCondition() { return true; }
|
||||
|
||||
public:
|
||||
C4KeyCB(TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=NULL, CallbackFunc pFuncPressed=NULL)
|
||||
: rTarget(rTarget), pFuncDown(pFuncDown), pFuncUp(pFuncUp), pFuncPressed(pFuncPressed) {}
|
||||
C4KeyCB(TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=NULL, CallbackFunc pFuncPressed=NULL, CallbackFunc pFuncMoved=NULL)
|
||||
: rTarget(rTarget), pFuncDown(pFuncDown), pFuncUp(pFuncUp), pFuncPressed(pFuncPressed), pFuncMoved(pFuncMoved) {}
|
||||
};
|
||||
|
||||
// callback interface that passes the pressed key as a parameter
|
||||
|
@ -295,7 +329,7 @@ public:
|
|||
|
||||
protected:
|
||||
TargetClass &rTarget;
|
||||
CallbackFunc pFuncDown, pFuncUp, pFuncPressed;
|
||||
CallbackFunc pFuncDown, pFuncUp, pFuncPressed, pFuncMoved;
|
||||
|
||||
protected:
|
||||
virtual bool OnKeyEvent(const C4KeyCodeEx &key, C4KeyEventType eEv)
|
||||
|
@ -306,6 +340,7 @@ protected:
|
|||
case KEYEV_Down: return pFuncDown ? (rTarget.*pFuncDown)(key) : false;
|
||||
case KEYEV_Up: return pFuncUp ? (rTarget.*pFuncUp)(key) : false;
|
||||
case KEYEV_Pressed: return pFuncPressed ? (rTarget.*pFuncPressed)(key) : false;
|
||||
case KEYEV_Moved: return pFuncMoved ? (rTarget.*pFuncMoved)(key) : false;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
@ -313,8 +348,8 @@ protected:
|
|||
virtual bool CheckCondition() { return true; }
|
||||
|
||||
public:
|
||||
C4KeyCBPassKey(TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=NULL, CallbackFunc pFuncPressed=NULL)
|
||||
: rTarget(rTarget), pFuncDown(pFuncDown), pFuncUp(pFuncUp), pFuncPressed(pFuncPressed) {}
|
||||
C4KeyCBPassKey(TargetClass &rTarget, CallbackFunc pFuncDown, CallbackFunc pFuncUp=NULL, CallbackFunc pFuncPressed=NULL, CallbackFunc pFuncMoved=NULL)
|
||||
: rTarget(rTarget), pFuncDown(pFuncDown), pFuncUp(pFuncUp), pFuncPressed(pFuncPressed), pFuncMoved(pFuncMoved) {}
|
||||
};
|
||||
|
||||
// parameterized callback interface
|
||||
|
@ -325,7 +360,7 @@ public:
|
|||
|
||||
protected:
|
||||
TargetClass &rTarget;
|
||||
CallbackFunc pFuncDown, pFuncUp, pFuncPressed;
|
||||
CallbackFunc pFuncDown, pFuncUp, pFuncPressed, pFuncMoved;
|
||||
ParameterType par;
|
||||
|
||||
protected:
|
||||
|
@ -337,6 +372,7 @@ protected:
|
|||
case KEYEV_Down: return pFuncDown ? (rTarget.*pFuncDown)(par) : false;
|
||||
case KEYEV_Up: return pFuncUp ? (rTarget.*pFuncUp)(par) : false;
|
||||
case KEYEV_Pressed: return pFuncPressed ? (rTarget.*pFuncPressed)(par) : false;
|
||||
case KEYEV_Moved: return pFuncMoved ? (rTarget.*pFuncMoved)(par) : false;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
@ -344,8 +380,8 @@ protected:
|
|||
virtual bool CheckCondition() { return true; }
|
||||
|
||||
public:
|
||||
C4KeyCBEx(TargetClass &rTarget, const ParameterType &par, CallbackFunc pFuncDown, CallbackFunc pFuncUp=NULL, CallbackFunc pFuncPressed=NULL)
|
||||
: rTarget(rTarget), pFuncDown(pFuncDown), pFuncUp(pFuncUp), pFuncPressed(pFuncPressed), par(par) {}
|
||||
C4KeyCBEx(TargetClass &rTarget, const ParameterType &par, CallbackFunc pFuncDown, CallbackFunc pFuncUp=NULL, CallbackFunc pFuncPressed=NULL, CallbackFunc pFuncMoved=NULL)
|
||||
: rTarget(rTarget), pFuncDown(pFuncDown), pFuncUp(pFuncUp), pFuncPressed(pFuncPressed), pFuncMoved(pFuncMoved), par(par) {}
|
||||
};
|
||||
|
||||
template <class TargetClass, class ParameterType> class C4KeyCBExPassKey : public C4KeyboardCallbackInterface
|
||||
|
@ -355,7 +391,7 @@ public:
|
|||
|
||||
protected:
|
||||
TargetClass &rTarget;
|
||||
CallbackFunc pFuncDown, pFuncUp, pFuncPressed;
|
||||
CallbackFunc pFuncDown, pFuncUp, pFuncPressed, pFuncMoved;
|
||||
ParameterType par;
|
||||
|
||||
protected:
|
||||
|
@ -367,6 +403,7 @@ protected:
|
|||
case KEYEV_Down: return pFuncDown ? (rTarget.*pFuncDown)(key, par) : false;
|
||||
case KEYEV_Up: return pFuncUp ? (rTarget.*pFuncUp)(key, par) : false;
|
||||
case KEYEV_Pressed: return pFuncPressed ? (rTarget.*pFuncPressed)(key, par) : false;
|
||||
case KEYEV_Moved: return pFuncMoved ? (rTarget.*pFuncMoved)(key, par) : false;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
@ -374,8 +411,8 @@ protected:
|
|||
virtual bool CheckCondition() { return true; }
|
||||
|
||||
public:
|
||||
C4KeyCBExPassKey(TargetClass &rTarget, const ParameterType &par, CallbackFunc pFuncDown, CallbackFunc pFuncUp=NULL, CallbackFunc pFuncPressed=NULL)
|
||||
: rTarget(rTarget), pFuncDown(pFuncDown), pFuncUp(pFuncUp), pFuncPressed(pFuncPressed), par(par) {}
|
||||
C4KeyCBExPassKey(TargetClass &rTarget, const ParameterType &par, CallbackFunc pFuncDown, CallbackFunc pFuncUp=NULL, CallbackFunc pFuncPressed=NULL, CallbackFunc pFuncMoved=NULL)
|
||||
: rTarget(rTarget), pFuncDown(pFuncDown), pFuncUp(pFuncUp), pFuncPressed(pFuncPressed), pFuncMoved(pFuncMoved), par(par) {}
|
||||
};
|
||||
|
||||
// one mapped keyboard entry
|
||||
|
@ -424,7 +461,7 @@ public:
|
|||
const StdStrBuf &GetName() const { return Name; }
|
||||
C4KeyScope GetScope() const { return Scope; }
|
||||
unsigned int GetPriority() const { return uiPriority; }
|
||||
bool IsCodeMatched(const C4KeyCodeEx &key) const { const CodeList &codes = GetCodes(); return (std::find(codes.begin(),codes.end(),key) != codes.end()); }
|
||||
bool IsCodeMatched(const C4KeyCodeEx &key) const;
|
||||
|
||||
void Update(const C4CustomKey *pByKey); // merge given key into this
|
||||
bool Execute(C4KeyEventType eEv, C4KeyCodeEx key);
|
||||
|
|
|
@ -76,14 +76,14 @@ C4StartupMainDlg::C4StartupMainDlg() : C4StartupDlg(NULL) // create w/o title; i
|
|||
keys.push_back(C4KeyCodeEx(K_DOWN)); keys.push_back(C4KeyCodeEx(K_RIGHT));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Down))); // right will be done by Dialog already
|
||||
ControllerKeys::Down(keys); // right will be done by Dialog already
|
||||
}
|
||||
pKeyDown = new C4KeyBinding(keys, "StartupMainCtrlNext", KEYSCOPE_Gui,
|
||||
new C4GUI::DlgKeyCBEx<C4StartupMainDlg, bool>(*this, false, &C4StartupMainDlg::KeyAdvanceFocus), C4CustomKey::PRIO_CtrlOverride);
|
||||
keys.clear(); keys.push_back(C4KeyCodeEx(K_UP)); keys.push_back(C4KeyCodeEx(K_LEFT));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_Up))); // left will be done by Dialog already
|
||||
ControllerKeys::Up(keys); // left will be done by Dialog already
|
||||
}
|
||||
pKeyUp = new C4KeyBinding(keys, "StartupMainCtrlPrev", KEYSCOPE_Gui,
|
||||
new C4GUI::DlgKeyCBEx<C4StartupMainDlg, bool>(*this, true, &C4StartupMainDlg::KeyAdvanceFocus), C4CustomKey::PRIO_CtrlOverride);
|
||||
|
|
|
@ -212,7 +212,7 @@ bool C4StartupOptionsDlg::KeySelDialog::KeyDown(const C4KeyCodeEx &key)
|
|||
// --- C4StartupOptionsDlg::ControlConfigListBox::ControlAssignmentLabel
|
||||
|
||||
C4StartupOptionsDlg::ControlConfigListBox::ControlAssignmentLabel::ControlAssignmentLabel(class C4PlayerControlAssignment *assignment, class C4PlayerControlAssignmentSet *assignment_set, const C4Rect &bounds)
|
||||
: C4GUI::Label("", bounds, ALeft, 0xffffffff, NULL, false, false, false), assignment(assignment), assignment_set(assignment_set)
|
||||
: C4GUI::Label("", bounds, ALeft, 0xffffffff, NULL, false, false, true), assignment(assignment), assignment_set(assignment_set)
|
||||
{
|
||||
UpdateAssignmentString();
|
||||
}
|
||||
|
@ -391,7 +391,7 @@ void C4StartupOptionsDlg::ControlConfigListBox::SetUserKey(class C4PlayerControl
|
|||
// --- C4StartupOptionsDlg::ControlConfigArea
|
||||
|
||||
C4StartupOptionsDlg::ControlConfigArea::ControlConfigArea(const C4Rect &rcArea, int32_t iHMargin, int32_t iVMargin, bool fGamepad, C4StartupOptionsDlg *pOptionsDlg)
|
||||
: C4GUI::Window(), fGamepad(fGamepad), pGamepadOpener(NULL), pOptionsDlg(pOptionsDlg), pGUICtrl(NULL)
|
||||
: C4GUI::Window(), fGamepad(fGamepad), pOptionsDlg(pOptionsDlg), pGUICtrl(NULL)
|
||||
{
|
||||
CStdFont *pUseFontSmall = &(C4Startup::Get()->Graphics.BookSmallFont);
|
||||
SetBounds(rcArea);
|
||||
|
@ -436,7 +436,6 @@ C4StartupOptionsDlg::ControlConfigArea::ControlConfigArea(const C4Rect &rcArea,
|
|||
C4StartupOptionsDlg::ControlConfigArea::~ControlConfigArea()
|
||||
{
|
||||
delete [] ppKeyControlSetBtns;
|
||||
if (pGamepadOpener) delete pGamepadOpener;
|
||||
}
|
||||
|
||||
void C4StartupOptionsDlg::ControlConfigArea::OnCtrlSetBtn(C4GUI::Control *btn)
|
||||
|
|
|
@ -227,7 +227,6 @@ private:
|
|||
int32_t iSelectedCtrlSet; // keyboard or gamepad set that is currently being configured
|
||||
class C4GUI::IconButton ** ppKeyControlSetBtns; // buttons to select configured control set - array in length of iMaxControlSets
|
||||
class KeySelButton * KeyControlBtns[C4MaxKey]; // buttons to configure individual kbd set buttons
|
||||
C4GamePadOpener *pGamepadOpener; // opened gamepad for configuration
|
||||
C4StartupOptionsDlg *pOptionsDlg;
|
||||
ControlConfigListBox *control_list;
|
||||
class C4GUI::CheckBox *pGUICtrl;
|
||||
|
|
|
@ -527,7 +527,7 @@ C4StartupPlrSelDlg::C4StartupPlrSelDlg() : C4StartupDlg("W"), eMode(PSDM_Player)
|
|||
keys.push_back(C4KeyCodeEx(K_ESCAPE));
|
||||
if (Config.Controls.GamepadGuiControl)
|
||||
{
|
||||
keys.push_back(C4KeyCodeEx(KEY_Gamepad(0, KEY_JOY_AnyHighButton)));
|
||||
ControllerKeys::Cancel(keys);
|
||||
}
|
||||
pKeyBack = new C4KeyBinding(keys, "StartupPlrSelBack", KEYSCOPE_Gui,
|
||||
new C4GUI::DlgKeyCB<C4StartupPlrSelDlg>(*this, &C4StartupPlrSelDlg::KeyBack), C4CustomKey::PRIO_CtrlOverride);
|
||||
|
|
|
@ -162,12 +162,15 @@ void C4AbstractApp::HandleSDLEvent(SDL_Event& e)
|
|||
SDL_GetMouseState(&x, &y);
|
||||
C4GUI::MouseMove(C4MC_Button_Wheel, x, y, flags, NULL);
|
||||
break;
|
||||
case SDL_JOYAXISMOTION:
|
||||
case SDL_JOYHATMOTION:
|
||||
case SDL_JOYBALLMOTION:
|
||||
case SDL_JOYBUTTONDOWN:
|
||||
case SDL_JOYBUTTONUP:
|
||||
Application.pGamePadControl->FeedEvent(e);
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
Application.pGamePadControl->FeedEvent(e, C4GamePadControl::FEED_BUTTONS);
|
||||
break;
|
||||
case SDL_JOYDEVICEADDED:
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
Application.pGamePadControl->CheckGamePad(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,178 +25,26 @@
|
|||
#include <C4Log.h>
|
||||
#include <C4Game.h>
|
||||
|
||||
// regardless of WIN32 or SDL
|
||||
void C4GamePadControl::DoAxisInput()
|
||||
{
|
||||
// Send axis strength changes
|
||||
Execute(true);
|
||||
}
|
||||
|
||||
#ifdef USE_WIN32_WINDOWS
|
||||
|
||||
C4GamePadControl *C4GamePadControl::pInstance = NULL;
|
||||
|
||||
C4GamePadControl::C4GamePadControl()
|
||||
{
|
||||
for (int i=0; i<CStdGamepad_MaxGamePad; ++i)
|
||||
{
|
||||
Gamepads[i].pGamepad = NULL;
|
||||
Gamepads[i].iRefCount = 0;
|
||||
}
|
||||
iNumGamepads = 0;
|
||||
// singleton
|
||||
if (!pInstance) pInstance = this;
|
||||
}
|
||||
|
||||
C4GamePadControl::~C4GamePadControl()
|
||||
{
|
||||
if (pInstance == this) pInstance = NULL;
|
||||
Clear();
|
||||
}
|
||||
|
||||
void C4GamePadControl::Clear()
|
||||
{
|
||||
for (int i=0; i<CStdGamepad_MaxGamePad; ++i)
|
||||
while (Gamepads[i].iRefCount) CloseGamepad(i);
|
||||
}
|
||||
|
||||
void C4GamePadControl::OpenGamepad(int id)
|
||||
{
|
||||
if (!Inside(id, 0, CStdGamepad_MaxGamePad-1)) return;
|
||||
// add gamepad ref
|
||||
if (!(Gamepads[id].iRefCount++))
|
||||
{
|
||||
// this is the first gamepad opening: Init it
|
||||
Pad &rPad = Gamepads[id];
|
||||
rPad.pGamepad = new CStdGamePad(id);
|
||||
rPad.Buttons= 0;
|
||||
for (int i=0; i< CStdGamepad_MaxAxis; ++i)
|
||||
{
|
||||
rPad.AxisPosis[i] = CStdGamePad::Mid;
|
||||
rPad.AxisStrengths[i] = 0;
|
||||
}
|
||||
rPad.pGamepad->SetCalibration(&(Config.Gamepads[id].AxisMin[0]), &(Config.Gamepads[id].AxisMax[0]), &(Config.Gamepads[id].AxisCalibrated[0]));
|
||||
++iNumGamepads;
|
||||
}
|
||||
}
|
||||
|
||||
void C4GamePadControl::CloseGamepad(int id)
|
||||
{
|
||||
if (!Inside(id, 0, CStdGamepad_MaxGamePad-1)) return;
|
||||
// del gamepad ref
|
||||
if (!--Gamepads[id].iRefCount)
|
||||
{
|
||||
Gamepads[id].pGamepad->GetCalibration(&(Config.Gamepads[id].AxisMin[0]), &(Config.Gamepads[id].AxisMax[0]), &(Config.Gamepads[id].AxisCalibrated[0]));
|
||||
delete Gamepads[id].pGamepad; Gamepads[id].pGamepad = NULL;
|
||||
--iNumGamepads;
|
||||
}
|
||||
}
|
||||
|
||||
int C4GamePadControl::GetGamePadCount()
|
||||
{
|
||||
JOYINFOEX joy;
|
||||
ZeroMem(&joy, sizeof(JOYINFOEX)); joy.dwSize = sizeof(JOYINFOEX); joy.dwFlags = JOY_RETURNALL;
|
||||
int iCnt=0;
|
||||
while (iCnt<CStdGamepad_MaxGamePad && ::joyGetPosEx(iCnt, &joy) == JOYERR_NOERROR) ++iCnt;
|
||||
return iCnt;
|
||||
}
|
||||
|
||||
const int MaxGamePadButton=10;
|
||||
|
||||
void C4GamePadControl::Execute(bool send_axis_strength_changes)
|
||||
{
|
||||
// Get gamepad inputs
|
||||
int iNum = iNumGamepads;
|
||||
for (int idGamepad=0; iNum && idGamepad<CStdGamepad_MaxGamePad; ++idGamepad)
|
||||
{
|
||||
Pad &rPad = Gamepads[idGamepad];
|
||||
if (!rPad.iRefCount) continue;
|
||||
--iNum;
|
||||
if (!rPad.pGamepad->Update()) continue;
|
||||
for (int iAxis = 0; iAxis < CStdGamepad_MaxAxis; ++iAxis)
|
||||
{
|
||||
int32_t iStrength = 100;
|
||||
CStdGamePad::AxisPos eAxisPos = rPad.pGamepad->GetAxisPos(iAxis, &iStrength), ePrevAxisPos = rPad.AxisPosis[iAxis];
|
||||
int32_t iPrevStrength = rPad.AxisStrengths[iAxis];
|
||||
// Evaluate changes and pass single controls
|
||||
// this is a generic Gamepad-control: Create events
|
||||
if (eAxisPos != ePrevAxisPos || (send_axis_strength_changes && Abs(iPrevStrength-iStrength) > AxisStrengthChangeThreshold))
|
||||
{
|
||||
rPad.AxisPosis[iAxis] = eAxisPos;
|
||||
rPad.AxisStrengths[iAxis] = iStrength;
|
||||
if (ePrevAxisPos != CStdGamePad::Mid && eAxisPos != ePrevAxisPos)
|
||||
Game.DoKeyboardInput(KEY_Gamepad(idGamepad, KEY_JOY_Axis(iAxis, (ePrevAxisPos==CStdGamePad::High))), KEYEV_Up, false, false, false, false);
|
||||
// it's tempting to send fRepeated here for eAxisPos == ePrevAxisPos, but it would cause the key to be ignored for sync controls
|
||||
// might improve the check in sync controls so they accept repeated keys if strength is updated?
|
||||
if (eAxisPos != CStdGamePad::Mid)
|
||||
Game.DoKeyboardInput(KEY_Gamepad(idGamepad, KEY_JOY_Axis(iAxis, (eAxisPos==CStdGamePad::High))), KEYEV_Down, false, false, false, false, NULL, false, iStrength);
|
||||
}
|
||||
}
|
||||
uint32_t Buttons = rPad.pGamepad->GetButtons();
|
||||
uint32_t PrevButtons = rPad.Buttons;
|
||||
if (Buttons != PrevButtons)
|
||||
{
|
||||
rPad.Buttons = Buttons;
|
||||
for (int iButton = 0; iButton < MaxGamePadButton; ++iButton)
|
||||
if ((Buttons & (1 << iButton)) != (PrevButtons & (1 << iButton)))
|
||||
{
|
||||
bool fRelease = ((Buttons & (1 << iButton)) == 0);
|
||||
Game.DoKeyboardInput(KEY_Gamepad(idGamepad, KEY_JOY_Button(iButton)), fRelease ? KEYEV_Up : KEYEV_Down, false, false, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool C4GamePadControl::AnyButtonDown()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
C4GamePadOpener::C4GamePadOpener(int iGamepad)
|
||||
{
|
||||
assert(C4GamePadControl::pInstance);
|
||||
this->iGamePad = iGamepad;
|
||||
C4GamePadControl::pInstance->OpenGamepad(iGamePad);
|
||||
}
|
||||
|
||||
C4GamePadOpener::~C4GamePadOpener()
|
||||
{
|
||||
if (C4GamePadControl::pInstance)
|
||||
C4GamePadControl::pInstance->CloseGamepad(iGamePad);
|
||||
}
|
||||
|
||||
void C4GamePadOpener::SetGamePad(int iNewGamePad)
|
||||
{
|
||||
if (iNewGamePad == iGamePad) return;
|
||||
assert(C4GamePadControl::pInstance);
|
||||
C4GamePadControl::pInstance->CloseGamepad(iGamePad);
|
||||
C4GamePadControl::pInstance->OpenGamepad(iGamePad = iNewGamePad);
|
||||
}
|
||||
|
||||
#elif defined(HAVE_SDL) && !defined(USE_CONSOLE)
|
||||
#if defined(HAVE_SDL) && !defined(USE_CONSOLE)
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
bool C4GamePadControl::AnyButtonDown()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
C4GamePadControl::C4GamePadControl()
|
||||
{
|
||||
// FIXME: Port to SDL_INIT_GAMECONTROLLER
|
||||
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_EVENTS) != 0)
|
||||
LogF("SDL_InitSubSystem(SDL_INIT_JOYSTICK): %s", SDL_GetError());
|
||||
SDL_JoystickEventState(SDL_ENABLE);
|
||||
if (!SDL_NumJoysticks()) Log("No Gamepad found");
|
||||
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC | SDL_INIT_EVENTS) != 0)
|
||||
LogF("SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER): %s", SDL_GetError());
|
||||
SDL_GameControllerEventState(SDL_ENABLE);
|
||||
if (!GetGamePadCount()) Log("No Gamepad found");
|
||||
}
|
||||
|
||||
C4GamePadControl::~C4GamePadControl()
|
||||
{
|
||||
SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_EVENTS);
|
||||
// All gamepads have to be released before quitting SDL.
|
||||
Gamepads.clear();
|
||||
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS);
|
||||
}
|
||||
|
||||
void C4GamePadControl::Execute(bool)
|
||||
void C4GamePadControl::Execute()
|
||||
{
|
||||
#ifndef USE_SDL_MAINLOOP
|
||||
SDL_Event event;
|
||||
|
@ -204,12 +52,15 @@ void C4GamePadControl::Execute(bool)
|
|||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_JOYAXISMOTION:
|
||||
case SDL_JOYBALLMOTION:
|
||||
case SDL_JOYHATMOTION:
|
||||
case SDL_JOYBUTTONDOWN:
|
||||
case SDL_JOYBUTTONUP:
|
||||
FeedEvent(event);
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
FeedEvent(event, FEED_BUTTONS);
|
||||
break;
|
||||
case SDL_JOYDEVICEADDED:
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
CheckGamePad(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -218,143 +69,185 @@ void C4GamePadControl::Execute(bool)
|
|||
|
||||
namespace
|
||||
{
|
||||
const int deadZone = 13337;
|
||||
const int deadZone = 16000;
|
||||
|
||||
int amplify(int i)
|
||||
// Axis strength uses the full signed 16 bit integer range. As we're
|
||||
// splitting axes in left/right and up/down, it's preferable to have
|
||||
// symmetrical ranges [0, 2^15 - 1] in both directions.
|
||||
inline int32_t abs_strength(int32_t strength)
|
||||
{
|
||||
if (i < 0)
|
||||
return -(deadZone + 1);
|
||||
if (i > 0)
|
||||
return deadZone + 1;
|
||||
return 0;
|
||||
return strength >= 0 ? strength : -(strength + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void C4GamePadControl::FeedEvent(SDL_Event& event)
|
||||
void C4GamePadControl::FeedEvent(const SDL_Event& event, int feed)
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_JOYHATMOTION:
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
{
|
||||
SDL_Event fakeX;
|
||||
fakeX.jaxis.type = SDL_JOYAXISMOTION;
|
||||
fakeX.jaxis.which = event.jhat.which;
|
||||
fakeX.jaxis.axis = event.jhat.hat * 2 + 6; /* *magic*number* */
|
||||
fakeX.jaxis.value = 0;
|
||||
SDL_Event fakeY = fakeX;
|
||||
fakeY.jaxis.axis += 1;
|
||||
switch (event.jhat.value)
|
||||
{
|
||||
case SDL_HAT_LEFTUP: fakeX.jaxis.value = amplify(-1); fakeY.jaxis.value = amplify(-1); break;
|
||||
case SDL_HAT_LEFT: fakeX.jaxis.value = amplify(-1); break;
|
||||
case SDL_HAT_LEFTDOWN: fakeX.jaxis.value = amplify(-1); fakeY.jaxis.value = amplify(+1); break;
|
||||
case SDL_HAT_UP: fakeY.jaxis.value = amplify(-1); break;
|
||||
case SDL_HAT_DOWN: fakeY.jaxis.value = amplify(+1); break;
|
||||
case SDL_HAT_RIGHTUP: fakeX.jaxis.value = amplify(+1); fakeY.jaxis.value = amplify(-1); break;
|
||||
case SDL_HAT_RIGHT: fakeX.jaxis.value = amplify(+1); break;
|
||||
case SDL_HAT_RIGHTDOWN: fakeX.jaxis.value = amplify(+1); fakeY.jaxis.value = amplify(+1); break;
|
||||
}
|
||||
FeedEvent(fakeX);
|
||||
FeedEvent(fakeY);
|
||||
return;
|
||||
}
|
||||
case SDL_JOYBALLMOTION:
|
||||
{
|
||||
SDL_Event fake;
|
||||
fake.jaxis.type = SDL_JOYAXISMOTION;
|
||||
fake.jaxis.which = event.jball.which;
|
||||
fake.jaxis.axis = event.jball.ball * 2 + 12; /* *magic*number* */
|
||||
fake.jaxis.value = amplify(event.jball.xrel);
|
||||
FeedEvent(event);
|
||||
fake.jaxis.axis += 1;
|
||||
fake.jaxis.value = amplify(event.jball.yrel);
|
||||
FeedEvent(event);
|
||||
return;
|
||||
}
|
||||
case SDL_JOYAXISMOTION:
|
||||
{
|
||||
C4KeyCode minCode = KEY_Gamepad(event.jaxis.which, KEY_JOY_Axis(event.jaxis.axis, false));
|
||||
C4KeyCode maxCode = KEY_Gamepad(event.jaxis.which, KEY_JOY_Axis(event.jaxis.axis, true));
|
||||
C4KeyCode minCode = KEY_Gamepad(KEY_CONTROLLER_Axis(event.caxis.axis, false));
|
||||
C4KeyCode maxCode = KEY_Gamepad(KEY_CONTROLLER_Axis(event.caxis.axis, true));
|
||||
int32_t value = abs_strength(event.caxis.value);
|
||||
uint8_t which = event.caxis.which;
|
||||
C4KeyCode keyCode = event.caxis.value >= 0 ? maxCode : minCode;
|
||||
|
||||
// FIXME: This assumes that the axis really rests around (0, 0) if it is not used, which is not always true.
|
||||
if (event.jaxis.value < -deadZone)
|
||||
auto doInput = [&](C4KeyEventType event, int32_t strength)
|
||||
{
|
||||
if (PressedAxis.count(minCode) == 0)
|
||||
Game.DoKeyboardInput(
|
||||
C4KeyCodeEx(KEY_Gamepad(keyCode), KEYS_None, false, which),
|
||||
event, NULL, false, strength);
|
||||
};
|
||||
|
||||
if (feed & FEED_BUTTONS)
|
||||
{
|
||||
// Also emulate button presses.
|
||||
if (PressedAxis.count(keyCode) && value <= deadZone)
|
||||
{
|
||||
Game.DoKeyboardInput(
|
||||
KEY_Gamepad(event.jaxis.which, minCode),
|
||||
KEYEV_Down, false, false, false, false);
|
||||
PressedAxis.insert(minCode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PressedAxis.count(minCode) != 0)
|
||||
{
|
||||
Game.DoKeyboardInput(
|
||||
KEY_Gamepad(event.jaxis.which, minCode),
|
||||
KEYEV_Up, false, false, false, false);
|
||||
PressedAxis.erase(minCode);
|
||||
}
|
||||
}
|
||||
if (event.jaxis.value > +deadZone)
|
||||
{
|
||||
if (PressedAxis.count(maxCode) == 0)
|
||||
{
|
||||
Game.DoKeyboardInput(
|
||||
KEY_Gamepad(event.jaxis.which, maxCode),
|
||||
KEYEV_Down, false, false, false, false);
|
||||
PressedAxis.insert(maxCode);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PressedAxis.count(maxCode) != 0)
|
||||
{
|
||||
Game.DoKeyboardInput(
|
||||
KEY_Gamepad(event.jaxis.which, maxCode),
|
||||
KEYEV_Up, false, false, false, false);
|
||||
PressedAxis.erase(maxCode);
|
||||
PressedAxis.erase(keyCode);
|
||||
doInput(KEYEV_Up, -1);
|
||||
}
|
||||
else if (!PressedAxis.count(keyCode) && value > deadZone)
|
||||
{
|
||||
PressedAxis.insert(keyCode);
|
||||
doInput(KEYEV_Down, -1);
|
||||
}
|
||||
}
|
||||
if (feed & FEED_MOVED)
|
||||
doInput(KEYEV_Moved, value);
|
||||
|
||||
AxisEvents[keyCode] = event;
|
||||
|
||||
break;
|
||||
}
|
||||
case SDL_JOYBUTTONDOWN:
|
||||
Game.DoKeyboardInput(
|
||||
KEY_Gamepad(event.jbutton.which, KEY_JOY_Button(event.jbutton.button)),
|
||||
KEYEV_Down, false, false, false, false);
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
if (feed & FEED_BUTTONS)
|
||||
Game.DoKeyboardInput(
|
||||
C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_Button(event.cbutton.button)), KEYS_None, false, event.cbutton.which),
|
||||
KEYEV_Down);
|
||||
break;
|
||||
case SDL_JOYBUTTONUP:
|
||||
Game.DoKeyboardInput(
|
||||
KEY_Gamepad(event.jbutton.which, KEY_JOY_Button(event.jbutton.button)),
|
||||
KEYEV_Up, false, false, false, false);
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
if (feed & FEED_BUTTONS)
|
||||
Game.DoKeyboardInput(
|
||||
C4KeyCodeEx(KEY_Gamepad(KEY_CONTROLLER_Button(event.cbutton.button)), KEYS_None, false, event.cbutton.which),
|
||||
KEYEV_Up);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void C4GamePadControl::CheckGamePad(const SDL_Event& e)
|
||||
{
|
||||
switch (e.type)
|
||||
{
|
||||
case SDL_JOYDEVICEADDED:
|
||||
// Report that an unsupported joystick device has been detected, to help with controller issues.
|
||||
if (!SDL_IsGameController(e.jdevice.which))
|
||||
LogF("Gamepad %s isn't supported.", SDL_JoystickNameForIndex(e.jdevice.which));
|
||||
break;
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
{
|
||||
auto device = std::make_shared<C4GamePadOpener>(e.cdevice.which);
|
||||
Gamepads[device->GetID()] = device;
|
||||
LogF("Gamepad #%d connected: %s", device->GetID(), SDL_JoystickNameForIndex(e.cdevice.which));
|
||||
break;
|
||||
}
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
LogF("Gamepad #%d disconnected.", e.cdevice.which);
|
||||
Gamepads.erase(e.cdevice.which);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void C4GamePadControl::DoAxisInput()
|
||||
{
|
||||
for (auto const &e : AxisEvents)
|
||||
{
|
||||
FeedEvent(e.second, FEED_MOVED);
|
||||
}
|
||||
AxisEvents.clear();
|
||||
}
|
||||
|
||||
int C4GamePadControl::GetGamePadCount()
|
||||
{
|
||||
return(SDL_NumJoysticks());
|
||||
// Not all Joysticks are game controllers.
|
||||
int count = 0;
|
||||
for (int i = 0; i < SDL_NumJoysticks(); i++)
|
||||
if (SDL_IsGameController(i))
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
std::shared_ptr<C4GamePadOpener> C4GamePadControl::GetGamePad(int gamepad)
|
||||
{
|
||||
if (gamepad >= 0)
|
||||
for (const auto& p : Gamepads)
|
||||
if (gamepad-- == 0)
|
||||
return p.second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<C4GamePadOpener> C4GamePadControl::GetGamePadByID(int32_t id)
|
||||
{
|
||||
auto it = Gamepads.find(id);
|
||||
if (it != Gamepads.end())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<C4GamePadOpener> C4GamePadControl::GetAvailableGamePad()
|
||||
{
|
||||
for (const auto& p : Gamepads)
|
||||
if (p.second->GetPlayer() < 0)
|
||||
return p.second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
C4GamePadOpener::C4GamePadOpener(int iGamepad)
|
||||
{
|
||||
Joy = SDL_JoystickOpen(iGamepad);
|
||||
if (!Joy) LogF("SDL: %s", SDL_GetError());
|
||||
int n = iGamepad;
|
||||
for (int i = 0; i < SDL_NumJoysticks(); i++)
|
||||
if (SDL_IsGameController(i) && n-- == 0)
|
||||
{
|
||||
controller = SDL_GameControllerOpen(i);
|
||||
if (!controller) LogF("SDL: %s", SDL_GetError());
|
||||
SDL_Joystick *joystick = SDL_GameControllerGetJoystick(controller);
|
||||
haptic = SDL_HapticOpenFromJoystick(joystick);
|
||||
if (haptic && SDL_HapticRumbleSupported(haptic))
|
||||
SDL_HapticRumbleInit(haptic);
|
||||
else
|
||||
LogF("Gamepad #%d %s does not support rumbling.", SDL_JoystickInstanceID(joystick), SDL_JoystickName(joystick));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!controller) LogF("Gamepad %d not available", iGamepad);
|
||||
}
|
||||
|
||||
C4GamePadOpener::~C4GamePadOpener()
|
||||
{
|
||||
if (Joy) SDL_JoystickClose(Joy);
|
||||
if (haptic) SDL_HapticClose(haptic);
|
||||
if (controller) SDL_GameControllerClose(controller);
|
||||
}
|
||||
|
||||
void C4GamePadOpener::SetGamePad(int iGamepad)
|
||||
int32_t C4GamePadOpener::GetID()
|
||||
{
|
||||
if (Joy)
|
||||
SDL_JoystickClose(Joy);
|
||||
Joy = SDL_JoystickOpen(iGamepad);
|
||||
if (!Joy)
|
||||
LogF("SDL: %s", SDL_GetError());
|
||||
return SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller));
|
||||
}
|
||||
|
||||
bool C4GamePadOpener::IsAttached()
|
||||
{
|
||||
return !!SDL_GameControllerGetAttached(controller);
|
||||
}
|
||||
|
||||
void C4GamePadOpener::PlayRumble(float strength, uint32_t length)
|
||||
{
|
||||
if (SDL_HapticRumbleSupported(haptic))
|
||||
SDL_HapticRumblePlay(haptic, strength, length);
|
||||
}
|
||||
|
||||
void C4GamePadOpener::StopRumble()
|
||||
{
|
||||
if (SDL_HapticRumbleSupported(haptic))
|
||||
SDL_HapticRumbleStop(haptic);
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -363,12 +256,18 @@ void C4GamePadOpener::SetGamePad(int iGamepad)
|
|||
|
||||
C4GamePadControl::C4GamePadControl() { Log("WARNING: Engine without Gamepad support"); }
|
||||
C4GamePadControl::~C4GamePadControl() { }
|
||||
void C4GamePadControl::Execute(bool) { }
|
||||
void C4GamePadControl::Execute() { }
|
||||
void C4GamePadControl::DoAxisInput() { }
|
||||
int C4GamePadControl::GetGamePadCount() { return 0; }
|
||||
bool C4GamePadControl::AnyButtonDown() { return false; }
|
||||
std::shared_ptr<C4GamePadOpener> C4GamePadControl::GetGamePad(int gamepad) { return nullptr; }
|
||||
std::shared_ptr<C4GamePadOpener> C4GamePadControl::GetGamePadByID(int32_t id) { return nullptr; }
|
||||
std::shared_ptr<C4GamePadOpener> C4GamePadControl::GetAvailableGamePad() { return nullptr; }
|
||||
|
||||
C4GamePadOpener::C4GamePadOpener(int iGamepad) { }
|
||||
C4GamePadOpener::~C4GamePadOpener() {}
|
||||
void C4GamePadOpener::SetGamePad(int iGamepad) { }
|
||||
int32_t C4GamePadOpener::GetID() { return -1; }
|
||||
bool C4GamePadOpener::IsAttached() { return false; }
|
||||
void C4GamePadOpener::PlayRumble(float strength, uint32_t length) { }
|
||||
void C4GamePadOpener::StopRumble() { }
|
||||
|
||||
#endif //_WIN32
|
||||
#endif
|
||||
|
|
|
@ -20,70 +20,71 @@
|
|||
#ifndef INC_C4GamePadCon
|
||||
#define INC_C4GamePadCon
|
||||
|
||||
#ifdef USE_WIN32_WINDOWS
|
||||
#include <StdJoystick.h>
|
||||
#endif
|
||||
#include <memory>
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
#include <C4KeyboardInput.h>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
#include <SDL.h>
|
||||
#endif
|
||||
|
||||
struct _SDL_Joystick;
|
||||
typedef struct _SDL_Joystick SDL_Joystick;
|
||||
|
||||
union SDL_Event;
|
||||
typedef union SDL_Event SDL_Event;
|
||||
class C4GamePadOpener;
|
||||
|
||||
class C4GamePadControl
|
||||
{
|
||||
#ifdef USE_WIN32_WINDOWS
|
||||
private:
|
||||
struct Pad
|
||||
{
|
||||
CStdGamePad *pGamepad;
|
||||
int iRefCount;
|
||||
uint32_t Buttons;
|
||||
CStdGamePad::AxisPos AxisPosis[CStdGamepad_MaxAxis];
|
||||
int32_t AxisStrengths[CStdGamepad_MaxAxis];
|
||||
#ifdef HAVE_SDL
|
||||
public:
|
||||
enum {
|
||||
FEED_BUTTONS = 1,
|
||||
FEED_MOVED = 2,
|
||||
};
|
||||
Pad Gamepads[CStdGamepad_MaxGamePad];
|
||||
int iNumGamepads;
|
||||
|
||||
enum { AxisStrengthChangeThreshold = 2 }; // if axis strength change > this value, a new control is issued
|
||||
|
||||
public:
|
||||
void OpenGamepad(int id); // add gamepad ref
|
||||
void CloseGamepad(int id); // del gamepad ref
|
||||
static C4GamePadControl *pInstance; // singleton
|
||||
#elif defined(HAVE_SDL)
|
||||
public:
|
||||
void FeedEvent(SDL_Event& e);
|
||||
// Called from C4AppSDL
|
||||
void FeedEvent(const SDL_Event& e, int feed);
|
||||
void CheckGamePad(const SDL_Event& e);
|
||||
private:
|
||||
std::set<C4KeyCode> PressedAxis;
|
||||
std::set<C4KeyCode> PressedAxis; // for button emulation
|
||||
std::map<C4KeyCode, SDL_Event> AxisEvents; // for analog movement events
|
||||
std::map<int32_t, std::shared_ptr<C4GamePadOpener> > Gamepads; // gamepad instance id -> gamepad
|
||||
#endif
|
||||
public:
|
||||
static const int32_t MaxStrength = 32767; // 2^15 - 1
|
||||
|
||||
C4GamePadControl();
|
||||
~C4GamePadControl();
|
||||
void Clear();
|
||||
int GetGamePadCount();
|
||||
void Execute(bool send_axis_strength_changes=false);
|
||||
void Execute();
|
||||
void DoAxisInput(); // period axis strength update controls sent on each control frame creation
|
||||
static bool AnyButtonDown();
|
||||
|
||||
std::shared_ptr<C4GamePadOpener> GetGamePad(int gamepad); // Gets the nth gamepad.
|
||||
std::shared_ptr<C4GamePadOpener> GetGamePadByID(int32_t id); // Gets a gamepad by its instance id.
|
||||
std::shared_ptr<C4GamePadOpener> GetAvailableGamePad(); // Looks for a gamepad that doesn't have an assigned player.
|
||||
};
|
||||
|
||||
class C4GamePadOpener
|
||||
{
|
||||
#ifdef USE_WIN32_WINDOWS
|
||||
int iGamePad;
|
||||
int GetGamePadIndex() const { return iGamePad; }
|
||||
#endif
|
||||
int32_t player = -1;
|
||||
|
||||
public:
|
||||
C4GamePadOpener(int iGamePad);
|
||||
~C4GamePadOpener();
|
||||
void SetGamePad(int iNewGamePad);
|
||||
|
||||
// A gamepad can be assigned to a player.
|
||||
int32_t GetPlayer() const { return player; }
|
||||
void SetPlayer(int32_t plr) { player = plr; }
|
||||
|
||||
int32_t GetID(); // Returns the gamepad's instance id.
|
||||
bool IsAttached(); // Returns whether the gamepad is currently attached.
|
||||
|
||||
// Force feedback: simple rumbling
|
||||
void PlayRumble(float strength, uint32_t length); // strength: 0-1, length: milliseconds
|
||||
void StopRumble();
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
SDL_Joystick *Joy;
|
||||
SDL_GameController *controller;
|
||||
SDL_Haptic *haptic;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
/*
|
||||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 1998-2000, Matthes Bender
|
||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
||||
* Copyright (c) 2010-2013, The OpenClonk Team and contributors
|
||||
*
|
||||
* Distributed under the terms of the ISC license; see accompanying file
|
||||
* "COPYING" for details.
|
||||
*
|
||||
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
||||
* See accompanying file "TRADEMARK" for details.
|
||||
*
|
||||
* To redistribute this file separately, substitute the full license texts
|
||||
* for the above references.
|
||||
*/
|
||||
|
||||
/* Simple joystick handling with DirectInput 1 */
|
||||
|
||||
#include "C4Include.h"
|
||||
#include <StdJoystick.h>
|
||||
|
||||
#include <C4windowswrapper.h>
|
||||
#include <windowsx.h>
|
||||
|
||||
uint32_t POV2Position(DWORD dwPOV, bool fVertical)
|
||||
{
|
||||
// POV value is a 360° angle multiplied by 100
|
||||
double dAxis;
|
||||
// Centered
|
||||
if (dwPOV == JOY_POVCENTERED)
|
||||
dAxis = 0.0;
|
||||
// Angle: convert to linear value -100 to +100
|
||||
else
|
||||
dAxis = (fVertical ? -cos((dwPOV/100) * M_PI / 180.0) : sin((dwPOV/100) * M_PI / 180.0)) * 100.0;
|
||||
// Gamepad configuration wants unsigned and gets 0 to 200
|
||||
return (uint32_t) (dAxis + 100.0);
|
||||
}
|
||||
|
||||
CStdGamePad::CStdGamePad(int id) : id(id)
|
||||
{
|
||||
ResetCalibration();
|
||||
}
|
||||
|
||||
void CStdGamePad::ResetCalibration()
|
||||
{
|
||||
// no calibration yet
|
||||
for (int i=0; i<CStdGamepad_MaxCalAxis; ++i)
|
||||
{
|
||||
fAxisCalibrated[i]=false;
|
||||
}
|
||||
}
|
||||
|
||||
void CStdGamePad::SetCalibration(uint32_t *pdwAxisMin, uint32_t *pdwAxisMax, bool *pfAxisCalibrated)
|
||||
{
|
||||
// params to calibration
|
||||
for (int i=0; i<CStdGamepad_MaxCalAxis; ++i)
|
||||
{
|
||||
dwAxisMin[i] = pdwAxisMin[i];
|
||||
dwAxisMax[i] = pdwAxisMax[i];
|
||||
fAxisCalibrated[i] = pfAxisCalibrated[i];
|
||||
}
|
||||
}
|
||||
|
||||
void CStdGamePad::GetCalibration(uint32_t *pdwAxisMin, uint32_t *pdwAxisMax, bool *pfAxisCalibrated)
|
||||
{
|
||||
// calibration to params
|
||||
for (int i=0; i<CStdGamepad_MaxCalAxis; ++i)
|
||||
{
|
||||
pdwAxisMin[i] = dwAxisMin[i];
|
||||
pdwAxisMax[i] = dwAxisMax[i];
|
||||
pfAxisCalibrated[i] = fAxisCalibrated[i];
|
||||
}
|
||||
}
|
||||
|
||||
bool CStdGamePad::Update()
|
||||
{
|
||||
joynfo.dwSize=sizeof(joynfo);
|
||||
joynfo.dwFlags=JOY_RETURNBUTTONS | JOY_RETURNRAWDATA | JOY_RETURNX | JOY_RETURNY | JOY_RETURNZ | JOY_RETURNR | JOY_RETURNU | JOY_RETURNV | JOY_RETURNPOV;
|
||||
return joyGetPosEx(JOYSTICKID1+id,&joynfo) == JOYERR_NOERROR;
|
||||
}
|
||||
|
||||
uint32_t CStdGamePad::GetButtons()
|
||||
{
|
||||
return joynfo.dwButtons;
|
||||
}
|
||||
|
||||
CStdGamePad::AxisPos CStdGamePad::GetAxisPos(int idAxis, int32_t *out_strength)
|
||||
{
|
||||
if (out_strength) *out_strength = 0; // default no strength
|
||||
if (idAxis<0 || idAxis>=CStdGamepad_MaxAxis) return Mid; // wrong axis
|
||||
// get raw axis data
|
||||
if (idAxis<CStdGamepad_MaxCalAxis)
|
||||
{
|
||||
uint32_t dwPos = (&joynfo.dwXpos)[idAxis];
|
||||
// evaluate axis calibration
|
||||
if (fAxisCalibrated[idAxis])
|
||||
{
|
||||
// update it
|
||||
dwAxisMin[idAxis] = std::min(dwAxisMin[idAxis], dwPos);
|
||||
dwAxisMax[idAxis] = std::max(dwAxisMax[idAxis], dwPos);
|
||||
// Calculate center
|
||||
DWORD dwCenter = (dwAxisMin[idAxis] + dwAxisMax[idAxis]) / 2;
|
||||
// Axis strength
|
||||
DWORD dwRange = (dwAxisMax[idAxis] - dwCenter);
|
||||
// Trigger range is 20% off center
|
||||
DWORD dwThresh = dwRange / 5;
|
||||
if (dwPos < dwCenter - dwThresh)
|
||||
{
|
||||
if (out_strength && dwRange) *out_strength = (dwCenter-dwPos)*100/dwRange;
|
||||
return Low;
|
||||
}
|
||||
if (dwPos > dwCenter + dwThresh)
|
||||
{
|
||||
if (out_strength && dwRange) *out_strength = (dwPos-dwCenter)*100/dwRange;
|
||||
return High;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// init it
|
||||
dwAxisMin[idAxis] = dwAxisMax[idAxis] = dwPos;
|
||||
fAxisCalibrated[idAxis] = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's a POV head
|
||||
DWORD dwPos = POV2Position(joynfo.dwPOV, idAxis==PAD_Axis_POVy);
|
||||
if (out_strength) *out_strength = Abs(int32_t(dwPos) - 100);
|
||||
if (dwPos > 130) return High; else if (dwPos < 70) return Low;
|
||||
}
|
||||
return Mid;
|
||||
}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 1998-2000, Matthes Bender
|
||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
||||
* Copyright (c) 2009-2013, The OpenClonk Team and contributors
|
||||
*
|
||||
* Distributed under the terms of the ISC license; see accompanying file
|
||||
* "COPYING" for details.
|
||||
*
|
||||
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
||||
* See accompanying file "TRADEMARK" for details.
|
||||
*
|
||||
* To redistribute this file separately, substitute the full license texts
|
||||
* for the above references.
|
||||
*/
|
||||
|
||||
/* Simple joystick handling with DirectInput 1 */
|
||||
|
||||
#ifndef INC_StdJoystick
|
||||
#define INC_StdJoystick
|
||||
|
||||
#include <C4windowswrapper.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
const int32_t PAD_Axis_POVx = 6;
|
||||
const int32_t PAD_Axis_POVy = 7; // virtual axises of the coolie hat
|
||||
|
||||
const int CStdGamepad_MaxGamePad = 15, // maximum number of supported gamepads
|
||||
CStdGamepad_MaxCalAxis = 6, // maximum number of calibrated axises
|
||||
CStdGamepad_MaxAxis = 8; // number of axises plus coolie hat axises
|
||||
|
||||
class CStdGamePad
|
||||
{
|
||||
public:
|
||||
enum AxisPos { Low, Mid, High, }; // quantized axis positions
|
||||
private:
|
||||
int id; // gamepad number
|
||||
JOYINFOEX joynfo; // WIN32 gamepad info
|
||||
|
||||
public:
|
||||
uint32_t dwAxisMin[CStdGamepad_MaxCalAxis], dwAxisMax[CStdGamepad_MaxCalAxis]; // axis ranges - auto calibrated
|
||||
bool fAxisCalibrated[CStdGamepad_MaxCalAxis]; // set if an initial value for axis borders has been determined already
|
||||
|
||||
CStdGamePad(int id); // ctor
|
||||
|
||||
void ResetCalibration(); // resets axis min and max
|
||||
void SetCalibration(uint32_t *pdwAxisMin, uint32_t *pdwAxisMax, bool *pfAxisCalibrated);
|
||||
void GetCalibration(uint32_t *pdwAxisMin, uint32_t *pdwAxisMax, bool *pfAxisCalibrated);
|
||||
|
||||
bool Update(); // read current gamepad data
|
||||
uint32_t GetButtons(); // returns bitmask of pressed buttons for last retrieved info
|
||||
AxisPos GetAxisPos(int idAxis, int32_t *out_strength=NULL); // return axis extension - mid for error or center position
|
||||
};
|
||||
|
||||
#endif
|
|
@ -66,7 +66,6 @@ C4Player::C4Player() : C4PlayerInfoCore()
|
|||
LastControlType = PCID_None;
|
||||
LastControlID = 0;
|
||||
pMsgBoardQuery = NULL;
|
||||
pGamepad = NULL;
|
||||
NoEliminationCheck = false;
|
||||
Evaluated = false;
|
||||
ZoomLimitMinWdt = ZoomLimitMinHgt = ZoomLimitMaxWdt = ZoomLimitMaxHgt = ZoomWdt = ZoomHgt = 0;
|
||||
|
@ -86,7 +85,6 @@ C4Player::~C4Player()
|
|||
delete pMsgBoardQuery;
|
||||
pMsgBoardQuery = pNext;
|
||||
}
|
||||
delete pGamepad; pGamepad = NULL;
|
||||
ClearControl();
|
||||
}
|
||||
|
||||
|
@ -212,6 +210,30 @@ void C4Player::Execute()
|
|||
Menu.TryClose(false, false);
|
||||
}
|
||||
|
||||
// Do we have a gamepad?
|
||||
if (pGamepad)
|
||||
{
|
||||
// Check whether it's still connected.
|
||||
if (!pGamepad->IsAttached())
|
||||
{
|
||||
// Allow the player to plug the gamepad back in. This allows
|
||||
// battery replacement or plugging the controller back
|
||||
// in after someone tripped over the wire.
|
||||
if (!FindGamepad())
|
||||
{
|
||||
LogF("%s: No gamepad available.", Name.getData());
|
||||
::Game.Pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Should we have one? The player may have started the game
|
||||
// without turning their controller on, only noticing this
|
||||
// after the game started.
|
||||
else if (LocalControl && ControlSet && ControlSet->HasGamepad())
|
||||
{
|
||||
FindGamepad();
|
||||
}
|
||||
|
||||
// Tick1
|
||||
UpdateView();
|
||||
ExecuteControl();
|
||||
|
@ -1349,8 +1371,12 @@ void C4Player::ClearControl()
|
|||
LocalControl = false;
|
||||
ControlSetName.Clear();
|
||||
ControlSet=NULL;
|
||||
if (pGamepad) { delete pGamepad; pGamepad=NULL; }
|
||||
MouseControl = false;
|
||||
if (pGamepad)
|
||||
{
|
||||
pGamepad->SetPlayer(NO_OWNER);
|
||||
pGamepad.reset();
|
||||
}
|
||||
// no controls issued yet
|
||||
ControlCount = ActionCount = 0;
|
||||
LastControlType = PCID_None;
|
||||
|
@ -1382,7 +1408,11 @@ void C4Player::InitControl()
|
|||
// init gamepad
|
||||
if (ControlSet && ControlSet->HasGamepad())
|
||||
{
|
||||
pGamepad = new C4GamePadOpener(ControlSet->GetGamepadIndex());
|
||||
if (!FindGamepad())
|
||||
{
|
||||
LogF("No gamepad available for %s, please plug one in!", Name.getData());
|
||||
::Game.Pause();
|
||||
}
|
||||
}
|
||||
// Mouse
|
||||
if (ControlSet && ControlSet->HasMouse() && PrefMouse)
|
||||
|
@ -1396,6 +1426,18 @@ void C4Player::InitControl()
|
|||
Control.RegisterKeyset(Number, ControlSet);
|
||||
}
|
||||
|
||||
bool C4Player::FindGamepad()
|
||||
{
|
||||
auto newPad = Application.pGamePadControl->GetAvailableGamePad();
|
||||
if (!newPad) return false;
|
||||
newPad->SetPlayer(ID);
|
||||
// Release the old gamepad.
|
||||
if (pGamepad) pGamepad->SetPlayer(NO_OWNER);
|
||||
pGamepad = newPad;
|
||||
LogF("%s: Using gamepad #%d.", Name.getData(), pGamepad->GetID());
|
||||
return true;
|
||||
}
|
||||
|
||||
int igOffX, igOffY;
|
||||
|
||||
int VisibilityCheck(int iVis, int sx, int sy, int cx, int cy)
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "C4PlayerControl.h"
|
||||
#include <C4Value.h>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
|
||||
const int32_t C4PVM_Cursor = 0,
|
||||
C4PVM_Target = 1,
|
||||
|
@ -130,7 +131,7 @@ public:
|
|||
C4PlayerControl Control;
|
||||
C4ObjectPtr Cursor, ViewCursor;
|
||||
int32_t CursorFlash;
|
||||
class C4GamePadOpener *pGamepad;
|
||||
std::shared_ptr<class C4GamePadOpener> pGamepad;
|
||||
// Message
|
||||
int32_t MessageStatus;
|
||||
char MessageBuf[256+1];
|
||||
|
@ -265,6 +266,9 @@ private:
|
|||
bool AdjustZoomParameter(int32_t *range_par, int32_t new_val, bool no_increase, bool no_decrease);
|
||||
bool AdjustZoomParameter(C4Fixed *zoom_par, C4Fixed new_val, bool no_increase, bool no_decrease);
|
||||
|
||||
// Finds a new gamepad to use, returning true on success.
|
||||
bool FindGamepad();
|
||||
|
||||
public:
|
||||
// custom scenario achievements
|
||||
bool GainScenarioAchievement(const char *achievement_id, int32_t value, const char *scen_name_override=NULL);
|
||||
|
|
Loading…
Reference in New Issue