ambient light for level (doesn't work with dynamic lights; I. WANT. SHADERS!)
[d2df-sdl.git] / src / game / g_holmes_cmd.inc
blob63b0ae55a83587d0e1118b97633e2053ed3185d2
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>;
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 ansistrEquCB (constref a, b: AnsiString): Boolean; begin result := (a = b); end;
114 function ansistrHashCB (constref a: AnsiString): LongWord; begin if (Length(a) > 0) then result := fnvHash(PChar(a)^, Length(a)) else result := 0; end;
116 function hashNewCommand (): TCmdHash;
117 begin
118   result := TCmdHash.Create(ansistrHashCB, ansistrEquCB);
119 end;
122 // ////////////////////////////////////////////////////////////////////////// //
123 type
124   PHBA = ^THBA;
125   THBA = array of THolmesBinding;
129   cmdlist: TCmdHash = nil;
130   keybinds: THBA = nil;
131   msbinds: THBA = nil;
132   keybindsInited: Boolean = false;
135 // ////////////////////////////////////////////////////////////////////////// //
136 function cmdNewInternal (const aname: AnsiString; const ahelp: AnsiString; const asection: AnsiString): PHolmesCommand;
137 begin
138   if (cmdlist = nil) then cmdlist := hashNewCommand();
139   if not cmdlist.get(aname, result) then
140   begin
141     GetMem(result, sizeof(THolmesCommand));
142     FillChar(result^, sizeof(THolmesCommand), 0);
143     result.name := aname;
144     result.help := ahelp;
145     result.section := asection;
146     result.cb := nil;
147     result.ctype := result.TType.TArgLess;
148     cmdlist.put(aname, result);
149   end
150   else
151   begin
152     result.help := ahelp;
153     result.section := asection;
154   end;
155 end;
158 // ////////////////////////////////////////////////////////////////////////// //
159 procedure cmdAdd (const aname: AnsiString; cb: TBindArgLessCB; const ahelp: AnsiString; const asection: AnsiString); overload;
161   cmd: PHolmesCommand;
162 begin
163   if (Length(aname) = 0) or not assigned(cb) then exit;
164   cmd := cmdNewInternal(aname, ahelp, asection);
165   cmd.cb := Pointer(@cb);
166   cmd.ctype := cmd.TType.TArgLess;
167 end;
170 procedure cmdAdd (const aname: AnsiString; cb: TBindToggleCB; const ahelp: AnsiString; const asection: AnsiString); overload;
172   cmd: PHolmesCommand;
173 begin
174   if (Length(aname) = 0) or not assigned(cb) then exit;
175   cmd := cmdNewInternal(aname, ahelp, asection);
176   cmd.cb := Pointer(@cb);
177   cmd.ctype := cmd.TType.TToggle;
178 end;
181 procedure cmdAdd (const aname: AnsiString; cb: TBindStringCB; const ahelp: AnsiString; const asection: AnsiString); overload;
183   cmd: PHolmesCommand;
184 begin
185   if (Length(aname) = 0) or not assigned(cb) then exit;
186   cmd := cmdNewInternal(aname, ahelp, asection);
187   cmd.cb := Pointer(@cb);
188   cmd.ctype := cmd.TType.TString;
189 end;
192 // ////////////////////////////////////////////////////////////////////////// //
193 function getCommandHelp (const aname: AnsiString): AnsiString;
195   cmd: PHolmesCommand = nil;
196 begin
197   if not cmdlist.get(aname, cmd) then result := '' else result := cmd.help;
198 end;
201 function getCommandSection (const aname: AnsiString): AnsiString;
203   cmd: PHolmesCommand = nil;
204 begin
205   if not cmdlist.get(aname, cmd) then result := '' else result := cmd.section;
206 end;
209 // ////////////////////////////////////////////////////////////////////////// //
210 function internalBindAdd (ba: PHBA; const akey: AnsiString; const acmd: AnsiString): Boolean;
212   f, c: Integer;
213 begin
214   for f := 0 to High(ba^) do
215   begin
216     if (CompareText(ba^[f].key, akey) = 0) then
217     begin
218       if (Length(acmd) = 0) then
219       begin
220         // remove
221         result := false;
222         for c := f+1 to High(ba^) do ba^[c-1] := ba^[c];
223         SetLength(ba^, Length(ba^)-1);
224       end
225       else
226       begin
227         // replace
228         result := true;
229         ba^[f].cmd := acmd;
230       end;
231       exit;
232     end;
233   end;
234   if (Length(acmd) > 0) then
235   begin
236     result := true;
237     SetLength(ba^, Length(ba^)+1);
238     ba^[High(ba^)].key := akey;
239     ba^[High(ba^)].cmd := acmd;
240   end
241   else
242   begin
243     result := false;
244   end;
245 end;
248 procedure keybindAdd (const akey: AnsiString; const acmd: AnsiString);
249 begin
250   internalBindAdd(@keybinds, akey, acmd);
251   keybindsInited := true;
252 end;
254 procedure msbindAdd (const akey: AnsiString; const acmd: AnsiString);
255 begin
256   internalBindAdd(@msbinds, akey, acmd);
257   keybindsInited := true;
258 end;
261 procedure execCommand (const s: AnsiString);
263   pr: TTextParser = nil;
264   cmd: AnsiString;
265   cc: PHolmesCommand;
266 begin
267   if (cmdlist = nil) then
268   begin
269     conwriteln('holmes command system is not initialized!');
270     exit;
271   end;
272   try
273     pr := TStrTextParser.Create(s);
274     if (pr.tokType = pr.TTStr) then cmd := pr.expectStr(false) else cmd := pr.expectId();
275     if cmdlist.get(cmd, cc) then cc.execute(pr) else conwritefln('holmes command ''%s'' not found', [cmd]);
276   except on E: Exception do
277     begin
278       conwritefln('error executing holmes command: [%s]', [s]);
279       //conwritefln('* [%s] [%s]', [Integer(pr.tokType), E.message]);
280     end;
281   end;
282   pr.Free();
283 end;
286 function keybindExecute (var ev: THKeyEvent): Boolean;
288   f: Integer;
289 begin
290   result := false;
291   for f := 0 to High(keybinds) do
292   begin
293     if (ev = keybinds[f].key) then
294     begin
295       result := true;
296       //conwritefln('found command [%s] for keybind <%s>', [keybinds[f].cmd, keybinds[f].key]);
297       execCommand(keybinds[f].cmd);
298       exit;
299     end;
300   end;
301 end;
304 function msbindExecute (var ev: THMouseEvent): Boolean;
306   f: Integer;
307 begin
308   result := false;
309   for f := 0 to High(msbinds) do
310   begin
311     if (ev = msbinds[f].key) then
312     begin
313       result := true;
314       execCommand(msbinds[f].cmd);
315       exit;
316     end;
317   end;
318 end;