DEADSOFTWARE

274a50e6e1ec1c33a9f5d7e1d11fdd0488bfd99e
[d2df-sdl.git] / src / game / g_holmes_cmd.inc
1 (* Copyright (C) Doom 2D: Forever Developers
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
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.
12 *
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/>.
15 *)
16 // ////////////////////////////////////////////////////////////////////////// //
17 type
18 TBindArgLessCB = procedure ();
19 TBindToggleCB = procedure (arg: Integer); // -1: no arg
20 TBindStringCB = procedure (s: AnsiString);
22 PHolmesCommand = ^THolmesCommand;
23 THolmesCommand = record
24 public
25 type TType = (TArgLess, TToggle, TString);
27 public
28 name: AnsiString;
29 help: AnsiString;
30 section: AnsiString;
31 cb: Pointer;
32 ctype: TType;
33 helpmark: Boolean;
35 // command name already taken
36 procedure execute (pr: TTextParser);
37 end;
39 PHolmesBinding = ^THolmesBinding;
40 THolmesBinding = record
41 key: AnsiString; // or mouse
42 cmd: AnsiString;
44 function cmdName (): AnsiString;
45 end;
47 TCmdHash = specialize THashBase<AnsiString, PHolmesCommand, THashKeyStr>;
50 // ////////////////////////////////////////////////////////////////////////// //
51 function THolmesBinding.cmdName (): AnsiString;
52 var
53 pr: TTextParser = nil;
54 begin
55 result := '';
56 try
57 pr := TStrTextParser.Create(cmd);
58 if (pr.tokType = pr.TTStr) then result := pr.expectStr(false) else result := pr.expectId();
59 except on E: Exception do
60 begin
61 result := '';
62 end;
63 end;
64 pr.Free();
65 end;
68 // ////////////////////////////////////////////////////////////////////////// //
69 // command name already taken
70 procedure THolmesCommand.execute (pr: TTextParser);
71 var
72 a: Integer = -1;
73 s: AnsiString = '';
74 begin
75 if not assigned(cb) then exit;
76 case ctype of
77 TType.TToggle:
78 begin
79 if (pr.tokType <> pr.TTEOF) then
80 begin
81 if pr.eatId('true') or pr.eatId('tan') or pr.eatId('yes') then a := 1
82 else if pr.eatId('false') or pr.eatId('ona') or pr.eatId('no') then a := 0
83 else begin conwritefln('%s: invalid argument', [name]); exit; end;
84 if (pr.tokType <> pr.TTEOF) then begin conwritefln('%s: too many arguments', [name]); exit; end;
85 end;
86 TBindToggleCB(cb)(a);
87 end;
88 TType.TArgLess:
89 begin
90 if (pr.tokType <> pr.TTEOF) then begin conwritefln('%s: too many arguments', [name]); exit; end;
91 TBindArgLessCB(cb)();
92 end;
93 TType.TString:
94 begin
95 if (pr.tokType <> pr.TTEOF) then
96 begin
97 if (pr.tokType = pr.TTStr) then s := pr.expectStr(false) else s := pr.expectId;
98 if (pr.tokType <> pr.TTEOF) then begin conwritefln('%s: too many arguments', [name]); exit; end;
99 end
100 else
101 begin
102 conwritefln('%s: string argument expected', [name]);
103 exit;
104 end;
105 TBindStringCB(cb)(s);
106 end;
107 else assert(false);
108 end;
109 end;
112 // ////////////////////////////////////////////////////////////////////////// //
113 function hashNewCommand (): TCmdHash;
114 begin
115 result := TCmdHash.Create();
116 end;
119 // ////////////////////////////////////////////////////////////////////////// //
120 type
121 PHBA = ^THBA;
122 THBA = array of THolmesBinding;
125 var
126 cmdlist: TCmdHash = nil;
127 keybinds: THBA = nil;
128 msbinds: THBA = nil;
129 keybindsInited: Boolean = false;
132 // ////////////////////////////////////////////////////////////////////////// //
133 function cmdNewInternal (const aname: AnsiString; const ahelp: AnsiString; const asection: AnsiString): PHolmesCommand;
134 begin
135 if (cmdlist = nil) then cmdlist := hashNewCommand();
136 if not cmdlist.get(aname, result) then
137 begin
138 GetMem(result, sizeof(THolmesCommand));
139 FillChar(result^, sizeof(THolmesCommand), 0);
140 result.name := aname;
141 result.help := ahelp;
142 result.section := asection;
143 result.cb := nil;
144 result.ctype := result.TType.TArgLess;
145 cmdlist.put(aname, result);
146 end
147 else
148 begin
149 result.help := ahelp;
150 result.section := asection;
151 end;
152 end;
155 // ////////////////////////////////////////////////////////////////////////// //
156 procedure cmdAdd (const aname: AnsiString; cb: TBindArgLessCB; const ahelp: AnsiString; const asection: AnsiString); overload;
157 var
158 cmd: PHolmesCommand;
159 begin
160 if (Length(aname) = 0) or not assigned(cb) then exit;
161 cmd := cmdNewInternal(aname, ahelp, asection);
162 cmd.cb := Pointer(@cb);
163 cmd.ctype := cmd.TType.TArgLess;
164 end;
167 procedure cmdAdd (const aname: AnsiString; cb: TBindToggleCB; const ahelp: AnsiString; const asection: AnsiString); overload;
168 var
169 cmd: PHolmesCommand;
170 begin
171 if (Length(aname) = 0) or not assigned(cb) then exit;
172 cmd := cmdNewInternal(aname, ahelp, asection);
173 cmd.cb := Pointer(@cb);
174 cmd.ctype := cmd.TType.TToggle;
175 end;
178 procedure cmdAdd (const aname: AnsiString; cb: TBindStringCB; const ahelp: AnsiString; const asection: AnsiString); overload;
179 var
180 cmd: PHolmesCommand;
181 begin
182 if (Length(aname) = 0) or not assigned(cb) then exit;
183 cmd := cmdNewInternal(aname, ahelp, asection);
184 cmd.cb := Pointer(@cb);
185 cmd.ctype := cmd.TType.TString;
186 end;
189 // ////////////////////////////////////////////////////////////////////////// //
190 function getCommandHelp (const aname: AnsiString): AnsiString;
191 var
192 cmd: PHolmesCommand = nil;
193 begin
194 if not cmdlist.get(aname, cmd) then result := '' else result := cmd.help;
195 end;
198 function getCommandSection (const aname: AnsiString): AnsiString;
199 var
200 cmd: PHolmesCommand = nil;
201 begin
202 if not cmdlist.get(aname, cmd) then result := '' else result := cmd.section;
203 end;
206 // ////////////////////////////////////////////////////////////////////////// //
207 function internalBindAdd (ba: PHBA; const akey: AnsiString; const acmd: AnsiString): Boolean;
208 var
209 f, c: Integer;
210 begin
211 for f := 0 to High(ba^) do
212 begin
213 if (CompareText(ba^[f].key, akey) = 0) then
214 begin
215 if (Length(acmd) = 0) then
216 begin
217 // remove
218 result := false;
219 for c := f+1 to High(ba^) do ba^[c-1] := ba^[c];
220 SetLength(ba^, Length(ba^)-1);
221 end
222 else
223 begin
224 // replace
225 result := true;
226 ba^[f].cmd := acmd;
227 end;
228 exit;
229 end;
230 end;
231 if (Length(acmd) > 0) then
232 begin
233 result := true;
234 SetLength(ba^, Length(ba^)+1);
235 ba^[High(ba^)].key := akey;
236 ba^[High(ba^)].cmd := acmd;
237 end
238 else
239 begin
240 result := false;
241 end;
242 end;
245 procedure keybindAdd (const akey: AnsiString; const acmd: AnsiString);
246 begin
247 internalBindAdd(@keybinds, akey, acmd);
248 keybindsInited := true;
249 end;
251 procedure msbindAdd (const akey: AnsiString; const acmd: AnsiString);
252 begin
253 internalBindAdd(@msbinds, akey, acmd);
254 keybindsInited := true;
255 end;
258 procedure execCommand (const s: AnsiString);
259 var
260 pr: TTextParser = nil;
261 cmd: AnsiString;
262 cc: PHolmesCommand;
263 begin
264 if (cmdlist = nil) then
265 begin
266 conwriteln('holmes command system is not initialized!');
267 exit;
268 end;
269 try
270 pr := TStrTextParser.Create(s);
271 if (pr.tokType = pr.TTStr) then cmd := pr.expectStr(false) else cmd := pr.expectId();
272 if cmdlist.get(cmd, cc) then cc.execute(pr) else conwritefln('holmes command ''%s'' not found', [cmd]);
273 except on E: Exception do
274 begin
275 conwritefln('error executing holmes command: [%s]', [s]);
276 //conwritefln('* [%s] [%s]', [Integer(pr.tokType), E.message]);
277 end;
278 end;
279 pr.Free();
280 end;
283 function keybindExecute (var ev: TFUIEvent): Boolean;
284 var
285 f: Integer;
286 begin
287 result := false;
288 for f := 0 to High(keybinds) do
289 begin
290 if (ev = keybinds[f].key) then
291 begin
292 result := true;
293 //conwritefln('found command [%s] for keybind <%s>', [keybinds[f].cmd, keybinds[f].key]);
294 execCommand(keybinds[f].cmd);
295 exit;
296 end;
297 end;
298 end;
301 function msbindExecute (var ev: TFUIEvent): Boolean;
302 var
303 f: Integer;
304 begin
305 result := false;
306 for f := 0 to High(msbinds) do
307 begin
308 if (ev = msbinds[f].key) then
309 begin
310 result := true;
311 execCommand(msbinds[f].cmd);
312 exit;
313 end;
314 end;
315 end;