1 /* 2 * OpenClonk, http://www.openclonk.org 3 * 4 * Copyright (c) 2005-2009, RedWolf Design GmbH, http://www.clonk.de/ 5 * Copyright (c) 2009-2016, The OpenClonk Team and contributors 6 * 7 * Distributed under the terms of the ISC license; see accompanying file 8 * "COPYING" for details. 9 * 10 * "Clonk" is a registered trademark of Matthes Bender, used with permission. 11 * See accompanying file "TRADEMARK" for details. 12 * 13 * To redistribute this file separately, substitute the full license texts 14 * for the above references. 15 */ 16 // Input to player control mapping 17 18 #ifndef INC_C4PlayerControl 19 #define INC_C4PlayerControl 20 21 #include "gui/C4KeyboardInput.h" 22 #include "c4group/C4LangStringTable.h" 23 #include "object/C4Id.h" 24 #include "platform/C4TimeMilliseconds.h" 25 26 const float C4GFX_ZoomStep = 1.1040895f; 27 28 // one control definition, e.g. "Left", "Throw", etc. 29 class C4PlayerControlDef 30 { 31 public: 32 enum CoordinateSpace // coordinate space for mouse position 33 { 34 COS_Game = 0, // game (landscape) coordinates 35 COS_Viewport = 1 // viewport (GUI) coordinates 36 }; 37 enum Actions //action to be performed when control is triggered 38 { 39 CDA_None = 0, // do nothing 40 CDA_Script, // default: Script callback 41 CDA_Menu, // open player menu (async) 42 CDA_MenuOK, CDA_MenuCancel, CDA_MenuLeft, CDA_MenuUp, CDA_MenuRight, CDA_MenuDown, // player menu controls (async) 43 CDA_ObjectMenuTextComplete, // object menu fast-foward through text animation (async) 44 CDA_ObjectMenuOK, CDA_ObjectMenuOKAll, CDA_ObjectMenuSelect, CDA_ObjectMenuCancel, CDA_ObjectMenuLeft, CDA_ObjectMenuUp, CDA_ObjectMenuRight, CDA_ObjectMenuDown, // object menu controls (sync) 45 CDA_ZoomIn, CDA_ZoomOut // player viewport control (async) 46 }; 47 48 private: 49 StdCopyStrBuf sIdentifier; // name as seen in script and config 50 StdCopyStrBuf sGUIName; // name as displayed to player 51 StdCopyStrBuf sGUIDesc; // key description displayed to player in config dialog 52 bool fGlobal{false}; // if true, control can be bound to the global player only 53 bool fIsHoldKey{false}; // if true, the control can be in down and up state 54 bool fDefaultDisabled{false}; // if true, the control is disabled by default and needs to be enabled by script 55 bool fSendCursorPos{false}; // if true, x/y parameters will be set by current GUI mouse cursor pos (or GetCursor()-GUI coordinate pos for gamepad) 56 int32_t iRepeatDelay; // if >0, the key will generate successive events when held down 57 int32_t iInitialRepeatDelay; // delay after which KeyRepeat will be enabled 58 C4ID idControlExtraData; // extra data to be passed to script function 59 CoordinateSpace eCoordSpace{COS_Game}; // coordinate space to be used for mouse coordinates when control is triggered by mouse 60 Actions eAction{CDA_Script}; 61 62 public: C4PlayerControlDef()63 C4PlayerControlDef() : 64 idControlExtraData(C4ID::None) 65 {} 66 ~C4PlayerControlDef() = default; 67 68 void CompileFunc(StdCompiler *pComp); 69 GetIdentifier()70 const char *GetIdentifier() const { return sIdentifier.getData(); } GetGUIName()71 const char *GetGUIName() const { return sGUIName.getData(); } GetGUIDesc()72 const char *GetGUIDesc() const { return sGUIDesc.getData(); } GetAction()73 Actions GetAction() const { return eAction; } IsHoldKey()74 bool IsHoldKey() const { return fIsHoldKey; } GetExtraData()75 C4ID GetExtraData() const { return idControlExtraData; } IsGlobal()76 bool IsGlobal() const { return fGlobal; } GetRepeatDelay()77 int32_t GetRepeatDelay() const { return iRepeatDelay; } GetInitialRepeatDelay()78 int32_t GetInitialRepeatDelay() const { return iInitialRepeatDelay; } IsDefaultDisabled()79 bool IsDefaultDisabled() const { return fDefaultDisabled; } GetCoordinateSpace()80 CoordinateSpace GetCoordinateSpace() const { return eCoordSpace; } IsSendCursorPos()81 bool IsSendCursorPos() const { return fSendCursorPos; } 82 83 bool operator ==(const C4PlayerControlDef &cmp) const; 84 85 bool Execute(bool fUp, const C4KeyEventData &rKeyExtraData); // key was triggered - execute and return if handled IsSyncObjectMenuControl()86 bool IsSyncObjectMenuControl() const { return eAction>=CDA_ObjectMenuOK && eAction<=CDA_ObjectMenuDown; } IsAsync()87 bool IsAsync() const { return eAction != CDA_None && eAction != CDA_Script && !IsSyncObjectMenuControl(); } // true if to be executed directly when triggered IsSync()88 bool IsSync() const { return eAction == CDA_Script || IsSyncObjectMenuControl(); } // true if to be executed via control queue IsValid()89 bool IsValid() const { return eAction != CDA_None; } 90 }; 91 92 // CON_* constants are indices into the C4PlayerControlDefs list 93 enum { CON_None = -1 }; // No/undefined control 94 95 // list of all known player control definitions 96 class C4PlayerControlDefs 97 { 98 private: 99 typedef std::vector<C4PlayerControlDef> DefVecImpl; 100 DefVecImpl Defs; 101 bool clear_previous{false}; // if set is merged, all previous control defs are cleared - use 102 103 public: 104 struct CInternalCons 105 { 106 int32_t CON_ObjectMenuSelect{CON_None}, CON_ObjectMenuOKAll{CON_None}, CON_ObjectMenuOK{CON_None}, CON_ObjectMenuCancel{CON_None}, CON_CursorPos{CON_None}; 107 CInternalCons() = default; 108 } InternalCons; 109 110 void UpdateInternalCons(); 111 112 public: 113 C4PlayerControlDefs() = default; 114 ~C4PlayerControlDefs() = default; 115 void Clear(); 116 117 void CompileFunc(StdCompiler *pComp); 118 void MergeFrom(const C4PlayerControlDefs &Src); // copy all defs from source file; overwrite defs of same name if found 119 120 void FinalInit(); // after all defs have been loaded: register script constants 121 122 const C4PlayerControlDef *GetControlByIndex(int32_t idx) const; 123 int32_t GetControlIndexByIdentifier(const char *szIdentifier) const; // return CON_None for not found GetCount()124 size_t GetCount() const { return Defs.size(); } 125 126 bool operator ==(const C4PlayerControlDefs &cmp) const { return Defs == cmp.Defs && clear_previous == cmp.clear_previous; } 127 }; 128 129 struct C4PlayerControlRecentKey 130 { 131 C4KeyCodeEx pressed_key, matched_key; 132 C4TimeMilliseconds tTime; C4PlayerControlRecentKeyC4PlayerControlRecentKey133 C4PlayerControlRecentKey(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key, C4TimeMilliseconds tTime) : pressed_key(pressed_key), matched_key(matched_key), tTime(tTime) {} 134 bool operator ==(const C4KeyCodeEx &cmp) { return pressed_key==cmp; } // comparison op for finding items in lists: Search for the pressed key only 135 }; 136 137 typedef std::list<C4PlayerControlRecentKey> C4PlayerControlRecentKeyList; 138 139 typedef std::vector<C4KeyCodeEx> C4KeyCodeExVec; 140 141 // a key/mouse/gamepad assignment to a PlayerControlDef 142 class C4PlayerControlAssignment 143 { 144 public: 145 // action to be performed on the control upon this key 146 enum TriggerModes 147 { 148 CTM_Default = 0, // standard behaviour: The control will be triggered 149 CTM_Hold = 1 << 0, // the control will be put into "down"-mode 150 CTM_Release = 1 << 1, // the hold mode of the control will be released 151 CTM_AlwaysUnhandled = 1 << 2, // the key will not block handling of other keys even if it got handled 152 CTM_HandleDownStatesOnly = 1 << 3, // used when an already handled release key is processed to reset down states of overridden keys only 153 CTM_ClearRecentKeys = 1 << 4 // if this assignment is triggered, RecentKeys are reset so no more combos can be generated 154 }; 155 156 private: 157 // KeyCombo list: 158 // if size()>1, the control is triggered only if this combo is fulfilled 159 // used for simultanuous keypresses or sequences 160 struct KeyComboItem 161 { 162 C4KeyCodeEx Key; 163 StdCopyStrBuf sKeyName; 164 void CompileFunc(StdCompiler *pComp); 165 void UpdateKeyName(); 166 bool operator ==(const KeyComboItem &cmp) const { return Key==cmp.Key; } 167 }; 168 typedef std::vector<KeyComboItem> KeyComboVec; 169 KeyComboVec KeyCombo; 170 bool fComboIsSequence; // if true, the keys must be pressed in sequence. Otherwise, they must be pressed simultanuously 171 172 // trigger key: key/mouse/gamepad event triggering this assignment. For combinations, the last key of the combo. 173 C4KeyCodeEx TriggerKey; 174 175 StdCopyStrBuf sControlName; // name of the control to be executed on this key 176 StdCopyStrBuf sGUIName; // name as displayed to player. If empty, name stored in control def should be used. 177 StdCopyStrBuf sGUIDesc; // key description displayed to player in config dialog. If empty, name stored in control def should be used. 178 bool fGUIDisabled; // whether this key can't be reassigned through the GUI dialogue 179 bool fOverrideAssignments{false}; // override all other assignments to the same key? 180 bool is_inherited{false}; // set for assignments that were copied from a parent set without modification 181 bool fRefsResolved{false}; // set to true after sControlName and sKeyNames have been resolved to runtime values 182 int32_t iGUIGroup{0}; // in which this control is grouped in the gui 183 int32_t iControl{CON_None}; // the control to be executed on this key, i.e. the resolved sControlName 184 int32_t iPriority{0}; // higher priority assignments get handled first 185 int32_t iTriggerMode{CTM_Default}; 186 187 const C4PlayerControlAssignment *inherited_assignment{nullptr}; // valid for assignments that were copied from a parent: source assignment 188 189 public: C4PlayerControlAssignment()190 C4PlayerControlAssignment() : 191 TriggerKey() 192 {} 193 ~C4PlayerControlAssignment() = default; 194 195 void CompileFunc(StdCompiler *pComp); 196 void CopyKeyFrom(const C4PlayerControlAssignment &src_assignment); 197 bool ResolveRefs(class C4PlayerControlAssignmentSet *pParentSet, C4PlayerControlDefs *pControlDefs); // resolve references between assignments 198 bool IsComboMatched(const C4PlayerControlRecentKeyList &DownKeys, const C4PlayerControlRecentKeyList &RecentKeys) const; // check if combo is currently fulfilled (assuming TriggerKey is already matched) SetInherited(bool to_val)199 void SetInherited(bool to_val) { is_inherited = to_val; } SetInheritedAssignment(const C4PlayerControlAssignment * to_val)200 void SetInheritedAssignment(const C4PlayerControlAssignment *to_val) { inherited_assignment = to_val; } 201 void ResetKeyToInherited(); 202 bool IsKeyChanged() const; SetControlName(const char * control_name)203 void SetControlName(const char *control_name) { sControlName.Copy(control_name); } 204 void SetKey(const C4KeyCodeEx &key); 205 206 bool operator ==(const C4PlayerControlAssignment &cmp) const; // doesn't compare resolved TriggerKey/iControl 207 bool operator <(const C4PlayerControlAssignment &cmp) const { return iPriority > cmp.iPriority; } // assignments are processed in DESCENDING priority! GetControlName()208 const char *GetControlName() const { return sControlName.getData(); } GetControl()209 int32_t GetControl() const { return iControl; } 210 const char *GetGUIName(const C4PlayerControlDefs &defs) const; 211 const char *GetGUIDesc(const C4PlayerControlDefs &defs) const; 212 bool IsGUIDisabled() const; 213 int32_t GetGUIGroup() const; IsRefsResolved()214 bool IsRefsResolved() const { return fRefsResolved; } ResetRefsResolved()215 void ResetRefsResolved() { fRefsResolved = false; } // Mark references to other assignments as not resolved IsAlwaysUnhandled()216 bool IsAlwaysUnhandled() const { return !!(iTriggerMode & CTM_AlwaysUnhandled); } GetTriggerMode()217 int32_t GetTriggerMode() const { return iTriggerMode; } GetTriggerKey()218 const C4KeyCodeEx &GetTriggerKey() const { return TriggerKey; } HasCombo()219 bool HasCombo() const { return KeyCombo.size()>1; } IsOverrideAssignments()220 bool IsOverrideAssignments() const { return fOverrideAssignments; } IsInherited()221 bool IsInherited() const { return is_inherited; } GetInheritedAssignment()222 const C4PlayerControlAssignment *GetInheritedAssignment() const { return inherited_assignment; } 223 StdStrBuf GetKeysAsString(bool human_readable, bool short_name) const; 224 }; 225 226 typedef std::vector<C4PlayerControlAssignment> C4PlayerControlAssignmentVec; 227 typedef std::vector<const C4PlayerControlAssignment *> C4PlayerControlAssignmentPVec; 228 229 230 // a set of key/mouse/gamepad assignments to all controls 231 class C4PlayerControlAssignmentSet 232 { 233 private: 234 StdCopyStrBuf sName, sGUIName, sParentSetName; 235 const C4PlayerControlAssignmentSet *parent_set{nullptr}; 236 C4PlayerControlAssignmentVec Assignments; // ordered by priority 237 238 bool has_keyboard{true}; 239 bool has_mouse{true}; 240 bool has_gamepad{false}; 241 242 public: 243 C4PlayerControlAssignmentSet() = default; 244 ~C4PlayerControlAssignmentSet() = default; 245 void InitEmptyFromTemplate(const C4PlayerControlAssignmentSet &template_set); // copy all fields except assignments 246 247 void CompileFunc(StdCompiler *pComp); 248 bool ResolveRefs(C4PlayerControlDefs *pControlDefs); // resolve references between assignments 249 void SortAssignments(); 250 251 enum MergeMode { MM_Normal, MM_LowPrio, MM_Inherit, MM_ConfigOverload }; 252 253 void MergeFrom(const C4PlayerControlAssignmentSet &Src, MergeMode merge_mode); // take over all assignments defined in Src 254 C4PlayerControlAssignment *CreateAssignmentForControl(const char *control_name); 255 void RemoveAssignmentByControlName(const char *control_name); 256 GetName()257 const char *GetName() const { return sName.getData(); } GetGUIName()258 const char *GetGUIName() const { return sGUIName.getData(); } IsWildcardName()259 bool IsWildcardName() const { return IsWildcardString(sName.getData()); } 260 261 C4PlayerControlAssignment *GetAssignmentByIndex(int32_t index); // assignments are ordered by priority 262 C4PlayerControlAssignment *GetAssignmentByControlName(const char *szControlName); 263 C4PlayerControlAssignment *GetAssignmentByControl(int32_t control); 264 void GetAssignmentsByKey(const C4PlayerControlDefs &rDefs, const C4KeyCodeEx &key, bool fHoldKeysOnly, C4PlayerControlAssignmentPVec *pOutVec, const C4PlayerControlRecentKeyList &DownKeys, const C4PlayerControlRecentKeyList &RecentKeys) const; // match only by TriggerKey (last key of Combo) if fHoldKeysOnly 265 void GetTriggerKeys(const C4PlayerControlDefs &rDefs, C4KeyCodeExVec *pRegularKeys, C4KeyCodeExVec *pHoldKeys) const; // put all trigger keys of keyset into output vectors 266 267 bool operator ==(const C4PlayerControlAssignmentSet &cmp) const; 268 269 C4Facet GetPicture() const; // get image to be drawn to represent this control set 270 // todo HasKeyboard()271 bool HasKeyboard() const { return has_keyboard; } HasMouse()272 bool HasMouse() const { return has_mouse; } HasGamepad()273 bool HasGamepad() const { return has_gamepad; } GetLayoutOrder()274 int32_t GetLayoutOrder() const { return 0; } // returns position on keyboard (increasing from left to right) for viewport sorting 275 bool IsMouseControlAssigned(int32_t mouseevent) const; 276 }; 277 278 // list of C4PlayerControlAssignmentSet 279 class C4PlayerControlAssignmentSets 280 { 281 private: 282 typedef std::list<C4PlayerControlAssignmentSet> AssignmentSetList; 283 AssignmentSetList Sets; 284 bool clear_previous{false}; 285 286 public: 287 C4PlayerControlAssignmentSets() = default; 288 ~C4PlayerControlAssignmentSets() = default; 289 void Clear(); 290 291 void CompileFunc(StdCompiler *pComp); 292 bool operator ==(const C4PlayerControlAssignmentSets &cmp) const; 293 bool ResolveRefs(C4PlayerControlDefs *pControlDefs); // resolve references between assignments 294 void SortAssignments(); 295 296 void MergeFrom(const C4PlayerControlAssignmentSets &Src, C4PlayerControlAssignmentSet::MergeMode merge_mode); // take over all assignments in known sets and new sets defined in Src 297 298 C4PlayerControlAssignmentSet *CreateEmptySetByTemplate(const C4PlayerControlAssignmentSet &template_set); 299 void RemoveSetByName(const char *set_name); 300 301 C4PlayerControlAssignmentSet *GetSetByName(const char *szName); 302 C4PlayerControlAssignmentSet *GetDefaultSet(); 303 int32_t GetSetIndex(const C4PlayerControlAssignmentSet *set) const; 304 C4PlayerControlAssignmentSet *GetSetByIndex(int32_t index); GetSetCount()305 size_t GetSetCount() const { return Sets.size(); } 306 }; 307 308 // contents of one PlayerControls.txt file 309 class C4PlayerControlFile 310 { 311 private: 312 C4PlayerControlDefs ControlDefs; 313 C4PlayerControlAssignmentSets AssignmentSets; 314 public: 315 void Clear(); 316 void CompileFunc(StdCompiler *pComp); 317 bool Load(C4Group &hGroup, const char *szFilename, C4LangStringTable *pLang); 318 bool Save(C4Group &hGroup, const char *szFilename); 319 GetControlDefs()320 const C4PlayerControlDefs &GetControlDefs() const { return ControlDefs; } GetAssignmentSets()321 const C4PlayerControlAssignmentSets &GetAssignmentSets() const { return AssignmentSets; } 322 }; 323 324 // runtime information about a player's control 325 class C4PlayerControl 326 { 327 public: 328 enum { MaxRecentKeyLookback = 3000, MaxSequenceKeyDelay = 800 }; // milliseconds: Time to press key combos 329 330 enum ControlState { 331 CONS_Down = 0, 332 CONS_Up, 333 CONS_Moved, 334 }; 335 336 private: 337 C4PlayerControlDefs &ControlDefs; // shortcut 338 339 // owner 340 int32_t iPlr{-1}; 341 342 // async values 343 C4PlayerControlAssignmentSet *pControlSet{nullptr}; // the control set used by this player - may be nullptr if the player cannot be controlled! 344 typedef std::list<C4KeyBinding *> KeyBindingList; 345 KeyBindingList KeyBindings; // keys registered into Game.KeyboardInput 346 C4PlayerControlRecentKeyList RecentKeys; // keys pressed recently; for combinations 347 C4PlayerControlRecentKeyList DownKeys; // keys currently held down 348 bool IsCursorPosRequested{false}; // set to true when a SendCursorPos-control had been issued 349 350 public: 351 // sync values 352 struct CSync 353 { 354 struct ControlDownState 355 { 356 C4KeyEventData DownState, MovedState; // control is down if DownState.iStrength>0 357 int32_t iDownFrame{0}, iMovedFrame; // frame when control was pressed 358 bool fDownByUser{false}; // if true, the key is actually pressed. Otherwise, it's triggered as down by another key ControlDownStateCSync::ControlDownState359 ControlDownState(const C4KeyEventData &rDownState, int32_t iDownFrame, bool fDownByUser) 360 : DownState(rDownState), iDownFrame(iDownFrame), fDownByUser(fDownByUser) {} IsDownCSync::ControlDownState361 bool IsDown() const { return DownState.iStrength>0; } 362 ControlDownStateCSync::ControlDownState363 ControlDownState() : DownState() {} 364 void CompileFunc(StdCompiler *pComp); 365 bool operator ==(const ControlDownState &cmp) const; 366 }; 367 typedef std::vector<ControlDownState> DownStateVec; 368 DownStateVec ControlDownStates; // indexed by C4PlayerControlID: Down-state of a control. 0=up, 100=down; values inbetween e.g. for gamepad sticks 369 typedef std::vector<int32_t> DisableStateVec; 370 DisableStateVec ControlDisableStates; // indexed by C4PlayerControlID: Disable-states of controls. >0 is disabled. 371 372 const ControlDownState *GetControlDownState(int32_t iControl) const; 373 int32_t GetControlDisabled(int32_t iControl) const; IsControlDisabledCSync374 bool IsControlDisabled(int32_t iControl) const { return GetControlDisabled(iControl)>0; } 375 void SetControlDownState(int32_t iControl, const C4KeyEventData &rDownState, int32_t iDownFrame, bool fDownByUser); 376 void SetControlMovedState(int32_t iControl, const C4KeyEventData &rMovedState, int32_t iMovedFrame); 377 void ResetControlDownState(int32_t iControl); 378 bool SetControlDisabled(int32_t iControl, int32_t iVal); 379 380 void InitDefaults(const C4PlayerControlDefs &ControlDefs); 381 void Clear(); 382 void CompileFunc(StdCompiler *pComp); 383 bool operator ==(const CSync &cmp) const; 384 }; 385 386 private: 387 CSync Sync; 388 389 // callbacks from Game.KeyboardInput 390 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=nullptr); 391 bool ProcessKeyDown(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key); 392 bool ProcessKeyUp(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key); 393 bool ProcessKeyMoved(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key); 394 395 // execute single control. return if handled. 396 bool ExecuteControl(int32_t iControl, ControlState state, const C4KeyEventData &rKeyExtraData, int32_t iTriggerMode, bool fRepeated, bool fHandleDownStateOnly); 397 bool ExecuteControlAction(int32_t iControl, C4PlayerControlDef::Actions eAction, C4ID idControlExtraData, ControlState state, const C4KeyEventData &rKeyExtraData, bool fRepeated); 398 bool ExecuteControlScript(int32_t iControl, C4ID idControlExtraData, ControlState state, const C4KeyEventData &rKeyExtraData, bool fRepeated); 399 400 // init 401 void AddKeyBinding(const C4KeyCodeEx &key, bool fHoldKey, int32_t idx); 402 403 // helper function: get current cursor position of controlling player in GUI coordinates 404 // used e.g. to open menus at cursor pos 405 bool GetCurrentPlayerCursorPos(int32_t *x_out, int32_t *y_out, int32_t *game_x_out, int32_t *game_y_out); 406 407 public: 408 C4PlayerControl(); ~C4PlayerControl()409 ~C4PlayerControl() { Clear(); } 410 void Clear(); 411 412 // first-time init call after player join 413 // not called again after control set change/savegame resume 414 // does DefaultDisabled controls 415 void Init(); 416 417 void CompileFunc(StdCompiler *pComp); 418 419 void RegisterKeyset(int32_t iPlr, C4PlayerControlAssignmentSet *pKeyset); // register all keys into Game.KeyboardInput creating KeyBindings 420 IsGlobal()421 bool IsGlobal() const { return iPlr==-1; } GetControlDownState(int32_t iControl)422 const CSync::ControlDownState *GetControlDownState(int32_t iControl) const 423 { return Sync.GetControlDownState(iControl); } 424 425 // callback from control queue 426 void ExecuteControlPacket(const class C4ControlPlayerControl *pCtrl); 427 428 // sync execution: Do keyrepeat, etc. 429 void Execute(); 430 431 // mouse input 432 bool DoMouseInput(uint8_t mouse_id, int32_t mouseevent, float game_x, float game_y, float gui_x, float gui_y, DWORD flags); 433 434 // control enable/disable SetControlDisabled(int ctrl,bool is_disabled)435 bool SetControlDisabled(int ctrl, bool is_disabled) { return Sync.SetControlDisabled(ctrl, is_disabled); } IsControlDisabled(int ctrl)436 bool IsControlDisabled(int ctrl) const { return Sync.IsControlDisabled(ctrl); } 437 438 // callback from C4GameControl when the next control packet is finalized 439 void PrepareInput(); 440 441 }; 442 443 444 #endif // INC_C4PlayerControl 445