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