DEADSOFTWARE

Holmes UI: events now can be compared with strings
[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 e_log, e_input,
23 g_textures, g_basic, e_graphics, g_phys, g_grid, g_player, g_monsters,
24 g_window, g_map, g_triggers, g_items, g_game, g_panel, g_console,
25 xprofiler;
28 type
29 THMouseEvent = record
30 public
31 const
32 // both for but and for bstate
33 Left = $0001;
34 Right = $0002;
35 Middle = $0004;
36 WheelUp = $0008;
37 WheelDown = $0010;
39 // event types
40 Release = 0;
41 Press = 1;
42 Motion = 2;
44 public
45 kind: Byte; // motion, press, release
46 x, y: Integer;
47 dx, dy: Integer; // for wheel this is wheel motion, otherwise this is relative mouse motion
48 but: Word; // current pressed/released button, or 0 for motion
49 bstate: Word; // button state
50 kstate: Word; // keyboard state (see THKeyEvent);
51 end;
53 THKeyEvent = record
54 public
55 const
56 // modifiers
57 ModCtrl = $0001;
58 ModAlt = $0002;
59 ModShift = $0004;
61 // event types
62 Release = 0;
63 Press = 1;
65 public
66 kind: Byte;
67 scan: Word; // SDL_SCANCODE_XXX
68 sym: Word; // SDLK_XXX
69 bstate: Word; // button state
70 kstate: Word; // keyboard state
72 public
73 end;
75 procedure g_Holmes_VidModeChanged ();
76 procedure g_Holmes_WindowFocused ();
77 procedure g_Holmes_WindowBlured ();
79 procedure g_Holmes_Draw ();
80 procedure g_Holmes_DrawUI ();
82 function g_Holmes_MouseEvent (var ev: THMouseEvent): Boolean; // returns `true` if event was eaten
83 function g_Holmes_KeyEvent (var ev: THKeyEvent): Boolean; // returns `true` if event was eaten
85 // hooks for player
86 procedure g_Holmes_plrView (viewPortX, viewPortY, viewPortW, viewPortH: Integer);
87 procedure g_Holmes_plrLaser (ax0, ay0, ax1, ay1: Integer);
90 operator = (constref ev: THKeyEvent; const s: AnsiString): Boolean;
91 operator = (const s: AnsiString; constref ev: THKeyEvent): Boolean;
93 operator = (constref ev: THMouseEvent; const s: AnsiString): Boolean;
94 operator = (const s: AnsiString; constref ev: THMouseEvent): Boolean;
97 var
98 g_holmes_enabled: Boolean = {$IF DEFINED(D2F_DEBUG)}true{$ELSE}false{$ENDIF};
101 implementation
103 uses
104 SysUtils, GL, SDL2,
105 MAPDEF, g_options;
108 var
109 //globalInited: Boolean = false;
110 msX: Integer = -666;
111 msY: Integer = -666;
112 msB: Word = 0; // button state
113 kbS: Word = 0; // keyboard modifiers state
114 showGrid: Boolean = true;
115 showMonsInfo: Boolean = false;
116 showMonsLOS2Plr: Boolean = false;
117 showAllMonsCells: Boolean = false;
118 showMapCurPos: Boolean = false;
119 showLayersWindow: Boolean = false;
120 showOutlineWindow: Boolean = false;
122 // ////////////////////////////////////////////////////////////////////////// //
123 {$INCLUDE g_holmes.inc}
124 {$INCLUDE g_holmes_ui.inc}
127 // ////////////////////////////////////////////////////////////////////////// //
128 // any mods = 255: nothing was defined
129 function parseModKeys (const s: AnsiString; out kmods: Byte; out mbuts: Byte): AnsiString;
130 var
131 pos, epos: Integer;
132 begin
133 kmods := 255;
134 mbuts := 255;
135 pos := 1;
136 while (pos <= Length(s)) and (s[pos] <= ' ') do Inc(pos);
137 while (pos < Length(s)) do
138 begin
139 if (Length(s)-pos >= 2) and (s[pos+1] = '-') then
140 begin
141 case s[pos] of
142 'C', 'c': begin if (kmods = 255) then kmods := 0; kmods := kmods or THKeyEvent.ModCtrl; Inc(pos, 2); continue; end;
143 'M', 'm': begin if (kmods = 255) then kmods := 0; kmods := kmods or THKeyEvent.ModAlt; Inc(pos, 2); continue; end;
144 'S', 's': begin if (kmods = 255) then kmods := 0; kmods := kmods or THKeyEvent.ModShift; Inc(pos, 2); continue; end;
145 end;
146 break;
147 end;
148 if (Length(s)-pos >= 4) and ((s[pos+1] = 'M') or (s[pos+1] = 'm')) and ((s[pos+2] = 'B') or (s[pos+1] = 'b')) and (s[pos+3] = '-') then
149 begin
150 case s[pos] of
151 'L', 'l': begin if (mbuts = 255) then mbuts := 0; mbuts := mbuts or THMouseEvent.Left; Inc(pos, 4); continue; end;
152 'R', 'r': begin if (mbuts = 255) then mbuts := 0; mbuts := mbuts or THMouseEvent.Right; Inc(pos, 4); continue; end;
153 'M', 'm': begin if (mbuts = 255) then mbuts := 0; mbuts := mbuts or THMouseEvent.Middle; Inc(pos, 4); continue; end;
154 end;
155 break;
156 end;
157 break;
158 end;
159 epos := Length(s)+1;
160 while (epos > pos) and (s[epos-1] <= ' ') do Dec(epos);
161 if (epos > pos) then result := Copy(s, pos, epos-pos) else result := '';
162 end;
165 operator = (constref ev: THKeyEvent; const s: AnsiString): Boolean;
166 var
167 f: Integer;
168 kmods: Byte = 255;
169 mbuts: Byte = 255;
170 kname: AnsiString;
171 begin
172 result := false;
173 kname := parseModKeys(s, kmods, mbuts);
174 if (kmods = 255) then kmods := 0;
175 if (ev.kstate <> kmods) then exit;
176 if (mbuts <> 255) and (ev.bstate <> mbuts) then exit;
177 for f := 1 to High(e_KeyNames) do
178 begin
179 if (CompareText(kname, e_KeyNames[f]) = 0) then
180 begin
181 result := (ev.scan = f);
182 exit;
183 end;
184 end;
185 end;
188 operator = (const s: AnsiString; constref ev: THKeyEvent): Boolean;
189 begin
190 result := (ev = s);
191 end;
194 operator = (constref ev: THMouseEvent; const s: AnsiString): Boolean;
195 var
196 kmods: Byte = 255;
197 mbuts: Byte = 255;
198 kname: AnsiString;
199 but: Integer = -1;
200 begin
201 result := false;
202 kname := parseModKeys(s, kmods, mbuts);
203 if (CompareText(kname, 'LMB') = 0) then but := THMouseEvent.Left
204 else if (CompareText(kname, 'RMB') = 0) then but := THMouseEvent.Right
205 else if (CompareText(kname, 'MMB') = 0) then but := THMouseEvent.Middle
206 else if (CompareText(kname, 'None') = 0) then but := 0
207 else exit;
209 //conwritefln('s=[%s]; kname=[%s]; kmods=%s; mbuts=%s; but=%s', [s, kname, kmods, mbuts, but]);
211 if (mbuts = 255) then mbuts := 0;
212 if (kmods <> 255) and (ev.kstate <> kmods) then exit;
214 if (ev.kind = ev.Press) then mbuts := mbuts or but
215 else if (ev.kind = ev.Release) then mbuts := mbuts and (not but);
217 //conwritefln(' ev.bstate=%s; ev.but=%s; mbuts=%s', [ev.bstate, ev.but, mbuts]);
219 result := (ev.bstate = mbuts) and (ev.but = but);
220 end;
223 operator = (const s: AnsiString; constref ev: THMouseEvent): Boolean;
224 begin
225 result := (ev = s);
226 end;
229 // ////////////////////////////////////////////////////////////////////////// //
230 var
231 g_ol_nice: Boolean = false;
232 g_ol_fill_walls: Boolean = false;
233 g_ol_rlayer_back: Boolean = false;
234 g_ol_rlayer_step: Boolean = false;
235 g_ol_rlayer_wall: Boolean = false;
236 g_ol_rlayer_door: Boolean = false;
237 g_ol_rlayer_acid1: Boolean = false;
238 g_ol_rlayer_acid2: Boolean = false;
239 g_ol_rlayer_water: Boolean = false;
240 g_ol_rlayer_fore: Boolean = false;
243 // ////////////////////////////////////////////////////////////////////////// //
244 var
245 winHelp: THTopWindow = nil;
246 winOptions: THTopWindow = nil;
247 winLayers: THTopWindow = nil;
248 winOutlines: THTopWindow = nil;
251 procedure createHelpWindow ();
252 var
253 llb: THCtlSimpleText;
254 begin
255 llb := THCtlSimpleText.Create(0, 0);
256 llb.appendItem('common keys', true, true);
257 llb.appendItem(' F1 -- toggle this window');
258 llb.appendItem(' M-F1 -- toggle options window');
259 llb.appendItem('');
260 llb.appendItem('control keys', true, true);
261 llb.appendItem(' M-M -- one monster think step');
262 llb.appendItem(' M-I -- toggle monster info');
263 llb.appendItem(' M-K -- toggle monster LOS to player');
264 llb.appendItem(' M-G -- toggle "show all cells occupied by monsters" (SLOW!)');
265 llb.appendItem(' M-A -- wake up monster');
266 llb.appendItem(' C-T -- teleport player');
267 llb.appendItem(' C-P -- show cursor position on the map');
268 llb.appendItem(' C-G -- toggle grid');
269 llb.appendItem(' C-L -- toggle layers window');
270 llb.appendItem(' C-O -- toggle outlines window');
271 llb.appendItem('');
272 llb.appendItem('mouse', true, true);
273 llb.appendItem(' LMB -- select monster');
274 llb.appendItem(' M-LMB -- dump monsters in cell (to log)');
275 llb.appendItem(' RMB -- dump wall info to log');
276 llb.appendItem(' M-LMB -- disable wall');
277 winHelp := THTopWindow.Create('Holmes Help', 10, 10);
278 winHelp.escClose := true;
279 winHelp.appendChild(llb);
280 winHelp.centerInScreen();
281 end;
284 procedure winLayersClosed (me: THControl; dummy: Integer); begin showLayersWindow := false; end;
285 procedure winOutlinesClosed (me: THControl; dummy: Integer); begin showOutlineWindow := false; end;
287 procedure createLayersWindow ();
288 var
289 llb: THCtlCBListBox;
290 begin
291 llb := THCtlCBListBox.Create(0, 0);
292 llb.appendItem('background', @g_rlayer_back);
293 llb.appendItem('steps', @g_rlayer_step);
294 llb.appendItem('walls', @g_rlayer_wall);
295 llb.appendItem('doors', @g_rlayer_door);
296 llb.appendItem('acid1', @g_rlayer_acid1);
297 llb.appendItem('acid2', @g_rlayer_acid2);
298 llb.appendItem('water', @g_rlayer_water);
299 llb.appendItem('foreground', @g_rlayer_fore);
300 winLayers := THTopWindow.Create('visible', 10, 10);
301 winLayers.escClose := true;
302 winLayers.appendChild(llb);
303 winLayers.closeCB := winLayersClosed;
304 end;
307 procedure createOutlinesWindow ();
308 var
309 llb: THCtlCBListBox;
310 begin
311 llb := THCtlCBListBox.Create(0, 0);
312 llb.appendItem('background', @g_ol_rlayer_back);
313 llb.appendItem('steps', @g_ol_rlayer_step);
314 llb.appendItem('walls', @g_ol_rlayer_wall);
315 llb.appendItem('doors', @g_ol_rlayer_door);
316 llb.appendItem('acid1', @g_ol_rlayer_acid1);
317 llb.appendItem('acid2', @g_ol_rlayer_acid2);
318 llb.appendItem('water', @g_ol_rlayer_water);
319 llb.appendItem('foreground', @g_ol_rlayer_fore);
320 llb.appendItem('OPTIONS', nil);
321 llb.appendItem('fill walls', @g_ol_fill_walls);
322 llb.appendItem('contours', @g_ol_nice);
323 winOutlines := THTopWindow.Create('outlines', 100, 10);
324 winOutlines.escClose := true;
325 winOutlines.appendChild(llb);
326 winOutlines.closeCB := winOutlinesClosed;
327 end;
330 procedure toggleLayersWindow (me: THControl; checked: Integer);
331 begin
332 if showLayersWindow then
333 begin
334 if (winLayers = nil) then createLayersWindow();
335 uiAddWindow(winLayers);
336 end
337 else
338 begin
339 uiRemoveWindow(winLayers);
340 end;
341 end;
344 procedure toggleOutlineWindow (me: THControl; checked: Integer);
345 begin
346 if showOutlineWindow then
347 begin
348 if (winOutlines = nil) then createOutlinesWindow();
349 uiAddWindow(winOutlines);
350 end
351 else
352 begin
353 uiRemoveWindow(winOutlines);
354 end;
355 end;
358 procedure createOptionsWindow ();
359 var
360 llb: THCtlCBListBox;
361 begin
362 llb := THCtlCBListBox.Create(0, 0);
363 llb.appendItem('map grid', @showGrid);
364 llb.appendItem('cursor position on map', @showMapCurPos);
365 llb.appendItem('monster info', @showMonsInfo);
366 llb.appendItem('monster LOS to player', @showMonsLOS2Plr);
367 llb.appendItem('monster cells (SLOW!)', @showAllMonsCells);
368 llb.appendItem('WINDOWS', nil);
369 llb.appendItem('layers window', @showLayersWindow, toggleLayersWindow);
370 llb.appendItem('outline window', @showOutlineWindow, toggleOutlineWindow);
371 winOptions := THTopWindow.Create('Holmes Options', 100, 100);
372 winOptions.escClose := true;
373 winOptions.appendChild(llb);
374 winOptions.centerInScreen();
375 end;
378 // ////////////////////////////////////////////////////////////////////////// //
379 procedure g_Holmes_VidModeChanged ();
380 begin
381 e_WriteLog(Format('Holmes: videomode changed: %dx%d', [gScreenWidth, gScreenHeight]), MSG_NOTIFY);
382 // texture space is possibly lost here, idc
383 curtexid := 0;
384 font6texid := 0;
385 font8texid := 0;
386 prfont6texid := 0;
387 prfont8texid := 0;
388 //createCursorTexture();
389 end;
391 procedure g_Holmes_WindowFocused ();
392 begin
393 msB := 0;
394 kbS := 0;
395 end;
397 procedure g_Holmes_WindowBlured ();
398 begin
399 end;
402 // ////////////////////////////////////////////////////////////////////////// //
403 var
404 vpSet: Boolean = false;
405 vpx, vpy: Integer;
406 vpw, vph: Integer;
407 laserSet: Boolean = false;
408 laserX0, laserY0, laserX1, laserY1: Integer;
409 monMarkedUID: Integer = -1;
412 procedure g_Holmes_plrView (viewPortX, viewPortY, viewPortW, viewPortH: Integer);
413 begin
414 vpSet := true;
415 vpx := viewPortX;
416 vpy := viewPortY;
417 vpw := viewPortW;
418 vph := viewPortH;
419 end;
421 procedure g_Holmes_plrLaser (ax0, ay0, ax1, ay1: Integer);
422 begin
423 laserSet := true;
424 laserX0 := ax0;
425 laserY0 := ay0;
426 laserX1 := ax1;
427 laserY1 := ay1;
428 laserSet := laserSet; // shut up, fpc!
429 end;
432 function pmsCurMapX (): Integer; inline; begin result := msX+vpx; end;
433 function pmsCurMapY (): Integer; inline; begin result := msY+vpy; end;
436 procedure plrDebugMouse (var ev: THMouseEvent);
438 function wallToggle (pan: TPanel; tag: Integer): Boolean;
439 begin
440 result := false; // don't stop
441 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);
442 if (kbS = THKeyEvent.ModAlt) then
443 begin
444 if pan.Enabled then g_Map_DisableWall(pan.arrIdx) else g_Map_EnableWall(pan.arrIdx);
445 end;
446 end;
448 function monsAtDump (mon: TMonster; tag: Integer): Boolean;
449 begin
450 result := false; // don't stop
451 e_WriteLog(Format('monster #%d; UID=%d', [mon.arrIdx, mon.UID]), MSG_NOTIFY);
452 monMarkedUID := mon.UID;
453 //if pan.Enabled then g_Map_DisableWall(pan.arrIdx) else g_Map_EnableWall(pan.arrIdx);
454 end;
456 function monsInCell (mon: TMonster; tag: Integer): Boolean;
457 begin
458 result := false; // don't stop
459 e_WriteLog(Format('monster #%d (UID:%u) (proxyid:%d)', [mon.arrIdx, mon.UID, mon.proxyId]), MSG_NOTIFY);
460 end;
462 begin
463 //e_WriteLog(Format('mouse: x=%d; y=%d; but=%d; bstate=%d', [msx, msy, but, bstate]), MSG_NOTIFY);
464 if (gPlayer1 = nil) or not vpSet then exit;
465 if (ev.kind <> THMouseEvent.Press) then exit;
467 e_WriteLog(Format('mev: %d', [Integer(ev.kind)]), MSG_NOTIFY);
469 if (ev.but = THMouseEvent.Right) then
470 begin
471 // dump/toggle wall
472 e_WriteLog('=== TOGGLE WALL ===', MSG_NOTIFY);
473 mapGrid.forEachAtPoint(pmsCurMapX, pmsCurMapY, wallToggle, (GridTagWall or GridTagDoor));
474 e_WriteLog('--- toggle wall ---', MSG_NOTIFY);
475 exit;
476 end;
478 if (ev.but = THMouseEvent.Left) then
479 begin
480 if (kbS = THKeyEvent.ModAlt) then
481 begin
482 // dump monsters in cell
483 e_WriteLog('===========================', MSG_NOTIFY);
484 monsGrid.forEachInCell(pmsCurMapX, pmsCurMapY, monsInCell);
485 e_WriteLog('---------------------------', MSG_NOTIFY);
486 end
487 else if (kbS = 0) then
488 begin
489 monMarkedUID := -1;
490 e_WriteLog('===========================', MSG_NOTIFY);
491 monsGrid.forEachAtPoint(pmsCurMapX, pmsCurMapY, monsAtDump);
492 e_WriteLog('---------------------------', MSG_NOTIFY);
493 end;
494 exit;
495 end;
496 end;
499 var
500 edgeBmp: array of Byte = nil;
503 procedure drawOutlines ();
504 var
505 r, g, b: Integer;
507 procedure clearEdgeBmp ();
508 begin
509 SetLength(edgeBmp, (gWinSizeX+4)*(gWinSizeY+4));
510 FillChar(edgeBmp[0], Length(edgeBmp)*sizeof(edgeBmp[0]), 0);
511 end;
513 procedure drawPanel (pan: TPanel);
514 var
515 sx, len, y0, y1: Integer;
516 begin
517 if (pan = nil) or (pan.Width < 1) or (pan.Height < 1) then exit;
518 if (pan.X+pan.Width <= vpx-1) or (pan.Y+pan.Height <= vpy-1) then exit;
519 if (pan.X >= vpx+vpw+1) or (pan.Y >= vpy+vph+1) then exit;
520 if g_ol_nice or g_ol_fill_walls then
521 begin
522 sx := pan.X-(vpx-1);
523 len := pan.Width;
524 if (len > gWinSizeX+4) then len := gWinSizeX+4;
525 if (sx < 0) then begin len += sx; sx := 0; end;
526 if (sx+len > gWinSizeX+4) then len := gWinSizeX+4-sx;
527 if (len < 1) then exit;
528 assert(sx >= 0);
529 assert(sx+len <= gWinSizeX+4);
530 y0 := pan.Y-(vpy-1);
531 y1 := y0+pan.Height;
532 if (y0 < 0) then y0 := 0;
533 if (y1 > gWinSizeY+4) then y1 := gWinSizeY+4;
534 while (y0 < y1) do
535 begin
536 FillChar(edgeBmp[y0*(gWinSizeX+4)+sx], len*sizeof(edgeBmp[0]), 1);
537 Inc(y0);
538 end;
539 end
540 else
541 begin
542 drawRect(pan.X, pan.Y, pan.Width, pan.Height, r, g, b);
543 end;
544 end;
546 var
547 lsx: Integer = -1;
548 lex: Integer = -1;
549 lsy: Integer = -1;
551 procedure flushLine ();
552 begin
553 if (lsy > 0) and (lsx > 0) then
554 begin
555 if (lex = lsx) then
556 begin
557 glBegin(GL_POINTS);
558 glVertex2f(lsx-1+vpx+0.37, lsy-1+vpy+0.37);
559 glEnd();
560 end
561 else
562 begin
563 glBegin(GL_LINES);
564 glVertex2f(lsx-1+vpx+0.37, lsy-1+vpy+0.37);
565 glVertex2f(lex-0+vpx+0.37, lsy-1+vpy+0.37);
566 glEnd();
567 end;
568 end;
569 lsx := -1;
570 lex := -1;
571 end;
573 procedure startLine (y: Integer);
574 begin
575 flushLine();
576 lsy := y;
577 end;
579 procedure putPixel (x: Integer);
580 begin
581 if (x < 1) then exit;
582 if (lex+1 <> x) then flushLine();
583 if (lsx < 0) then lsx := x;
584 lex := x;
585 end;
587 procedure drawEdges ();
588 var
589 x, y: Integer;
590 a: PByte;
591 begin
592 glDisable(GL_BLEND);
593 glDisable(GL_TEXTURE_2D);
594 glLineWidth(1);
595 glPointSize(1);
596 glDisable(GL_LINE_SMOOTH);
597 glDisable(GL_POLYGON_SMOOTH);
598 glColor4f(r/255.0, g/255.0, b/255.0, 1.0);
599 for y := 1 to vph do
600 begin
601 a := @edgeBmp[y*(gWinSizeX+4)+1];
602 startLine(y);
603 for x := 1 to vpw do
604 begin
605 if (a[0] <> 0) then
606 begin
607 if (a[-1] = 0) or (a[1] = 0) or (a[-(gWinSizeX+4)] = 0) or (a[gWinSizeX+4] = 0) or
608 (a[-(gWinSizeX+4)-1] = 0) or (a[-(gWinSizeX+4)+1] = 0) or
609 (a[gWinSizeX+4-1] = 0) or (a[gWinSizeX+4+1] = 0) then
610 begin
611 putPixel(x);
612 end;
613 end;
614 Inc(a);
615 end;
616 flushLine();
617 end;
618 end;
620 procedure drawFilledWalls ();
621 var
622 x, y: Integer;
623 a: PByte;
624 begin
625 glDisable(GL_BLEND);
626 glDisable(GL_TEXTURE_2D);
627 glLineWidth(1);
628 glPointSize(1);
629 glDisable(GL_LINE_SMOOTH);
630 glDisable(GL_POLYGON_SMOOTH);
631 glColor4f(r/255.0, g/255.0, b/255.0, 1.0);
632 for y := 1 to vph do
633 begin
634 a := @edgeBmp[y*(gWinSizeX+4)+1];
635 startLine(y);
636 for x := 1 to vpw do
637 begin
638 if (a[0] <> 0) then putPixel(x);
639 Inc(a);
640 end;
641 flushLine();
642 end;
643 end;
645 procedure doWallsOld (parr: array of TPanel; ptype: Word; ar, ag, ab: Integer);
646 var
647 f: Integer;
648 pan: TPanel;
649 begin
650 r := ar;
651 g := ag;
652 b := ab;
653 if g_ol_nice or g_ol_fill_walls then clearEdgeBmp();
654 for f := 0 to High(parr) do
655 begin
656 pan := parr[f];
657 if (pan = nil) or not pan.Enabled or (pan.Width < 1) or (pan.Height < 1) then continue;
658 if ((pan.PanelType and ptype) = 0) then continue;
659 drawPanel(pan);
660 end;
661 if g_ol_nice then drawEdges();
662 if g_ol_fill_walls then drawFilledWalls();
663 end;
665 var
666 xptag: Word;
668 function doWallCB (pan: TPanel; tag: Integer): Boolean;
669 begin
670 result := false; // don't stop
671 //if (pan = nil) or not pan.Enabled or (pan.Width < 1) or (pan.Height < 1) then exit;
672 if ((pan.PanelType and xptag) = 0) then exit;
673 drawPanel(pan);
674 end;
676 procedure doWalls (parr: array of TPanel; ptype: Word; ar, ag, ab: Integer);
677 begin
678 r := ar;
679 g := ag;
680 b := ab;
681 xptag := ptype;
682 if ((ptype and (PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR)) <> 0) then ptype := GridTagWall or GridTagDoor
683 else panelTypeToTag(ptype);
684 if g_ol_nice or g_ol_fill_walls then clearEdgeBmp();
685 mapGrid.forEachInAABB(vpx-1, vpy-1, vpw+2, vph+2, doWallCB, ptype);
686 if g_ol_nice then drawEdges();
687 if g_ol_fill_walls then drawFilledWalls();
688 end;
690 begin
691 if g_ol_rlayer_back then doWallsOld(gRenderBackgrounds, PANEL_BACK, 255, 127, 0);
692 if g_ol_rlayer_step then doWallsOld(gSteps, PANEL_STEP, 192, 192, 192);
693 if g_ol_rlayer_wall then doWallsOld(gWalls, PANEL_WALL, 255, 255, 255);
694 if g_ol_rlayer_door then doWallsOld(gWalls, PANEL_OPENDOOR or PANEL_CLOSEDOOR, 0, 255, 0);
695 if g_ol_rlayer_acid1 then doWallsOld(gAcid1, PANEL_ACID1, 255, 0, 0);
696 if g_ol_rlayer_acid2 then doWallsOld(gAcid2, PANEL_ACID2, 198, 198, 0);
697 if g_ol_rlayer_water then doWallsOld(gWater, PANEL_WATER, 0, 255, 255);
698 if g_ol_rlayer_fore then doWallsOld(gRenderForegrounds, PANEL_FORE, 210, 210, 210);
699 end;
702 procedure plrDebugDraw ();
704 procedure drawTileGrid ();
705 var
706 x, y: Integer;
707 begin
708 for y := 0 to (mapGrid.gridHeight div mapGrid.tileSize) do
709 begin
710 drawLine(mapGrid.gridX0, mapGrid.gridY0+y*mapGrid.tileSize, mapGrid.gridX0+mapGrid.gridWidth, mapGrid.gridY0+y*mapGrid.tileSize, 96, 96, 96, 255);
711 end;
713 for x := 0 to (mapGrid.gridWidth div mapGrid.tileSize) do
714 begin
715 drawLine(mapGrid.gridX0+x*mapGrid.tileSize, mapGrid.gridY0, mapGrid.gridX0+x*mapGrid.tileSize, mapGrid.gridY0+y*mapGrid.gridHeight, 96, 96, 96, 255);
716 end;
717 end;
719 procedure hilightCell (cx, cy: Integer);
720 begin
721 fillRect(cx, cy, monsGrid.tileSize, monsGrid.tileSize, 0, 128, 0, 64);
722 end;
724 procedure hilightCell1 (cx, cy: Integer);
725 begin
726 //e_WriteLog(Format('h1: (%d,%d)', [cx, cy]), MSG_NOTIFY);
727 fillRect(cx, cy, monsGrid.tileSize, monsGrid.tileSize, 255, 255, 0, 92);
728 end;
730 function hilightWallTrc (pan: TPanel; tag: Integer; x, y, prevx, prevy: Integer): Boolean;
731 begin
732 result := false; // don't stop
733 if (pan = nil) then exit; // cell completion, ignore
734 //e_WriteLog(Format('h1: (%d,%d)', [cx, cy]), MSG_NOTIFY);
735 fillRect(pan.X, pan.Y, pan.Width, pan.Height, 0, 128, 128, 64);
736 end;
738 function monsCollector (mon: TMonster; tag: Integer): Boolean;
739 var
740 ex, ey: Integer;
741 mx, my, mw, mh: Integer;
742 begin
743 result := false;
744 mon.getMapBox(mx, my, mw, mh);
745 e_DrawQuad(mx, my, mx+mw-1, my+mh-1, 255, 255, 0, 96);
746 if lineAABBIntersects(laserX0, laserY0, laserX1, laserY1, mx, my, mw, mh, ex, ey) then
747 begin
748 e_DrawPoint(8, ex, ey, 0, 255, 0);
749 end;
750 end;
752 procedure drawMonsterInfo (mon: TMonster);
753 var
754 mx, my, mw, mh: Integer;
756 procedure drawMonsterTargetLine ();
757 var
758 emx, emy, emw, emh: Integer;
759 enemy: TMonster;
760 eplr: TPlayer;
761 ex, ey: Integer;
762 begin
763 if (g_GetUIDType(mon.MonsterTargetUID) = UID_PLAYER) then
764 begin
765 eplr := g_Player_Get(mon.MonsterTargetUID);
766 if (eplr <> nil) then eplr.getMapBox(emx, emy, emw, emh) else exit;
767 end
768 else if (g_GetUIDType(mon.MonsterTargetUID) = UID_MONSTER) then
769 begin
770 enemy := g_Monsters_ByUID(mon.MonsterTargetUID);
771 if (enemy <> nil) then enemy.getMapBox(emx, emy, emw, emh) else exit;
772 end
773 else
774 begin
775 exit;
776 end;
777 mon.getMapBox(mx, my, mw, mh);
778 drawLine(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, 255, 0, 0, 255);
779 if (g_Map_traceToNearestWall(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, @ex, @ey) <> nil) then
780 begin
781 drawLine(mx+mw div 2, my+mh div 2, ex, ey, 0, 255, 0, 255);
782 end;
783 end;
785 procedure drawLOS2Plr ();
786 var
787 emx, emy, emw, emh: Integer;
788 eplr: TPlayer;
789 ex, ey: Integer;
790 begin
791 eplr := gPlayers[0];
792 if (eplr = nil) then exit;
793 eplr.getMapBox(emx, emy, emw, emh);
794 mon.getMapBox(mx, my, mw, mh);
795 drawLine(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, 255, 0, 0, 255);
796 {$IF DEFINED(D2F_DEBUG)}
797 //mapGrid.dbgRayTraceTileHitCB := hilightCell1;
798 {$ENDIF}
799 if (g_Map_traceToNearestWall(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, @ex, @ey) <> nil) then
800 //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
801 begin
802 drawLine(mx+mw div 2, my+mh div 2, ex, ey, 0, 255, 0, 255);
803 end;
804 {$IF DEFINED(D2F_DEBUG)}
805 //mapGrid.dbgRayTraceTileHitCB := nil;
806 {$ENDIF}
807 end;
809 begin
810 if (mon = nil) then exit;
811 mon.getMapBox(mx, my, mw, mh);
812 //mx += mw div 2;
814 monsGrid.forEachBodyCell(mon.proxyId, hilightCell);
816 if showMonsInfo then
817 begin
818 //fillRect(mx-4, my-7*8-6, 110, 7*8+6, 0, 0, 94, 250);
819 darkenRect(mx-4, my-7*8-6, 110, 7*8+6, 128);
820 my -= 8;
821 my -= 2;
823 // type
824 drawText6(mx, my, Format('%s(U:%u)', [monsTypeToString(mon.MonsterType), mon.UID]), 255, 127, 0); my -= 8;
825 // beh
826 drawText6(mx, my, Format('Beh: %s', [monsBehToString(mon.MonsterBehaviour)]), 255, 127, 0); my -= 8;
827 // state
828 drawText6(mx, my, Format('State:%s (%d)', [monsStateToString(mon.MonsterState), mon.MonsterSleep]), 255, 127, 0); my -= 8;
829 // health
830 drawText6(mx, my, Format('Health:%d', [mon.MonsterHealth]), 255, 127, 0); my -= 8;
831 // ammo
832 drawText6(mx, my, Format('Ammo:%d', [mon.MonsterAmmo]), 255, 127, 0); my -= 8;
833 // target
834 drawText6(mx, my, Format('TgtUID:%u', [mon.MonsterTargetUID]), 255, 127, 0); my -= 8;
835 drawText6(mx, my, Format('TgtTime:%d', [mon.MonsterTargetTime]), 255, 127, 0); my -= 8;
836 end;
838 drawMonsterTargetLine();
839 if showMonsLOS2Plr then drawLOS2Plr();
841 property MonsterRemoved: Boolean read FRemoved write FRemoved;
842 property MonsterPain: Integer read FPain write FPain;
843 property MonsterAnim: Byte read FCurAnim write FCurAnim;
845 end;
847 function highlightAllMonsterCells (mon: TMonster): Boolean;
848 begin
849 result := false; // don't stop
850 monsGrid.forEachBodyCell(mon.proxyId, hilightCell);
851 end;
853 var
854 mon: TMonster;
855 mx, my, mw, mh: Integer;
856 begin
857 if (gPlayer1 = nil) then exit;
859 glEnable(GL_SCISSOR_TEST);
860 glScissor(0, gWinSizeY-gPlayerScreenSize.Y-1, vpw, vph);
862 glPushMatrix();
863 glTranslatef(-vpx, -vpy, 0);
865 if (showGrid) then drawTileGrid();
866 drawOutlines();
868 if (laserSet) then g_Mons_AlongLine(laserX0, laserY0, laserX1, laserY1, monsCollector, true);
870 if (monMarkedUID <> -1) then
871 begin
872 mon := g_Monsters_ByUID(monMarkedUID);
873 if (mon <> nil) then
874 begin
875 mon.getMapBox(mx, my, mw, mh);
876 e_DrawQuad(mx, my, mx+mw-1, my+mh-1, 255, 0, 0, 30);
877 drawMonsterInfo(mon);
878 end;
879 end;
881 if showAllMonsCells then g_Mons_ForEach(highlightAllMonsterCells);
883 glPopMatrix();
885 glDisable(GL_SCISSOR_TEST);
887 if showMapCurPos then drawText8(4, gWinSizeY-10, Format('mappos:(%d,%d)', [pmsCurMapX, pmsCurMapY]), 255, 255, 0);
888 end;
891 // ////////////////////////////////////////////////////////////////////////// //
892 function g_Holmes_MouseEvent (var ev: THMouseEvent): Boolean;
893 begin
894 result := true;
895 msX := ev.x;
896 msY := ev.y;
897 msB := ev.bstate;
898 kbS := ev.kstate;
899 msB := msB;
900 if not uiMouseEvent(ev) then plrDebugMouse(ev);
901 end;
904 function g_Holmes_KeyEvent (var ev: THKeyEvent): Boolean;
905 var
906 mon: TMonster;
907 pan: TPanel;
908 x, y, w, h: Integer;
909 ex, ey: Integer;
910 dx, dy: Integer;
912 procedure dummyWallTrc (cx, cy: Integer);
913 begin
914 end;
916 begin
917 result := false;
918 msB := ev.bstate;
919 kbS := ev.kstate;
920 case ev.scan of
921 SDL_SCANCODE_LCTRL, SDL_SCANCODE_RCTRL,
922 SDL_SCANCODE_LALT, SDL_SCANCODE_RALT,
923 SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT:
924 result := true;
925 end;
926 if uiKeyEvent(ev) then begin result := true; exit; end;
927 // press
928 if (ev.kind = THKeyEvent.Press) then
929 begin
930 // M-M: one monster think step
931 if (ev = 'M-M') then
932 begin
933 result := true;
934 gmon_debug_think := false;
935 gmon_debug_one_think_step := true; // do one step
936 exit;
937 end;
938 // M-I: toggle monster info
939 if (ev = 'M-I') then
940 begin
941 result := true;
942 showMonsInfo := not showMonsInfo;
943 exit;
944 end;
945 // M-L: toggle monster LOS to player
946 if (ev = 'M-L') then
947 begin
948 result := true;
949 showMonsLOS2Plr := not showMonsLOS2Plr;
950 exit;
951 end;
952 // M-G: toggle "show all cells occupied by monsters"
953 if (ev = 'M-G') then
954 begin
955 result := true;
956 showAllMonsCells := not showAllMonsCells;
957 exit;
958 end;
959 // M-A: wake up monster
960 if (ev = 'M-A') then
961 begin
962 result := true;
963 if (monMarkedUID <> -1) then
964 begin
965 mon := g_Monsters_ByUID(monMarkedUID);
966 if (mon <> nil) then mon.WakeUp();
967 end;
968 exit;
969 end;
970 // C-T: teleport player
971 if (ev = 'C-T') then
972 begin
973 result := true;
974 //e_WriteLog(Format('TELEPORT: (%d,%d)', [pmsCurMapX, pmsCurMapY]), MSG_NOTIFY);
975 if (gPlayers[0] <> nil) then
976 begin
977 gPlayers[0].getMapBox(x, y, w, h);
978 gPlayers[0].TeleportTo(pmsCurMapX-w div 2, pmsCurMapY-h div 2, true, 69); // 69: don't change dir
979 end;
980 exit;
981 end;
982 // C-P: show cursor position on the map
983 if (ev = 'C-P') then
984 begin
985 result := true;
986 showMapCurPos := not showMapCurPos;
987 exit;
988 end;
989 // C-G: toggle grid
990 if (ev = 'C-G') then
991 begin
992 result := true;
993 showGrid := not showGrid;
994 exit;
995 end;
996 // C-L: toggle layers window
997 if (ev = 'C-L') then
998 begin
999 result := true;
1000 showLayersWindow := not showLayersWindow;
1001 toggleLayersWindow(nil, 0);
1002 exit;
1003 end;
1004 // C-O: toggle outlines window
1005 if (ev = 'C-O') then
1006 begin
1007 result := true;
1008 showOutlineWindow := not showOutlineWindow;
1009 toggleOutlineWindow(nil, 0);
1010 exit;
1011 end;
1012 // F1: toggle options window
1013 if (ev = 'F1') then
1014 begin
1015 result := true;
1016 if (winHelp = nil) then createHelpWindow();
1017 if not uiVisibleWindow(winHelp) then uiAddWindow(winHelp) else uiRemoveWindow(winHelp);
1018 exit;
1019 end;
1020 // M-F1: toggle options window
1021 if (ev = 'M-F1') then
1022 begin
1023 result := true;
1024 if (winOptions = nil) then createOptionsWindow();
1025 if not uiVisibleWindow(winOptions) then uiAddWindow(winOptions) else uiRemoveWindow(winOptions);
1026 exit;
1027 end;
1028 {$IF DEFINED(D2F_DEBUG)}
1029 // C-UP, C-DOWN, C-LEFT, C-RIGHT: trace 10 pixels from cursor in the respective direction
1030 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
1031 ((ev.kstate and THKeyEvent.ModCtrl) <> 0) then
1032 begin
1033 result := true;
1034 dx := pmsCurMapX;
1035 dy := pmsCurMapY;
1036 case ev.scan of
1037 SDL_SCANCODE_UP: dy -= 120;
1038 SDL_SCANCODE_DOWN: dy += 120;
1039 SDL_SCANCODE_LEFT: dx -= 120;
1040 SDL_SCANCODE_RIGHT: dx += 120;
1041 end;
1042 {$IF DEFINED(D2F_DEBUG)}
1043 //mapGrid.dbgRayTraceTileHitCB := dummyWallTrc;
1044 mapGrid.dbgShowTraceLog := true;
1045 {$ENDIF}
1046 pan := g_Map_traceToNearest(pmsCurMapX, pmsCurMapY, dx, dy, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
1047 {$IF DEFINED(D2F_DEBUG)}
1048 //mapGrid.dbgRayTraceTileHitCB := nil;
1049 mapGrid.dbgShowTraceLog := false;
1050 {$ENDIF}
1051 e_LogWritefln('v-trace: (%d,%d)-(%d,%d); end=(%d,%d); hit=%d', [pmsCurMapX, pmsCurMapY, dx, dy, ex, ey, (pan <> nil)]);
1052 exit;
1053 end;
1054 {$ENDIF}
1055 end;
1056 end;
1059 // ////////////////////////////////////////////////////////////////////////// //
1060 procedure g_Holmes_Draw ();
1061 begin
1062 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
1063 glDisable(GL_STENCIL_TEST);
1064 glDisable(GL_BLEND);
1065 glDisable(GL_SCISSOR_TEST);
1066 glDisable(GL_TEXTURE_2D);
1068 if gGameOn then
1069 begin
1070 plrDebugDraw();
1071 end;
1073 laserSet := false;
1074 end;
1077 procedure g_Holmes_DrawUI ();
1078 begin
1079 uiDraw();
1080 drawCursor();
1081 end;
1084 end.