89 while (code <= cKeyMaxIndex) and (KeyNames[code] <> name) do inc(code); |
92 while (code <= cKeyMaxIndex) and (KeyNames[code] <> name) do inc(code); |
90 |
93 |
91 MaskModifier(Modifier, code); |
94 MaskModifier(Modifier, code); |
92 KeyNameToCode:= code; |
95 KeyNameToCode:= code; |
93 end; |
96 end; |
|
97 |
|
98 // Takes a control name (e.g. 'quit') and returns the corresponding key code, |
|
99 // if it has been bound. |
|
100 // Returns -1 if the control has not been bound. |
|
101 function KeyBindToCode(bind: shortstring): LongInt; |
|
102 var code, index: LongInt; |
|
103 begin |
|
104 index:= 0; |
|
105 while (index <= High(CurrentBinds.binds)) and (CurrentBinds.binds[index] <> bind) do inc(index); |
|
106 if index > High(CurrentBinds.binds) then |
|
107 // Return error |
|
108 KeyBindToCode:= -1 |
|
109 else begin |
|
110 code:= 0; |
|
111 while (code <= High(CurrentBinds.indices)) and (CurrentBinds.indices[code] <> index) do inc(code); |
|
112 checkFails(code <= High(CurrentBinds.indices), 'binds registry inconsistency', True); |
|
113 KeyBindToCode:= code; |
|
114 end; |
|
115 end; |
|
116 |
|
117 // Takes a control name (e.g. 'quit') and returns the corresponding |
|
118 // human-readable key name from SDL. |
|
119 // FIXME: Does not work 100% for all keys yet, but at least it no |
|
120 // longer hardcodes any key name. |
|
121 // TODO: Localize |
|
122 function KeyBindToName(bind: shortstring): shortstring; |
|
123 var code: LongInt; |
|
124 name: shortstring; |
|
125 begin |
|
126 code:= KeyBindToCode(bind); |
|
127 if code = -1 then |
|
128 KeyBindToName:= trmsg[sidUnknownKey] |
|
129 else |
|
130 begin |
|
131 name:= SDL_GetKeyName(SDL_GetKeyFromScancode(code)); |
|
132 if (name = 'Escape') then |
|
133 // Let's shorten the name “Escape” for the quit menu |
|
134 KeyBindToName:= 'Esc' |
|
135 else if (length(name) <> 0) then |
|
136 KeyBindToName:= name |
|
137 else |
|
138 begin |
|
139 WriteLnToConsole('Error: KeyBindToName('+bind+') failed to find SDL key name!'); |
|
140 KeyBindToName:= trmsg[sidUnknownKey]; |
|
141 end; |
|
142 end; |
|
143 end; |
|
144 |
94 (* |
145 (* |
95 procedure MaskModifier(var code: LongInt; Modifier: LongWord); |
146 procedure MaskModifier(var code: LongInt; Modifier: LongWord); |
96 begin |
147 begin |
97 if(Modifier and KMOD_LSHIFT) <> 0 then code:= code or LSHIFT; |
148 if(Modifier and KMOD_LSHIFT) <> 0 then code:= code or LSHIFT; |
98 if(Modifier and KMOD_RSHIFT) <> 0 then code:= code or LSHIFT; |
149 if(Modifier and KMOD_RSHIFT) <> 0 then code:= code or LSHIFT; |
160 // on OS X it this is expected behaviour |
211 // on OS X it this is expected behaviour |
161 if tkbd[KeyNameToCode('left_meta')] or tkbd[KeyNameToCode('right_meta')] then |
212 if tkbd[KeyNameToCode('left_meta')] or tkbd[KeyNameToCode('right_meta')] then |
162 {$ELSE} |
213 {$ELSE} |
163 // on other systems use this shortcut only if the keys are not bound to any command |
214 // on other systems use this shortcut only if the keys are not bound to any command |
164 if tkbd[KeyNameToCode('left_ctrl')] or tkbd[KeyNameToCode('right_ctrl')] then |
215 if tkbd[KeyNameToCode('left_ctrl')] or tkbd[KeyNameToCode('right_ctrl')] then |
165 if ((CurrentBinds[KeyNameToCode('left_ctrl')] = '') or |
216 if ((CurrentBinds.indices[KeyNameToCode('left_ctrl')] = 0) or |
166 (CurrentBinds[KeyNameToCode('right_ctrl')] = '')) and |
217 (CurrentBinds.indices[KeyNameToCode('right_ctrl')] = 0)) and |
167 (CurrentBinds[SDLK_w] = '') then |
218 (CurrentBinds.indices[SDLK_w] = 0) then |
168 {$ENDIF} |
219 {$ENDIF} |
169 ParseCommand('forcequit', true); |
220 ParseCommand('forcequit', true); |
170 end; |
221 end; |
171 |
222 |
172 if CurrentBinds[code][0] <> #0 then |
223 if CurrentBinds.indices[code] > 0 then |
173 begin |
224 begin |
174 if (code < cKeyMaxIndex - 2) // means not mouse buttons |
225 if (code < cKeyMaxIndex - 2) // means not mouse buttons |
175 and KeyDown |
226 and KeyDown |
176 and (not ((CurrentBinds[code] = 'put') or (CurrentBinds[code] = 'ammomenu') or (CurrentBinds[code] = '+cur_u') or (CurrentBinds[code] = '+cur_d') or (CurrentBinds[code] = '+cur_l') or (CurrentBinds[code] = '+cur_r'))) |
227 and (not ((CurrentBinds.binds[CurrentBinds.indices[code]] = 'put') |
|
228 or (CurrentBinds.binds[CurrentBinds.indices[code]] = 'ammomenu') |
|
229 or (CurrentBinds.binds[CurrentBinds.indices[code]] = '+cur_u') |
|
230 or (CurrentBinds.binds[CurrentBinds.indices[code]] = '+cur_d') |
|
231 or (CurrentBinds.binds[CurrentBinds.indices[code]] = '+cur_l') |
|
232 or (CurrentBinds.binds[CurrentBinds.indices[code]] = '+cur_r'))) |
177 and (CurrentTeam <> nil) |
233 and (CurrentTeam <> nil) |
178 and (not CurrentTeam^.ExtDriven) |
234 and (not CurrentTeam^.ExtDriven) |
179 then bShowAmmoMenu:= false; |
235 then bShowAmmoMenu:= false; |
180 |
236 |
181 if KeyDown then |
237 if KeyDown then |
182 begin |
238 begin |
183 Trusted:= Trusted and (not isPaused); //releasing keys during pause should be allowed on the other hand |
239 Trusted:= Trusted and (not isPaused); //releasing keys during pause should be allowed on the other hand |
184 |
240 |
185 if CurrentBinds[code] = 'switch' then |
241 if CurrentBinds.binds[CurrentBinds.indices[code]] = 'switch' then |
186 LocalMessage:= LocalMessage or gmSwitch |
242 LocalMessage:= LocalMessage or gmSwitch |
187 else if CurrentBinds[code] = '+precise' then |
243 else if CurrentBinds.binds[CurrentBinds.indices[code]] = '+precise' then |
188 LocalMessage:= LocalMessage or gmPrecise; |
244 LocalMessage:= LocalMessage or gmPrecise; |
189 |
245 |
190 ParseCommand(CurrentBinds[code], Trusted); |
246 ParseCommand(CurrentBinds.binds[CurrentBinds.indices[code]], Trusted); |
191 if (CurrentTeam <> nil) and (not CurrentTeam^.ExtDriven) and (ReadyTimeLeft > 1) then |
247 if (CurrentTeam <> nil) and (not CurrentTeam^.ExtDriven) and (ReadyTimeLeft > 1) then |
192 ParseCommand('gencmd R', true) |
248 ParseCommand('gencmd R', true) |
193 end |
249 end |
194 else if (CurrentBinds[code][1] = '+') then |
250 else if (CurrentBinds.binds[CurrentBinds.indices[code]][1] = '+') then |
195 begin |
251 begin |
196 if CurrentBinds[code] = '+precise' then |
252 if CurrentBinds.binds[CurrentBinds.indices[code]] = '+precise' then |
197 LocalMessage:= LocalMessage and (not gmPrecise); |
253 LocalMessage:= LocalMessage and (not gmPrecise); |
198 s:= CurrentBinds[code]; |
254 s:= CurrentBinds.binds[CurrentBinds.indices[code]]; |
199 s[1]:= '-'; |
255 s[1]:= '-'; |
200 ParseCommand(s, Trusted); |
256 ParseCommand(s, Trusted); |
201 if (CurrentTeam <> nil) and (not CurrentTeam^.ExtDriven) and (ReadyTimeLeft > 1) then |
257 if (CurrentTeam <> nil) and (not CurrentTeam^.ExtDriven) and (ReadyTimeLeft > 1) then |
202 ParseCommand('gencmd R', true) |
258 ParseCommand('gencmd R', true) |
203 end |
259 end |
204 else |
260 else |
205 begin |
261 begin |
206 if CurrentBinds[code] = 'switch' then |
262 if CurrentBinds.binds[CurrentBinds.indices[code]] = 'switch' then |
207 LocalMessage:= LocalMessage and (not gmSwitch) |
263 LocalMessage:= LocalMessage and (not gmSwitch) |
208 end |
264 end |
209 end |
265 end |
210 end; |
266 end; |
211 |
267 |
272 for t:= 0 to cKbdMaxIndex do |
328 for t:= 0 to cKbdMaxIndex do |
273 if tkbd[t] then |
329 if tkbd[t] then |
274 ProcessKey(t, False); |
330 ProcessKey(t, False); |
275 end; |
331 end; |
276 |
332 |
|
333 procedure RegisterBind(var binds: TBinds; key, value: shortstring); |
|
334 var code: LongInt; |
|
335 begin |
|
336 checkFails(binds.lastIndex < 255, 'too many binds', true); |
|
337 |
|
338 code:= KeyNameToCode(key); |
|
339 |
|
340 checkFails(code >= 0, 'unknown key', true); |
|
341 |
|
342 if binds.indices[code] > 0 then |
|
343 begin |
|
344 binds.binds[binds.indices[code]]:= value |
|
345 end |
|
346 else begin |
|
347 inc(binds.lastIndex); |
|
348 binds.indices[code]:= binds.lastIndex; |
|
349 binds.binds[binds.indices[code]]:= value |
|
350 end; |
|
351 end; |
277 |
352 |
278 procedure InitDefaultBinds; |
353 procedure InitDefaultBinds; |
279 var i: Longword; |
354 var i: Longword; |
280 begin |
355 begin |
281 DefaultBinds[KeyNameToCode('escape')]:= 'quit'; |
356 RegisterBind(DefaultBinds, 'escape', 'quit'); |
282 DefaultBinds[KeyNameToCode(_S'`')]:= 'history'; |
357 RegisterBind(DefaultBinds, _S'`', 'history'); |
283 DefaultBinds[KeyNameToCode('delete')]:= 'rotmask'; |
358 RegisterBind(DefaultBinds, 'delete', 'rotmask'); |
|
359 RegisterBind(DefaultBinds, 'home', 'rottags'); |
284 |
360 |
285 //numpad |
361 //numpad |
286 //DefaultBinds[265]:= '+volup'; |
362 //DefaultBinds[265]:= '+volup'; |
287 //DefaultBinds[256]:= '+voldown'; |
363 //DefaultBinds[256]:= '+voldown'; |
288 |
364 |
289 DefaultBinds[KeyNameToCode(_S'0')]:= '+volup'; |
365 RegisterBind(DefaultBinds, _S'0', '+volup'); |
290 DefaultBinds[KeyNameToCode(_S'9')]:= '+voldown'; |
366 RegisterBind(DefaultBinds, _S'9', '+voldown'); |
291 DefaultBinds[KeyNameToCode(_S'8')]:= 'mute'; |
367 RegisterBind(DefaultBinds, _S'8', 'mute'); |
292 DefaultBinds[KeyNameToCode(_S'c')]:= 'capture'; |
368 RegisterBind(DefaultBinds, _S'c', 'capture'); |
293 DefaultBinds[KeyNameToCode(_S'r')]:= 'record'; |
369 RegisterBind(DefaultBinds, _S'r', 'record'); |
294 DefaultBinds[KeyNameToCode(_S'h')]:= 'findhh'; |
370 RegisterBind(DefaultBinds, _S'h', 'findhh'); |
295 DefaultBinds[KeyNameToCode(_S'p')]:= 'pause'; |
371 RegisterBind(DefaultBinds, _S'p', 'pause'); |
296 DefaultBinds[KeyNameToCode(_S's')]:= '+speedup'; |
372 RegisterBind(DefaultBinds, _S's', '+speedup'); |
297 DefaultBinds[KeyNameToCode(_S't')]:= 'chat'; |
373 RegisterBind(DefaultBinds, _S't', 'chat'); |
298 DefaultBinds[KeyNameToCode(_S'y')]:= 'confirm'; |
374 RegisterBind(DefaultBinds, _S'y', 'confirm'); |
299 |
375 |
300 DefaultBinds[KeyNameToCode('mousem')]:= 'zoomreset'; |
376 RegisterBind(DefaultBinds, 'mousem', 'zoomreset'); |
301 DefaultBinds[KeyNameToCode('wheelup')]:= 'zoomin'; |
377 RegisterBind(DefaultBinds, 'wheelup', 'zoomin'); |
302 DefaultBinds[KeyNameToCode('wheeldown')]:= 'zoomout'; |
378 RegisterBind(DefaultBinds, 'wheeldown', 'zoomout'); |
303 |
379 |
304 DefaultBinds[KeyNameToCode('f12')]:= 'fullscr'; |
380 RegisterBind(DefaultBinds, 'f12', 'fullscr'); |
305 |
381 |
306 |
382 |
307 DefaultBinds[KeyNameToCode('mousel')]:= '/put'; |
383 RegisterBind(DefaultBinds, 'mousel', '/put'); |
308 DefaultBinds[KeyNameToCode('mouser')]:= 'ammomenu'; |
384 RegisterBind(DefaultBinds, 'mouser', 'ammomenu'); |
309 DefaultBinds[KeyNameToCode('backspace')]:= 'hjump'; |
385 RegisterBind(DefaultBinds, 'backspace', 'hjump'); |
310 DefaultBinds[KeyNameToCode('tab')]:= 'switch'; |
386 RegisterBind(DefaultBinds, 'tab', 'switch'); |
311 DefaultBinds[KeyNameToCode('return')]:= 'ljump'; |
387 RegisterBind(DefaultBinds, 'return', 'ljump'); |
312 DefaultBinds[KeyNameToCode('space')]:= '+attack'; |
388 RegisterBind(DefaultBinds, 'space', '+attack'); |
313 DefaultBinds[KeyNameToCode('up')]:= '+up'; |
389 RegisterBind(DefaultBinds, 'up', '+up'); |
314 DefaultBinds[KeyNameToCode('down')]:= '+down'; |
390 RegisterBind(DefaultBinds, 'down', '+down'); |
315 DefaultBinds[KeyNameToCode('left')]:= '+left'; |
391 RegisterBind(DefaultBinds, 'left', '+left'); |
316 DefaultBinds[KeyNameToCode('right')]:= '+right'; |
392 RegisterBind(DefaultBinds, 'right', '+right'); |
317 DefaultBinds[KeyNameToCode('left_shift')]:= '+precise'; |
393 RegisterBind(DefaultBinds, 'left_shift', '+precise'); |
318 |
394 |
319 |
395 |
320 DefaultBinds[KeyNameToCode('j0a0u')]:= '+left'; |
396 RegisterBind(DefaultBinds, 'j0a0u', '+left'); |
321 DefaultBinds[KeyNameToCode('j0a0d')]:= '+right'; |
397 RegisterBind(DefaultBinds, 'j0a0d', '+right'); |
322 DefaultBinds[KeyNameToCode('j0a1u')]:= '+up'; |
398 RegisterBind(DefaultBinds, 'j0a1u', '+up'); |
323 DefaultBinds[KeyNameToCode('j0a1d')]:= '+down'; |
399 RegisterBind(DefaultBinds, 'j0a1d', '+down'); |
324 for i:= 1 to 10 do DefaultBinds[KeyNameToCode('f'+IntToStr(i))]:= 'slot '+char(48+i); |
400 for i:= 1 to 10 do RegisterBind(DefaultBinds, 'f'+IntToStr(i), 'slot '+char(48+i)); |
325 for i:= 1 to 5 do DefaultBinds[KeyNameToCode(IntToStr(i))]:= 'timer '+IntToStr(i); |
401 for i:= 1 to 5 do RegisterBind(DefaultBinds, IntToStr(i), 'timer '+IntToStr(i)); |
326 |
402 |
327 loadBinds('dbind', cPathz[ptConfig] + '/settings.ini'); |
403 loadBinds('dbind', cPathz[ptConfig] + '/settings.ini'); |
328 end; |
404 end; |
329 |
405 |
330 |
406 |
577 end; |
653 end; |
578 |
654 |
579 |
655 |
580 procedure addBind(var binds: TBinds; var id: shortstring); |
656 procedure addBind(var binds: TBinds; var id: shortstring); |
581 var KeyName, Modifier, tmp: shortstring; |
657 var KeyName, Modifier, tmp: shortstring; |
582 i, b: LongInt; |
658 i, newCode, code, b: LongInt; |
583 begin |
659 begin |
584 KeyName:= ''; |
660 KeyName:= ''; |
585 Modifier:= ''; |
661 Modifier:= ''; |
586 |
662 |
587 if(Pos('mod:', id) <> 0)then |
663 if(Pos('mod:', id) <> 0)then |
|
664 begin |
|
665 tmp:= ''; |
|
666 SplitBySpace(id, tmp); |
|
667 Modifier:= id; |
|
668 id:= tmp; |
|
669 end; |
|
670 |
|
671 SplitBySpace(id, KeyName); |
|
672 if KeyName[1]='"' then |
|
673 Delete(KeyName, 1, 1); |
|
674 if KeyName[byte(KeyName[0])]='"' then |
|
675 Delete(KeyName, byte(KeyName[0]), 1); |
|
676 b:= KeyNameToCode(id, Modifier); |
|
677 if b = 0 then |
|
678 OutError(errmsgUnknownVariable + ' "' + id + '"', false) |
|
679 else |
588 begin |
680 begin |
589 tmp:= ''; |
681 // add bind: first check if this cmd is already bound, and remove old bind |
590 SplitBySpace(id, tmp); |
682 i:= Low(binds.binds); |
591 Modifier:= id; |
683 while (i <= High(binds.binds)) and (binds.binds[i] <> KeyName) do |
592 id:= tmp; |
684 inc(i); |
593 end; |
685 |
594 |
686 if (i <= High(binds.binds)) then |
595 SplitBySpace(id, KeyName); |
687 begin |
596 if KeyName[1]='"' then |
688 code:= Low(binds.indices); |
597 Delete(KeyName, 1, 1); |
689 while (code <= High(binds.indices)) and (binds.indices[code] <> i) do |
598 if KeyName[byte(KeyName[0])]='"' then |
690 inc(code); |
599 Delete(KeyName, byte(KeyName[0]), 1); |
691 |
600 b:= KeyNameToCode(id, Modifier); |
692 checkFails(code <= High(binds.indices), 'binds registry inconsistency', true); |
601 if b = 0 then |
693 |
602 OutError(errmsgUnknownVariable + ' "' + id + '"', false) |
694 binds.indices[code]:= 0; |
603 else |
695 binds.binds[i]:= '' |
604 begin |
696 end; |
605 // add bind: first check if this cmd is already bound, and remove old bind |
697 |
606 i:= cKbdMaxIndex; |
698 if binds.indices[b] > 0 then |
607 repeat |
699 newCode:= binds.indices[b] |
608 dec(i) |
700 else if i >= High(binds.binds) then |
609 until (i < 0) or (binds[i] = KeyName); |
701 begin |
610 if (i >= 0) then |
702 inc(binds.lastIndex); |
611 binds[i]:= ''; |
703 checkFails(binds.lastIndex < High(binds.binds), 'too many binds', true); |
612 |
704 newCode:= binds.lastIndex |
613 binds[b]:= KeyName; |
705 end else |
|
706 newCode:= i; |
|
707 |
|
708 |
|
709 binds.indices[b]:= newCode; |
|
710 binds.binds[binds.indices[b]]:= KeyName |
614 end |
711 end |
615 end; |
712 end; |
616 |
713 |
617 // Bind that isn't a team bind, but overrides defaultbinds. |
714 // Bind that isn't a team bind, but overrides defaultbinds. |
618 procedure chDefaultBind(var id: shortstring); |
715 procedure chDefaultBind(var id: shortstring); |