1 /* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, version 3 of the License ONLY.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 module iv
.cmdcon
.keybinds
/*is aliced*/;
19 import arsd
.simpledisplay
: KeyEvent
, Key
;
25 // ////////////////////////////////////////////////////////////////////////// //
46 registerBindCommands!Key;
48 // then in KeyEvent handler:
49 if (PlayerBinds.processKeyEvent(event)) return;
51 // and in frame handler:
52 if (PlayerBinds[Key.Forward]) doAction();
54 PlayerBinds.frameComplete();
58 // ////////////////////////////////////////////////////////////////////////// //
59 public struct PlayerBinds
{
61 __gshared
ubyte[256] keys
; // max 256 actions
63 static public @trusted:
64 /// this will put command in console command queue and return `true`, or do nothing and return `false`
65 bool processKeyEvent() (in auto ref KeyEvent event
) {
66 if (auto pkp
= event
.key
in (event
.pressed ? boundkeysDown
: boundkeysUp
)) {
68 if (cmd
.length
) concmd(cmd
);
75 /// call this in frame handler
76 void frameComplete () {
77 foreach (ref k
; keys
[]) k
&= ((k
>>1)&0x01)^
1;
80 bool opIndex (int idx
) {
82 return (idx
>= 0 && idx
< keys
.length ?
((keys
.ptr
[idx
]&0x01) != 0) : false);
86 void opIndexAssign (bool down
, int idx
) {
88 if (idx
>= 0 && idx
< keys
.length
) keys
.ptr
[idx
] |
= (down ?
1 : 2);
93 // ////////////////////////////////////////////////////////////////////////// //
94 __gshared string
[Key
] boundkeysDown
;
95 __gshared string
[Key
] boundkeysUp
;
98 // ////////////////////////////////////////////////////////////////////////// //
99 public void clearBindings () {
100 boundkeysDown
.clear();
105 public void saveBindings (scope void delegate (scope ConString s
) wdg
) {
106 if (wdg
is null) return;
107 wdg("bind_clear_all\n");
108 foreach (ref kv
; boundkeysDown
.byKeyValue
) {
109 if (kv
.key
== cast(Key
)0) continue;
112 foreach (string nm
; __traits(allMembers
, Key
)) {
113 if (__traits(getMember
, Key
, nm
) == kv
.key
) { ksname
= nm
; break; }
115 if (ksname
.length
== 0) continue;
116 if (kv
.value
.length
== 0) continue;
117 if (ksname
.length
== 2 && ksname
[0] == 'N' && ksname
[1] >= '0' && ksname
[1] <= '9') ksname
= ksname
[1..$];
118 if (kv
.value
[0] == '+') {
119 if (auto kup
= kv
.key
in boundkeysUp
) {
121 if (uv
.length
== kv
.value
.length
&& uv
[0] == '-' && kv
.value
[1..$] == uv
[1..$]) {
123 ConCommand
.quoteStringDG(ksname
, wdg
);
125 ConCommand
.quoteStringDG(kv
.value
, wdg
);
133 ConCommand
.quoteStringDG
!true(ksname
, delegate (scope ConString s
) {
134 if (s
.length
== 0) return; // just in case
138 if (s
.length
> 1) wdg(s
[1..$]);
145 ConCommand
.quoteStringDG(kv
.value
, wdg
);
148 if (auto kup
= kv
.key
in boundkeysUp
) {
150 if (uv
.length
== 0) continue; // just in case
153 ConCommand
.quoteStringDG
!true(ksname
, delegate (scope ConString s
) {
154 if (s
.length
== 0) return; // just in case
158 if (s
.length
> 1) wdg(s
[1..$]);
165 ConCommand
.quoteStringDG(uv
, wdg
);
172 // ////////////////////////////////////////////////////////////////////////// //
173 public enum KeyState
{ All
, Down
, Up
}
176 public Key
findKeyByName (ConString name
, KeyState
* ks
=null) {
178 if (ks
!is null) *ks
= KeyState
.All
;
179 if (name
.length
&& name
[0] == '+') {
180 if (ks
!is null) *ks
= KeyState
.Down
;
182 } else if (name
.length
&& name
[0] == '-') {
183 if (ks
!is null) *ks
= KeyState
.Up
;
187 foreach (string kn
; __traits(allMembers
, Key
)) {
188 if (strEquCI(name
, kn
)) return __traits(getMember
, Key
, kn
);
190 if (name
.length
== 1 && name
[0] >= '1' && name
[0] <= '9') return cast(Key
)(Key
.N1
+name
[0]-'1');
191 if (name
.length
== 1 && name
[0] == '0') return Key
.N0
;
193 return cast(Key
)0; // HACK!
197 // ////////////////////////////////////////////////////////////////////////// //
198 public void registerBindCommands(ET
) () if (is(ET
== enum)) {
199 foreach (string nm
; __traits(allMembers
, ET
)) {
200 enum v
= __traits(getMember
, ET
, nm
);
202 foreach (char ch
; nm
) ls
~= ch
.tolower
;
203 conRegFunc
!(() { PlayerBinds
[v
] = true; })("+"~ls
, "start '"~ls
~"' action");
204 conRegFunc
!(() { PlayerBinds
[v
] = false; })("-"~ls
, "stop '"~ls
~"' action");
207 conRegFunc
!clearBindings("bind_clear_all", "remove all command bindings");
209 conRegFunc
!((ConFuncVA va
) {
210 auto key
= ConCommand
.getWord(va
.cmdline
);
211 if (key
.length
== 0) { conwriteln("bind: empty key!"); return; }
212 // strip spaces from cmdline
213 va
.cmdline
= va
.cmdline
.xstrip
;
214 if (va
.cmdline
.length
>= 2 && va
.cmdline
[0] == '"' && va
.cmdline
[$-1] == '"') {
215 va
.cmdline
= va
.cmdline
[1..$-1];
216 va
.cmdline
= va
.cmdline
.xstrip
;
218 if (va
.cmdline
.length
== 0) { conwriteln("bind: empty command!"); return; }
220 auto kk
= findKeyByName(key
, &ks
);
221 if (kk
== 0) { conwriteln("bind: unknown key '", key
, "'"); return; }
224 if (va
.cmdline
[0] == '+') {
225 // "+command": automatically add "-command" for releasing
226 if (va
.cmdline
.length
== 1) { conwriteln("bind: empty command!"); return; }
227 char[] c0
= va
.cmdline
.dup
;
228 char[] c1
= va
.cmdline
.dup
;
230 boundkeysDown
[kk
] = cast(string
)c0
; // it is safe to cast here
231 boundkeysUp
[kk
] = cast(string
)c1
; // it is safe to cast here
233 // "command": remove releasing action
234 boundkeysDown
[kk
] = va
.cmdline
.idup
;
235 boundkeysUp
.remove(kk
);
239 boundkeysDown
[kk
] = va
.cmdline
.idup
;
242 boundkeysUp
[kk
] = va
.cmdline
.idup
;
245 })("bind", "bind key to action(s)");
247 conRegFunc
!((ConString key
) {
248 if (key
.length
== 0) { conwriteln("unbind: empty key!"); return; }
250 auto kk
= findKeyByName(key
, &ks
);
251 if (kk
== 0) { conwriteln("unbind: unknown key '", key
, "'"); return; }
254 boundkeysDown
.remove(kk
);
255 boundkeysUp
.remove(kk
);
258 boundkeysDown
.remove(kk
);
261 boundkeysUp
.remove(kk
);
264 })("unbind", "unbind key");