OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4PlayerControl.cpp
Go to the documentation of this file.
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 #include "C4Include.h"
21 
22 #include "object/C4DefList.h"
24 #include "player/C4Player.h"
25 #include "player/C4PlayerList.h"
26 #include "control/C4Control.h"
27 #include "game/C4Game.h"
28 #include "platform/C4GamePadCon.h"
29 #include "lib/C4Log.h"
31 #include "gui/C4MouseControl.h"
32 #include "game/C4GraphicsSystem.h"
33 #include "game/C4Viewport.h"
34 #include "object/C4Object.h"
35 #include "object/C4ObjectMenu.h"
36 #include "script/C4Aul.h"
37 
38 #include <algorithm>
39 
40 #include "control/C4Record.h"
41 
42 /* C4PlayerControlDef */
43 
45 {
46  if (!pComp->Name("ControlDef")) { pComp->NameEnd(); pComp->excNotFound("ControlDef"); }
47  pComp->Value(mkNamingAdapt(mkParAdapt(sIdentifier, StdCompiler::RCT_Idtf), "Identifier", "None"));
48  pComp->Value(mkNamingAdapt(mkParAdapt(sGUIName, StdCompiler::RCT_All), "GUIName", ""));
49  pComp->Value(mkNamingAdapt(mkParAdapt(sGUIDesc, StdCompiler::RCT_All), "GUIDesc", ""));
50  pComp->Value(mkNamingAdapt(fGlobal, "Global", false));
51  pComp->Value(mkNamingAdapt(fIsHoldKey, "Hold", false));
52  pComp->Value(mkNamingAdapt(iRepeatDelay, "RepeatDelay", 0));
53  pComp->Value(mkNamingAdapt(iInitialRepeatDelay, "InitialRepeatDelay", 0));
54  pComp->Value(mkNamingAdapt(fDefaultDisabled, "DefaultDisabled", false));
55  pComp->Value(mkNamingAdapt(idControlExtraData, "ExtraData", C4ID::None));
56  const StdEnumEntry<CoordinateSpace> CoordSpaceNames[] =
57  {
58  { "Game", COS_Game },
59  { "Viewport", COS_Viewport },
60  { nullptr, COS_Game }
61  };
62  pComp->Value(mkNamingAdapt(mkEnumAdapt<CoordinateSpace, int32_t>(eCoordSpace, CoordSpaceNames), "CoordinateSpace", COS_Game));
63  pComp->Value(mkNamingAdapt(fSendCursorPos, "SendCursorPos", false));
64  const StdEnumEntry<Actions> ActionNames[] =
65  {
66  { "None", CDA_None },
67  { "Script", CDA_Script },
68  { "Menu", CDA_Menu },
69  { "MenuOK", CDA_MenuOK },
70  { "MenuCancel", CDA_MenuCancel },
71  { "MenuLeft", CDA_MenuLeft },
72  { "MenuUp", CDA_MenuUp },
73  { "MenuRight", CDA_MenuRight },
74  { "MenuDown", CDA_MenuDown },
75  { "ObjectMenuTextComplete", CDA_ObjectMenuTextComplete },
76  { "ObjectMenuOK", CDA_ObjectMenuOK },
77  { "ObjectMenuOKAll", CDA_ObjectMenuOKAll },
78  { "ObjectMenuSelect",CDA_ObjectMenuSelect },
79  { "ObjectMenuCancel",CDA_ObjectMenuCancel },
80  { "ObjectMenuLeft", CDA_ObjectMenuLeft },
81  { "ObjectMenuUp", CDA_ObjectMenuUp },
82  { "ObjectMenuRight", CDA_ObjectMenuRight },
83  { "ObjectMenuDown", CDA_ObjectMenuDown },
84  { "ZoomIn", CDA_ZoomIn },
85  { "ZoomOut", CDA_ZoomOut },
86  { nullptr, CDA_None }
87  };
88  pComp->Value(mkNamingAdapt(mkEnumAdapt<Actions, int32_t>(eAction, ActionNames), "Action", CDA_Script));
89  pComp->NameEnd();
90 }
91 
93 {
94  return sIdentifier == cmp.sIdentifier
95  && sGUIName == cmp.sGUIName
96  && sGUIDesc == cmp.sGUIDesc
97  && fGlobal == cmp.fGlobal
98  && fIsHoldKey == cmp.fIsHoldKey
99  && iRepeatDelay == cmp.iRepeatDelay
100  && iInitialRepeatDelay == cmp.iInitialRepeatDelay
101  && fDefaultDisabled == cmp.fDefaultDisabled
102  && idControlExtraData == cmp.idControlExtraData
103  && fSendCursorPos == cmp.fSendCursorPos
104  && eAction == cmp.eAction;
105 }
106 
107 
108 /* C4PlayerControlDefs */
109 
111 {
117 }
118 
120 {
121  clear_previous = false;
122  Defs.clear();
124 }
125 
127 {
128  pComp->Value(mkNamingAdapt(clear_previous, "ClearPrevious", false));
130  if (pComp->isDeserializer()) UpdateInternalCons();
131 }
132 
134 {
135  // Clear previous defs if specified in merge set
136  if (Src.clear_previous) Defs.clear();
137  // copy all defs from source file; overwrite defs of same name if found
138  for (DefVecImpl::const_iterator i = Src.Defs.begin(); i != Src.Defs.end(); ++i)
139  {
140  const C4PlayerControlDef &SrcDef = *i;
141  // overwrite if def of same name existed
142  int32_t iPrevIdx = GetControlIndexByIdentifier(SrcDef.GetIdentifier());
143  if (iPrevIdx != CON_None)
144  {
145  Defs[iPrevIdx] = SrcDef;
146  }
147  else
148  {
149  // new def: Append a copy
150  Defs.push_back(SrcDef);
151  }
152  }
154 }
155 
157 {
158  // safe index
159  if (idx<0 || idx>=int32_t(Defs.size())) return nullptr;
160  return &(Defs[idx]);
161 }
162 
163 int32_t C4PlayerControlDefs::GetControlIndexByIdentifier(const char *szIdentifier) const
164 {
165  for (DefVecImpl::const_iterator i = Defs.begin(); i != Defs.end(); ++i)
166  if (SEqual((*i).GetIdentifier(), szIdentifier))
167  return i-Defs.begin();
168  return CON_None;
169 }
170 
172 {
173  // Assume all defs have been loaded
174  // Register scritp constants
175  for (DefVecImpl::const_iterator i = Defs.begin(); i != Defs.end(); ++i)
176  {
177  const char *szIdtf = (*i).GetIdentifier();
178  if (szIdtf && *szIdtf && !SEqual(szIdtf, "None"))
179  {
180  ::ScriptEngine.RegisterGlobalConstant(FormatString("CON_%s", szIdtf).getData(), C4VInt(i-Defs.begin()));
181  }
182  }
183 }
184 
185 
186 /* C4PlayerControlAssignment */
187 
189 {
190  // if key is compiled, also store as a string into KeyName for later resolving
191  if (pComp->isDeserializer())
192  {
193  Key.dwShift = 0;
194  sKeyName.Clear();
195  pComp->Value(mkParAdapt(Key, &sKeyName));
196  }
197  else
198  {
199  // decompiler: If there's a stored key name, just write it. Regardless of whether it's a key, undefined or a reference
200  // If no key name is stored, it was probably assigned at runtime and sKeyName needs to be recreated
201  if (!sKeyName) UpdateKeyName();
202  pComp->Value(mkParAdapt(sKeyName, StdCompiler::RCT_Idtf));
203  }
204 }
205 
206 void C4PlayerControlAssignment::KeyComboItem::UpdateKeyName()
207 {
208  // update key name from key
209  sKeyName.Copy(Key.ToString(false, false));
210  if (Key.dwShift)
211  sKeyName.Take(FormatString("%s+%s", C4KeyCodeEx::KeyShift2String((C4KeyShiftState) Key.dwShift).getData(), sKeyName.getData()));
212 }
213 
215 {
216  if (!pComp->Name("Assignment")) { pComp->NameEnd(); pComp->excNotFound("Assignment"); }
217  pComp->Value(mkNamingAdapt(mkSTLContainerAdapt(KeyCombo), "Key", KeyComboVec()));
218  pComp->Value(mkNamingAdapt(fComboIsSequence, "ComboIsSequence", false));
219  pComp->Value(mkNamingAdapt(mkParAdapt(sControlName, StdCompiler::RCT_Idtf), "Control", "None"));
220  pComp->Value(mkNamingAdapt(mkParAdapt(sGUIName, StdCompiler::RCT_All), "GUIName", ""));
221  pComp->Value(mkNamingAdapt(mkParAdapt(sGUIDesc, StdCompiler::RCT_All), "GUIDesc", ""));
222  pComp->Value(mkNamingAdapt(iGUIGroup,"GUIGroup",0));
223  pComp->Value(mkNamingAdapt(fGUIDisabled, "GUIDisabled", false));
224  pComp->Value(mkNamingAdapt(iPriority, "Priority", 0));
225  const StdBitfieldEntry<int32_t> TriggerModeNames[] =
226  {
227  { "Default", CTM_Default },
228  { "Hold", CTM_Hold },
229  { "Release", CTM_Release },
230  { "AlwaysUnhandled", CTM_AlwaysUnhandled },
231  { "ClearRecentKeys", CTM_ClearRecentKeys },
232  { nullptr, 0 }
233  };
234  pComp->Value(mkNamingAdapt(mkBitfieldAdapt< int32_t>(iTriggerMode, TriggerModeNames), "TriggerMode", CTM_Default));
235  pComp->Value(mkNamingAdapt(fOverrideAssignments, "OverrideAssignments", false));
236  pComp->NameEnd();
237  // newly loaded structures are not resolved
238  if (pComp->isDeserializer()) fRefsResolved = false;
239 }
240 
242 {
243  if (inherited_assignment) CopyKeyFrom(*inherited_assignment);
244 }
245 
247 {
248  // no inherited assignment? Then the key is always custom
249  if (!inherited_assignment) return true;
250  // otherwise, compare
251  return KeyCombo != inherited_assignment->KeyCombo || fComboIsSequence != inherited_assignment->fComboIsSequence;
252 }
253 
255 {
256  // set as one-key-combo
257  KeyCombo.resize(1);
258  KeyCombo[0].Key = key;
259  KeyCombo[0].Key.fRepeated = false;
260  KeyCombo[0].sKeyName.Clear();
261  fComboIsSequence = false;
262  TriggerKey = key;
263 }
264 
266 {
267  // just copy key settings; keep control and priorities
268  KeyCombo = src_assignment.KeyCombo;
269  TriggerKey = src_assignment.TriggerKey;
270  fComboIsSequence = src_assignment.fComboIsSequence;
271  if (!src_assignment.fRefsResolved) fRefsResolved = false;
272 }
273 
275 {
276  // avoid circular chains
277  static int32_t recursion_check = 0;
278  if (recursion_check > 10)
279  {
280  LogFatal(FormatString("Maximum recursion limit reached while resolving player control assignments of set %s in assignment for key %s. This is probably due to a circular control chain.", pParentSet->GetName(), GetControlName()).getData());
281  return false;
282  }
283  ++recursion_check;
284  // resolve control name
285  iControl = pControlDefs->GetControlIndexByIdentifier(sControlName.getData());
286  // resolve keys
287  KeyComboVec NewCombo;
288  for (KeyComboVec::iterator i = KeyCombo.begin(); i != KeyCombo.end(); ++i)
289  {
290  KeyComboItem &rKeyComboItem = *i;
291  const char *szKeyName = rKeyComboItem.sKeyName.getData();
292  // check if this is a key reference. A key reference must be preceded by CON_
293  // it may also be preceded by modifiers (Shift+), which are already set in rKeyComboItem.Key.dwShift
294  bool is_key_reference = false;
295  int last_shift_delim_pos;
296  if (szKeyName && *szKeyName)
297  {
298  if ((last_shift_delim_pos=SCharLastPos('+', szKeyName)) > -1) szKeyName += last_shift_delim_pos+1;
299  if (SEqual2(szKeyName, "CON_"))
300  {
301  is_key_reference = true;
302  szKeyName +=4;
303  }
304  }
305  if (is_key_reference)
306  {
307  // this is a key reference
308  // - find referenced target assignment
309  C4PlayerControlAssignment *pRefAssignment = pParentSet->GetAssignmentByControlName(szKeyName);
310  if (pRefAssignment)
311  {
312  // resolve itself if necessary
313  if (!pRefAssignment->IsRefsResolved()) if (!pRefAssignment->ResolveRefs(pParentSet, pControlDefs)) { --recursion_check; return false; }
314  // insert all keys of that combo into own combo
315  // add any extra shift states from reference
316  DWORD ref_shift = rKeyComboItem.Key.dwShift;
317  if (ref_shift)
318  {
319  for (KeyComboVec::iterator j = pRefAssignment->KeyCombo.begin(); j != pRefAssignment->KeyCombo.end(); ++j)
320  {
321  KeyComboItem assignment_combo_item = *j;
322  assignment_combo_item.Key.dwShift |= ref_shift;
323  NewCombo.push_back(assignment_combo_item);
324  }
325  }
326  else
327  {
328  NewCombo.insert(NewCombo.end(), pRefAssignment->KeyCombo.begin(), pRefAssignment->KeyCombo.end());
329  }
330  }
331  else
332  {
333  // undefined reference? Not fatal, but inform user
334  LogF("WARNING: Control %s of set %s contains reference to unassigned control %s.", GetControlName(), pParentSet->GetName(), rKeyComboItem.sKeyName.getData());
335  NewCombo.clear();
336  }
337  }
338  else
339  {
340  // non-reference: check if the assignment was valid
341 #ifndef USE_CONSOLE
342  if (rKeyComboItem.Key == KEY_Default)
343  LogF("WARNING: Control %s of set %s contains undefined key \"%s\".", GetControlName(), pParentSet->GetName(), szKeyName);
344 #endif
345  // ...and just keep this item.
346  NewCombo.push_back(rKeyComboItem);
347  }
348  }
349  KeyCombo = NewCombo;
350  // adjust Control and Shift into key states for non-sequence combo keys
351  // e.g. LeftControl,A should become LeftControl,Ctrl+A.
352  if (KeyCombo.size() > 1 && !fComboIsSequence)
353  {
354  int32_t shift = 0;
355  for (KeyComboVec::iterator i = KeyCombo.begin(); i != KeyCombo.end(); ++i)
356  {
357  if (i->Key.Key == K_CONTROL_L || i->Key.Key == K_CONTROL_R) shift |= KEYS_Control;
358  if (i->Key.Key == K_SHIFT_L || i->Key.Key == K_SHIFT_R) shift |= KEYS_Shift;
359  shift |= i->Key.dwShift;
360  }
361  for (KeyComboVec::iterator i = KeyCombo.begin(); i != KeyCombo.end(); ++i) i->Key.dwShift |= shift;
362  }
363  // remove control/shift duplications
364  for (KeyComboVec::iterator i = KeyCombo.begin(); i != KeyCombo.end(); ++i) i->Key.FixShiftKeys();
365  // the trigger key is always last of the chain
366  if (KeyCombo.size()) TriggerKey = KeyCombo.back().Key; else TriggerKey = C4KeyCodeEx();
367  // done
368  fRefsResolved = true;
369  --recursion_check;
370  return true;
371 }
372 
374 {
375  assert(HasCombo());
376  // check if combo is currently fulfilled (assuming TriggerKey is already matched)
377  if (fComboIsSequence)
378  {
380  // combo is a sequence: The last keys of RecentKeys must match the sequence
381  // the last ComboKey is the TriggerKey, which is omitted because it has already been matched and is not to be found in RecentKeys yet
382  KeyComboVec::const_reverse_iterator i = KeyCombo.rbegin()+1;
383  for (C4PlayerControlRecentKeyList::const_reverse_iterator ri = RecentKeys.rbegin(); i!=KeyCombo.rend(); ++ri)
384  {
385  // no more keys pressed but combo didn't end? -> no combo match
386  if (ri == RecentKeys.rend()) return false;
387  const C4PlayerControlRecentKey &rk = *ri;
388  // user waited for too long?
389  C4TimeMilliseconds tKeyRecent = rk.tTime;
390  if (tKeyLast - tKeyRecent > C4PlayerControl::MaxSequenceKeyDelay) return false;
391  // key doesn't match?
392  const KeyComboItem &k = *i;
393  if (!(rk.matched_key == k.Key))
394  {
395  // mouse movement commands do not break sequences
397  return false;
398  }
399  // key OK
400  ++i;
401  }
402  }
403  else
404  {
405  // combo requires keys to be down simultanuously: check that all keys of the combo are in the down-list
406  for (KeyComboVec::const_iterator i = KeyCombo.begin(); i!=KeyCombo.end(); ++i)
407  {
408  const KeyComboItem &k = *i;
409  bool fFound = false;
410  for (C4PlayerControlRecentKeyList::const_iterator di = DownKeys.begin(); di!=DownKeys.end(); ++di)
411  {
412  const C4PlayerControlRecentKey &dk = *di;
413  if (dk.matched_key == k.Key) { fFound = true; break; }
414  }
415  if (!fFound) return false;
416  }
417  }
418  // combo OK!
419  return true;
420 }
421 
423 {
424  // doesn't compare resolved TriggerKey/iControl
425  return KeyCombo == cmp.KeyCombo
426  && sControlName == cmp.sControlName
427  && sGUIName == cmp.sGUIName
428  && sGUIDesc == cmp.sGUIDesc
429  && fGUIDisabled == cmp.fGUIDisabled
430  && iTriggerMode == cmp.iTriggerMode
431  && iPriority == cmp.iPriority;
432 }
433 
434 StdStrBuf C4PlayerControlAssignment::GetKeysAsString(bool human_readable, bool short_name) const
435 {
436  // create a short, human-readable string of the assigned key
437  // to be displayed e.g. in tutorial messages explaining controls
438  StdStrBuf result;
439  if (!KeyCombo.size()) return result;
440  // trigger key
441  KeyComboVec::const_iterator i=KeyCombo.begin();
442  result.Take(i->Key.ToString(human_readable, short_name));
443  // extra keys of combo
444  while (++i != KeyCombo.end())
445  {
446  result.AppendChar(fComboIsSequence ? ',' : '+');
447  result.Append(i->Key.ToString(human_readable, short_name));
448  }
449  return result;
450 }
451 
453 {
454  // local name?
455  if (sGUIName.getLength())
456  {
457  // special: None defaults to empty name
458  if (sGUIName == "None") return "";
459  return sGUIName.getData();
460  }
461  // otherwise, fall back to def
462  const C4PlayerControlDef *def = defs.GetControlByIndex(GetControl());
463  if (def) return def->GetGUIName();
464  // no def and no name...
465  return nullptr;
466 }
467 
469 {
470  // local desc?
471  if (sGUIDesc.getLength()) return sGUIDesc.getData();
472  // otherwise, fall back to def
473  const C4PlayerControlDef *def = defs.GetControlByIndex(GetControl());
474  if (def) return def->GetGUIDesc();
475  // no def and no desc...
476  return nullptr;
477 }
478 
480 {
481  return fGUIDisabled;
482 }
483 
485 {
486  return iGUIGroup;
487 }
488 
489 /* C4PlayerControlAssignmentSet */
490 
492 {
493  // copy all fields except assignments
494  sName.Copy(template_set.sName);
495  sGUIName.Copy(template_set.sGUIName);
496  sParentSetName.Copy(template_set.sParentSetName);
497  has_keyboard = template_set.has_keyboard;
498  has_mouse = template_set.has_mouse;
499  has_gamepad = template_set.has_gamepad;
500 }
501 
503 {
504  if (!pComp->Name("ControlSet")) { pComp->NameEnd(); pComp->excNotFound("ControlSet"); }
505  pComp->Value(mkNamingAdapt(mkParAdapt(sName, StdCompiler::RCT_All), "Name", "None")); // can't do RCT_Idtf because of wildcards
506  pComp->Value(mkNamingAdapt(mkParAdapt(sGUIName, StdCompiler::RCT_All), "GUIName", "undefined"));
507  pComp->Value(mkNamingAdapt(mkParAdapt(sParentSetName, StdCompiler::RCT_Idtf), "Parent", ""));
508  pComp->Value(mkNamingAdapt(has_keyboard, "Keyboard", true));
509  pComp->Value(mkNamingAdapt(has_mouse, "Mouse", true));
510  pComp->Value(mkNamingAdapt(has_gamepad, "Gamepad", false));
511  pComp->Value(mkSTLContainerAdapt(Assignments, StdCompiler::SEP_NONE));
512  pComp->NameEnd();
513 }
514 
515 
516 
518 {
519  // take over all assignments defined in Src
520  for (C4PlayerControlAssignmentVec::const_iterator i = Src.Assignments.begin(); i != Src.Assignments.end(); ++i)
521  {
522  const C4PlayerControlAssignment &SrcAssignment = *i;
523  bool fIsReleaseKey = !!(SrcAssignment.GetTriggerMode() & C4PlayerControlAssignment::CTM_Release);
524  // overwrite same def and release key state
525  if (merge_mode != MM_LowPrio && SrcAssignment.IsOverrideAssignments())
526  {
527  // high priority override control clears all previous (very inefficient method...might as well recreate the whole list)
528  bool any_remaining = true;
529  while (any_remaining)
530  {
531  any_remaining = false;
532  for (C4PlayerControlAssignmentVec::iterator j = Assignments.begin(); j != Assignments.end(); ++j)
533  if (SEqual((*j).GetControlName(), SrcAssignment.GetControlName()))
534  {
535  bool fSelfIsReleaseKey = !!((*j).GetTriggerMode() & C4PlayerControlAssignment::CTM_Release);
536  if (fSelfIsReleaseKey == fIsReleaseKey)
537  {
538  Assignments.erase(j);
539  any_remaining = true;
540  break;
541  }
542  }
543  }
544  }
545  else if (merge_mode == MM_LowPrio || merge_mode == MM_ConfigOverload)
546  {
547  // if this is low priority, another override control kills this
548  bool any_override = false;
549  for (C4PlayerControlAssignmentVec::iterator j = Assignments.begin(); j != Assignments.end(); ++j)
550  if (SEqual((*j).GetControlName(), SrcAssignment.GetControlName()))
551  {
552  bool fSelfIsReleaseKey = !!((*j).GetTriggerMode() & C4PlayerControlAssignment::CTM_Release);
553  if (fSelfIsReleaseKey == fIsReleaseKey)
554  {
555  any_override = true;
556  // config overloads just change the key of the inherited assignment
557  if (merge_mode == MM_ConfigOverload)
558  {
559  (*j).CopyKeyFrom(SrcAssignment);
560  (*j).SetInherited(false);
561  }
562  break;
563  }
564  }
565  if (any_override) continue;
566  }
567  // new def: Append a copy
568  Assignments.push_back(SrcAssignment);
569  // inherited marker
570  if (merge_mode == MM_Inherit)
571  {
572  Assignments.back().SetInherited(true);
573  Assignments.back().SetInheritedAssignment(&SrcAssignment);
574  }
575  }
576 }
577 
579 {
580  Assignments.push_back(C4PlayerControlAssignment());
581  Assignments.back().SetControlName(control_name);
582  return &Assignments.back();
583 }
584 
586 {
587  for (C4PlayerControlAssignmentVec::iterator i = Assignments.begin(); i != Assignments.end(); ++i)
588  if (SEqual((*i).GetControlName(), control_name))
589  {
590  Assignments.erase(i);
591  return;
592  }
593 }
594 
596 {
597  // reset all resolved flags to allow re-resolve after overloads
598  for (C4PlayerControlAssignmentVec::iterator i = Assignments.begin(); i != Assignments.end(); ++i)
599  (*i).ResetRefsResolved();
600  // resolve in order; ignore already resolved because they might have been resolved by cross reference
601  for (C4PlayerControlAssignmentVec::iterator i = Assignments.begin(); i != Assignments.end(); ++i)
602  if (!(*i).IsRefsResolved())
603  if (!(*i).ResolveRefs(this, pDefs))
604  return false;
605  return true;
606 }
607 
609 {
610  // final init: sort assignments by priority
611  // note this screws up sorting for config dialog
612  std::sort(Assignments.begin(), Assignments.end());
613 }
614 
616 {
617  if (index<0 || index>=int32_t(Assignments.size())) return nullptr;
618  return &Assignments[index];
619 }
620 
622 {
623  for (C4PlayerControlAssignmentVec::iterator i = Assignments.begin(); i != Assignments.end(); ++i)
624  if (SEqual((*i).GetControlName(), szControlName))
625  // We don't like release keys... (2do)
626  if (!((*i).GetTriggerMode() & C4PlayerControlAssignment::CTM_Release))
627  return &*i;
628  return nullptr;
629 }
630 
632 {
633  // TODO: Might want to stuff this into a vector indexed by control for faster lookup
634  for (C4PlayerControlAssignmentVec::iterator i = Assignments.begin(); i != Assignments.end(); ++i)
635  if ((*i).GetControl() == control)
636  // We don't like release keys... (2do)
637  if (!((*i).GetTriggerMode() & C4PlayerControlAssignment::CTM_Release))
638  return &*i;
639  return nullptr;
640 }
641 
643 {
644  return Assignments == cmp.Assignments
645  && sName == cmp.sName;
646 }
647 
649 {
650  assert(pOutVec);
651  // primary match by TriggerKey (todo: Might use a hash map here if matching speed becomes an issue due to large control sets)
652  for (C4PlayerControlAssignmentVec::const_iterator i = Assignments.begin(); i != Assignments.end(); ++i)
653  {
654  const C4PlayerControlAssignment &rAssignment = *i;
655  const C4KeyCodeEx &rAssignmentTriggerKey = rAssignment.GetTriggerKey();
656  if (!(rAssignmentTriggerKey.Key == key.Key)) continue;
657  // special: hold-keys-only ignore shift, because shift state might have been release during hold
658  if (!fHoldKeysOnly) if (rAssignmentTriggerKey.dwShift != key.dwShift) continue;
659  // check linked control def
660  const C4PlayerControlDef *pCtrl = rDefs.GetControlByIndex(rAssignment.GetControl());
661  if (!pCtrl) continue;
662  // only want hold keys?
663  if (fHoldKeysOnly)
664  {
665  // a hold/release-trigger key is not a real hold key, even if the underlying control is
667  }
668  else if (rAssignment.HasCombo())
669  {
670  // hold-only events match the trigger key only (i.e., Release-events are generated as soon as the trigger key goes up)
671  // other events must match either the sequence or the down-key-combination
672  if (!rAssignment.IsComboMatched(DownKeys, RecentKeys)) continue;
673  }
674  // we got match! Store it
675  pOutVec->push_back(&rAssignment);
676  }
677 }
678 
680 {
681  // put all trigger keys of keyset into output vectors
682  // first all hold keys
683  for (C4PlayerControlAssignmentVec::const_iterator i = Assignments.begin(); i != Assignments.end(); ++i)
684  {
685  const C4PlayerControlAssignment &rAssignment = *i;
686  const C4PlayerControlDef *pDef = rDefs.GetControlByIndex(rAssignment.GetControl());
687  if (pDef && pDef->IsHoldKey())
688  {
689  const C4KeyCodeEx &rKey = rAssignment.GetTriggerKey();
690  if (std::find(pHoldKeys->begin(), pHoldKeys->end(), rKey) == pHoldKeys->end()) pHoldKeys->push_back(rKey);
691  }
692  }
693  // then all regular keys that aren't in the hold keys list yet
694  for (C4PlayerControlAssignmentVec::const_iterator i = Assignments.begin(); i != Assignments.end(); ++i)
695  {
696  const C4PlayerControlAssignment &rAssignment = *i;
697  const C4PlayerControlDef *pDef = rDefs.GetControlByIndex(rAssignment.GetControl());
698  if (pDef && !pDef->IsHoldKey())
699  {
700  const C4KeyCodeEx &rKey = rAssignment.GetTriggerKey();
701  if (std::find(pHoldKeys->begin(), pHoldKeys->end(), rKey) == pHoldKeys->end())
702  if (std::find(pRegularKeys->begin(), pRegularKeys->end(), rKey) == pRegularKeys->end())
703  pRegularKeys->push_back(rKey);
704  }
705  }
706 }
707 
709 {
710  // get image to be drawn to represent this control set
711  // picture per set not implemented yet. So just default to out standard images
713 // if (HasMouse()) return ::GraphicsResource.fctMouse; // might be useful again with changing control sets
715  return C4Facet();
716 }
717 
719 {
720  // TODO
721  return true;
722 }
723 
724 
725 /* C4PlayerControlAssignmentSets */
726 
728 {
729  Sets.clear();
730 }
731 
733 {
734  if (pComp->isSerializer() && pComp->isRegistry())
735  {
736  pComp->Default("ControlSets"); // special registry compiler: Clean out everything before
737  }
738  pComp->Value(mkNamingAdapt(clear_previous, "ClearPrevious", false));
740 }
741 
743 {
744  return Sets == cmp.Sets && clear_previous == cmp.clear_previous;
745 }
746 
748 {
749  // if source set is flagged to clear previous, do this!
750  if (Src.clear_previous) Sets.clear();
751  // take over all assignments in known sets and new sets defined in Src
752  for (AssignmentSetList::const_iterator i = Src.Sets.begin(); i != Src.Sets.end(); ++i)
753  {
754  const C4PlayerControlAssignmentSet &SrcSet = *i;
755  // overwrite if def of same name existed if it's not low priority anyway
756  bool fIsWildcardSet = SrcSet.IsWildcardName();
757  if (!fIsWildcardSet)
758  {
759  C4PlayerControlAssignmentSet *pPrevSet = GetSetByName(SrcSet.GetName());
760  if (!pPrevSet && merge_mode == C4PlayerControlAssignmentSet::MM_Inherit)
761  {
762  // inherited sets must go through merge procedure to set inherited links
763  pPrevSet = CreateEmptySetByTemplate(SrcSet);
764  }
765  if (pPrevSet)
766  {
767  pPrevSet->MergeFrom(SrcSet, merge_mode);
768  }
769  else
770  {
771  // new def: Append a copy
772  Sets.push_back(SrcSet);
773  }
774  }
775  else
776  {
777  // source is a wildcard: Merge with all matching sets
778  for (AssignmentSetList::iterator j = Sets.begin(); j != Sets.end(); ++j)
779  {
780  C4PlayerControlAssignmentSet &DstSet = *j;
781  if (WildcardMatch(SrcSet.GetName(), DstSet.GetName()))
782  {
783  DstSet.MergeFrom(SrcSet, merge_mode);
784  }
785  }
786  }
787  }
788 }
789 
791 {
792  for (AssignmentSetList::iterator i = Sets.begin(); i != Sets.end(); ++i)
793  if (!(*i).ResolveRefs(pDefs)) return false;
794  return true;
795 }
796 
798 {
799  for (AssignmentSetList::iterator i = Sets.begin(); i != Sets.end(); ++i)
800  (*i).SortAssignments();
801 }
802 
804 {
805  for (AssignmentSetList::iterator i = Sets.begin(); i != Sets.end(); ++i)
806  if (WildcardMatch(szName, (*i).GetName()))
807  return &*i;
808  return nullptr;
809 }
810 
812 {
813  // default set is first defined control set
814  if (Sets.empty()) return nullptr; // nothing defined :(
815  return &Sets.front();
816 }
817 
819 {
820  // find set in list; return index
821  int32_t index = 0;
822  for (AssignmentSetList::const_iterator i = Sets.begin(); i != Sets.end(); ++i,++index)
823  if (&*i == set)
824  return index;
825  return -1; // not found
826 }
827 
829 {
830  // bounds check
831  if (index < 0 || index >= (int32_t)Sets.size()) return nullptr;
832  // return indexed set
833  AssignmentSetList::iterator i = Sets.begin();
834  while (index--) ++i;
835  return &*i;
836 }
837 
839 {
840  Sets.push_back(C4PlayerControlAssignmentSet());
841  Sets.back().InitEmptyFromTemplate(template_set);
842  return &Sets.back();
843 }
844 
846 {
847  for (AssignmentSetList::iterator i = Sets.begin(); i != Sets.end(); ++i)
848  if (SEqual(set_name, (*i).GetName()))
849  {
850  Sets.erase(i);
851  return;
852  }
853 }
854 
855 
856 /* C4PlayerControlFile */
857 
859 {
860  pComp->Value(mkNamingAdapt(ControlDefs, "ControlDefs", C4PlayerControlDefs()));
861  pComp->Value(mkNamingAdapt(AssignmentSets, "ControlSets", C4PlayerControlAssignmentSets()));
862 }
863 
864 bool C4PlayerControlFile::Load(C4Group &hGroup, const char *szFilename, C4LangStringTable *pLang)
865 {
866  // clear previous
867  Clear();
868  // load and prepare file contents
869  StdStrBuf Buf;
870  if (!hGroup.LoadEntryString(szFilename, &Buf)) return false;
871  if (pLang) pLang->ReplaceStrings(Buf);
872  // parse it!
873  if (!CompileFromBuf_LogWarn<StdCompilerINIRead>(*this, Buf, szFilename)) return false;
874  return true;
875 }
876 
877 bool C4PlayerControlFile::Save(C4Group &hGroup, const char *szFilename)
878 {
879  // decompile to buffer and save buffer to group
880  StdStrBuf Buf;
881  if (!DecompileToBuf_Log<StdCompilerINIWrite>(*this, &Buf, szFilename)) return false;
882  hGroup.Add(szFilename, Buf, false, true);
883  return true;
884 }
885 
887 {
888  ControlDefs.Clear();
889  AssignmentSets.Clear();
890 }
891 
892 
893 /* C4PlayerControl */
894 
896 {
897  pComp->Value(DownState);
898  pComp->Separator();
899  pComp->Value(MovedState);
900  pComp->Separator();
901  pComp->Value(iDownFrame);
902  pComp->Separator();
903  pComp->Value(iMovedFrame);
904  pComp->Separator();
905  pComp->Value(fDownByUser);
906 }
907 
909 {
910  return DownState == cmp.DownState && MovedState == cmp.MovedState && iDownFrame == cmp.iDownFrame && iMovedFrame == cmp.iMovedFrame && fDownByUser == cmp.fDownByUser;
911 }
912 
914 {
915  // safe access
916  if (iControl < 0 || iControl >= int32_t(ControlDownStates.size())) return nullptr;
917  return &ControlDownStates[iControl];
918 }
919 
920 int32_t C4PlayerControl::CSync::GetControlDisabled(int32_t iControl) const
921 {
922  // safe access
923  if (iControl < 0 || iControl >= int32_t(ControlDisableStates.size())) return 0;
924  return ControlDisableStates[iControl];
925 }
926 
927 void C4PlayerControl::CSync::SetControlDownState(int32_t iControl, const C4KeyEventData &rDownState, int32_t iDownFrame, bool fDownByUser)
928 {
929  // update state
930  if (iControl < 0) return;
931  if (iControl >= int32_t(ControlDownStates.size())) ControlDownStates.resize(iControl+1);
932  ControlDownState &rState = ControlDownStates[iControl];
933  rState.DownState = rDownState;
934  rState.iDownFrame = iDownFrame;
935  rState.fDownByUser = fDownByUser;
936 }
937 
938 void C4PlayerControl::CSync::SetControlMovedState(int32_t iControl, const C4KeyEventData &rMovedState, int32_t iMovedFrame)
939 {
940  // update state
941  if (iControl < 0) return;
942  if (iControl >= int32_t(ControlDownStates.size())) ControlDownStates.resize(iControl+1);
943  ControlDownState &rState = ControlDownStates[iControl];
944  rState.MovedState = rMovedState;
945  rState.iMovedFrame = iMovedFrame;
946 }
947 
948 bool C4PlayerControl::CSync::SetControlDisabled(int32_t iControl, int32_t iVal)
949 {
950  // disable control
951  if (iControl < 0) return false;
952  if (iControl >= int32_t(ControlDisableStates.size())) ControlDisableStates.resize(iControl+1);
953  ControlDisableStates[iControl] = iVal;
954  // if a control is disabled, its down-state is reset silently
955  ResetControlDownState(iControl);
956  return true;
957 }
958 
960 {
961  // silently reset down state of control
962  const ControlDownState *pDownState = GetControlDownState(iControl);
963  if (pDownState && pDownState->IsDown())
964  {
965  C4KeyEventData KeyDownState = pDownState->DownState;
966  KeyDownState.iStrength = 0;
967  SetControlDownState(iControl, KeyDownState, 0, false);
968  SetControlMovedState(iControl, KeyDownState, 0);
969  }
970 }
971 
973 {
974  const C4PlayerControlDef *def;
975  int32_t i=0;
976  while ((def = ControlDefs.GetControlByIndex(i)))
977  {
978  if (def->IsDefaultDisabled()) SetControlDisabled(i, true);
979  ++i;
980  }
981 }
982 
984 {
985  ControlDownStates.clear();
986  ControlDisableStates.clear();
987 }
988 
990 {
993 }
994 
996 {
999 }
1000 
1002 {
1003  // defaultdisabled controls
1004  Sync.InitDefaults(ControlDefs);
1005 }
1006 
1008 {
1009  // compile sync values only
1010  CSync DefaultSync;
1011  DefaultSync.InitDefaults(ControlDefs);
1012  pComp->Value(mkNamingAdapt(Sync, "PlayerControl", DefaultSync));
1013 }
1014 
1015 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)
1016 {
1017  if (Key_IsGamepad(pressed_key.Key))
1018  {
1019  // We have to filter gamepad events here.
1020  C4Player *plr = ::Players.Get(iPlr);
1021  if (!plr || !plr->pGamepad || plr->pGamepad->GetID() != pressed_key.deviceId)
1022  return false;
1023  }
1024  // collect all matching keys
1026  assert(pControlSet); // shouldn't get this callback for players without control set
1027  pControlSet->GetAssignmentsByKey(ControlDefs, matched_key, state != CONS_Down, &Matches, DownKeys, RecentKeys);
1028  // process async controls
1029  bool cursor_pos_added = false;
1030  C4ControlPlayerControl *pControlPacket = nullptr;
1031  for (C4PlayerControlAssignmentPVec::const_iterator i = Matches.begin(); i != Matches.end(); ++i)
1032  {
1033  const C4PlayerControlAssignment *pAssignment = *i;
1034  assert(pAssignment);
1035  int32_t iControlIndex = pAssignment->GetControl();
1036  const C4PlayerControlDef *pControlDef = ControlDefs.GetControlByIndex(iControlIndex);
1037  if (pControlDef && pControlDef->IsValid() && !Sync.IsControlDisabled(iControlIndex) && (state == CONS_Down || pControlDef->IsHoldKey()))
1038  {
1039  // clear RecentKeys if requested by this assignment. Must be done before sync queue, so multiple combos can be issued in a single control frame.
1040  if (clear_recent_keys && (pAssignment->GetTriggerMode() & C4PlayerControlAssignment::CTM_ClearRecentKeys)) *clear_recent_keys = true;
1041  // extra data from key or overwrite by current cursor pos if definition requires it
1042  if (pControlDef->IsAsync() && !pControlPacket)
1043  {
1044  if (pControlDef->IsSendCursorPos()) IsCursorPosRequested = true; // async cursor pos request - doesn't really make sense to set this flag for async controls
1045  if (ExecuteControl(iControlIndex, state, rKeyExtraData, pAssignment->GetTriggerMode(), pressed_key.IsRepeated(), reset_down_states_only))
1046  return true;
1047  }
1048  else
1049  {
1050  // sync control
1051  // ignore key repeats, because we do our own key repeat for sync controls
1052  if (pressed_key.IsRepeated()) return false;
1053  // sync control has higher priority - no more async execution then
1054  // 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
1055  if (!pControlPacket) pControlPacket = new C4ControlPlayerControl(iPlr, state, rKeyExtraData);
1056  int32_t extra_trigger_mode = 0;
1057  if (reset_down_states_only) extra_trigger_mode |= C4PlayerControlAssignment::CTM_HandleDownStatesOnly;
1058  pControlPacket->AddControl(iControlIndex, pAssignment->GetTriggerMode() | extra_trigger_mode);
1059  // sync cursor pos request; pos will be added to control before it is synced/executed
1060  if (pControlDef->IsSendCursorPos() && !cursor_pos_added)
1061  {
1062  int32_t x, y, game_x, game_y;
1063  // Add current cursor pos in GUI and game coordinates to input
1064  if (GetCurrentPlayerCursorPos(&x, &y, &game_x, &game_y))
1065  {
1066  C4KeyEventData cursor_key_data(rKeyExtraData);
1067  cursor_key_data.vp_x = x; cursor_key_data.vp_y = y;
1068  cursor_key_data.game_x = game_x; cursor_key_data.game_y = game_y;
1069  pControlPacket->SetExtraData(cursor_key_data);
1070  }
1071  // Will also send a CON_CursorPos packet separately
1072  IsCursorPosRequested = true;
1073  cursor_pos_added = true;
1074  }
1075  }
1076  }
1077  }
1078  // push sync control to input
1079  if (pControlPacket)
1080  {
1081  Game.Input.Add(CID_PlrControl, pControlPacket);
1082  // assume processed (although we can't really know that yet)
1083  return true;
1084  }
1085  return false;
1086 }
1087 
1088 bool C4PlayerControl::ProcessKeyDown(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key)
1089 {
1090  // add key to local "down" list if it's not already in there
1091  // except for some mouse events for which a down state does not make sense
1092  C4PlayerControlRecentKey RKey(pressed_key,matched_key,C4TimeMilliseconds::Now());
1093  if (!Key_IsMouse(pressed_key.Key) || Inside<uint8_t>(Key_GetMouseEvent(pressed_key.Key), KEY_MOUSE_Button1, KEY_MOUSE_ButtonMax))
1094  {
1095  if (std::find(DownKeys.begin(), DownKeys.end(), pressed_key) == DownKeys.end()) DownKeys.push_back(RKey);
1096  }
1097  // process!
1098  bool clear_recent_keys = false;
1099  bool fResult = ProcessKeyEvent(pressed_key, matched_key, CONS_Down, Game.KeyboardInput.GetLastKeyExtraData(), false, &clear_recent_keys);
1100  // unless assignment requests a clear, always add keys to recent list even if not handled
1101  if (clear_recent_keys)
1102  RecentKeys.clear();
1103  else if (!pressed_key.IsRepeated()) // events caused by holding down the key are not added to recent list (so you cannot cause "double-Q" just by holding down Q)
1104  RecentKeys.push_back(RKey);
1105  return fResult;
1106 }
1107 
1108 bool C4PlayerControl::ProcessKeyUp(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key)
1109 {
1110  // remove key from "down" list
1111  // except for some mouse events for which a down state does not make sense
1112  if (!Key_IsMouse(pressed_key.Key) || Inside<uint8_t>(Key_GetMouseEvent(pressed_key.Key), KEY_MOUSE_Button1, KEY_MOUSE_ButtonMax))
1113  {
1114  C4PlayerControlRecentKeyList::iterator i = find(DownKeys.begin(), DownKeys.end(), pressed_key);
1115  if (i != DownKeys.end()) DownKeys.erase(i);
1116  }
1117  // process!
1118  return ProcessKeyEvent(pressed_key, matched_key, CONS_Up, Game.KeyboardInput.GetLastKeyExtraData());
1119 }
1120 
1121 bool C4PlayerControl::ProcessKeyMoved(const C4KeyCodeEx &pressed_key, const C4KeyCodeEx &matched_key)
1122 {
1123  // process!
1124  return ProcessKeyEvent(pressed_key, matched_key, CONS_Moved, Game.KeyboardInput.GetLastKeyExtraData());
1125 }
1126 
1128 {
1129  // callback from control queue. Execute controls in packet until one of them gets processed
1130  // assume async packets always as not processed to ensure sync safety (usually, sync commands should better not ovberride async commands anyway)
1131  bool fHandleDownStateOnly = false;
1132  for (C4ControlPlayerControl::ControlItemVec::const_iterator i = pCtrl->GetControlItems().begin(); i != pCtrl->GetControlItems().end(); ++i)
1133  {
1134  const C4ControlPlayerControl::ControlItem &rItem = *i;
1135  const C4PlayerControlDef *pCtrlDef = ControlDefs.GetControlByIndex(rItem.iControl);
1136  if (pCtrlDef)
1137  {
1138  if (Config.General.DebugRec)
1139  {
1140  if (pCtrlDef->IsSync())
1141  {
1142  AddDbgRec(RCT_PlrCom, &rItem.iControl, sizeof(rItem.iControl));
1143  }
1144  }
1145  if (ExecuteControl(rItem.iControl, pCtrl->GetState(), pCtrl->GetExtraData(), rItem.iTriggerMode, false, fHandleDownStateOnly))
1146  if (pCtrlDef->IsSync())
1147  {
1148  if (pCtrl->GetState() == CONS_Up)
1149  {
1150  // control processed. however, for key releases, overriden keys are released silently so following down events aren't handled as key repeats
1151  // note this does not affect CTM_Hold/CTM_Release, because they ignore release controls anyway
1152  fHandleDownStateOnly = true;
1153  }
1154  else
1155  {
1156  break;
1157  }
1158  }
1159  }
1160  }
1161 }
1162 
1163 bool C4PlayerControl::ExecuteControl(int32_t iControl, ControlState state, const C4KeyEventData &rKeyExtraData, int32_t iTriggerMode, bool fRepeated, bool fHandleDownStateOnly)
1164 {
1165  // execute single control. return if handled
1166  const C4PlayerControlDef *pControlDef = ControlDefs.GetControlByIndex(iControl);
1167  if (!pControlDef || Sync.IsControlDisabled(iControl)) return false;
1168  C4PlayerControlDef::Actions eAction = pControlDef->GetAction();
1169  C4KeyEventData KeyExtraData(rKeyExtraData);
1170  const CSync::ControlDownState *pCtrlDownState = Sync.GetControlDownState(iControl);
1171  bool fWasDown = pCtrlDownState ? pCtrlDownState->IsDown() : false;
1172  // global controls only in global context
1173  if (IsGlobal() != pControlDef->IsGlobal()) return false;
1174  // down state handling only?
1175  if (iTriggerMode & C4PlayerControlAssignment::CTM_HandleDownStatesOnly) fHandleDownStateOnly = true;
1176  // hold-actions only work on script controls with the hold flag
1178  {
1179  if (eAction != C4PlayerControlDef::CDA_Script) return false;
1180  if (!pControlDef->IsHoldKey()) return false;
1181  if (state == CONS_Up) return false; // hold triggers have no "up"-event
1182  // perform hold/release
1183  if (fWasDown)
1184  {
1185  // control is currently down: release?
1186  if (iTriggerMode & C4PlayerControlAssignment::CTM_Release)
1187  {
1188  KeyExtraData.iStrength = 0;
1189  Sync.SetControlDownState(iControl, KeyExtraData, Game.FrameCounter, false);
1190  // now process as a regular "Up" event
1191  state = CONS_Up;
1192  fRepeated = false;
1193  }
1194  else
1195  {
1196  assert(iTriggerMode & C4PlayerControlAssignment::CTM_Hold);
1197  // control is down but trigger key is pressed again: Refresh down state
1198  // (this will restart the KeyRepeat time)
1199  Sync.SetControlDownState(iControl, KeyExtraData, Game.FrameCounter, false);
1200  // now process as a regular, repeated "down" event
1201  fRepeated = true;
1202  }
1203  }
1204  else
1205  {
1206  // control is currently up. Put into hold-down-state if this is a hold key
1207  if (iTriggerMode & C4PlayerControlAssignment::CTM_Hold)
1208  {
1209  Sync.SetControlDownState(iControl, KeyExtraData, Game.FrameCounter, false);
1210  // now process as a regular "down" event
1211  fRepeated = false;
1212  }
1213  else
1214  {
1215  //. Ignore if it's only a release key
1216  return false;
1217  }
1218  }
1219  }
1220  else if (state == CONS_Up)
1221  {
1222  // regular ControlUp: Only valid if that control was down
1223  if (!fWasDown) return false;
1224  Sync.SetControlDownState(iControl, KeyExtraData, Game.FrameCounter, true);
1225  }
1226  else if (pControlDef->IsHoldKey())
1227  {
1228  if (state == CONS_Moved)
1229  {
1230  Sync.SetControlMovedState(iControl, KeyExtraData, Game.FrameCounter);
1231  fRepeated = true;
1232  }
1233  else
1234  {
1235  // regular ControlDown on Hold Key: Set in down list
1236  Sync.SetControlDownState(iControl, KeyExtraData, Game.FrameCounter, true);
1237  fRepeated = fWasDown;
1238  }
1239  }
1240  // down state handling done
1241  if (fHandleDownStateOnly) return false;
1242  // perform action for this control
1243  bool fHandled = ExecuteControlAction(iControl, eAction, pControlDef->GetExtraData(), state, KeyExtraData, fRepeated);
1244  // handled controls hide control display
1245  C4Player *pPlr;
1246  if ((pPlr = ::Players.Get(iPlr))) if (pPlr->ShowStartup) pPlr->ShowStartup = false;
1247  // return if handled, unless control is defined as always unhandled
1248  return fHandled && !(iTriggerMode & C4PlayerControlAssignment::CTM_AlwaysUnhandled);
1249 }
1250 
1251 bool C4PlayerControl::ExecuteControlAction(int32_t iControl, C4PlayerControlDef::Actions eAction, C4ID idControlExtraData, ControlState state, const C4KeyEventData &rKeyExtraData, bool fRepeated)
1252 {
1253  // moved events don't make sense for menus and are only handled by script
1254  if (state == CONS_Moved && eAction != C4PlayerControlDef::CDA_Script) return false;
1255  // get affected player
1256  C4Player *pPlr = nullptr;
1257  C4Viewport *pVP;
1258  C4Object *pCursor = nullptr;
1259  C4Menu *pCursorMenu = nullptr;
1260  if (iPlr > -1)
1261  {
1262  pPlr = ::Players.Get(iPlr);
1263  if (!pPlr) return false;
1264  pCursor = pPlr->Cursor;
1265  if (pCursor && pCursor->Menu && pCursor->Menu->IsActive()) pCursorMenu = pCursor->Menu;
1266  }
1267  bool fUp = state == CONS_Up;
1268  // exec action (on player)
1269  switch (eAction)
1270  {
1271  // scripted player control
1273  return ExecuteControlScript(iControl, idControlExtraData, state, rKeyExtraData, fRepeated);
1274 
1275  // menu controls
1276  case C4PlayerControlDef::CDA_Menu: if (!pPlr || fUp) return false; if (pPlr->Menu.IsActive()) pPlr->Menu.TryClose(false, true); else pPlr->ActivateMenuMain(); return true; // toggle
1277  case C4PlayerControlDef::CDA_MenuOK: if (!pPlr || !pPlr->Menu.IsActive() || fUp) return false; pPlr->Menu.Control(COM_MenuEnter,0); return true; // ok on item
1278  case C4PlayerControlDef::CDA_MenuCancel: if (!pPlr || !pPlr->Menu.IsActive() || fUp) return false; pPlr->Menu.Control(COM_MenuClose,0); return true; // close menu
1279  case C4PlayerControlDef::CDA_MenuLeft: if (!pPlr || !pPlr->Menu.IsActive() || fUp) return false; pPlr->Menu.Control(COM_MenuLeft ,0); return true; // navigate
1280  case C4PlayerControlDef::CDA_MenuUp: if (!pPlr || !pPlr->Menu.IsActive() || fUp) return false; pPlr->Menu.Control(COM_MenuUp ,0); return true; // navigate
1281  case C4PlayerControlDef::CDA_MenuRight: if (!pPlr || !pPlr->Menu.IsActive() || fUp) return false; pPlr->Menu.Control(COM_MenuRight,0); return true; // navigate
1282  case C4PlayerControlDef::CDA_MenuDown: if (!pPlr || !pPlr->Menu.IsActive() || fUp) return false; pPlr->Menu.Control(COM_MenuDown,0); return true; // navigate
1283  case C4PlayerControlDef::CDA_ObjectMenuTextComplete: if (!pCursorMenu || fUp || !pCursorMenu->IsTextProgressing()) return false; pCursorMenu->Control(COM_MenuShowText,0); return true; // fast-foward text display
1284  case C4PlayerControlDef::CDA_ObjectMenuOK: if (!pCursorMenu || fUp) return false; pCursorMenu->Control(COM_MenuEnter,0); return true; // ok on item
1285  case C4PlayerControlDef::CDA_ObjectMenuOKAll: if (!pCursorMenu || fUp) return false; pCursorMenu->Control(COM_MenuEnterAll,0); return true; // alt ok on item
1286  case C4PlayerControlDef::CDA_ObjectMenuSelect: if (!pCursorMenu || fUp) return false; pCursorMenu->Control(COM_MenuSelect,rKeyExtraData.iStrength); return true; // select an item directly
1287  case C4PlayerControlDef::CDA_ObjectMenuCancel: if (!pCursorMenu || fUp) return false; pCursorMenu->Control(COM_MenuClose,0); return true; // close menu
1288  case C4PlayerControlDef::CDA_ObjectMenuLeft: if (!pCursorMenu || fUp) return false; pCursorMenu->Control(COM_MenuLeft ,0); return true; // navigate
1289  case C4PlayerControlDef::CDA_ObjectMenuUp: if (!pCursorMenu || fUp) return false; pCursorMenu->Control(COM_MenuUp ,0); return true; // navigate
1290  case C4PlayerControlDef::CDA_ObjectMenuRight: if (!pCursorMenu || fUp) return false; pCursorMenu->Control(COM_MenuRight,0); return true; // navigate
1291  case C4PlayerControlDef::CDA_ObjectMenuDown: if (!pCursorMenu || fUp) return false; pCursorMenu->Control(COM_MenuDown ,0); return true; // navigate
1292 
1293  case C4PlayerControlDef::CDA_ZoomIn: if (!pPlr || fUp || !(pVP = ::Viewports.GetViewport(iPlr))) return false; pVP->ChangeZoom(C4GFX_ZoomStep); return true; // viewport zoom
1294  case C4PlayerControlDef::CDA_ZoomOut: if (!pPlr || fUp || !(pVP = ::Viewports.GetViewport(iPlr))) return false; pVP->ChangeZoom(1.0f/C4GFX_ZoomStep); return true; // viewport zoom
1295 
1296  //unknown action
1297  default: return false;
1298  }
1299 }
1300 
1301 bool C4PlayerControl::ExecuteControlScript(int32_t iControl, C4ID idControlExtraData, ControlState state, const C4KeyEventData &rKeyExtraData, bool fRepeated)
1302 {
1303  C4Player *pPlr = ::Players.Get(iPlr);
1304  if (pPlr)
1305  {
1306  // Not for eliminated (checked again in DirectCom, but make sure no control is generated for eliminated players!)
1307  if (pPlr->Eliminated) return false;
1308  // control count for statistics (but don't count analog stick wiggles)
1309  if (state != CONS_Moved)
1310  pPlr->CountControl(C4Player::PCID_DirectCom, iControl*2+state);
1311  }
1312  else if (iPlr > -1)
1313  {
1314  // player lost?
1315  return false;
1316  }
1317  // get coordinates
1318  int32_t x,y;
1319  const C4PlayerControlDef *def = ControlDefs.GetControlByIndex(iControl);
1321  {
1322  x = rKeyExtraData.vp_x; y = rKeyExtraData.vp_y;
1323  }
1324  else
1325  {
1326  x = rKeyExtraData.game_x; y = rKeyExtraData.game_y;
1327  }
1330  // exec control function
1331  C4AulParSet Pars(iPlr, iControl, C4Id2Def(idControlExtraData), vx, vy, rKeyExtraData.iStrength, fRepeated, C4VInt(state));
1333 }
1334 
1335 
1337 {
1338  // sync execution: Do keyrepeat
1339  for (size_t i=0; i<ControlDefs.GetCount(); ++i)
1340  {
1341  const CSync::ControlDownState *pControlDownState = Sync.GetControlDownState(i);
1342  if (pControlDownState && pControlDownState->IsDown())
1343  {
1344  const C4PlayerControlDef *pCtrlDef = ControlDefs.GetControlByIndex(i);
1345  assert(pCtrlDef);
1346  int32_t iCtrlRepeatDelay = pCtrlDef->GetRepeatDelay();
1347  if (iCtrlRepeatDelay)
1348  {
1349  int32_t iFrameDiff = Game.FrameCounter - pControlDownState->iDownFrame;
1350  int32_t iCtrlInitialRepeatDelay = pCtrlDef->GetInitialRepeatDelay();
1351  if (iFrameDiff && iFrameDiff >= iCtrlInitialRepeatDelay)
1352  {
1353  if (!((iFrameDiff-iCtrlInitialRepeatDelay) % iCtrlRepeatDelay))
1354  {
1355  // it's RepeatTime for this key!
1356  ExecuteControlAction(i, pCtrlDef->GetAction(), pCtrlDef->GetExtraData(), CONS_Down, pControlDownState->DownState, true);
1357  }
1358  }
1359  }
1360  }
1361  }
1362  // cleanup old recent keys
1364  C4PlayerControlRecentKeyList::iterator irk;
1365  for (irk = RecentKeys.begin(); irk != RecentKeys.end(); ++irk)
1366  {
1367  C4PlayerControlRecentKey &rk = *irk;
1368  if (rk.tTime + MaxRecentKeyLookback > tNow) break;
1369  }
1370  if (irk != RecentKeys.begin()) RecentKeys.erase(RecentKeys.begin(), irk);
1371 }
1372 
1373 C4PlayerControl::C4PlayerControl() : ControlDefs(Game.PlayerControlDefs), iPlr(-1), pControlSet(nullptr), IsCursorPosRequested(false)
1374 {
1375 }
1376 
1378 {
1379  iPlr = NO_OWNER;
1380  pControlSet = nullptr;
1381  for (KeyBindingList::iterator i = KeyBindings.begin(); i != KeyBindings.end(); ++i) delete *i;
1382  KeyBindings.clear();
1383  RecentKeys.clear();
1384  DownKeys.clear();
1385  Sync.Clear();
1386  IsCursorPosRequested = false;
1387 }
1388 
1390 {
1391  // setup
1392  pControlSet = pKeyset;
1393  this->iPlr = iPlr;
1394  // register all keys into Game.KeyboardInput creating KeyBindings
1395  if (pControlSet)
1396  {
1397  C4KeyCodeExVec RegularKeys, HoldKeys;
1398  pControlSet->GetTriggerKeys(ControlDefs, &RegularKeys, &HoldKeys);
1399  int32_t idx=0;
1400  for (C4KeyCodeExVec::const_iterator i = RegularKeys.begin(); i != RegularKeys.end(); ++i) AddKeyBinding(*i, false, idx++);
1401  for (C4KeyCodeExVec::const_iterator i = HoldKeys.begin(); i != HoldKeys.end(); ++i) AddKeyBinding(*i, true, idx++);
1402  }
1403 }
1404 
1405 void C4PlayerControl::AddKeyBinding(const C4KeyCodeEx &key, bool fHoldKey, int32_t idx)
1406 {
1407  KeyBindings.push_back(new C4KeyBinding(
1408  key, FormatString("PlrKey%02d", idx).getData(), KEYSCOPE_Control,
1409  new C4KeyCBExPassKey<C4PlayerControl, C4KeyCodeEx>(*this, key, &C4PlayerControl::ProcessKeyDown, fHoldKey ? &C4PlayerControl::ProcessKeyUp : nullptr, nullptr, fHoldKey ? &C4PlayerControl::ProcessKeyMoved : nullptr),
1411 }
1412 
1413 bool C4PlayerControl::DoMouseInput(uint8_t mouse_id, int32_t mouseevent, float game_x, float game_y, float gui_x, float gui_y, bool is_ctrl_down, bool is_shift_down, bool is_alt_down, int wheel_dir)
1414 {
1415  // convert moueevent to key code
1416  uint8_t mouseevent_code;
1417  C4KeyCodeEx mouseevent_keycode;
1418  bool is_down = true;
1419  switch (mouseevent)
1420  {
1421  case C4MC_Button_None: mouseevent_code = KEY_MOUSE_Move; break;
1422  case C4MC_Button_LeftUp: is_down = false; // nobreak
1423  case C4MC_Button_LeftDown: mouseevent_code = KEY_MOUSE_ButtonLeft; break;
1424  case C4MC_Button_LeftDouble: mouseevent_code = KEY_MOUSE_ButtonLeftDouble; break;
1425  case C4MC_Button_RightUp: is_down = false; // nobreak
1426  case C4MC_Button_RightDown: mouseevent_code = KEY_MOUSE_ButtonRight; break;
1427  case C4MC_Button_RightDouble: mouseevent_code = KEY_MOUSE_ButtonRightDouble; break;
1428  case C4MC_Button_MiddleUp: is_down = false; // nobreak
1429  case C4MC_Button_MiddleDown: mouseevent_code = KEY_MOUSE_ButtonMiddle; break;
1430  case C4MC_Button_MiddleDouble: mouseevent_code = KEY_MOUSE_ButtonMiddleDouble; break;
1431  case C4MC_Button_X1Up: is_down = false; // nobreak
1432  case C4MC_Button_X1Down: mouseevent_code = KEY_MOUSE_ButtonX1; break;
1433  case C4MC_Button_X1Double: mouseevent_code = KEY_MOUSE_ButtonX1Double; break;
1434  case C4MC_Button_X2Up: is_down = false; // nobreak
1435  case C4MC_Button_X2Down: mouseevent_code = KEY_MOUSE_ButtonX2; break;
1436  case C4MC_Button_X2Double: mouseevent_code = KEY_MOUSE_ButtonX2Double; break;
1437  case C4MC_Button_Wheel:
1438  if (!wheel_dir) return false;
1439  mouseevent_code = (wheel_dir > 0) ? KEY_MOUSE_Wheel1Up : KEY_MOUSE_Wheel1Down; break;
1440  default: assert(false); return false;
1441  }
1442  // compose keycode
1443  if (is_ctrl_down) mouseevent_keycode.dwShift |= KEYS_Control;
1444  if (is_shift_down) mouseevent_keycode.dwShift |= KEYS_Shift;
1445  if (is_alt_down) mouseevent_keycode.dwShift |= KEYS_Alt;
1446  mouseevent_keycode.Key = KEY_Mouse(mouse_id, mouseevent_code);
1447  // first, try processing it as GUI mouse event. if not assigned, process as Game mous event
1448  // TODO: May route this through Game.DoKeyboardInput instead - would allow assignment of mouse events in CustomConfig
1449  // and would get rid of the Game.KeyboardInput.SetLastKeyExtraData-hack
1450  C4KeyEventData mouseevent_data;
1451  mouseevent_data.iStrength = 100*is_down; // TODO: May get pressure from tablet here
1452  mouseevent_data.vp_x = uint32_t(gui_x);
1453  mouseevent_data.vp_y = uint32_t(gui_y);
1454  mouseevent_data.game_x = uint32_t(game_x);
1455  mouseevent_data.game_y = uint32_t(game_y);
1456  Game.KeyboardInput.SetLastKeyExtraData(mouseevent_data); // ProcessKeyDown/Up queries it from there...
1457  bool result;
1458  if (is_down)
1459  result = ProcessKeyDown(mouseevent_keycode, mouseevent_keycode);
1460  else
1461  result = ProcessKeyUp(mouseevent_keycode, mouseevent_keycode);
1462  return result;
1463 }
1464 
1465 bool C4PlayerControl::GetCurrentPlayerCursorPos(int32_t *x_out, int32_t *y_out, int32_t *game_x_out, int32_t *game_y_out)
1466 {
1467  // prefer mouse position if this is a mouse control
1468  if (pControlSet && pControlSet->HasMouse())
1469  {
1470  if (MouseControl.GetLastCursorPos(x_out, y_out, game_x_out, game_y_out))
1471  {
1472  return true;
1473  }
1474  // if getting the mouse position failed, better fall back to cursor pos
1475  }
1476  // no mouse position known. Use cursor.
1477  C4Player *plr = Players.Get(iPlr);
1478  if (!plr) return false;
1479  C4Object *cursor_obj = plr->Cursor;
1480  if (!cursor_obj) return false;
1481  C4Viewport *vp = ::Viewports.GetViewport(iPlr);
1482  if (!vp) return false;
1483  int32_t game_x = cursor_obj->GetX(), game_y=cursor_obj->GetY();
1484  *game_x_out = game_x; *game_y_out = game_y;
1485  // game coordinate to screen coordinates...
1486  float screen_x = (float(game_x) - vp->last_game_draw_cgo.TargetX - vp->last_game_draw_cgo.X) * vp->GetZoom();
1487  float screen_y = (float(game_y) - vp->last_game_draw_cgo.TargetY - vp->last_game_draw_cgo.Y) * vp->GetZoom();
1488  // ...and screen coordinates to GUI coordinates (might push this into a helper function of C4Viewport?)
1489  float gui_x = (screen_x - vp->last_game_draw_cgo.X) / C4GUI::GetZoom() + vp->last_game_draw_cgo.X;
1490  float gui_y = (screen_y - vp->last_game_draw_cgo.Y) / C4GUI::GetZoom() + vp->last_game_draw_cgo.Y;
1491  *x_out = int32_t(gui_x); *y_out = int32_t(gui_y);
1492  return true;
1493 }
1494 
1496 {
1497  if (IsCursorPosRequested)
1498  {
1499  int32_t x, y, game_x, game_y;
1500  // add current cursor pos in GUI coordinates to input
1501  if (GetCurrentPlayerCursorPos(&x, &y, &game_x, &game_y))
1502  {
1503  // CON_CursorPos might not have been defined in definition file
1504  if (ControlDefs.InternalCons.CON_CursorPos != CON_None)
1505  {
1506  C4KeyEventData ev;
1507  ev.iStrength = 0;
1508  ev.vp_x = x; ev.vp_y = y;
1509  ev.game_x = game_x; ev.game_y = game_y;
1510  C4ControlPlayerControl *pControlPacket = new C4ControlPlayerControl(iPlr, CONS_Down, ev);
1512  // make sure it's added at head, because controls that have SendCursorPos=1 set will follow, which will rely
1513  // on the cursor pos being known
1514  Game.Input.AddHead(CID_PlrControl, pControlPacket);
1515  }
1516  }
1517  else
1518  {
1519  // no cursor is known (e.g.: Cursor Clonk dead, etc.). Don't create a control.
1520  // Script will probably fall back to last known cursor pos
1521  }
1522  IsCursorPosRequested = false;
1523  }
1524 }
const char * getData() const
Definition: StdBuf.h:450
const char * GetName() const
C4PlayerControl::ControlState GetState() const
Definition: C4Control.h:233
int32_t GetY() const
Definition: C4Object.h:287
void ExecuteControlPacket(const class C4ControlPlayerControl *pCtrl)
virtual bool Separator(Sep eSep=SEP_SEP)
Definition: StdCompiler.h:129
bool Load(C4Group &hGroup, const char *szFilename, C4LangStringTable *pLang)
void RegisterGlobalConstant(const char *szName, const C4Value &rValue)
const int32_t C4MC_Button_MiddleUp
const BYTE COM_MenuLeft
Definition: C4Constants.h:129
Definition: StdAdaptors.h:848
C4Config Config
Definition: C4Config.cpp:831
const int32_t C4MC_Button_LeftDown
float Y
Definition: C4Facet.h:120
C4ObjectPtr Cursor
Definition: C4Player.h:132
C4PlayerControlAssignmentSet * GetDefaultSet()
CoordinateSpace GetCoordinateSpace() const
void GetTriggerKeys(const C4PlayerControlDefs &rDefs, C4KeyCodeExVec *pRegularKeys, C4KeyCodeExVec *pHoldKeys) const
C4PlayerControlAssignmentSet * GetSetByName(const char *szName)
Definition: StdAdaptors.h:760
C4PropListStatic * GetPropList()
Definition: C4Aul.h:153
C4MainMenu Menu
Definition: C4Player.h:105
C4Game Game
Definition: C4Globals.cpp:52
void MergeFrom(const C4PlayerControlAssignmentSet &Src, MergeMode merge_mode)
C4AulScriptEngine ScriptEngine
Definition: C4Globals.cpp:43
void SetControlDownState(int32_t iControl, const C4KeyEventData &rDownState, int32_t iDownFrame, bool fDownByUser)
C4ConfigGeneral General
Definition: C4Config.h:252
const C4KeyCode KEY_MOUSE_ButtonX1
const BYTE COM_MenuShowText
Definition: C4Constants.h:128
C4PlayerControlAssignment * GetAssignmentByControl(int32_t control)
void MergeFrom(const C4PlayerControlAssignmentSets &Src, C4PlayerControlAssignmentSet::MergeMode merge_mode)
struct C4PlayerControlDefs::CInternalCons InternalCons
C4KeyShiftState
C4Value C4VInt(int32_t i)
Definition: C4Value.h:242
void Add(C4PacketType eType, C4ControlPacket *pCtrl)
Definition: C4Control.h:82
bool ShowStartup
Definition: C4Player.h:111
const int32_t C4MC_Button_RightUp
bool SetControlDisabled(int32_t iControl, int32_t iVal)
void CompileFunc(StdCompiler *pComp)
Definition: C4Menu.h:122
const char * GetGUIDesc(const C4PlayerControlDefs &defs) const
virtual bool Name(const char *szName)
Definition: StdCompiler.h:87
const C4KeyCode KEY_MOUSE_Button1
const int32_t C4MC_Button_X2Down
int32_t GetControlDisabled(int32_t iControl) const
const C4KeyCode KEY_MOUSE_Wheel1Up
bool getBool() const
Definition: C4Value.h:113
bool IsGlobal() const
C4Viewport * GetViewport(int32_t iPlayer, C4Viewport *pPrev=nullptr)
bool GetLastCursorPos(int32_t *x_out_gui, int32_t *y_out_gui, int32_t *x_out_game, int32_t *y_out_game) const
C4MouseControl MouseControl
Definition: C4Globals.cpp:47
const ControlDownState * GetControlDownState(int32_t iControl) const
const int32_t C4MC_Button_MiddleDouble
C4GraphicsResource GraphicsResource
bool ResolveRefs(C4PlayerControlDefs *pControlDefs)
bool SEqual(const char *szStr1, const char *szStr2)
Definition: Standard.h:97
bool IsSync() const
void ResetControlDownState(int32_t iControl)
const int32_t C4MC_Button_X1Double
void ChangeZoom(float by_factor)
Definition: C4Viewport.cpp:440
const BYTE COM_MenuEnterAll
Definition: C4Constants.h:126
bool operator==(const C4PlayerControlAssignment &cmp) const
const BYTE COM_MenuEnter
Definition: C4Constants.h:125
const C4KeyEventData & GetExtraData() const
Definition: C4Control.h:234
C4PlayerControlAssignment * GetAssignmentByControlName(const char *szControlName)
C4KeyCode KEY_Mouse(uint8_t mouse_id, uint8_t mouseevent)
const char * GetGUIName() const
std::shared_ptr< class C4GamePadOpener > pGamepad
Definition: C4Player.h:134
const int32_t C4MC_Button_LeftDouble
const C4PlayerControlDef * GetControlByIndex(int32_t idx) const
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:93
float GetZoom()
Definition: C4Gui.h:2828
void AppendChar(char cChar)
Definition: StdBuf.h:596
C4ID GetExtraData() const
bool operator==(const C4PlayerControlDef &cmp) const
C4PlayerControlAssignmentSet * GetSetByIndex(int32_t index)
bool DoMouseInput(uint8_t mouse_id, int32_t mouseevent, float game_x, float game_y, float gui_x, float gui_y, bool is_ctrl_down, bool is_shift_down, bool is_alt_down, int wheel_dir)
void CompileFunc(StdCompiler *pComp)
bool isSerializer()
Definition: StdCompiler.h:64
uint8_t Key_GetMouseEvent(C4KeyCode key)
C4Player * Get(int iPlayer) const
class C4ObjectMenu * Menu
Definition: C4Object.h:140
int32_t FrameCounter
Definition: C4Game.h:130
const int32_t C4MC_Button_X2Double
int32_t GetControlIndexByIdentifier(const char *szIdentifier) const
bool IsDefaultDisabled() const
DownStateVec ControlDownStates
int32_t GetSetIndex(const C4PlayerControlAssignmentSet *set) const
const int32_t C4MC_Button_LeftUp
void SetExtraData(const C4KeyEventData &new_extra_data)
Definition: C4Control.h:235
int SCharLastPos(char cTarget, const char *szInStr)
Definition: Standard.cpp:217
const C4KeyCodeEx & GetTriggerKey() const
const BYTE COM_MenuUp
Definition: C4Constants.h:131
void CompileFunc(StdCompiler *pComp)
const int32_t C4MC_Button_X1Up
void MergeFrom(const C4PlayerControlDefs &Src)
bool IsRepeated() const
C4KeyboardInput & KeyboardInput
Definition: C4Game.h:98
C4TimeMilliseconds tTime
bool IsHoldKey() const
const C4KeyCode KEY_MOUSE_ButtonLeftDouble
bool IsValid() const
const C4KeyCode KEY_Default
bool IsGlobal() const
Actions GetAction() const
static const C4ID None
Definition: C4Id.h:42
int32_t deviceId
const C4KeyCode KEY_MOUSE_ButtonX1Double
StdStrBuf GetKeysAsString(bool human_readable, bool short_name) const
bool Save(C4Group &hGroup, const char *szFilename)
const char * GetGUIDesc() const
void Take(char *pnData)
Definition: StdBuf.h:465
bool IsSendCursorPos() const
void SetLastKeyExtraData(const C4KeyEventData &data)
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:527
bool Key_IsMouse(C4KeyCode key)
C4PlayerList Players
void RegisterKeyset(int32_t iPlr, C4PlayerControlAssignmentSet *pKeyset)
int32_t GetX() const
Definition: C4Object.h:286
bool ResolveRefs(class C4PlayerControlAssignmentSet *pParentSet, C4PlayerControlDefs *pControlDefs)
void AddDbgRec(C4RecordChunkType eType, const void *pData, int iSize)
Definition: C4Record.cpp:36
const BYTE COM_MenuDown
Definition: C4Constants.h:132
bool Key_IsGamepad(C4KeyCode key)
const int NO_OWNER
Definition: C4Constants.h:138
void CompileFunc(StdCompiler *pComp)
void excNotFound(const char *szMessage,...)
Definition: StdCompiler.h:243
void ReplaceStrings(StdStrBuf &rBuf)
bool LogFatal(const char *szMessage)
Definition: C4Log.cpp:230
const char * GetIdentifier() const
const char * GetControlName() const
void Value(const T &rStruct)
Definition: StdCompiler.h:171
const int32_t C4MC_Button_X1Down
Definition: C4Id.h:28
std::vector< int32_t > DisableStateVec
int32_t Eliminated
Definition: C4Player.h:85
DisableStateVec ControlDisableStates
bool IsMouseControlAssigned(int32_t mouseevent) const
const C4KeyCode KEY_MOUSE_ButtonX2
const int32_t C4MC_Button_RightDown
bool ActivateMenuMain()
Definition: C4Player.cpp:1764
bool IsActive()
Definition: C4Menu.cpp:483
C4Control & Input
Definition: C4Game.h:84
C4KeyCode Key
bool TryClose(bool fOK, bool fControl)
Definition: C4Menu.cpp:281
const C4KeyCode KEY_MOUSE_ButtonX2Double
C4TargetFacet last_game_draw_cgo
Definition: C4Viewport.h:41
C4PlayerControlAssignmentSet * CreateEmptySetByTemplate(const C4PlayerControlAssignmentSet &template_set)
DECLARE_C4CONTROL_VIRTUALS void AddControl(int32_t iControl, int32_t iTriggerMode)
Definition: C4Control.h:230
std::vector< const C4PlayerControlAssignment * > C4PlayerControlAssignmentPVec
const C4KeyCode KEY_MOUSE_Move
virtual bool isDeserializer()
Definition: StdCompiler.h:63
bool IsAsync() const
void SetKey(const C4KeyCodeEx &key)
const int32_t C4MC_Button_Wheel
C4PlayerControlAssignment * CreateAssignmentForControl(const char *control_name)
void InitEmptyFromTemplate(const C4PlayerControlAssignmentSet &template_set)
const C4Value C4VNull
Definition: C4Value.cpp:32
void CompileFunc(StdCompiler *pComp)
const C4KeyCode KEY_MOUSE_ButtonRightDouble
float TargetX
Definition: C4Facet.h:167
C4Def * C4Id2Def(C4ID id)
Definition: C4DefList.h:80
int32_t GetInitialRepeatDelay() const
C4ViewportList Viewports
Definition: C4Viewport.cpp:841
bool Control(BYTE byCom, int32_t iData)
Definition: C4Menu.cpp:423
const BYTE COM_MenuSelect
Definition: C4Constants.h:133
const BYTE COM_MenuClose
Definition: C4Constants.h:127
std::list< C4PlayerControlRecentKey > C4PlayerControlRecentKeyList
void CountControl(ControlType eType, int32_t iID, int32_t iCntAdd=1)
Definition: C4Player.cpp:1635
bool LoadEntryString(const char *szEntryName, StdStrBuf *Buf)
Definition: C4Group.cpp:1932
bool IsComboMatched(const C4PlayerControlRecentKeyList &DownKeys, const C4PlayerControlRecentKeyList &RecentKeys) const
StdSTLContainerAdapt< C > mkSTLContainerAdapt(C &rTarget, StdCompiler::Sep eSep=StdCompiler::SEP_SEP)
Definition: StdAdaptors.h:679
const float C4GFX_ZoomStep
const BYTE COM_MenuRight
Definition: C4Constants.h:130
float GetZoom()
Definition: C4Viewport.h:45
C4PlayerControlAssignment * GetAssignmentByIndex(int32_t index)
bool operator==(const C4PlayerControlAssignmentSets &cmp) const
const int32_t C4MC_Button_RightDouble
bool WildcardMatch(const char *szWildcard, const char *szString)
Definition: StdFile.cpp:384
const int32_t C4MC_Button_None
bool IsTextProgressing() const
Definition: C4Menu.h:201
int32_t GetTriggerMode() const
const char * GetGUIName(const C4PlayerControlDefs &defs) const
bool SEqual2(const char *szStr1, const char *szStr2)
Definition: Standard.cpp:168
bool operator==(const C4PlayerControlAssignmentSet &cmp) const
bool Add(const char *szFile, const char *szAddAs)
Definition: C4Group.cpp:1316
virtual bool Default(const char *szName)
Definition: StdCompiler.h:98
void AddHead(C4PacketType eType, C4ControlPacket *pCtrl)
Definition: C4Control.h:81
#define PSF_PlayerControl
Definition: C4GameScript.h:118
StdParameterAdapt< T, P > mkParAdapt(T &&rObj, P &&rPar)
Definition: StdAdaptors.h:456
void CompileFunc(StdCompiler *pComp)
void CopyKeyFrom(const C4PlayerControlAssignment &src_assignment)
size_t getLength() const
Definition: StdBuf.h:453
int32_t DebugRec
Definition: C4Config.h:61
std::vector< C4KeyCodeEx > C4KeyCodeExVec
const C4KeyCode KEY_MOUSE_ButtonMiddleDouble
const C4KeyCode KEY_MOUSE_ButtonRight
const int32_t C4MC_Button_MiddleDown
void SetControlMovedState(int32_t iControl, const C4KeyEventData &rMovedState, int32_t iMovedFrame)
float TargetY
Definition: C4Facet.h:167
bool IsOverrideAssignments() const
C4Value Call(C4PropertyName k, C4AulParSet *pPars=0, bool fPassErrors=false)
Definition: C4PropList.h:112
uint32_t DWORD
const C4KeyCode KEY_MOUSE_ButtonLeft
void GetAssignmentsByKey(const C4PlayerControlDefs &rDefs, const C4KeyCodeEx &key, bool fHoldKeysOnly, C4PlayerControlAssignmentPVec *pOutVec, const C4PlayerControlRecentKeyList &DownKeys, const C4PlayerControlRecentKeyList &RecentKeys) const
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:253
bool ResolveRefs(C4PlayerControlDefs *pControlDefs)
void Copy()
Definition: StdBuf.h:475
C4Facet GetPhase(int iPhaseX=0, int iPhaseY=0)
Definition: C4Facet.cpp:59
bool operator==(const ControlDownState &cmp) const
void CompileFunc(StdCompiler *pComp)
void CompileFunc(StdCompiler *pComp)
C4PlayerControlAssignmentSets PlayerControlUserAssignmentSets
Definition: C4Game.h:95
float X
Definition: C4Facet.h:120
const ControlItemVec & GetControlItems() const
Definition: C4Control.h:232
virtual void NameEnd(bool fBreak=false)
Definition: StdCompiler.h:88
const C4KeyCode KEY_MOUSE_ButtonMax
void InitDefaults(const C4PlayerControlDefs &ControlDefs)
void CompileFunc(C4Real &rValue, StdCompiler *pComp)
Definition: C4Real.cpp:9033
static C4TimeMilliseconds Now()
int32_t GetRepeatDelay() const
void RemoveAssignmentByControlName(const char *control_name)
bool operator==(const CSync &cmp) const
const C4KeyCode KEY_MOUSE_ButtonMiddle
const C4KeyCode KEY_MOUSE_Wheel1Down
std::vector< ControlDownState > DownStateVec
virtual bool isRegistry()
Definition: StdCompiler.h:75
const int32_t C4MC_Button_X2Up
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277
int32_t GetControl() const
const C4KeyEventData & GetLastKeyExtraData() const
void RemoveSetByName(const char *set_name)
static StdStrBuf KeyShift2String(C4KeyShiftState eShift)