Xonotic
keybinder.qc
Go to the documentation of this file.
1 #include "keybinder.qh"
2 
3 #include <common/weapons/_all.qh>
4 .int flags;
5 
6 #include "button.qh"
8 
9 const string KEY_NOT_BOUND_CMD = "// not bound";
10 
11 const int MAX_KEYS_PER_FUNCTION = 2;
12 const int MAX_KEYBINDS = 256;
15 int KeyBinds_Count = -1;
16 
18 {
19  KeyBinds_Count = 0;
20 
21  #define KEYBIND_DEF(func, desc) MACRO_BEGIN \
22  if((KeyBinds_Count < MAX_KEYBINDS)) { \
23  KeyBinds_Functions[KeyBinds_Count] = strzone(func); \
24  KeyBinds_Descriptions[KeyBinds_Count] = strzone(desc); \
25  ++KeyBinds_Count; \
26  } \
27  MACRO_END
28 
29  #define KEYBIND_EMPTY_LINE() KEYBIND_DEF("", "")
30  #define KEYBIND_HEADER(str) KEYBIND_DEF("", str)
31 
32  #define KEYBIND_IS_SPECIAL(func) (substring(func, 0 ,1) == "*")
33  #define KEYBIND_SPECIAL_DEF(key, desc) KEYBIND_DEF(strcat("*", key), desc)
34 
35  KEYBIND_HEADER(_("Moving"));
36  KEYBIND_DEF("+forward" , _("forward"));
37  KEYBIND_DEF("+back" , _("backpedal"));
38  KEYBIND_DEF("+moveleft" , _("strafe left"));
39  KEYBIND_DEF("+moveright" , _("strafe right"));
40  KEYBIND_DEF("+jump" , _("jump / swim"));
41  KEYBIND_DEF("+crouch" , _("crouch / sink"));
42  KEYBIND_DEF("+hook" , _("off-hand hook"));
43  KEYBIND_DEF("+jetpack" , _("jetpack"));
45 
46  KEYBIND_HEADER(_("Attacking"));
47  KEYBIND_DEF("+fire" , _("primary fire"));
48  KEYBIND_DEF("+fire2" , _("secondary fire"));
50 
51  KEYBIND_HEADER(_("Weapons"));
52  KEYBIND_DEF("weapprev" , CTX(_("WEAPON^previous")));
53  KEYBIND_DEF("weapnext" , CTX(_("WEAPON^next")));
54  KEYBIND_DEF("weaplast" , CTX(_("WEAPON^previously used")));
55  KEYBIND_DEF("weapbest" , CTX(_("WEAPON^best")));
56  KEYBIND_DEF("reload" , _("reload"));
57  KEYBIND_DEF("dropweapon" , _("drop weapon / throw nade"));
58 
59  #define ADD_TO_W_LIST(pred) \
60  FOREACH(Weapons, it != WEP_Null, { \
61  if (it.impulse != imp) continue; \
62  if (!(pred)) continue; \
63  w_list = strcat(w_list, it.m_name, " / "); \
64  })
65 
66  for(int imp = 1; imp <= 9; ++imp)
67  {
68  string w_list = "";
70  ADD_TO_W_LIST((it.spawnflags & WEP_FLAG_SUPERWEAPON) && !(it.spawnflags & WEP_FLAG_HIDDEN));
71  ADD_TO_W_LIST((it.spawnflags & WEP_FLAG_MUTATORBLOCKED) && !(it.spawnflags & (WEP_FLAG_HIDDEN | WEP_FLAG_SPECIALATTACK)));
72  if(w_list)
73  KEYBIND_DEF(strcat("weapon_group_", itos(imp)), substring(w_list, 0, -4));
74  if(imp == 0)
75  break;
76  if(imp == 9)
77  imp = -1;
78  }
79  #undef ADD_TO_W_LIST
80 
82 
83  KEYBIND_HEADER(_("View"));
84  KEYBIND_DEF("+zoom" , _("hold zoom"));
85  KEYBIND_DEF("togglezoom" , _("toggle zoom"));
86  KEYBIND_DEF("+showscores" , _("show scores"));
87  KEYBIND_DEF("screenshot" , _("screen shot"));
88  KEYBIND_DEF("+hud_panel_radar_maximized" , _("maximize radar"));
89  KEYBIND_DEF("toggle chase_active" , _("3rd person view"));
90  KEYBIND_DEF("spec" , _("enter spectator mode"));
92 
93  KEYBIND_HEADER(_("Communication"));
94  KEYBIND_DEF("messagemode" , _("public chat"));
95  KEYBIND_DEF("messagemode2" , _("team chat"));
96  KEYBIND_DEF("+con_chat_maximize" , _("show chat history"));
97  KEYBIND_DEF("vyes" , _("vote YES"));
98  KEYBIND_DEF("vno" , _("vote NO"));
99  KEYBIND_DEF("ready" , _("ready"));
101 
102  KEYBIND_HEADER(_("Client"));
103  KEYBIND_DEF("+show_info" , _("server info"));
104  // display the hardcoded shortcut to open the console as it works for all
105  // non-English keyboard layouts, unlike default keys (` and ~)
106  KEYBIND_DEF("toggleconsole" , _("enter console"));
107  string console_shortcut = strcat(translate_key("SHIFT"), "+", translate_key("ESCAPE"));
108  KEYBIND_SPECIAL_DEF(console_shortcut, _("enter console"));
109  KEYBIND_DEF("disconnect" , _("disconnect"));
110  KEYBIND_DEF("menu_showquitdialog" , _("quit"));
112 
113  KEYBIND_HEADER(_("Teamplay"));
114  KEYBIND_DEF("team_auto" , _("auto-join team"));
115  KEYBIND_DEF("menu_showteamselect" , _("team menu"));
116  KEYBIND_DEF("spec" , _("spectate"));
118 
119  KEYBIND_HEADER(_("Misc"));
120  KEYBIND_DEF("+use" , _("drop key/flag, exit vehicle"));
121  KEYBIND_DEF("kill" , _("suicide / respawn"));
122  KEYBIND_DEF("quickmenu" , _("quick menu"));
124 
125  KEYBIND_HEADER(_("User defined"));
126 
127  for(int i = 1; i <= 32; ++i)
128  KEYBIND_DEF(strcat("+userbind ", itos(i)), strcat("$userbind", itos(i)));
129 
131 
132  KEYBIND_HEADER(_("Development"));
133  KEYBIND_DEF("menu_showsandboxtools" , _("sandbox menu"));
134  KEYBIND_DEF("+button8" , _("drag object (sandbox)"));
135  KEYBIND_DEF("wpeditor_menu" , _("waypoint editor menu"));
136 
137  #undef KEYBIND_DEF
138 }
139 
141 {
142  entity me;
143  me = NEW(XonoticKeyBinder);
144  me.configureXonoticKeyBinder(me);
145  return me;
146 }
147 void replace_bind(string from, string to)
148 {
149  int n, j;
150  float k; // not sure if float or int
151  n = tokenize(findkeysforcommand(from, 0)); // uses '...' strings
152  for(j = 0; j < n; ++j)
153  {
154  k = stof(argv(j));
155  if(k != -1)
156  localcmd("\nbind \"", keynumtostring(k), "\" \"", to, "\"\n");
157  }
158  if(n)
159  cvar_set("_hud_showbinds_reload", "1");
160 }
162 {
163  me.configureXonoticListBox(me);
164  me.nItems = 0;
165 
166  // TEMP: Xonotic 0.1 to later
167  replace_bind("impulse 1", "weapon_group_1");
168  replace_bind("impulse 2", "weapon_group_2");
169  replace_bind("impulse 3", "weapon_group_3");
170  replace_bind("impulse 4", "weapon_group_4");
171  replace_bind("impulse 5", "weapon_group_5");
172  replace_bind("impulse 6", "weapon_group_6");
173  replace_bind("impulse 7", "weapon_group_7");
174  replace_bind("impulse 8", "weapon_group_8");
175  replace_bind("impulse 9", "weapon_group_9");
176  replace_bind("impulse 14", "weapon_group_0");
177 }
179 {
180  bool force_initial_selection = false;
181  if(KeyBinds_Count < 0) // me.handle not loaded yet?
182  force_initial_selection = true;
184  me.nItems = KeyBinds_Count;
185  if(force_initial_selection)
186  me.setSelected(me, 0);
187 }
189 {
190  me.destroy(me);
191  me.loadKeyBinds(me);
192 }
193 void XonoticKeyBinder_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
194 {
195  SUPER(XonoticKeyBinder).resizeNotify(me, relOrigin, relSize, absOrigin, absSize);
196 
197  me.realFontSize_y = me.fontSize / (absSize.y * me.itemHeight);
198  me.realFontSize_x = me.fontSize / (absSize.x * (1 - me.controlWidth));
199  me.realUpperMargin = 0.5 * (1 - me.realFontSize.y);
200 
201  me.columnFunctionOrigin = 0;
202  me.columnKeysSize = me.realFontSize.x * 12;
203  me.columnFunctionSize = 1 - me.columnKeysSize - 2 * me.realFontSize.x;
204  me.columnKeysOrigin = me.columnFunctionOrigin + me.columnFunctionSize + me.realFontSize.x;
205 }
207 {
208  string func = KeyBinds_Functions[me.selectedItem];
209  if(func == "" || KEYBIND_IS_SPECIAL(func))
210  return;
211 
212  me.keyGrabButton.forcePressed = 1;
213  me.clearButton.disabled = 1;
214  keyGrabber = me;
215 }
216 void XonoticKeyBinder_keyGrabbed(entity me, int key, bool ascii)
217 {
218  int n, j, nvalid;
219  float k;
220 
221  me.keyGrabButton.forcePressed = 0;
222  me.clearButton.disabled = 0;
223 
224  if(key == K_ESCAPE)
225  return;
226 
227  // forbid these keys from being bound in the menu
228  if(key == K_CAPSLOCK || key == K_NUMLOCK)
229  {
230  KeyBinder_Bind_Change(me, me);
231  return;
232  }
233 
234  string func = KeyBinds_Functions[me.selectedItem];
235  if(func == "" || KEYBIND_IS_SPECIAL(func))
236  return;
237 
238  n = tokenize(findkeysforcommand(func, 0)); // uses '...' strings
239  nvalid = 0;
240  for(j = 0; j < n; ++j)
241  {
242  k = stof(argv(j));
243  if(k != -1)
244  ++nvalid;
245  }
246  if(nvalid >= MAX_KEYS_PER_FUNCTION)
247  {
248  for(j = 0; j < n; ++j)
249  {
250  k = stof(argv(j));
251  if(k != -1)
252  {
253  // bind to empty cmd instead of using unbind so it gets saved in config and overrides any default binds
254  localcmd("\nbind \"", keynumtostring(k), "\" \"", KEY_NOT_BOUND_CMD, "\"\n");
255  }
256  }
257  }
259  localcmd("\nbind \"", keynumtostring(key), "\" \"", func, "\"\n");
260  localcmd("-zoom\n"); // to make sure we aren't in togglezoom'd state
261  cvar_set("_hud_showbinds_reload", "1");
262 }
264 {
265  if(KeyBinds_Count < 0)
266  return;
267 
268  for(int i = 0; i < MAX_KEYBINDS; ++i)
269  {
272  }
273  KeyBinds_Count = 0;
274 }
275 void XonoticKeyBinder_editUserbind(entity me, string theName, string theCommandPress, string theCommandRelease)
276 {
277  if(!me.userbindEditDialog)
278  return;
279 
280  string func = KeyBinds_Functions[me.selectedItem];
281  if(func == "" || KEYBIND_IS_SPECIAL(func))
282  return;
283 
284  string descr = KeyBinds_Descriptions[me.selectedItem];
285  if(substring(descr, 0, 1) != "$")
286  return;
287  descr = substring(descr, 1, strlen(descr) - 1);
288 
289  // Hooray! It IS a user bind!
290  cvar_set(strcat(descr, "_description"), theName);
291  cvar_set(strcat(descr, "_press"), theCommandPress);
292  cvar_set(strcat(descr, "_release"), theCommandRelease);
293 }
295 {
296  if(!me.userbindEditDialog)
297  return;
298 
299  string func = KeyBinds_Functions[me.selectedItem];
300  if(func == "" || KEYBIND_IS_SPECIAL(func))
301  return;
302 
303  string descr = KeyBinds_Descriptions[me.selectedItem];
304  if(substring(descr, 0, 1) != "$")
305  return;
306  descr = substring(descr, 1, strlen(descr) - 1);
307 
308  // Hooray! It IS a user bind!
309  me.userbindEditDialog.loadUserBind(me.userbindEditDialog, cvar_string(strcat(descr, "_description")), cvar_string(strcat(descr, "_press")), cvar_string(strcat(descr, "_release")));
310 
311  DialogOpenButton_Click(btn, me.userbindEditDialog);
312 }
314 {
315  float n, j, k;
316 
317  string func = KeyBinds_Functions[me.selectedItem];
318  if(func == "" || KEYBIND_IS_SPECIAL(func))
319  return;
320 
321  n = tokenize(findkeysforcommand(func, 0)); // uses '...' strings
322  for(j = 0; j < n; ++j)
323  {
324  k = stof(argv(j));
325  if(k != -1)
326  {
327  // bind to empty cmd instead of using unbind so it gets saved in config and overrides any default binds
328  localcmd("\nbind \"", keynumtostring(k), "\" \"", KEY_NOT_BOUND_CMD, "\"\n");
329  }
330  }
332  localcmd("-zoom\n"); // to make sure we aren't in togglezoom'd state
333  cvar_set("_hud_showbinds_reload", "1");
334 }
336 {
337  localcmd("unbindall\n");
338  localcmd("exec binds-xonotic.cfg\n");
339  localcmd("-zoom\n"); // to make sure we aren't in togglezoom'd state
340  cvar_set("_hud_showbinds_reload", "1");
341  me.close(me);
342 }
344 {
346 }
348 {
349  // handling of "unselectable" items
350  i = floor(0.5 + bound(0, i, me.nItems - 1));
352  {
353  SUPER(XonoticKeyBinder).setSelected(me, i - 1);
354  return;
355  }
356  if(me.pressed == 0 || me.pressed == 1) // keyboard or scrolling - skip unselectable items
357  {
358  if(i > me.previouslySelected)
359  {
360  while((i < me.nItems - 1) && (KeyBinds_Functions[i] == ""))
361  ++i;
362  }
363  while((i > 0) && (KeyBinds_Functions[i] == ""))
364  --i;
365  while((i < me.nItems - 1) && (KeyBinds_Functions[i] == ""))
366  ++i;
367  }
368  if(me.pressed == 3) // released the mouse - fall back to last valid item
369  {
370  if(KeyBinds_Functions[i] == "")
371  i = me.previouslySelected;
372  }
373  if(KeyBinds_Functions[i] != "")
374  me.previouslySelected = i;
375  if(me.userbindEditButton)
376  me.userbindEditButton.disabled = (substring(KeyBinds_Descriptions[i], 0, 1) != "$");
377  SUPER(XonoticKeyBinder).setSelected(me, i);
378 }
379 float XonoticKeyBinder_keyDown(entity me, int key, bool ascii, float shift)
380 {
381  bool r = true;
382  switch(key)
383  {
384  case K_ENTER:
385  case K_KP_ENTER:
386  case K_SPACE:
387  KeyBinder_Bind_Change(me, me);
388  break;
389  case K_DEL:
390  case K_KP_DEL:
391  case K_BACKSPACE:
392  KeyBinder_Bind_Clear(me, me);
393  break;
394  case K_MOUSE2:
395  KeyBinder_Bind_Edit(me, me);
396  break;
397  default:
398  r = SUPER(XonoticKeyBinder).keyDown(me, key, ascii, shift);
399  break;
400  }
401  return r;
402 }
403 
404 void XonoticKeyBinder_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused)
405 {
406  vector theColor;
407  float theAlpha;
408  float extraMargin;
409 
410  string descr = KeyBinds_Descriptions[i];
411  string func = KeyBinds_Functions[i];
412 
413  if(func == "")
414  {
415  theColor = SKINCOLOR_KEYGRABBER_TITLES;
416  theAlpha = SKINALPHA_KEYGRABBER_TITLES;
417  extraMargin = 0;
418  }
419  else
420  {
421  if(isSelected)
422  {
423  if(keyGrabber == me)
424  draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_WAITING, SKINALPHA_LISTBOX_WAITING);
425  else
426  draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
427  }
428  else if(isFocused)
429  {
430  me.focusedItemAlpha = getFadedAlpha(me.focusedItemAlpha, SKINALPHA_LISTBOX_FOCUSED, SKINFADEALPHA_LISTBOX_FOCUSED);
431  draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_FOCUSED, me.focusedItemAlpha);
432  }
433 
434  theAlpha = SKINALPHA_KEYGRABBER_KEYS;
435  theColor = SKINCOLOR_KEYGRABBER_KEYS;
436  extraMargin = me.realFontSize.x * 0.5;
437  }
438 
439  if(substring(descr, 0, 1) == "$")
440  {
441  string s = substring(descr, 1, strlen(descr) - 1);
442  descr = cvar_string(strcat(s, "_description"));
443  if(descr == "")
444  descr = s;
445  if(cvar_string(strcat(s, "_press")) == "")
446  if(cvar_string(strcat(s, "_release")) == "")
447  theAlpha *= SKINALPHA_DISABLED;
448  }
449 
450  string s = draw_TextShortenToWidth(descr, me.columnFunctionSize, 0, me.realFontSize);
451  draw_Text(me.realUpperMargin * eY + extraMargin * eX, s, me.realFontSize, theColor, theAlpha, 0);
452 
453  if (func == "")
454  return;
455 
456  s = "";
457  if (KEYBIND_IS_SPECIAL(func))
458  s = substring(func, 1, -1);
459  else
460  {
461  bool joy_active = cvar("joy_active");
462  int n = tokenize(findkeysforcommand(func, 0)); // uses '...' strings
463  for(int j = 0; j < n; ++j)
464  {
465  float k = stof(argv(j));
466  if(k != -1)
467  {
468  string key = keynumtostring(k);
469  if (!joy_active && startsWith(key, "JOY"))
470  continue;
471  if(s != "")
472  s = strcat(s, ", ");
473  s = strcat(s, translate_key(key));
474  }
475  }
476  }
477  s = draw_TextShortenToWidth(s, me.columnKeysSize, 0, me.realFontSize);
478  draw_CenterText(me.realUpperMargin * eY + (me.columnKeysOrigin + 0.5 * me.columnKeysSize) * eX, s, me.realFontSize, theColor, theAlpha, 0);
479 }
int int int imp
Definition: impulse.qc:90
float K_ESCAPE
Definition: keycodes.qc:9
int flags
Definition: keybinder.qc:4
string KeyBinds_Descriptions[MAX_KEYBINDS]
Definition: keybinder.qc:14
float K_NUMLOCK
Definition: keycodes.qc:44
const vector eY
Definition: vector.qh:45
void KeyBinder_Bind_Edit(entity btn, entity me)
Definition: keybinder.qc:294
#define NEW(cname,...)
Definition: oo.qh:105
string KeyBinds_Functions[MAX_KEYBINDS]
Definition: keybinder.qc:13
#define KEYBIND_DEF(func, desc)
#define KEYBIND_SPECIAL_DEF(key, desc)
void replace_bind(string from, string to)
Definition: keybinder.qc:147
#define KEYBIND_IS_SPECIAL(func)
float K_DEL
Definition: keycodes.qc:38
entity() spawn
void DialogOpenButton_Click(entity button, entity tab)
void KeyBinds_BuildList()
Definition: keybinder.qc:17
#define ADD_TO_W_LIST(pred)
void XonoticKeyBinder_editUserbind(entity me, string theName, string theCommandPress, string theCommandRelease)
Definition: keybinder.qc:275
entity to
Definition: self.qh:96
const int WEP_FLAG_HIDDEN
Definition: weapon.qh:200
#define itos(i)
Definition: int.qh:6
float K_CAPSLOCK
Definition: keycodes.qc:45
float K_SPACE
Definition: keycodes.qc:10
float K_KP_ENTER
Definition: keycodes.qc:74
const string KEY_NOT_BOUND_CMD
Definition: keybinder.qc:9
void XonoticKeyBinder_configureXonoticKeyBinder(entity me)
Definition: keybinder.qc:161
#define SUPER(cname)
Definition: oo.qh:219
float XonoticKeyBinder_keyDown(entity me, int key, bool ascii, float shift)
Definition: keybinder.qc:379
float K_BACKSPACE
Definition: keycodes.qc:14
void XonoticKeyBinder_keyGrabbed(entity me, int key, bool ascii)
Definition: keybinder.qc:216
#define KEYBIND_HEADER(str)
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
void XonoticKeyBinder_doubleClickListBoxItem(entity me, float i, vector where)
Definition: keybinder.qc:343
int KeyBinds_Count
Definition: keybinder.qc:15
void XonoticKeyBinder_loadKeyBinds(entity me)
Definition: keybinder.qc:178
float K_MOUSE2
Definition: keycodes.qc:130
#define NULL
Definition: post.qh:17
#define startsWith(haystack, needle)
Definition: string.qh:217
entity makeXonoticKeyBinder()
Definition: keybinder.qc:140
void XonoticKeyBinder_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
Definition: keybinder.qc:193
vector(float skel, float bonenum) _skel_get_boneabs_hidden
const int MAX_KEYBINDS
Definition: keybinder.qc:12
void XonoticKeyBinder_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused)
Definition: keybinder.qc:404
const int WEP_FLAG_SPECIALATTACK
Definition: weapon.qh:211
const vector eX
Definition: vector.qh:44
void KeyBinder_Bind_Clear(entity btn, entity me)
Definition: keybinder.qc:313
#define KEYBIND_EMPTY_LINE()
void KeyBinder_Bind_Change(entity btn, entity me)
Definition: keybinder.qc:206
void KeyBinder_Bind_Reset_All(entity btn, entity me)
Definition: keybinder.qc:335
void XonoticKeyBinder_showNotify(entity me)
Definition: keybinder.qc:188
#define strfree(this)
Definition: string.qh:56
float K_ENTER
Definition: keycodes.qc:8
ERASEABLE string CTX(string s)
Definition: i18n.qh:45
const int WEP_FLAG_MUTATORBLOCKED
Definition: weapon.qh:203
void XonoticKeyBinder_destroy(entity me)
Definition: keybinder.qc:263
string translate_key(string key)
Definition: util.qc:1376
float K_KP_DEL
Definition: keycodes.qc:68
void XonoticKeyBinder_setSelected(entity me, int i)
Definition: keybinder.qc:347
const int MAX_KEYS_PER_FUNCTION
Definition: keybinder.qc:11
const int WEP_FLAG_SUPERWEAPON
Definition: weapon.qh:202