Revert "fix: we can have more priorities than "real" weapons (consider berserk knuckl...
[d2df-sdl.git] / src / game / g_holmes_cmd.inc
blob274a50e6e1ec1c33a9f5d7e1d11fdd0488bfd99e
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;
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;
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;
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;
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;
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;
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;
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);
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;
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;
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;