some updates
[iv.d.git] / cmdcon / keybinds.d
blob3ee678f8eff938309788cffac2ab9c601d62cd06
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*/;
17 private:
19 import arsd.simpledisplay : KeyEvent, Key;
21 import iv.cmdcon;
22 import iv.strex;
25 // ////////////////////////////////////////////////////////////////////////// //
27 enum Key {
28 Forward,
29 Backward,
30 TurnLeft,
31 TurnRight,
32 StrafeLeft,
33 StrafeRight,
34 Attack,
35 Jump,
36 Run,
37 ExtraRun,
38 NoClip,
39 LookUp,
40 LookDown,
41 LookCenter,
42 //LookXLeft,
43 //LookXRight,
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();
53 ...
54 PlayerBinds.frameComplete();
58 // ////////////////////////////////////////////////////////////////////////// //
59 public struct PlayerBinds {
60 private:
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)) {
67 auto cmd = *pkp;
68 if (cmd.length) concmd(cmd);
69 return true;
71 return false;
74 nothrow @nogc:
75 /// call this in frame handler
76 void frameComplete () {
77 foreach (ref k; keys[]) k &= ((k>>1)&0x01)^1;
80 bool opIndex (int idx) {
81 pragma(inline, true);
82 return (idx >= 0 && idx < keys.length ? ((keys.ptr[idx]&0x01) != 0) : false);
85 /// don't use this
86 void opIndexAssign (bool down, int idx) {
87 pragma(inline, true);
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();
101 boundkeysUp.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;
110 wdg("bind ");
111 string ksname;
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) {
120 string uv = *kup;
121 if (uv.length == kv.value.length && uv[0] == '-' && kv.value[1..$] == uv[1..$]) {
122 // "all"
123 ConCommand.quoteStringDG(ksname, wdg);
124 wdg(" ");
125 ConCommand.quoteStringDG(kv.value, wdg);
126 wdg("\n");
127 continue;
131 // down
132 bool putPfx = true;
133 ConCommand.quoteStringDG!true(ksname, delegate (scope ConString s) {
134 if (s.length == 0) return; // just in case
135 if (putPfx) {
136 assert(s[0] == '"');
137 wdg(`"+`);
138 if (s.length > 1) wdg(s[1..$]);
139 putPfx = false;
140 } else {
141 wdg(s);
144 wdg(` `);
145 ConCommand.quoteStringDG(kv.value, wdg);
146 wdg("\n");
147 // up
148 if (auto kup = kv.key in boundkeysUp) {
149 string uv = *kup;
150 if (uv.length == 0) continue; // just in case
151 wdg(`bind `);
152 putPfx = true;
153 ConCommand.quoteStringDG!true(ksname, delegate (scope ConString s) {
154 if (s.length == 0) return; // just in case
155 if (putPfx) {
156 assert(s[0] == '"');
157 wdg(`"-`);
158 if (s.length > 1) wdg(s[1..$]);
159 putPfx = false;
160 } else {
161 wdg(s);
164 wdg(` `);
165 ConCommand.quoteStringDG(uv, wdg);
166 wdg("\n");
172 // ////////////////////////////////////////////////////////////////////////// //
173 public enum KeyState { All, Down, Up }
175 // 0: invalid
176 public Key findKeyByName (ConString name, KeyState* ks=null) {
177 auto anchor = name;
178 if (ks !is null) *ks = KeyState.All;
179 if (name.length && name[0] == '+') {
180 if (ks !is null) *ks = KeyState.Down;
181 name = name[1..$];
182 } else if (name.length && name[0] == '-') {
183 if (ks !is null) *ks = KeyState.Up;
184 name = name[1..$];
186 if (name.length) {
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);
201 string ls;
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; }
219 KeyState ks;
220 auto kk = findKeyByName(key, &ks);
221 if (kk == 0) { conwriteln("bind: unknown key '", key, "'"); return; }
222 final switch (ks) {
223 case KeyState.All:
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;
229 c1[0] = '-'; // hack
230 boundkeysDown[kk] = cast(string)c0; // it is safe to cast here
231 boundkeysUp[kk] = cast(string)c1; // it is safe to cast here
232 } else {
233 // "command": remove releasing action
234 boundkeysDown[kk] = va.cmdline.idup;
235 boundkeysUp.remove(kk);
237 break;
238 case KeyState.Down:
239 boundkeysDown[kk] = va.cmdline.idup;
240 break;
241 case KeyState.Up:
242 boundkeysUp[kk] = va.cmdline.idup;
243 break;
245 })("bind", "bind key to action(s)");
247 conRegFunc!((ConString key) {
248 if (key.length == 0) { conwriteln("unbind: empty key!"); return; }
249 KeyState ks;
250 auto kk = findKeyByName(key, &ks);
251 if (kk == 0) { conwriteln("unbind: unknown key '", key, "'"); return; }
252 final switch (ks) {
253 case KeyState.All:
254 boundkeysDown.remove(kk);
255 boundkeysUp.remove(kk);
256 break;
257 case KeyState.Down:
258 boundkeysDown.remove(kk);
259 break;
260 case KeyState.Up:
261 boundkeysUp.remove(kk);
262 break;
264 })("unbind", "unbind key");