DEADSOFTWARE

FlexUI: alot of fixes; Holmes help window now using new FlexUI controls and layouter
[d2df-sdl.git] / src / game / g_holmes.pas
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 {$INCLUDE ../shared/a_modes.inc}
17 unit g_holmes;
19 interface
21 uses
22 {$IFDEF USE_MEMPOOL}mempool,{$ENDIF} geom,
23 e_log, e_input,
24 g_textures, g_basic, e_graphics, g_phys, g_grid, g_player, g_monsters,
25 g_window, g_map, g_triggers, g_items, g_game, g_panel, g_console, g_gfx,
26 xprofiler,
27 sdlcarcass, glgfx, gh_ui_common, gh_ui;
30 procedure g_Holmes_Draw ();
31 procedure g_Holmes_DrawUI ();
33 procedure g_Holmes_MouseEvent (var ev: THMouseEvent);
34 procedure g_Holmes_KeyEvent (var ev: THKeyEvent);
36 // hooks for player
37 procedure g_Holmes_plrViewPos (viewPortX, viewPortY: Integer);
38 procedure g_Holmes_plrViewSize (viewPortW, viewPortH: Integer);
39 procedure g_Holmes_plrLaser (ax0, ay0, ax1, ay1: Integer);
42 var
43 g_holmes_enabled: Boolean = {$IF DEFINED(D2F_DEBUG)}true{$ELSE}false{$ENDIF};
46 implementation
48 uses
49 {rttiobj,} typinfo, e_texture,
50 SysUtils, Classes, GL, SDL2,
51 MAPDEF, g_main, g_options,
52 utils, hashtable, xparser;
55 var
56 //globalInited: Boolean = false;
57 msX: Integer = -666;
58 msY: Integer = -666;
59 msB: Word = 0; // button state
60 kbS: Word = 0; // keyboard modifiers state
61 showGrid: Boolean = {$IF DEFINED(D2F_DEBUG)}false{$ELSE}false{$ENDIF};
62 showMonsInfo: Boolean = false;
63 showMonsLOS2Plr: Boolean = false;
64 showAllMonsCells: Boolean = false;
65 showMapCurPos: Boolean = false;
66 showLayersWindow: Boolean = false;
67 showOutlineWindow: Boolean = false;
68 showTriggers: Boolean = {$IF DEFINED(D2F_DEBUG)}false{$ELSE}false{$ENDIF};
69 showTraceBox: Boolean = {$IF DEFINED(D2F_DEBUG)}false{$ELSE}false{$ENDIF};
72 // ////////////////////////////////////////////////////////////////////////// //
73 {$INCLUDE g_holmes.inc}
74 {$INCLUDE g_holmes_ol.inc} // outliner
77 // ////////////////////////////////////////////////////////////////////////// //
78 {$INCLUDE g_holmes_cmd.inc}
79 procedure holmesInitCommands (); forward;
80 procedure holmesInitBinds (); forward;
83 // ////////////////////////////////////////////////////////////////////////// //
84 var
85 g_ol_nice: Boolean = false;
86 g_ol_fill_walls: Boolean = false;
87 g_ol_rlayer_back: Boolean = false;
88 g_ol_rlayer_step: Boolean = false;
89 g_ol_rlayer_wall: Boolean = false;
90 g_ol_rlayer_door: Boolean = false;
91 g_ol_rlayer_acid1: Boolean = false;
92 g_ol_rlayer_acid2: Boolean = false;
93 g_ol_rlayer_water: Boolean = false;
94 g_ol_rlayer_fore: Boolean = false;
97 // ////////////////////////////////////////////////////////////////////////// //
98 var
99 winHelp: TUITopWindow = nil;
100 winOptions: TUITopWindow = nil;
101 winLayers: TUITopWindow = nil;
102 winOutlines: TUITopWindow = nil;
105 procedure createHelpWindow (); forward;
106 procedure createOptionsWindow (); forward;
107 procedure createLayersWindow (); forward;
108 procedure createOutlinesWindow (); forward;
111 procedure toggleLayersWindowCB (me: TUIControl; checked: Integer);
112 begin
113 if showLayersWindow then
114 begin
115 if (winLayers = nil) then createLayersWindow();
116 uiAddWindow(winLayers);
117 end
118 else
119 begin
120 uiRemoveWindow(winLayers);
121 end;
122 end;
125 procedure toggleOutlineWindowCB (me: TUIControl; checked: Integer);
126 begin
127 if showOutlineWindow then
128 begin
129 if (winOutlines = nil) then createOutlinesWindow();
130 uiAddWindow(winOutlines);
131 end
132 else
133 begin
134 uiRemoveWindow(winOutlines);
135 end;
136 end;
139 procedure createHelpWindow ();
140 procedure addHelpEmptyLine ();
141 var
142 stx: TUIStaticText;
143 begin
144 stx := TUIStaticText.Create();
145 stx.flExpand := true;
146 stx.halign := 0; // center
147 stx.text := '';
148 stx.header := false;
149 stx.line := false;
150 winHelp.appendChild(stx);
151 end;
153 procedure addHelpCaptionLine (const txt: AnsiString);
154 var
155 stx: TUIStaticText;
156 begin
157 stx := TUIStaticText.Create();
158 stx.flExpand := true;
159 stx.halign := 0; // center
160 stx.text := txt;
161 stx.header := true;
162 stx.line := true;
163 winHelp.appendChild(stx);
164 end;
166 procedure addHelpCaption (const txt: AnsiString);
167 var
168 stx: TUIStaticText;
169 begin
170 stx := TUIStaticText.Create();
171 stx.flExpand := true;
172 stx.halign := 0; // center
173 stx.text := txt;
174 stx.header := true;
175 stx.line := false;
176 winHelp.appendChild(stx);
177 end;
179 procedure addHelpKeyMouse (const key, txt, grp: AnsiString);
180 var
181 box: TUIHBox;
182 span: TUISpan;
183 stx: TUIStaticText;
184 begin
185 box := TUIHBox.Create();
186 box.flExpand := true;
187 // key
188 stx := TUIStaticText.Create();
189 stx.flExpand := true;
190 stx.halign := 1; // right
191 stx.valign := 0; // center
192 stx.text := key;
193 stx.header := true;
194 stx.line := false;
195 stx.flHGroup := grp;
196 box.appendChild(stx);
197 // span
198 span := TUISpan.Create();
199 span.flDefaultSize := TLaySize.Create(4, 1);
200 span.flExpand := true;
201 box.appendChild(span);
202 // text
203 stx := TUIStaticText.Create();
204 stx.flExpand := true;
205 stx.halign := -1; // left
206 stx.valign := 0; // center
207 stx.text := txt;
208 stx.header := false;
209 stx.line := false;
210 box.appendChild(stx);
211 winHelp.appendChild(box);
212 end;
214 procedure addHelpKey (const key, txt: AnsiString); begin addHelpKeyMouse(key, txt, 'help-keys'); end;
215 procedure addHelpMouse (const key, txt: AnsiString); begin addHelpKeyMouse(key, txt, 'help-mouse'); end;
217 var
218 slist: array of AnsiString = nil;
219 cmd: PHolmesCommand;
220 bind: THolmesBinding;
221 f: Integer;
223 llb: TUISimpleText;
224 maxkeylen: Integer;
225 s: AnsiString;
227 begin
228 winHelp := TUITopWindow.Create('Holmes Help', 10, 10);
229 winHelp.escClose := true;
230 winHelp.flHoriz := false;
232 // keyboard
233 for cmd in cmdlist do cmd.helpmark := false;
235 //maxkeylen := 0;
236 for bind in keybinds do
237 begin
238 if (Length(bind.key) = 0) then continue;
239 if cmdlist.get(bind.cmdName, cmd) then
240 begin
241 if (Length(cmd.help) > 0) then
242 begin
243 cmd.helpmark := true;
244 //if (maxkeylen < Length(bind.key)) then maxkeylen := Length(bind.key);
245 end;
246 end;
247 end;
249 for cmd in cmdlist do
250 begin
251 if not cmd.helpmark then continue;
252 if (Length(cmd.help) = 0) then begin cmd.helpmark := false; continue; end;
253 f := 0;
254 while (f < Length(slist)) and (CompareText(slist[f], cmd.section) <> 0) do Inc(f);
255 if (f = Length(slist)) then
256 begin
257 SetLength(slist, Length(slist)+1);
258 slist[High(slist)] := cmd.section;
259 end;
260 end;
262 addHelpCaptionLine('KEYBOARD');
263 //llb := TUISimpleText.Create(0, 0);
264 for f := 0 to High(slist) do
265 begin
266 //if (f > 0) then llb.appendItem('');
267 if (f > 0) then addHelpEmptyLine();
268 //llb.appendItem(slist[f], true, true);
269 addHelpCaption(slist[f]);
270 for cmd in cmdlist do
271 begin
272 if not cmd.helpmark then continue;
273 if (CompareText(cmd.section, slist[f]) <> 0) then continue;
274 for bind in keybinds do
275 begin
276 if (Length(bind.key) = 0) then continue;
277 if (cmd.name = bind.cmdName) then
278 begin
279 //s := bind.key;
280 //while (Length(s) < maxkeylen) do s += ' ';
281 //s := ' '+s+' -- '+cmd.help;
282 //llb.appendItem(s);
283 addHelpMouse(bind.key, cmd.help);
284 end;
285 end;
286 end;
287 end;
289 // mouse
290 for cmd in cmdlist do cmd.helpmark := false;
292 //maxkeylen := 0;
293 for bind in msbinds do
294 begin
295 if (Length(bind.key) = 0) then continue;
296 if cmdlist.get(bind.cmdName, cmd) then
297 begin
298 if (Length(cmd.help) > 0) then
299 begin
300 cmd.helpmark := true;
301 //if (maxkeylen < Length(bind.key)) then maxkeylen := Length(bind.key);
302 end;
303 end;
304 end;
306 //llb.appendItem('');
307 //llb.appendItem('mouse', true, true);
308 if (f > 0) then addHelpEmptyLine();
309 addHelpCaptionLine('MOUSE');
310 for bind in msbinds do
311 begin
312 if (Length(bind.key) = 0) then continue;
313 if cmdlist.get(bind.cmdName, cmd) then
314 begin
315 if (Length(cmd.help) > 0) then
316 begin
317 //s := bind.key;
318 //while (Length(s) < maxkeylen) do s += ' ';
319 //s := ' '+s+' -- '+cmd.help;
320 //llb.appendItem(s);
321 addHelpKey(bind.key, cmd.help);
322 end;
323 end;
324 end;
326 //winHelp.appendChild(llb);
328 winHelp.flMaxSize := TLaySize.Create(trunc(getScrWdt/gh_ui_scale), trunc(getScrHgt/gh_ui_scale));
329 uiLayoutCtl(winHelp);
330 winHelp.centerInScreen();
331 end;
334 procedure winLayersClosed (me: TUIControl; dummy: Integer); begin showLayersWindow := false; end;
335 procedure winOutlinesClosed (me: TUIControl; dummy: Integer); begin showOutlineWindow := false; end;
337 procedure createLayersWindow ();
338 var
339 llb: TUICBListBox;
340 begin
341 llb := TUICBListBox.Create(0, 0);
342 llb.appendItem('background', @g_rlayer_back);
343 llb.appendItem('steps', @g_rlayer_step);
344 llb.appendItem('walls', @g_rlayer_wall);
345 llb.appendItem('doors', @g_rlayer_door);
346 llb.appendItem('acid1', @g_rlayer_acid1);
347 llb.appendItem('acid2', @g_rlayer_acid2);
348 llb.appendItem('water', @g_rlayer_water);
349 llb.appendItem('foreground', @g_rlayer_fore);
350 winLayers := TUITopWindow.Create('layers', 10, 10);
351 winLayers.escClose := true;
352 winLayers.appendChild(llb);
353 winLayers.closeCB := winLayersClosed;
354 end;
357 procedure createOutlinesWindow ();
358 var
359 llb: TUICBListBox;
360 begin
361 llb := TUICBListBox.Create(0, 0);
362 llb.appendItem('background', @g_ol_rlayer_back);
363 llb.appendItem('steps', @g_ol_rlayer_step);
364 llb.appendItem('walls', @g_ol_rlayer_wall);
365 llb.appendItem('doors', @g_ol_rlayer_door);
366 llb.appendItem('acid1', @g_ol_rlayer_acid1);
367 llb.appendItem('acid2', @g_ol_rlayer_acid2);
368 llb.appendItem('water', @g_ol_rlayer_water);
369 llb.appendItem('foreground', @g_ol_rlayer_fore);
370 llb.appendItem('OPTIONS', nil);
371 llb.appendItem('fill walls', @g_ol_fill_walls);
372 llb.appendItem('contours', @g_ol_nice);
373 winOutlines := TUITopWindow.Create('outlines', 100, 10);
374 winOutlines.escClose := true;
375 winOutlines.appendChild(llb);
376 winOutlines.closeCB := winOutlinesClosed;
377 end;
380 procedure createOptionsWindow ();
381 var
382 llb: TUICBListBox;
383 begin
384 llb := TUICBListBox.Create(0, 0);
385 llb.appendItem('map grid', @showGrid);
386 llb.appendItem('cursor position on map', @showMapCurPos);
387 llb.appendItem('monster info', @showMonsInfo);
388 llb.appendItem('monster LOS to player', @showMonsLOS2Plr);
389 llb.appendItem('monster cells (SLOW!)', @showAllMonsCells);
390 llb.appendItem('draw triggers (SLOW!)', @showTriggers);
391 llb.appendItem('WINDOWS', nil);
392 llb.appendItem('layers window', @showLayersWindow, toggleLayersWindowCB);
393 llb.appendItem('outline window', @showOutlineWindow, toggleOutlineWindowCB);
394 winOptions := TUITopWindow.Create('Holmes Options', 100, 100);
395 winOptions.escClose := true;
396 winOptions.appendChild(llb);
397 winOptions.centerInScreen();
398 end;
401 procedure toggleLayersWindow (arg: Integer=-1);
402 begin
403 if (arg < 0) then showLayersWindow := not showLayersWindow else showLayersWindow := (arg > 0);
404 toggleLayersWindowCB(nil, 0);
405 end;
407 procedure toggleOutlineWindow (arg: Integer=-1);
408 begin
409 if (arg < 0) then showOutlineWindow := not showOutlineWindow else showOutlineWindow := (arg > 0);
410 toggleOutlineWindowCB(nil, 0);
411 end;
413 procedure toggleHelpWindow (arg: Integer=-1);
414 begin
415 if (winHelp = nil) then createHelpWindow();
416 if (arg < 0) then begin if not uiVisibleWindow(winHelp) then uiAddWindow(winHelp) else uiRemoveWindow(winHelp); end
417 else if (arg = 0) then begin if uiVisibleWindow(winHelp) then uiRemoveWindow(winHelp); end
418 else begin if not uiVisibleWindow(winHelp) then uiAddWindow(winHelp); end
419 end;
421 procedure toggleOptionsWindow (arg: Integer=-1);
422 begin
423 if (winOptions = nil) then createOptionsWindow();
424 if (arg < 0) then begin if not uiVisibleWindow(winOptions) then uiAddWindow(winOptions) else uiRemoveWindow(winOptions); end
425 else if (arg = 0) then begin if uiVisibleWindow(winOptions) then uiRemoveWindow(winOptions); end
426 else begin if not uiVisibleWindow(winOptions) then uiAddWindow(winOptions); end
427 end;
430 // ////////////////////////////////////////////////////////////////////////// //
431 var
432 vpSet: Boolean = false;
433 vpx, vpy: Integer;
434 vpw, vph: Integer;
435 laserSet: Boolean = false;
436 laserX0, laserY0, laserX1, laserY1: Integer;
437 monMarkedUID: Integer = -1;
438 platMarkedGUID: Integer = -1;
441 procedure g_Holmes_plrViewPos (viewPortX, viewPortY: Integer);
442 begin
443 vpSet := true;
444 vpx := viewPortX;
445 vpy := viewPortY;
446 end;
448 procedure g_Holmes_plrViewSize (viewPortW, viewPortH: Integer);
449 begin
450 vpSet := true;
451 vpw := viewPortW;
452 vph := viewPortH;
453 end;
455 procedure g_Holmes_plrLaser (ax0, ay0, ax1, ay1: Integer);
456 begin
457 laserSet := true;
458 laserX0 := ax0;
459 laserY0 := ay0;
460 laserX1 := ax1;
461 laserY1 := ay1;
462 laserSet := laserSet; // shut up, fpc!
463 end;
466 function pmsCurMapX (): Integer; inline; begin result := round(msX/g_dbg_scale)+vpx; end;
467 function pmsCurMapY (): Integer; inline; begin result := round(msY/g_dbg_scale)+vpy; end;
470 procedure plrDebugMouse (var ev: THMouseEvent);
471 begin
472 //e_WriteLog(Format('mouse: x=%d; y=%d; but=%d; bstate=%d', [msx, msy, but, bstate]), MSG_NOTIFY);
473 if (gPlayer1 = nil) or not vpSet then exit;
474 //if (ev.kind <> THMouseEvent.Press) then exit;
475 //e_WriteLog(Format('mev: %d', [Integer(ev.kind)]), MSG_NOTIFY);
476 msbindExecute(ev);
477 end;
480 {$IFDEF HOLMES_OLD_OUTLINES}
481 var
482 edgeBmp: array of Byte = nil;
485 procedure drawOutlines ();
486 var
487 r, g, b: Integer;
489 procedure clearEdgeBmp ();
490 begin
491 SetLength(edgeBmp, (gWinSizeX+4)*(gWinSizeY+4));
492 FillChar(edgeBmp[0], Length(edgeBmp)*sizeof(edgeBmp[0]), 0);
493 end;
495 procedure drawPanel (pan: TPanel);
496 var
497 sx, len, y0, y1: Integer;
498 begin
499 if (pan = nil) or (pan.Width < 1) or (pan.Height < 1) then exit;
500 if (pan.X+pan.Width <= vpx-1) or (pan.Y+pan.Height <= vpy-1) then exit;
501 if (pan.X >= vpx+vpw+1) or (pan.Y >= vpy+vph+1) then exit;
502 if g_ol_nice or g_ol_fill_walls then
503 begin
504 sx := pan.X-(vpx-1);
505 len := pan.Width;
506 if (len > gWinSizeX+4) then len := gWinSizeX+4;
507 if (sx < 0) then begin len += sx; sx := 0; end;
508 if (sx+len > gWinSizeX+4) then len := gWinSizeX+4-sx;
509 if (len < 1) then exit;
510 assert(sx >= 0);
511 assert(sx+len <= gWinSizeX+4);
512 y0 := pan.Y-(vpy-1);
513 y1 := y0+pan.Height;
514 if (y0 < 0) then y0 := 0;
515 if (y1 > gWinSizeY+4) then y1 := gWinSizeY+4;
516 while (y0 < y1) do
517 begin
518 FillChar(edgeBmp[y0*(gWinSizeX+4)+sx], len*sizeof(edgeBmp[0]), 1);
519 Inc(y0);
520 end;
521 end
522 else
523 begin
524 drawRect(pan.X, pan.Y, pan.Width, pan.Height, r, g, b);
525 end;
526 end;
528 var
529 lsx: Integer = -1;
530 lex: Integer = -1;
531 lsy: Integer = -1;
533 procedure flushLine ();
534 begin
535 if (lsy > 0) and (lsx > 0) then
536 begin
537 if (lex = lsx) then
538 begin
539 glBegin(GL_POINTS);
540 glVertex2f(lsx-1+vpx+0.37, lsy-1+vpy+0.37);
541 glEnd();
542 end
543 else
544 begin
545 glBegin(GL_LINES);
546 glVertex2f(lsx-1+vpx+0.37, lsy-1+vpy+0.37);
547 glVertex2f(lex-0+vpx+0.37, lsy-1+vpy+0.37);
548 glEnd();
549 end;
550 end;
551 lsx := -1;
552 lex := -1;
553 end;
555 procedure startLine (y: Integer);
556 begin
557 flushLine();
558 lsy := y;
559 end;
561 procedure putPixel (x: Integer);
562 begin
563 if (x < 1) then exit;
564 if (lex+1 <> x) then flushLine();
565 if (lsx < 0) then lsx := x;
566 lex := x;
567 end;
569 procedure drawEdges ();
570 var
571 x, y: Integer;
572 a: PByte;
573 begin
574 glDisable(GL_BLEND);
575 glDisable(GL_TEXTURE_2D);
576 glLineWidth(1);
577 glPointSize(1);
578 glDisable(GL_LINE_SMOOTH);
579 glDisable(GL_POLYGON_SMOOTH);
580 glColor4f(r/255.0, g/255.0, b/255.0, 1.0);
581 for y := 1 to vph do
582 begin
583 a := @edgeBmp[y*(gWinSizeX+4)+1];
584 startLine(y);
585 for x := 1 to vpw do
586 begin
587 if (a[0] <> 0) then
588 begin
589 if (a[-1] = 0) or (a[1] = 0) or (a[-(gWinSizeX+4)] = 0) or (a[gWinSizeX+4] = 0) or
590 (a[-(gWinSizeX+4)-1] = 0) or (a[-(gWinSizeX+4)+1] = 0) or
591 (a[gWinSizeX+4-1] = 0) or (a[gWinSizeX+4+1] = 0) then
592 begin
593 putPixel(x);
594 end;
595 end;
596 Inc(a);
597 end;
598 flushLine();
599 end;
600 end;
602 procedure drawFilledWalls ();
603 var
604 x, y: Integer;
605 a: PByte;
606 begin
607 glDisable(GL_BLEND);
608 glDisable(GL_TEXTURE_2D);
609 glLineWidth(1);
610 glPointSize(1);
611 glDisable(GL_LINE_SMOOTH);
612 glDisable(GL_POLYGON_SMOOTH);
613 glColor4f(r/255.0, g/255.0, b/255.0, 1.0);
614 for y := 1 to vph do
615 begin
616 a := @edgeBmp[y*(gWinSizeX+4)+1];
617 startLine(y);
618 for x := 1 to vpw do
619 begin
620 if (a[0] <> 0) then putPixel(x);
621 Inc(a);
622 end;
623 flushLine();
624 end;
625 end;
627 procedure doWallsOld (parr: array of TPanel; ptype: Word; ar, ag, ab: Integer);
628 var
629 f: Integer;
630 pan: TPanel;
631 begin
632 r := ar;
633 g := ag;
634 b := ab;
635 if g_ol_nice or g_ol_fill_walls then clearEdgeBmp();
636 for f := 0 to High(parr) do
637 begin
638 pan := parr[f];
639 if (pan = nil) or not pan.Enabled or (pan.Width < 1) or (pan.Height < 1) then continue;
640 if ((pan.PanelType and ptype) = 0) then continue;
641 drawPanel(pan);
642 end;
643 if g_ol_nice then drawEdges();
644 if g_ol_fill_walls then drawFilledWalls();
645 end;
647 var
648 xptag: Word;
650 function doWallCB (pan: TPanel; tag: Integer): Boolean;
651 begin
652 result := false; // don't stop
653 //if (pan = nil) or not pan.Enabled or (pan.Width < 1) or (pan.Height < 1) then exit;
654 if ((pan.PanelType and xptag) = 0) then exit;
655 drawPanel(pan);
656 end;
658 procedure doWalls (parr: array of TPanel; ptype: Word; ar, ag, ab: Integer);
659 begin
660 r := ar;
661 g := ag;
662 b := ab;
663 xptag := ptype;
664 if ((ptype and (PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR)) <> 0) then ptype := GridTagWall or GridTagDoor
665 else panelTypeToTag(ptype);
666 if g_ol_nice or g_ol_fill_walls then clearEdgeBmp();
667 mapGrid.forEachInAABB(vpx-1, vpy-1, vpw+2, vph+2, doWallCB, ptype);
668 if g_ol_nice then drawEdges();
669 if g_ol_fill_walls then drawFilledWalls();
670 end;
672 begin
673 if g_ol_rlayer_back then doWallsOld(gRenderBackgrounds, PANEL_BACK, 255, 127, 0);
674 if g_ol_rlayer_step then doWallsOld(gSteps, PANEL_STEP, 192, 192, 192);
675 if g_ol_rlayer_wall then doWallsOld(gWalls, PANEL_WALL, 255, 255, 255);
676 if g_ol_rlayer_door then doWallsOld(gWalls, PANEL_OPENDOOR or PANEL_CLOSEDOOR, 0, 255, 0);
677 if g_ol_rlayer_acid1 then doWallsOld(gAcid1, PANEL_ACID1, 255, 0, 0);
678 if g_ol_rlayer_acid2 then doWallsOld(gAcid2, PANEL_ACID2, 198, 198, 0);
679 if g_ol_rlayer_water then doWallsOld(gWater, PANEL_WATER, 0, 255, 255);
680 if g_ol_rlayer_fore then doWallsOld(gRenderForegrounds, PANEL_FORE, 210, 210, 210);
681 end;
683 {$ELSE}
684 var
685 oliner: TOutliner = nil;
687 procedure drawOutlines ();
688 var
689 r, g, b: Integer;
691 procedure clearOliner ();
692 begin
693 //if (oliner <> nil) and ((oliner.height <> vph+2) or (oliner.width <> vpw+2)) then begin oliner.Free(); oliner := nil; end;
694 if (oliner = nil) then oliner := TOutliner.Create(vpw+2, vph+2) else oliner.setup(vpw+2, vph+2);
695 end;
697 procedure drawOutline (ol: TOutliner; sx, sy: Integer);
698 procedure xline (x0, x1, y: Integer);
699 var
700 x: Integer;
701 begin
702 if (g_dbg_scale < 1.0) then
703 begin
704 glBegin(GL_POINTS);
705 for x := x0 to x1 do glVertex2f(sx+x+0.375, sy+y+0.375);
706 glEnd();
707 end
708 else
709 begin
710 glBegin(GL_QUADS);
711 glVertex2f(sx+x0+0, sy+y+0);
712 glVertex2f(sx+x1+1, sy+y+0);
713 glVertex2f(sx+x1+1, sy+y+1);
714 glVertex2f(sx+x0+0, sy+y+1);
715 glEnd();
716 end;
717 end;
718 var
719 y: Integer;
720 sp: TOutliner.TSpanX;
721 begin
722 if (ol = nil) then exit;
723 glPointSize(1);
724 glDisable(GL_POINT_SMOOTH);
725 for y := 0 to ol.height-1 do
726 begin
727 for sp in ol.eachSpanAtY(y) do
728 begin
729 if (g_dbg_scale <= 1.0) then
730 begin
731 glBegin(GL_POINTS);
732 glVertex2f(sx+sp.x0+0.375, sy+y+0.375);
733 glVertex2f(sx+sp.x1+0.375, sy+y+0.375);
734 glEnd();
735 end
736 else
737 begin
738 glBegin(GL_QUADS);
739 glVertex2f(sx+sp.x0+0, sy+y+0);
740 glVertex2f(sx+sp.x0+1, sy+y+0);
741 glVertex2f(sx+sp.x0+1, sy+y+1);
742 glVertex2f(sx+sp.x0+0, sy+y+1);
744 glVertex2f(sx+sp.x1+0, sy+y+0);
745 glVertex2f(sx+sp.x1+1, sy+y+0);
746 glVertex2f(sx+sp.x1+1, sy+y+1);
747 glVertex2f(sx+sp.x1+0, sy+y+1);
748 glEnd();
749 end;
750 end;
751 for sp in ol.eachSpanEdgeAtY(y, -1) do
752 begin
753 xline(sp.x0, sp.x1, y);
755 glBegin(GL_QUADS);
756 glVertex2f(sx+sp.x0+0, sy+y+0);
757 glVertex2f(sx+sp.x1+1, sy+y+0);
758 glVertex2f(sx+sp.x1+1, sy+y+1);
759 glVertex2f(sx+sp.x0+0, sy+y+1);
760 glEnd();
762 end;
763 for sp in ol.eachSpanEdgeAtY(y, +1) do
764 begin
765 xline(sp.x0, sp.x1, y);
767 glBegin(GL_QUADS);
768 glVertex2f(sx+sp.x0+0, sy+y+0);
769 glVertex2f(sx+sp.x1+1, sy+y+0);
770 glVertex2f(sx+sp.x1+1, sy+y+1);
771 glVertex2f(sx+sp.x0+0, sy+y+1);
772 glEnd();
774 end;
775 end;
776 end;
778 procedure doWallsOld (parr: array of TPanel; ptype: Word; ar, ag, ab: Integer);
779 var
780 f: Integer;
781 pan: TPanel;
782 begin
783 r := ar;
784 g := ag;
785 b := ab;
786 if g_ol_nice then clearOliner();
787 for f := 0 to High(parr) do
788 begin
789 pan := parr[f];
790 if (pan = nil) or not pan.Enabled or (pan.Width < 1) or (pan.Height < 1) then continue;
791 if ((pan.PanelType and ptype) = 0) then continue;
792 if (pan.X > vpx+vpw+41) or (pan.Y > vpy+vph+41) then continue;
793 if (pan.X+pan.Width < vpx-41) then continue;
794 if (pan.Y+pan.Height < vpy-41) then continue;
795 if g_ol_nice then
796 begin
797 oliner.addRect(pan.X-(vpx+1), pan.Y-(vpy+1), pan.Width, pan.Height);
798 end;
799 if g_ol_fill_walls then
800 begin
801 fillRect(pan.X, pan.Y, pan.Width, pan.Height, TGxRGBA.Create(r, g, b));
802 end
803 else if not g_ol_nice then
804 begin
805 drawRect(pan.X, pan.Y, pan.Width, pan.Height, TGxRGBA.Create(r, g, b));
806 end;
807 end;
808 if g_ol_nice then
809 begin
810 glColor4f(r/255.0, g/255.0, b/255.0, 1.0);
811 drawOutline(oliner, vpx+1, vpy+1);
812 end;
813 end;
815 begin
816 if (vpw < 2) or (vph < 2) then exit;
817 if g_ol_rlayer_back then doWallsOld(gRenderBackgrounds, PANEL_BACK, 255, 127, 0);
818 if g_ol_rlayer_step then doWallsOld(gSteps, PANEL_STEP, 192, 192, 192);
819 if g_ol_rlayer_wall then doWallsOld(gWalls, PANEL_WALL, 255, 255, 255);
820 if g_ol_rlayer_door then doWallsOld(gWalls, PANEL_OPENDOOR or PANEL_CLOSEDOOR, 0, 255, 0);
821 if g_ol_rlayer_acid1 then doWallsOld(gAcid1, PANEL_ACID1, 255, 0, 0);
822 if g_ol_rlayer_acid2 then doWallsOld(gAcid2, PANEL_ACID2, 198, 198, 0);
823 if g_ol_rlayer_water then doWallsOld(gWater, PANEL_WATER, 0, 255, 255);
824 if g_ol_rlayer_fore then doWallsOld(gRenderForegrounds, PANEL_FORE, 210, 210, 210);
825 end;
826 {$ENDIF}
829 procedure plrDebugDraw ();
830 procedure drawTileGrid ();
831 var
832 x, y: Integer;
833 begin
834 for y := 0 to (mapGrid.gridHeight div mapGrid.tileSize) do
835 begin
836 drawLine(mapGrid.gridX0, mapGrid.gridY0+y*mapGrid.tileSize, mapGrid.gridX0+mapGrid.gridWidth, mapGrid.gridY0+y*mapGrid.tileSize, TGxRGBA.Create(96, 96, 96));
837 end;
839 for x := 0 to (mapGrid.gridWidth div mapGrid.tileSize) do
840 begin
841 drawLine(mapGrid.gridX0+x*mapGrid.tileSize, mapGrid.gridY0, mapGrid.gridX0+x*mapGrid.tileSize, mapGrid.gridY0+y*mapGrid.gridHeight, TGxRGBA.Create(96, 96, 96));
842 end;
843 end;
845 procedure drawAwakeCells ();
846 var
847 x, y: Integer;
848 begin
849 for y := 0 to (mapGrid.gridHeight div mapGrid.tileSize) do
850 begin
851 for x := 0 to (mapGrid.gridWidth div mapGrid.tileSize) do
852 begin
853 if awmIsSetHolmes(x*mapGrid.tileSize+mapGrid.gridX0+1, y*mapGrid.tileSize++mapGrid.gridY0+1) then
854 begin
855 fillRect(x*mapGrid.tileSize++mapGrid.gridX0, y*mapGrid.tileSize++mapGrid.gridY0, monsGrid.tileSize, monsGrid.tileSize, TGxRGBA.Create(128, 0, 128, 64));
856 end;
857 end;
858 end;
859 end;
861 procedure drawTraceBox ();
862 var
863 plr: TPlayer;
864 px, py, pw, ph: Integer;
865 pdx, pdy: Integer;
866 ex, ey: Integer;
867 pan: TPanel;
868 begin
869 if (Length(gPlayers) < 1) then exit;
870 plr := gPlayers[0];
871 if (plr = nil) then exit;
872 plr.getMapBox(px, py, pw, ph);
873 drawRect(px, py, pw, ph, TGxRGBA.Create(255, 0, 255, 200));
874 pdx := pmsCurMapX-(px+pw div 2);
875 pdy := pmsCurMapY-(py+ph div 2);
876 drawLine(px+pw div 2, py+ph div 2, px+pw div 2+pdx, py+ph div 2+pdy, TGxRGBA.Create(255, 0, 255, 200));
877 pan := mapGrid.traceBox(ex, ey, px, py, pw, ph, pdx, pdy, nil, GridTagObstacle);
878 if (pan = nil) then
879 begin
880 drawRect(px+pdx, py+pdy, pw, ph, TGxRGBA.Create(255, 255, 255, 180));
881 end
882 else
883 begin
884 drawRect(px+pdx, py+pdy, pw, ph, TGxRGBA.Create(255, 255, 0, 180));
885 end;
886 drawRect(ex, ey, pw, ph, TGxRGBA.Create(255, 127, 0, 180));
887 end;
889 procedure hilightCell (cx, cy: Integer);
890 begin
891 fillRect(cx, cy, monsGrid.tileSize, monsGrid.tileSize, TGxRGBA.Create(0, 128, 0, 64));
892 end;
894 procedure hilightCell1 (cx, cy: Integer);
895 begin
896 //e_WriteLog(Format('h1: (%d,%d)', [cx, cy]), MSG_NOTIFY);
897 cx := cx and (not (monsGrid.tileSize-1));
898 cy := cy and (not (monsGrid.tileSize-1));
899 fillRect(cx, cy, monsGrid.tileSize, monsGrid.tileSize, TGxRGBA.Create(255, 255, 0, 92));
900 end;
902 function hilightWallTrc (pan: TPanel; tag: Integer; x, y, prevx, prevy: Integer): Boolean;
903 begin
904 result := false; // don't stop
905 if (pan = nil) then exit; // cell completion, ignore
906 //e_WriteLog(Format('h1: (%d,%d)', [cx, cy]), MSG_NOTIFY);
907 fillRect(pan.X, pan.Y, pan.Width, pan.Height, TGxRGBA.Create(0, 128, 128, 64));
908 end;
910 function monsCollector (mon: TMonster; tag: Integer): Boolean;
911 var
912 ex, ey: Integer;
913 mx, my, mw, mh: Integer;
914 begin
915 result := false;
916 mon.getMapBox(mx, my, mw, mh);
917 e_DrawQuad(mx, my, mx+mw-1, my+mh-1, 255, 255, 0, 96);
918 if lineAABBIntersects(laserX0, laserY0, laserX1, laserY1, mx, my, mw, mh, ex, ey) then
919 begin
920 e_DrawPoint(8, ex, ey, 0, 255, 0);
921 end;
922 end;
924 procedure drawMonsterInfo (mon: TMonster);
925 var
926 mx, my, mw, mh: Integer;
928 procedure drawMonsterTargetLine ();
929 var
930 emx, emy, emw, emh: Integer;
931 enemy: TMonster;
932 eplr: TPlayer;
933 ex, ey: Integer;
934 begin
935 if (g_GetUIDType(mon.MonsterTargetUID) = UID_PLAYER) then
936 begin
937 eplr := g_Player_Get(mon.MonsterTargetUID);
938 if (eplr <> nil) then eplr.getMapBox(emx, emy, emw, emh) else exit;
939 end
940 else if (g_GetUIDType(mon.MonsterTargetUID) = UID_MONSTER) then
941 begin
942 enemy := g_Monsters_ByUID(mon.MonsterTargetUID);
943 if (enemy <> nil) then enemy.getMapBox(emx, emy, emw, emh) else exit;
944 end
945 else
946 begin
947 exit;
948 end;
949 mon.getMapBox(mx, my, mw, mh);
950 drawLine(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, TGxRGBA.Create(255, 0, 0));
951 if (g_Map_traceToNearestWall(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, @ex, @ey) <> nil) then
952 begin
953 drawLine(mx+mw div 2, my+mh div 2, ex, ey, TGxRGBA.Create(0, 255, 0));
954 end;
955 end;
957 procedure drawLOS2Plr ();
958 var
959 emx, emy, emw, emh: Integer;
960 eplr: TPlayer;
961 ex, ey: Integer;
962 begin
963 eplr := gPlayers[0];
964 if (eplr = nil) then exit;
965 eplr.getMapBox(emx, emy, emw, emh);
966 mon.getMapBox(mx, my, mw, mh);
967 drawLine(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, TGxRGBA.Create(255, 0, 0));
968 {$IF DEFINED(D2F_DEBUG)}
969 mapGrid.dbgRayTraceTileHitCB := hilightCell1;
970 {$ENDIF}
971 if (g_Map_traceToNearestWall(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, @ex, @ey) <> nil) then
972 //if (mapGrid.traceRay(ex, ey, mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, hilightWallTrc, (GridTagWall or GridTagDoor)) <> nil) then
973 begin
974 drawLine(mx+mw div 2, my+mh div 2, ex, ey, TGxRGBA.Create(0, 255, 0));
975 end;
976 {$IF DEFINED(D2F_DEBUG)}
977 mapGrid.dbgRayTraceTileHitCB := nil;
978 {$ENDIF}
979 end;
981 begin
982 if (mon = nil) then exit;
983 mon.getMapBox(mx, my, mw, mh);
984 //mx += mw div 2;
986 monsGrid.forEachBodyCell(mon.proxyId, hilightCell);
988 if showMonsInfo then
989 begin
990 //fillRect(mx-4, my-7*8-6, 110, 7*8+6, 0, 0, 94, 250);
991 darkenRect(mx-4, my-7*8-6, 110, 7*8+6, 128);
992 my -= 8;
993 my -= 2;
995 // type
996 drawText6(mx, my, Format('%s(U:%u)', [monsTypeToString(mon.MonsterType), mon.UID]), TGxRGBA.Create(255, 127, 0)); my -= 8;
997 // beh
998 drawText6(mx, my, Format('Beh: %s', [monsBehToString(mon.MonsterBehaviour)]), TGxRGBA.Create(255, 127, 0)); my -= 8;
999 // state
1000 drawText6(mx, my, Format('State:%s (%d)', [monsStateToString(mon.MonsterState), mon.MonsterSleep]), TGxRGBA.Create(255, 127, 0)); my -= 8;
1001 // health
1002 drawText6(mx, my, Format('Health:%d', [mon.MonsterHealth]), TGxRGBA.Create(255, 127, 0)); my -= 8;
1003 // ammo
1004 drawText6(mx, my, Format('Ammo:%d', [mon.MonsterAmmo]), TGxRGBA.Create(255, 127, 0)); my -= 8;
1005 // target
1006 drawText6(mx, my, Format('TgtUID:%u', [mon.MonsterTargetUID]), TGxRGBA.Create(255, 127, 0)); my -= 8;
1007 drawText6(mx, my, Format('TgtTime:%d', [mon.MonsterTargetTime]), TGxRGBA.Create(255, 127, 0)); my -= 8;
1008 end;
1010 drawMonsterTargetLine();
1011 if showMonsLOS2Plr then drawLOS2Plr();
1013 property MonsterRemoved: Boolean read FRemoved write FRemoved;
1014 property MonsterPain: Integer read FPain write FPain;
1015 property MonsterAnim: Byte read FCurAnim write FCurAnim;
1017 end;
1019 function highlightAllMonsterCells (mon: TMonster): Boolean;
1020 begin
1021 result := false; // don't stop
1022 monsGrid.forEachBodyCell(mon.proxyId, hilightCell);
1023 end;
1025 procedure drawSelectedPlatformCells ();
1026 var
1027 pan: TPanel;
1028 begin
1029 if not showGrid then exit;
1030 pan := g_Map_PanelByGUID(platMarkedGUID);
1031 if (pan = nil) then exit;
1032 mapGrid.forEachBodyCell(pan.proxyId, hilightCell);
1033 drawRect(pan.x, pan.y, pan.width, pan.height, TGxRGBA.Create(0, 200, 0, 200));
1034 end;
1036 procedure drawTrigger (var trig: TTrigger);
1038 procedure drawPanelDest (pguid: Integer);
1039 var
1040 pan: TPanel;
1041 begin
1042 pan := g_Map_PanelByGUID(pguid);
1043 if (pan = nil) then exit;
1044 drawLine(
1045 trig.trigCenter.x, trig.trigCenter.y,
1046 pan.x+pan.width div 2, pan.y+pan.height div 2,
1047 TGxRGBA.Create(255, 0, 255, 220));
1048 end;
1050 var
1051 tts: AnsiString;
1052 tx: Integer;
1053 begin
1054 fillRect(trig.x, trig.y, trig.width, trig.height, TGxRGBA.Create(255, 0, 255, 96));
1055 tts := trigType2Str(trig.TriggerType);
1056 tx := trig.x+(trig.width-Length(tts)*6) div 2;
1057 darkenRect(tx-2, trig.y-10, Length(tts)*6+4, 10, 64);
1058 drawText6(tx, trig.y-9, tts, TGxRGBA.Create(255, 127, 0));
1059 tx := trig.x+(trig.width-Length(trig.mapId)*6) div 2;
1060 darkenRect(tx-2, trig.y-20, Length(trig.mapId)*6+4, 10, 64);
1061 drawText6(tx, trig.y-19, trig.mapId, TGxRGBA.Create(255, 255, 0));
1062 drawPanelDest(trig.trigPanelGUID);
1063 case trig.TriggerType of
1064 TRIGGER_NONE: begin end;
1065 TRIGGER_EXIT: begin end;
1066 TRIGGER_TELEPORT: begin end;
1067 TRIGGER_OPENDOOR: begin end;
1068 TRIGGER_CLOSEDOOR: begin end;
1069 TRIGGER_DOOR: begin end;
1070 TRIGGER_DOOR5: begin end;
1071 TRIGGER_CLOSETRAP: begin end;
1072 TRIGGER_TRAP: begin end;
1073 TRIGGER_SECRET: begin end;
1074 TRIGGER_LIFTUP: begin end;
1075 TRIGGER_LIFTDOWN: begin end;
1076 TRIGGER_LIFT: begin end;
1077 TRIGGER_TEXTURE: begin end;
1078 TRIGGER_ON, TRIGGER_OFF, TRIGGER_ONOFF, TRIGGER_PRESS:
1079 begin
1080 if (trig.trigDataRec.trigTWidth > 0) and (trig.trigDataRec.trigTHeight > 0) then
1081 begin
1082 fillRect(
1083 trig.trigDataRec.trigTX, trig.trigDataRec.trigTY,
1084 trig.trigDataRec.trigTWidth, trig.trigDataRec.trigTHeight,
1085 TGxRGBA.Create(0, 255, 255, 42));
1086 drawLine(
1087 trig.trigCenter.x, trig.trigCenter.y,
1088 trig.trigDataRec.trigTX+trig.trigDataRec.trigTWidth div 2,
1089 trig.trigDataRec.trigTY+trig.trigDataRec.trigTHeight div 2,
1090 TGxRGBA.Create(255, 0, 255, 220));
1091 end;
1092 end;
1093 TRIGGER_SOUND: begin end;
1094 TRIGGER_SPAWNMONSTER: begin end;
1095 TRIGGER_SPAWNITEM: begin end;
1096 TRIGGER_MUSIC: begin end;
1097 TRIGGER_PUSH: begin end;
1098 TRIGGER_SCORE: begin end;
1099 TRIGGER_MESSAGE: begin end;
1100 TRIGGER_DAMAGE: begin end;
1101 TRIGGER_HEALTH: begin end;
1102 TRIGGER_SHOT: begin end;
1103 TRIGGER_EFFECT: begin end;
1104 TRIGGER_SCRIPT: begin end;
1105 end;
1106 //trigType2Str
1107 //trigPanelId: Integer;
1108 end;
1110 procedure drawTriggers ();
1111 var
1112 f: Integer;
1113 begin
1114 for f := 0 to High(gTriggers) do drawTrigger(gTriggers[f]);
1115 end;
1117 procedure drawGibsBoxes ();
1118 var
1119 f: Integer;
1120 px, py, pw, ph: Integer;
1121 gib: PGib;
1122 begin
1123 for f := 0 to High(gGibs) do
1124 begin
1125 gib := @gGibs[f];
1126 if gib.alive then
1127 begin
1128 gib.getMapBox(px, py, pw, ph);
1129 drawRect(px, py, pw, ph, TGxRGBA.Create(255, 0, 255));
1130 end;
1131 end;
1132 end;
1134 var
1135 scisave: TScissorSave;
1136 mon: TMonster;
1137 mx, my, mw, mh: Integer;
1138 //pan: TPanel;
1139 //ex, ey: Integer;
1140 begin
1141 if (gPlayer1 = nil) then exit;
1143 scisave.save(true); // enable scissoring
1144 glPushMatrix();
1145 try
1146 //glScissor(0, gWinSizeY-gPlayerScreenSize.Y-1, vpw, vph);
1147 glScissor(0, gScreenHeight-gPlayerScreenSize.Y-1, gPlayerScreenSize.X, gPlayerScreenSize.Y);
1149 glScalef(g_dbg_scale, g_dbg_scale, 1.0);
1150 glTranslatef(-vpx, -vpy, 0);
1152 if (showGrid) then drawTileGrid();
1153 drawOutlines();
1155 if (laserSet) then g_Mons_AlongLine(laserX0, laserY0, laserX1, laserY1, monsCollector, true);
1157 if (monMarkedUID <> -1) then
1158 begin
1159 mon := g_Monsters_ByUID(monMarkedUID);
1160 if (mon <> nil) then
1161 begin
1162 mon.getMapBox(mx, my, mw, mh);
1163 e_DrawQuad(mx, my, mx+mw-1, my+mh-1, 255, 0, 0, 30);
1164 drawMonsterInfo(mon);
1165 end;
1166 end;
1168 if showAllMonsCells and showGrid then g_Mons_ForEach(highlightAllMonsterCells);
1169 if showTriggers then drawTriggers();
1170 if showGrid then drawSelectedPlatformCells();
1172 //drawAwakeCells();
1174 if showTraceBox then drawTraceBox();
1176 //drawGibsBoxes();
1179 //pan := g_Map_traceToNearest(16, 608, 16, 8, (GridTagObstacle or GridTagLiquid), @ex, @ey);
1180 (*
1181 {$IF DEFINED(D2F_DEBUG)}
1182 mapGrid.dbgRayTraceTileHitCB := hilightCell1;
1183 {$ENDIF}
1184 pan := mapGrid.traceRay(ex, ey, 16, 608, 16, 8, nil, (GridTagObstacle or GridTagLiquid));
1185 if (pan <> nil) then writeln('end=(', ex, ',', ey, ')');
1186 {$IF DEFINED(D2F_DEBUG)}
1187 mapGrid.dbgRayTraceTileHitCB := nil;
1188 {$ENDIF}
1190 pan := g_Map_PanelAtPoint(16, 608, (GridTagObstacle or GridTagLiquid));
1191 if (pan <> nil) then writeln('hit!');
1192 *)
1194 finally
1195 glPopMatrix();
1196 scisave.restore();
1197 end;
1199 if showMapCurPos then drawText8(4, gWinSizeY-10, Format('mappos:(%d,%d)', [pmsCurMapX, pmsCurMapY]), TGxRGBA.Create(255, 255, 0));
1200 end;
1203 // ////////////////////////////////////////////////////////////////////////// //
1204 procedure g_Holmes_MouseEvent (var ev: THMouseEvent);
1205 var
1206 he: THMouseEvent;
1207 begin
1208 if g_Game_IsNet then exit;
1209 if not g_holmes_enabled then exit;
1211 holmesInitCommands();
1212 holmesInitBinds();
1213 msX := ev.x;
1214 msY := ev.y;
1215 msB := ev.bstate;
1216 kbS := ev.kstate;
1217 msB := msB;
1218 he := ev;
1219 he.x := he.x;
1220 he.y := he.y;
1221 uiMouseEvent(he);
1222 if (not he.eaten) then plrDebugMouse(he);
1223 ev.eat();
1224 end;
1227 // ////////////////////////////////////////////////////////////////////////// //
1228 procedure g_Holmes_KeyEvent (var ev: THKeyEvent);
1229 var
1230 doeat: Boolean = false;
1231 {$IF DEFINED(D2F_DEBUG)}
1232 pan: TPanel;
1233 ex, ey: Integer;
1234 dx, dy: Integer;
1235 {$ENDIF}
1237 procedure dummyWallTrc (cx, cy: Integer);
1238 begin
1239 end;
1241 begin
1242 if g_Game_IsNet then exit;
1243 if not g_holmes_enabled then exit;
1245 holmesInitCommands();
1246 holmesInitBinds();
1248 msB := ev.bstate;
1249 kbS := ev.kstate;
1250 case ev.scan of
1251 SDL_SCANCODE_LCTRL, SDL_SCANCODE_RCTRL,
1252 SDL_SCANCODE_LALT, SDL_SCANCODE_RALT,
1253 SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT:
1254 doeat := true;
1255 end;
1257 uiKeyEvent(ev);
1258 if (ev.eaten) then exit;
1259 if keybindExecute(ev) then begin ev.eat(); exit; end;
1260 // press
1261 if (ev.press) then
1262 begin
1263 {$IF DEFINED(D2F_DEBUG)}
1264 // C-UP, C-DOWN, C-LEFT, C-RIGHT: trace 10 pixels from cursor in the respective direction
1265 if ((ev.scan = SDL_SCANCODE_UP) or (ev.scan = SDL_SCANCODE_DOWN) or (ev.scan = SDL_SCANCODE_LEFT) or (ev.scan = SDL_SCANCODE_RIGHT)) and
1266 ((ev.kstate and THKeyEvent.ModCtrl) <> 0) then
1267 begin
1268 ev.eat();
1269 dx := pmsCurMapX;
1270 dy := pmsCurMapY;
1271 case ev.scan of
1272 SDL_SCANCODE_UP: dy -= 120;
1273 SDL_SCANCODE_DOWN: dy += 120;
1274 SDL_SCANCODE_LEFT: dx -= 120;
1275 SDL_SCANCODE_RIGHT: dx += 120;
1276 end;
1277 {$IF DEFINED(D2F_DEBUG)}
1278 //mapGrid.dbgRayTraceTileHitCB := dummyWallTrc;
1279 mapGrid.dbgShowTraceLog := true;
1280 {$ENDIF}
1281 pan := g_Map_traceToNearest(pmsCurMapX, pmsCurMapY, dx, dy, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
1282 {$IF DEFINED(D2F_DEBUG)}
1283 //mapGrid.dbgRayTraceTileHitCB := nil;
1284 mapGrid.dbgShowTraceLog := false;
1285 {$ENDIF}
1286 e_LogWritefln('v-trace: (%d,%d)-(%d,%d); end=(%d,%d); hit=%d', [pmsCurMapX, pmsCurMapY, dx, dy, ex, ey, (pan <> nil)]);
1287 exit;
1288 end;
1289 {$ENDIF}
1290 end;
1291 if (doeat) then ev.eat();
1292 end;
1295 // ////////////////////////////////////////////////////////////////////////// //
1296 procedure g_Holmes_Draw ();
1297 begin
1298 if g_Game_IsNet then exit;
1300 {$IF not DEFINED(HEADLESS)}
1301 holmesInitCommands();
1302 holmesInitBinds();
1304 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
1305 glDisable(GL_STENCIL_TEST);
1306 glDisable(GL_BLEND);
1307 glDisable(GL_SCISSOR_TEST);
1308 glDisable(GL_TEXTURE_2D);
1310 if gGameOn then plrDebugDraw();
1311 {$ENDIF}
1313 laserSet := false;
1314 end;
1317 procedure g_Holmes_DrawUI ();
1318 begin
1319 if g_Game_IsNet then exit;
1320 if not g_holmes_enabled then exit;
1321 {$IF not DEFINED(HEADLESS)}
1322 gGfxDoClear := false;
1323 //if assigned(prerenderFrameCB) then prerenderFrameCB();
1324 uiDraw();
1325 glMatrixMode(GL_MODELVIEW);
1326 glPushMatrix();
1327 try
1328 //glLoadIdentity();
1329 if assigned(postrenderFrameCB) then postrenderFrameCB();
1330 finally
1331 glPopMatrix();
1332 end;
1333 {$ENDIF}
1334 end;
1337 // ////////////////////////////////////////////////////////////////////////// //
1338 procedure bcOneMonsterThinkStep (); begin gmon_debug_think := false; gmon_debug_one_think_step := true; end;
1339 procedure bcOneMPlatThinkStep (); begin g_dbgpan_mplat_active := false; g_dbgpan_mplat_step := true; end;
1340 procedure bcMPlatToggle (); begin g_dbgpan_mplat_active := not g_dbgpan_mplat_active; end;
1342 procedure bcToggleMonsterInfo (arg: Integer=-1); begin if (arg < 0) then showMonsInfo := not showMonsInfo else showMonsInfo := (arg > 0); end;
1343 procedure bcToggleMonsterLOSPlr (arg: Integer=-1); begin if (arg < 0) then showMonsLOS2Plr := not showMonsLOS2Plr else showMonsLOS2Plr := (arg > 0); end;
1344 procedure bcToggleMonsterCells (arg: Integer=-1); begin if (arg < 0) then showAllMonsCells := not showAllMonsCells else showAllMonsCells := (arg > 0); end;
1345 procedure bcToggleDrawTriggers (arg: Integer=-1); begin if (arg < 0) then showTriggers := not showTriggers else showTriggers := (arg > 0); end;
1347 procedure bcToggleCurPos (arg: Integer=-1); begin if (arg < 0) then showMapCurPos := not showMapCurPos else showMapCurPos := (arg > 0); end;
1348 procedure bcToggleGrid (arg: Integer=-1); begin if (arg < 0) then showGrid := not showGrid else showGrid := (arg > 0); end;
1350 procedure bcMonsterSpawn (s: AnsiString);
1351 var
1352 mon: TMonster;
1353 begin
1354 if not gGameOn or g_Game_IsClient then
1355 begin
1356 conwriteln('cannot spawn monster in this mode');
1357 exit;
1358 end;
1359 mon := g_Mons_SpawnAt(s, pmsCurMapX, pmsCurMapY);
1360 if (mon = nil) then begin conwritefln('unknown monster id: ''%s''', [s]); exit; end;
1361 monMarkedUID := mon.UID;
1362 end;
1364 procedure bcMonsterWakeup ();
1365 var
1366 mon: TMonster;
1367 begin
1368 if (monMarkedUID <> -1) then
1369 begin
1370 mon := g_Monsters_ByUID(monMarkedUID);
1371 if (mon <> nil) then mon.WakeUp();
1372 end;
1373 end;
1375 procedure bcPlayerTeleport ();
1376 var
1377 x, y, w, h: Integer;
1378 begin
1379 //e_WriteLog(Format('TELEPORT: (%d,%d)', [pmsCurMapX, pmsCurMapY]), MSG_NOTIFY);
1380 if (gPlayers[0] <> nil) then
1381 begin
1382 gPlayers[0].getMapBox(x, y, w, h);
1383 gPlayers[0].TeleportTo(pmsCurMapX-w div 2, pmsCurMapY-h div 2, true, 69); // 69: don't change dir
1384 end;
1385 end;
1387 procedure dbgToggleTraceBox (arg: Integer=-1); begin if (arg < 0) then showTraceBox := not showTraceBox else showTraceBox := (arg > 0); end;
1389 procedure dbgToggleHolmesPause (arg: Integer=-1); begin if (arg < 0) then g_Game_HolmesPause(not gPauseHolmes) else g_Game_HolmesPause(arg > 0); end;
1391 procedure cbAtcurSelectMonster ();
1392 function monsAtDump (mon: TMonster; tag: Integer): Boolean;
1393 begin
1394 result := true; // stop
1395 e_WriteLog(Format('monster #%d (UID:%u) (proxyid:%d)', [mon.arrIdx, mon.UID, mon.proxyId]), TMsgType.Notify);
1396 monMarkedUID := mon.UID;
1397 dumpPublishedProperties(mon);
1398 end;
1399 var
1400 plr: TPlayer;
1401 x, y, w, h: Integer;
1402 begin
1403 monMarkedUID := -1;
1404 if (Length(gPlayers) > 0) then
1405 begin
1406 plr := gPlayers[0];
1407 if (plr <> nil) then
1408 begin
1409 plr.getMapBox(x, y, w, h);
1410 if (pmsCurMapX >= x) and (pmsCurMapY >= y) and (pmsCurMapX < x+w) and (pmsCurMapY < y+h) then
1411 begin
1412 dumpPublishedProperties(plr);
1413 end;
1414 end;
1415 end;
1416 //e_WriteLog('===========================', MSG_NOTIFY);
1417 monsGrid.forEachAtPoint(pmsCurMapX, pmsCurMapY, monsAtDump);
1418 //e_WriteLog('---------------------------', MSG_NOTIFY);
1419 end;
1421 procedure cbAtcurDumpMonsters ();
1422 function monsAtDump (mon: TMonster; tag: Integer): Boolean;
1423 begin
1424 result := false; // don't stop
1425 e_WriteLog(Format('monster #%d (UID:%u) (proxyid:%d)', [mon.arrIdx, mon.UID, mon.proxyId]), TMsgType.Notify);
1426 end;
1427 begin
1428 e_WriteLog('===========================', TMsgType.Notify);
1429 monsGrid.forEachAtPoint(pmsCurMapX, pmsCurMapY, monsAtDump);
1430 e_WriteLog('---------------------------', TMsgType.Notify);
1431 end;
1433 procedure cbAtcurDumpWalls ();
1434 function wallToggle (pan: TPanel; tag: Integer): Boolean;
1435 begin
1436 result := false; // don't stop
1437 if (platMarkedGUID = -1) then platMarkedGUID := pan.guid;
1438 e_LogWritefln('wall ''%s'' #%d(%d); enabled=%d (%d); (%d,%d)-(%d,%d)', [pan.mapId, pan.arrIdx, pan.proxyId, Integer(pan.Enabled), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.X, pan.Y, pan.Width, pan.Height]);
1439 dumpPublishedProperties(pan);
1440 end;
1441 var
1442 hasTrigs: Boolean = false;
1443 f: Integer;
1444 trig: PTrigger;
1445 begin
1446 platMarkedGUID := -1;
1447 e_WriteLog('=== TOGGLE WALL ===', TMsgType.Notify);
1448 mapGrid.forEachAtPoint(pmsCurMapX, pmsCurMapY, wallToggle, (GridTagWall or GridTagDoor));
1449 e_WriteLog('--- toggle wall ---', TMsgType.Notify);
1450 if showTriggers then
1451 begin
1452 for f := 0 to High(gTriggers) do
1453 begin
1454 trig := @gTriggers[f];
1455 if (pmsCurMapX >= trig.x) and (pmsCurMapY >= trig.y) and (pmsCurMapX < trig.x+trig.width) and (pmsCurMapY < trig.y+trig.height) then
1456 begin
1457 if not hasTrigs then begin writeln('=== TRIGGERS ==='); hasTrigs := true; end;
1458 writeln('trigger ''', trig.mapId, ''' of type ''', trigType2Str(trig.TriggerType), '''');
1459 end;
1460 end;
1461 if hasTrigs then writeln('--- triggers ---');
1462 end;
1463 end;
1465 procedure cbAtcurToggleWalls ();
1466 function wallToggle (pan: TPanel; tag: Integer): Boolean;
1467 begin
1468 result := false; // don't stop
1469 //e_WriteLog(Format('wall #%d(%d); enabled=%d (%d); (%d,%d)-(%d,%d)', [pan.arrIdx, pan.proxyId, Integer(pan.Enabled), Integer(mapGrid.proxyEnabled[pan.proxyId]), pan.X, pan.Y, pan.Width, pan.Height]), MSG_NOTIFY);
1470 if pan.Enabled then g_Map_DisableWallGUID(pan.guid) else g_Map_EnableWallGUID(pan.guid);
1471 end;
1472 begin
1473 //e_WriteLog('=== TOGGLE WALL ===', MSG_NOTIFY);
1474 mapGrid.forEachAtPoint(pmsCurMapX, pmsCurMapY, wallToggle, (GridTagWall or GridTagDoor));
1475 //e_WriteLog('--- toggle wall ---', MSG_NOTIFY);
1476 end;
1479 // ////////////////////////////////////////////////////////////////////////// //
1480 procedure holmesInitCommands ();
1481 begin
1482 if (cmdlist <> nil) then exit;
1483 cmdAdd('win_layers', toggleLayersWindow, 'toggle layers window', 'window control');
1484 cmdAdd('win_outline', toggleOutlineWindow, 'toggle outline window', 'window control');
1485 cmdAdd('win_help', toggleHelpWindow, 'toggle help window', 'window control');
1486 cmdAdd('win_options', toggleOptionsWindow, 'toggle options window', 'window control');
1488 cmdAdd('mon_think_step', bcOneMonsterThinkStep, 'one monster think step', 'monster control');
1489 cmdAdd('mon_info', bcToggleMonsterInfo, 'toggle monster info', 'monster control');
1490 cmdAdd('mon_los_plr', bcToggleMonsterLOSPlr, 'toggle monster LOS to player', 'monster control');
1491 cmdAdd('mon_cells', bcToggleMonsterCells, 'toggle "show all cells occupied by monsters" (SLOW!)', 'monster control');
1492 cmdAdd('mon_wakeup', bcMonsterWakeup, 'wake up selected monster', 'monster control');
1494 cmdAdd('mon_spawn', bcMonsterSpawn, 'spawn monster', 'monster control');
1496 cmdAdd('mplat_step', bcOneMPlatThinkStep, 'one mplat think step', 'mplat control');
1497 cmdAdd('mplat_toggle', bcMPlatToggle, 'activate/deactivate moving platforms', 'mplat control');
1499 cmdAdd('plr_teleport', bcPlayerTeleport, 'teleport player', 'player control');
1501 cmdAdd('dbg_curpos', bcToggleCurPos, 'toggle "show cursor position on the map"', 'various');
1502 cmdAdd('dbg_grid', bcToggleGrid, 'toggle grid', 'various');
1503 cmdAdd('dbg_triggers', bcToggleDrawTriggers, 'show/hide triggers (SLOW!)', 'various');
1505 cmdAdd('atcur_select_monster', cbAtcurSelectMonster, 'select monster to operate', 'monster control');
1506 cmdAdd('atcur_dump_monsters', cbAtcurDumpMonsters, 'dump monsters in cell', 'monster control');
1507 cmdAdd('atcur_dump_walls', cbAtcurDumpWalls, 'dump walls in cell', 'wall control');
1508 cmdAdd('atcur_disable_walls', cbAtcurToggleWalls, 'disable walls', 'wall control');
1510 cmdAdd('dbg_tracebox', dbgToggleTraceBox, 'test traceBox()', 'player control');
1512 cmdAdd('hlm_pause', dbgToggleHolmesPause, '"Holmes" pause mode', 'game control');
1513 end;
1516 procedure holmesInitBinds ();
1517 var
1518 st: TStream = nil;
1519 pr: TTextParser = nil;
1520 s, kn, v: AnsiString;
1521 kmods: Byte;
1522 mbuts: Byte;
1523 begin
1524 kbS := kbS;
1525 if not keybindsInited then
1526 begin
1527 // keyboard
1528 keybindAdd('F1', 'win_help');
1529 keybindAdd('M-F1', 'win_options');
1530 keybindAdd('C-O', 'win_outline');
1531 keybindAdd('C-L', 'win_layers');
1533 keybindAdd('M-M', 'mon_think_step');
1534 keybindAdd('M-I', 'mon_info');
1535 keybindAdd('M-L', 'mon_los_plr');
1536 keybindAdd('M-G', 'mon_cells');
1537 keybindAdd('M-A', 'mon_wakeup');
1539 keybindAdd('M-P', 'mplat_step');
1540 keybindAdd('M-O', 'mplat_toggle');
1542 keybindAdd('C-T', 'plr_teleport');
1543 keybindAdd('M-T', 'dbg_tracebox');
1545 keybindAdd('C-P', 'dbg_curpos');
1546 keybindAdd('C-G', 'dbg_grid');
1547 keybindAdd('C-X', 'dbg_triggers');
1549 keybindAdd('C-1', 'mon_spawn zombie');
1551 keybindAdd('C-S-P', 'hlm_pause');
1553 // mouse
1554 msbindAdd('LMB', 'atcur_select_monster');
1555 msbindAdd('M-LMB', 'atcur_dump_monsters');
1556 msbindAdd('RMB', 'atcur_dump_walls');
1557 msbindAdd('M-RMB', 'atcur_disable_walls');
1559 // load bindings from file
1560 try
1561 st := openDiskFileRO(GameDir+'holmes.rc');
1562 pr := TFileTextParser.Create(st);
1563 conwriteln('parsing "holmes.rc"...');
1564 while (pr.tokType <> pr.TTEOF) do
1565 begin
1566 s := pr.expectId();
1567 if (s = 'stop') then break
1568 else if (s = 'unbind_keys') then keybinds := nil
1569 else if (s = 'unbind_mouse') then msbinds := nil
1570 else if (s = 'bind') then
1571 begin
1572 if (pr.tokType = pr.TTStr) then s := pr.expectStr(false)
1573 else if (pr.tokType = pr.TTInt) then s := Format('%d', [pr.expectInt()])
1574 else s := pr.expectId();
1576 if (pr.tokType = pr.TTStr) then v := pr.expectStr(false)
1577 else if (pr.tokType = pr.TTInt) then v := Format('%d', [pr.expectInt()])
1578 else v := pr.expectId();
1580 kn := parseModKeys(s, kmods, mbuts);
1581 if (CompareText(kn, 'lmb') = 0) or (CompareText(kn, 'rmb') = 0) or (CompareText(kn, 'mmb') = 0) or (CompareText(kn, 'None') = 0) then
1582 begin
1583 msbindAdd(s, v);
1584 end
1585 else
1586 begin
1587 keybindAdd(s, v);
1588 end;
1589 end;
1590 end;
1591 except on e: Exception do // sorry
1592 if (pr <> nil) then conwritefln('Holmes config parse error at (%s,%s): %s', [pr.tokLine, pr.tokCol, e.message]);
1593 end;
1594 if (pr <> nil) then pr.Free() else st.Free(); // ownership
1595 end;
1596 end;
1599 procedure onMouseEvent (var ev: THMouseEvent);
1600 begin
1601 if not g_holmes_enabled then exit;
1602 g_Holmes_MouseEvent(ev);
1603 end;
1605 procedure onKeyEvent (var ev: THKeyEvent);
1606 begin
1607 if not g_holmes_enabled then exit;
1608 g_Holmes_KeyEvent(ev);
1609 end;
1612 begin
1613 evMouseCB := onMouseEvent;
1614 evKeyCB := onKeyEvent;
1616 conRegVar('hlm_ui_scale', @gh_ui_scale, 0.01, 5.0, 'Holmes UI scale', '', false);
1617 end.