Implement "gamepad stick moved" events for analog input

Analog moved events are only sent once per control frame.
liquid_container
Lukas Werling 2016-02-15 16:20:37 +01:00
parent 937ddaf722
commit 8dd1450e94
10 changed files with 147 additions and 121 deletions

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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; }
};

View File

@ -994,12 +994,12 @@ 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)
{
// 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)
@ -1008,7 +1008,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;
@ -1016,7 +1016,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
@ -1026,7 +1026,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);
@ -1056,7 +1056,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();
@ -1075,7 +1075,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)
@ -1096,10 +1102,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
@ -1114,7 +1120,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);
@ -1132,7 +1138,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)
{
@ -1142,7 +1148,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
@ -1171,7 +1177,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;
@ -1179,14 +1185,19 @@ 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)
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;
@ -1194,8 +1205,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;
@ -1208,12 +1221,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
@ -1241,15 +1255,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)
{
@ -1270,7 +1285,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();
}
@ -1295,7 +1310,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);
}
}
}
@ -1348,7 +1363,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));
}
@ -1440,7 +1455,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

View File

@ -333,6 +333,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
@ -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);

View File

@ -3079,6 +3079,12 @@ 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 },
{ NULL, C4V_Nil, 0}
};

View File

@ -46,7 +46,8 @@ 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
@ -265,7 +266,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 +277,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 +285,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 +297,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 +308,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 +316,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 +328,7 @@ public:
protected:
TargetClass &rTarget;
CallbackFunc pFuncDown, pFuncUp, pFuncPressed;
CallbackFunc pFuncDown, pFuncUp, pFuncPressed, pFuncMoved;
ParameterType par;
protected:
@ -337,6 +340,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 +348,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 +359,7 @@ public:
protected:
TargetClass &rTarget;
CallbackFunc pFuncDown, pFuncUp, pFuncPressed;
CallbackFunc pFuncDown, pFuncUp, pFuncPressed, pFuncMoved;
ParameterType par;
protected:
@ -367,6 +371,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 +379,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

View File

@ -165,7 +165,7 @@ void C4AbstractApp::HandleSDLEvent(SDL_Event& e)
case SDL_CONTROLLERAXISMOTION:
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
Application.pGamePadControl->FeedEvent(e);
Application.pGamePadControl->FeedEvent(e, C4GamePadControl::FEED_BUTTONS);
break;
}
}

View File

@ -25,13 +25,6 @@
#include <C4Log.h>
#include <C4Game.h>
// regardless of WIN32 or SDL
void C4GamePadControl::DoAxisInput()
{
// Send axis strength changes
Execute(true);
}
#if defined(HAVE_SDL) && !defined(USE_CONSOLE)
#include <SDL.h>
@ -54,7 +47,7 @@ C4GamePadControl::~C4GamePadControl()
SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS);
}
void C4GamePadControl::Execute(bool)
void C4GamePadControl::Execute()
{
#ifndef USE_SDL_MAINLOOP
SDL_Event event;
@ -65,7 +58,7 @@ void C4GamePadControl::Execute(bool)
case SDL_CONTROLLERAXISMOTION:
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
FeedEvent(event);
FeedEvent(event, FEED_BUTTONS);
break;
}
}
@ -74,19 +67,10 @@ void C4GamePadControl::Execute(bool)
namespace
{
const int deadZone = 13337;
int amplify(int i)
{
if (i < 0)
return -(deadZone + 1);
if (i > 0)
return deadZone + 1;
return 0;
}
const int deadZone = 8000;
}
void C4GamePadControl::FeedEvent(SDL_Event& event)
void C4GamePadControl::FeedEvent(const SDL_Event& event, int feed)
{
switch (event.type)
{
@ -94,63 +78,62 @@ void C4GamePadControl::FeedEvent(SDL_Event& event)
{
C4KeyCode minCode = KEY_Gamepad(event.caxis.which, KEY_JOY_Axis(event.caxis.axis, false));
C4KeyCode maxCode = KEY_Gamepad(event.caxis.which, KEY_JOY_Axis(event.caxis.axis, true));
int32_t value = std::abs(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.caxis.value < -deadZone)
auto doInput = [&](C4KeyEventType event, int32_t strength)
{
if (PressedAxis.count(minCode) == 0)
Game.DoKeyboardInput(
KEY_Gamepad(which, keyCode), event,
false, false, false, false, NULL, false, strength);
};
if (feed & FEED_BUTTONS)
{
// Also emulate button presses.
if (PressedAxis.count(keyCode) && value <= deadZone)
{
Game.DoKeyboardInput(
KEY_Gamepad(event.caxis.which, minCode),
KEYEV_Down, false, false, false, false);
PressedAxis.insert(minCode);
}
}
else
{
if (PressedAxis.count(minCode) != 0)
{
Game.DoKeyboardInput(
KEY_Gamepad(event.caxis.which, minCode),
KEYEV_Up, false, false, false, false);
PressedAxis.erase(minCode);
}
}
if (event.caxis.value > +deadZone)
{
if (PressedAxis.count(maxCode) == 0)
{
Game.DoKeyboardInput(
KEY_Gamepad(event.caxis.which, maxCode),
KEYEV_Down, false, false, false, false);
PressedAxis.insert(maxCode);
}
}
else
{
if (PressedAxis.count(maxCode) != 0)
{
Game.DoKeyboardInput(
KEY_Gamepad(event.caxis.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_CONTROLLERBUTTONDOWN:
Game.DoKeyboardInput(
KEY_Gamepad(event.cbutton.which, KEY_JOY_Button(event.cbutton.button)),
KEYEV_Down, false, false, false, false);
if (feed & FEED_BUTTONS)
Game.DoKeyboardInput(
KEY_Gamepad(event.cbutton.which, KEY_JOY_Button(event.cbutton.button)),
KEYEV_Down, false, false, false, false);
break;
case SDL_CONTROLLERBUTTONUP:
Game.DoKeyboardInput(
KEY_Gamepad(event.cbutton.which, KEY_JOY_Button(event.cbutton.button)),
KEYEV_Up, false, false, false, false);
if (feed & FEED_BUTTONS)
Game.DoKeyboardInput(
KEY_Gamepad(event.cbutton.which, KEY_JOY_Button(event.cbutton.button)),
KEYEV_Up, false, false, false, false);
break;
}
}
void C4GamePadControl::DoAxisInput()
{
for (auto const &e : AxisEvents)
{
FeedEvent(e.second, FEED_MOVED);
}
AxisEvents.clear();
}
int C4GamePadControl::GetGamePadCount()
{
// Not all Joysticks are game controllers.
@ -192,7 +175,8 @@ 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; }

View File

@ -23,6 +23,7 @@
#ifdef HAVE_SDL
#include <C4KeyboardInput.h>
#include <set>
#include <map>
#endif
struct _SDL_GameController;
@ -35,16 +36,22 @@ class C4GamePadControl
{
#ifdef HAVE_SDL
public:
void FeedEvent(SDL_Event& e);
enum {
FEED_BUTTONS = 1,
FEED_MOVED = 2,
};
// Called from C4AppSDL
void FeedEvent(const SDL_Event& e, int feed);
private:
std::set<C4KeyCode> PressedAxis;
std::set<C4KeyCode> PressedAxis; // for button emulation
std::map<C4KeyCode, SDL_Event> AxisEvents; // for analog movement events
#endif
public:
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();
};