DEADSOFTWARE

simple UI subsystem for Holmes (yay, now we have two!)
[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,
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
71 end;
74 procedure g_Holmes_VidModeChanged ();
75 procedure g_Holmes_WindowFocused ();
76 procedure g_Holmes_WindowBlured ();
78 procedure g_Holmes_Draw ();
80 function g_Holmes_MouseEvent (var ev: THMouseEvent): Boolean; // returns `true` if event was eaten
81 function g_Holmes_KeyEvent (var ev: THKeyEvent): Boolean; // returns `true` if event was eaten
83 // hooks for player
84 procedure g_Holmes_plrView (viewPortX, viewPortY, viewPortW, viewPortH: Integer);
85 procedure g_Holmes_plrLaser (ax0, ay0, ax1, ay1: Integer);
88 var
89 g_holmes_enabled: Boolean = {$IF DEFINED(D2F_DEBUG)}true{$ELSE}false{$ENDIF};
92 implementation
94 uses
95 SysUtils, GL, SDL2,
96 MAPDEF, g_options;
99 var
100 //globalInited: Boolean = false;
101 msX: Integer = -666;
102 msY: Integer = -666;
103 msB: Word = 0; // button state
104 kbS: Word = 0; // keyboard modifiers state
105 showGrid: Boolean = true;
106 showMonsInfo: Boolean = false;
107 showMonsLOS2Plr: Boolean = false;
108 showAllMonsCells: Boolean = false;
109 showMapCurPos: Boolean = false;
112 // ////////////////////////////////////////////////////////////////////////// //
113 {$INCLUDE g_holmes.inc}
114 {$INCLUDE g_holmes_ui.inc}
117 // ////////////////////////////////////////////////////////////////////////// //
118 var
119 g_ol_rlayer_back: Boolean = false;
120 g_ol_rlayer_step: Boolean = false;
121 g_ol_rlayer_wall: Boolean = false;
122 g_ol_rlayer_door: Boolean = false;
123 g_ol_rlayer_acid1: Boolean = false;
124 g_ol_rlayer_acid2: Boolean = false;
125 g_ol_rlayer_water: Boolean = false;
126 g_ol_rlayer_fore: Boolean = false;
129 // ////////////////////////////////////////////////////////////////////////// //
130 var
131 winOptions: THTopWindow = nil;
132 winLayers: THTopWindow = nil;
133 winOutlines: THTopWindow = nil;
136 procedure createOptionsWindow ();
137 var
138 llb: THCtlCBListBox;
139 begin
140 llb := THCtlCBListBox.Create(0, 0);
141 llb.appendItem('map grid', @showGrid);
142 llb.appendItem('cursor position on map', @showMapCurPos);
143 llb.appendItem('monster info', @showMonsInfo);
144 llb.appendItem('monster LOS to player', @showMonsLOS2Plr);
145 llb.appendItem('monster cells (SLOW!)', @showAllMonsCells);
146 winOptions := THTopWindow.Create('Holmes Options', 100, 100);
147 winOptions.escClose := true;
148 winOptions.appendChild(llb);
149 end;
152 procedure createLayersWindow ();
153 var
154 llb: THCtlCBListBox;
155 begin
156 llb := THCtlCBListBox.Create(0, 0);
157 llb.appendItem('background', @g_rlayer_back);
158 llb.appendItem('steps', @g_rlayer_step);
159 llb.appendItem('walls', @g_rlayer_wall);
160 llb.appendItem('doors', @g_rlayer_door);
161 llb.appendItem('acid1', @g_rlayer_acid1);
162 llb.appendItem('acid2', @g_rlayer_acid2);
163 llb.appendItem('water', @g_rlayer_water);
164 llb.appendItem('foreground', @g_rlayer_fore);
165 winLayers := THTopWindow.Create('visible', 10, 10);
166 winLayers.escClose := true;
167 winLayers.appendChild(llb);
168 end;
171 procedure createOutlinesWindow ();
172 var
173 llb: THCtlCBListBox;
174 begin
175 llb := THCtlCBListBox.Create(0, 0);
176 llb.appendItem('background', @g_ol_rlayer_back);
177 llb.appendItem('steps', @g_ol_rlayer_step);
178 llb.appendItem('walls', @g_ol_rlayer_wall);
179 llb.appendItem('doors', @g_ol_rlayer_door);
180 llb.appendItem('acid1', @g_ol_rlayer_acid1);
181 llb.appendItem('acid2', @g_ol_rlayer_acid2);
182 llb.appendItem('water', @g_ol_rlayer_water);
183 llb.appendItem('foreground', @g_ol_rlayer_fore);
184 winOutlines := THTopWindow.Create('outlines', 100, 10);
185 winOutlines.escClose := true;
186 winOutlines.appendChild(llb);
187 end;
190 // ////////////////////////////////////////////////////////////////////////// //
191 procedure g_Holmes_VidModeChanged ();
192 begin
193 e_WriteLog(Format('Inspector: videomode changed: %dx%d', [gScreenWidth, gScreenHeight]), MSG_NOTIFY);
194 // texture space is possibly lost here, idc
195 curtexid := 0;
196 font6texid := 0;
197 font8texid := 0;
198 prfont6texid := 0;
199 prfont8texid := 0;
200 //createCursorTexture();
201 end;
203 procedure g_Holmes_WindowFocused ();
204 begin
205 msB := 0;
206 kbS := 0;
207 end;
209 procedure g_Holmes_WindowBlured ();
210 begin
211 end;
214 // ////////////////////////////////////////////////////////////////////////// //
215 var
216 vpSet: Boolean = false;
217 vpx, vpy: Integer;
218 vpw, vph: Integer;
219 laserSet: Boolean = false;
220 laserX0, laserY0, laserX1, laserY1: Integer;
221 monMarkedUID: Integer = -1;
224 procedure g_Holmes_plrView (viewPortX, viewPortY, viewPortW, viewPortH: Integer);
225 begin
226 vpSet := true;
227 vpx := viewPortX;
228 vpy := viewPortY;
229 vpw := viewPortW;
230 vph := viewPortH;
231 end;
233 procedure g_Holmes_plrLaser (ax0, ay0, ax1, ay1: Integer);
234 begin
235 laserSet := true;
236 laserX0 := ax0;
237 laserY0 := ay0;
238 laserX1 := ax1;
239 laserY1 := ay1;
240 laserSet := laserSet; // shut up, fpc!
241 end;
244 function pmsCurMapX (): Integer; inline; begin result := msX+vpx; end;
245 function pmsCurMapY (): Integer; inline; begin result := msY+vpy; end;
248 procedure plrDebugMouse (var ev: THMouseEvent);
250 function wallToggle (pan: TPanel; tag: Integer): Boolean;
251 begin
252 result := false; // don't stop
253 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);
254 if ((kbS and THKeyEvent.ModAlt) <> 0) then
255 begin
256 if pan.Enabled then g_Map_DisableWall(pan.arrIdx) else g_Map_EnableWall(pan.arrIdx);
257 end;
258 end;
260 function monsAtDump (mon: TMonster; tag: Integer): Boolean;
261 begin
262 result := false; // don't stop
263 e_WriteLog(Format('monster #%d; UID=%d', [mon.arrIdx, mon.UID]), MSG_NOTIFY);
264 monMarkedUID := mon.UID;
265 //if pan.Enabled then g_Map_DisableWall(pan.arrIdx) else g_Map_EnableWall(pan.arrIdx);
266 end;
268 function monsInCell (mon: TMonster; tag: Integer): Boolean;
269 begin
270 result := false; // don't stop
271 e_WriteLog(Format('monster #%d (UID:%u) (proxyid:%d)', [mon.arrIdx, mon.UID, mon.proxyId]), MSG_NOTIFY);
272 end;
274 begin
275 //e_WriteLog(Format('mouse: x=%d; y=%d; but=%d; bstate=%d', [msx, msy, but, bstate]), MSG_NOTIFY);
276 if (gPlayer1 = nil) or not vpSet then exit;
277 if (ev.kind <> THMouseEvent.Press) then exit;
279 e_WriteLog(Format('mev: %d', [Integer(ev.kind)]), MSG_NOTIFY);
281 if (ev.but = THMouseEvent.Left) then
282 begin
283 if ((kbS and THKeyEvent.ModShift) <> 0) then
284 begin
285 // dump monsters in cell
286 e_WriteLog('===========================', MSG_NOTIFY);
287 monsGrid.forEachInCell(pmsCurMapX, pmsCurMapY, monsInCell);
288 e_WriteLog('---------------------------', MSG_NOTIFY);
289 end
290 else
291 begin
292 // toggle wall
293 e_WriteLog('=== TOGGLE WALL ===', MSG_NOTIFY);
294 mapGrid.forEachAtPoint(pmsCurMapX, pmsCurMapY, wallToggle, (GridTagWall or GridTagDoor));
295 e_WriteLog('--- toggle wall ---', MSG_NOTIFY);
296 end;
297 exit;
298 end;
300 if (ev.but = THMouseEvent.Right) then
301 begin
302 monMarkedUID := -1;
303 e_WriteLog('===========================', MSG_NOTIFY);
304 monsGrid.forEachAtPoint(pmsCurMapX, pmsCurMapY, monsAtDump);
305 e_WriteLog('---------------------------', MSG_NOTIFY);
306 exit;
307 end;
308 end;
311 procedure plrDebugDraw ();
313 procedure drawTileGrid ();
314 var
315 x, y: Integer;
316 begin
318 y := mapGrid.gridY0;
319 while (y < mapGrid.gridY0+mapGrid.gridHeight) do
320 begin
321 x := mapGrid.gridX0;
322 while (x < mapGrid.gridX0+mapGrid.gridWidth) do
323 begin
324 if (x+mapGrid.tileSize > vpx) and (y+mapGrid.tileSize > vpy) and
325 (x < vpx+vpw) and (y < vpy+vph) then
326 begin
327 //e_DrawQuad(x, y, x+mapGrid.tileSize-1, y+mapGrid.tileSize-1, 96, 96, 96, 96);
328 drawRect(x, y, mapGrid.tileSize, mapGrid.tileSize, 96, 96, 96, 255);
329 end;
330 Inc(x, mapGrid.tileSize);
331 end;
332 Inc(y, mapGrid.tileSize);
333 end;
335 for y := 0 to (mapGrid.gridHeight div mapGrid.tileSize) do
336 begin
337 drawLine(mapGrid.gridX0, mapGrid.gridY0+y*mapGrid.tileSize, mapGrid.gridX0+mapGrid.gridWidth, mapGrid.gridY0+y*mapGrid.tileSize, 96, 96, 96, 255);
338 end;
340 for x := 0 to (mapGrid.gridWidth div mapGrid.tileSize) do
341 begin
342 drawLine(mapGrid.gridX0+x*mapGrid.tileSize, mapGrid.gridY0, mapGrid.gridX0+x*mapGrid.tileSize, mapGrid.gridY0+y*mapGrid.gridHeight, 96, 96, 96, 255);
343 end;
344 end;
346 procedure hilightCell (cx, cy: Integer);
347 begin
348 fillRect(cx, cy, monsGrid.tileSize, monsGrid.tileSize, 0, 128, 0, 64);
349 end;
351 procedure hilightCell1 (cx, cy: Integer);
352 begin
353 //e_WriteLog(Format('h1: (%d,%d)', [cx, cy]), MSG_NOTIFY);
354 fillRect(cx, cy, monsGrid.tileSize, monsGrid.tileSize, 255, 255, 0, 92);
355 end;
357 function hilightWallTrc (pan: TPanel; tag: Integer; x, y, prevx, prevy: Integer): Boolean;
358 begin
359 result := false; // don't stop
360 if (pan = nil) then exit; // cell completion, ignore
361 //e_WriteLog(Format('h1: (%d,%d)', [cx, cy]), MSG_NOTIFY);
362 fillRect(pan.X, pan.Y, pan.Width, pan.Height, 0, 128, 128, 64);
363 end;
365 function monsCollector (mon: TMonster; tag: Integer): Boolean;
366 var
367 ex, ey: Integer;
368 mx, my, mw, mh: Integer;
369 begin
370 result := false;
371 mon.getMapBox(mx, my, mw, mh);
372 e_DrawQuad(mx, my, mx+mw-1, my+mh-1, 255, 255, 0, 96);
373 if lineAABBIntersects(laserX0, laserY0, laserX1, laserY1, mx, my, mw, mh, ex, ey) then
374 begin
375 e_DrawPoint(8, ex, ey, 0, 255, 0);
376 end;
377 end;
379 procedure drawMonsterInfo (mon: TMonster);
380 var
381 mx, my, mw, mh: Integer;
383 procedure drawMonsterTargetLine ();
384 var
385 emx, emy, emw, emh: Integer;
386 enemy: TMonster;
387 eplr: TPlayer;
388 ex, ey: Integer;
389 begin
390 if (g_GetUIDType(mon.MonsterTargetUID) = UID_PLAYER) then
391 begin
392 eplr := g_Player_Get(mon.MonsterTargetUID);
393 if (eplr <> nil) then eplr.getMapBox(emx, emy, emw, emh) else exit;
394 end
395 else if (g_GetUIDType(mon.MonsterTargetUID) = UID_MONSTER) then
396 begin
397 enemy := g_Monsters_ByUID(mon.MonsterTargetUID);
398 if (enemy <> nil) then enemy.getMapBox(emx, emy, emw, emh) else exit;
399 end
400 else
401 begin
402 exit;
403 end;
404 mon.getMapBox(mx, my, mw, mh);
405 drawLine(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, 255, 0, 0, 255);
406 if (g_Map_traceToNearestWall(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, @ex, @ey) <> nil) then
407 begin
408 drawLine(mx+mw div 2, my+mh div 2, ex, ey, 0, 255, 0, 255);
409 end;
410 end;
412 procedure drawLOS2Plr ();
413 var
414 emx, emy, emw, emh: Integer;
415 eplr: TPlayer;
416 ex, ey: Integer;
417 begin
418 eplr := gPlayers[0];
419 if (eplr = nil) then exit;
420 eplr.getMapBox(emx, emy, emw, emh);
421 mon.getMapBox(mx, my, mw, mh);
422 drawLine(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, 255, 0, 0, 255);
423 {$IF DEFINED(D2F_DEBUG)}
424 //mapGrid.dbgRayTraceTileHitCB := hilightCell1;
425 {$ENDIF}
426 if (g_Map_traceToNearestWall(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, @ex, @ey) <> nil) then
427 //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
428 begin
429 drawLine(mx+mw div 2, my+mh div 2, ex, ey, 0, 255, 0, 255);
430 end;
431 {$IF DEFINED(D2F_DEBUG)}
432 //mapGrid.dbgRayTraceTileHitCB := nil;
433 {$ENDIF}
434 end;
436 begin
437 if (mon = nil) then exit;
438 mon.getMapBox(mx, my, mw, mh);
439 //mx += mw div 2;
441 monsGrid.forEachBodyCell(mon.proxyId, hilightCell);
443 if showMonsInfo then
444 begin
445 //fillRect(mx-4, my-7*8-6, 110, 7*8+6, 0, 0, 94, 250);
446 darkenRect(mx-4, my-7*8-6, 110, 7*8+6, 128);
447 my -= 8;
448 my -= 2;
450 // type
451 drawText6(mx, my, Format('%s(U:%u)', [monsTypeToString(mon.MonsterType), mon.UID]), 255, 127, 0); my -= 8;
452 // beh
453 drawText6(mx, my, Format('Beh: %s', [monsBehToString(mon.MonsterBehaviour)]), 255, 127, 0); my -= 8;
454 // state
455 drawText6(mx, my, Format('State:%s (%d)', [monsStateToString(mon.MonsterState), mon.MonsterSleep]), 255, 127, 0); my -= 8;
456 // health
457 drawText6(mx, my, Format('Health:%d', [mon.MonsterHealth]), 255, 127, 0); my -= 8;
458 // ammo
459 drawText6(mx, my, Format('Ammo:%d', [mon.MonsterAmmo]), 255, 127, 0); my -= 8;
460 // target
461 drawText6(mx, my, Format('TgtUID:%u', [mon.MonsterTargetUID]), 255, 127, 0); my -= 8;
462 drawText6(mx, my, Format('TgtTime:%d', [mon.MonsterTargetTime]), 255, 127, 0); my -= 8;
463 end;
465 drawMonsterTargetLine();
466 if showMonsLOS2Plr then drawLOS2Plr();
468 property MonsterRemoved: Boolean read FRemoved write FRemoved;
469 property MonsterPain: Integer read FPain write FPain;
470 property MonsterAnim: Byte read FCurAnim write FCurAnim;
472 end;
474 function highlightAllMonsterCells (mon: TMonster): Boolean;
475 begin
476 result := false; // don't stop
477 monsGrid.forEachBodyCell(mon.proxyId, hilightCell);
478 end;
480 var
481 mon: TMonster;
482 mx, my, mw, mh: Integer;
483 begin
484 if (gPlayer1 = nil) then exit;
486 glEnable(GL_SCISSOR_TEST);
487 glScissor(0, gWinSizeY-gPlayerScreenSize.Y-1, vpw, vph);
489 glPushMatrix();
490 glTranslatef(-vpx, -vpy, 0);
492 if (showGrid) then drawTileGrid();
494 if (laserSet) then g_Mons_AlongLine(laserX0, laserY0, laserX1, laserY1, monsCollector, true);
496 if (monMarkedUID <> -1) then
497 begin
498 mon := g_Monsters_ByUID(monMarkedUID);
499 if (mon <> nil) then
500 begin
501 mon.getMapBox(mx, my, mw, mh);
502 e_DrawQuad(mx, my, mx+mw-1, my+mh-1, 255, 0, 0, 30);
503 drawMonsterInfo(mon);
504 end;
505 end;
507 if showAllMonsCells then g_Mons_ForEach(highlightAllMonsterCells);
509 glPopMatrix();
511 glDisable(GL_SCISSOR_TEST);
513 if showMapCurPos then drawText8(4, gWinSizeY-10, Format('mappos:(%d,%d)', [pmsCurMapX, pmsCurMapY]), 255, 255, 0);
514 end;
517 // ////////////////////////////////////////////////////////////////////////// //
518 function g_Holmes_MouseEvent (var ev: THMouseEvent): Boolean;
519 begin
520 result := true;
521 msX := ev.x;
522 msY := ev.y;
523 msB := ev.bstate;
524 kbS := ev.kstate;
525 msB := msB;
526 if not uiMouseEvent(ev) then plrDebugMouse(ev);
527 end;
530 function g_Holmes_KeyEvent (var ev: THKeyEvent): Boolean;
531 var
532 mon: TMonster;
533 pan: TPanel;
534 x, y, w, h: Integer;
535 ex, ey: Integer;
536 dx, dy: Integer;
538 procedure dummyWallTrc (cx, cy: Integer);
539 begin
540 end;
542 begin
543 result := false;
544 msB := ev.bstate;
545 kbS := ev.kstate;
546 case ev.scan of
547 SDL_SCANCODE_LCTRL, SDL_SCANCODE_RCTRL,
548 SDL_SCANCODE_LALT, SDL_SCANCODE_RALT,
549 SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT:
550 result := true;
551 end;
552 if uiKeyEvent(ev) then begin result := true; exit; end;
553 // press
554 if (ev.kind = THKeyEvent.Press) then
555 begin
556 // M-M: one monster think step
557 if (ev.scan = SDL_SCANCODE_M) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
558 begin
559 result := true;
560 gmon_debug_think := false;
561 gmon_debug_one_think_step := true; // do one step
562 exit;
563 end;
564 // M-I: toggle monster info
565 if (ev.scan = SDL_SCANCODE_I) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
566 begin
567 result := true;
568 showMonsInfo := not showMonsInfo;
569 exit;
570 end;
571 // M-L: toggle monster LOS to player
572 if (ev.scan = SDL_SCANCODE_L) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
573 begin
574 result := true;
575 showMonsLOS2Plr := not showMonsLOS2Plr;
576 exit;
577 end;
578 // M-G: toggle "show all cells occupied by monsters"
579 if (ev.scan = SDL_SCANCODE_G) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
580 begin
581 result := true;
582 showAllMonsCells := not showAllMonsCells;
583 exit;
584 end;
585 // M-A: wake up monster
586 if (ev.scan = SDL_SCANCODE_A) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
587 begin
588 result := true;
589 if (monMarkedUID <> -1) then
590 begin
591 mon := g_Monsters_ByUID(monMarkedUID);
592 if (mon <> nil) then mon.WakeUp();
593 end;
594 exit;
595 end;
596 // C-T: teleport player
597 if (ev.scan = SDL_SCANCODE_T) and ((ev.kstate and THKeyEvent.ModCtrl) <> 0) then
598 begin
599 result := true;
600 //e_WriteLog(Format('TELEPORT: (%d,%d)', [pmsCurMapX, pmsCurMapY]), MSG_NOTIFY);
601 if (gPlayers[0] <> nil) then
602 begin
603 gPlayers[0].getMapBox(x, y, w, h);
604 gPlayers[0].TeleportTo(pmsCurMapX-w div 2, pmsCurMapY-h div 2, true, 69); // 69: don't change dir
605 end;
606 exit;
607 end;
608 // C-P: show cursor position on the map
609 if (ev.scan = SDL_SCANCODE_P) and ((ev.kstate and THKeyEvent.ModCtrl) <> 0) then
610 begin
611 result := true;
612 showMapCurPos := not showMapCurPos;
613 exit;
614 end;
615 // C-G: toggle grid
616 if (ev.scan = SDL_SCANCODE_G) and ((ev.kstate and THKeyEvent.ModCtrl) <> 0) then
617 begin
618 result := true;
619 showGrid := not showGrid;
620 exit;
621 end;
622 // C-L: toggle layers window
623 if (ev.scan = SDL_SCANCODE_L) and ((ev.kstate and THKeyEvent.ModCtrl) <> 0) then
624 begin
625 result := true;
626 if (winLayers = nil) then createLayersWindow();
627 if not uiVisibleWindow(winLayers) then uiAddWindow(winLayers) else uiRemoveWindow(winLayers);
628 exit;
629 end;
630 // C-O: toggle outlines window
631 if (ev.scan = SDL_SCANCODE_O) and ((ev.kstate and THKeyEvent.ModCtrl) <> 0) then
632 begin
633 result := true;
634 if (winOutlines = nil) then createOutlinesWindow();
635 if not uiVisibleWindow(winOutlines) then uiAddWindow(winOutlines) else uiRemoveWindow(winOutlines);
636 exit;
637 end;
638 // F1: toggle options window
639 if (ev.scan = SDL_SCANCODE_F1) and (ev.kstate = 0) then
640 begin
641 result := true;
642 if (winOptions = nil) then createOptionsWindow();
643 if not uiVisibleWindow(winOptions) then uiAddWindow(winOptions) else uiRemoveWindow(winOptions);
644 exit;
645 end;
646 // C-UP, C-DOWN, C-LEFT, C-RIGHT: trace 10 pixels from cursor in the respective direction
647 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
648 ((ev.kstate and THKeyEvent.ModCtrl) <> 0) then
649 begin
650 result := true;
651 dx := pmsCurMapX;
652 dy := pmsCurMapY;
653 case ev.scan of
654 SDL_SCANCODE_UP: dy -= 120;
655 SDL_SCANCODE_DOWN: dy += 120;
656 SDL_SCANCODE_LEFT: dx -= 120;
657 SDL_SCANCODE_RIGHT: dx += 120;
658 end;
659 {$IF DEFINED(D2F_DEBUG)}
660 //mapGrid.dbgRayTraceTileHitCB := dummyWallTrc;
661 mapGrid.dbgShowTraceLog := true;
662 {$ENDIF}
663 pan := g_Map_traceToNearest(pmsCurMapX, pmsCurMapY, dx, dy, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
664 {$IF DEFINED(D2F_DEBUG)}
665 //mapGrid.dbgRayTraceTileHitCB := nil;
666 mapGrid.dbgShowTraceLog := false;
667 {$ENDIF}
668 e_LogWritefln('v-trace: (%d,%d)-(%d,%d); end=(%d,%d); hit=%d', [pmsCurMapX, pmsCurMapY, dx, dy, ex, ey, (pan <> nil)]);
669 exit;
670 end;
671 end;
672 end;
675 // ////////////////////////////////////////////////////////////////////////// //
676 procedure g_Holmes_Draw ();
677 begin
678 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
679 glDisable(GL_STENCIL_TEST);
680 glDisable(GL_BLEND);
681 glDisable(GL_SCISSOR_TEST);
682 glDisable(GL_TEXTURE_2D);
684 if gGameOn then
685 begin
686 plrDebugDraw();
687 end;
689 uiDraw();
691 drawCursor();
693 laserSet := false;
694 end;
697 end.