DEADSOFTWARE

slightly faster outline rendering (draw hline as GL_LINE, not as GL_PIXELs)
[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 ();
79 procedure g_Holmes_DrawUI ();
81 function g_Holmes_MouseEvent (var ev: THMouseEvent): Boolean; // returns `true` if event was eaten
82 function g_Holmes_KeyEvent (var ev: THKeyEvent): Boolean; // returns `true` if event was eaten
84 // hooks for player
85 procedure g_Holmes_plrView (viewPortX, viewPortY, viewPortW, viewPortH: Integer);
86 procedure g_Holmes_plrLaser (ax0, ay0, ax1, ay1: Integer);
89 var
90 g_holmes_enabled: Boolean = {$IF DEFINED(D2F_DEBUG)}true{$ELSE}false{$ENDIF};
93 implementation
95 uses
96 SysUtils, GL, SDL2,
97 MAPDEF, g_options;
100 var
101 //globalInited: Boolean = false;
102 msX: Integer = -666;
103 msY: Integer = -666;
104 msB: Word = 0; // button state
105 kbS: Word = 0; // keyboard modifiers state
106 showGrid: Boolean = true;
107 showMonsInfo: Boolean = false;
108 showMonsLOS2Plr: Boolean = false;
109 showAllMonsCells: Boolean = false;
110 showMapCurPos: Boolean = false;
111 showLayersWindow: Boolean = false;
112 showOutlineWindow: Boolean = false;
113 oldShowLayersWindow: Boolean = false;
114 oldShowOutlineWindow: Boolean = false;
116 // ////////////////////////////////////////////////////////////////////////// //
117 {$INCLUDE g_holmes.inc}
118 {$INCLUDE g_holmes_ui.inc}
121 // ////////////////////////////////////////////////////////////////////////// //
122 var
123 g_ol_nice: Boolean = false;
124 g_ol_fill_walls: Boolean = false;
125 g_ol_rlayer_back: Boolean = false;
126 g_ol_rlayer_step: Boolean = false;
127 g_ol_rlayer_wall: Boolean = false;
128 g_ol_rlayer_door: Boolean = false;
129 g_ol_rlayer_acid1: Boolean = false;
130 g_ol_rlayer_acid2: Boolean = false;
131 g_ol_rlayer_water: Boolean = false;
132 g_ol_rlayer_fore: Boolean = false;
135 // ////////////////////////////////////////////////////////////////////////// //
136 var
137 winOptions: THTopWindow = nil;
138 winLayers: THTopWindow = nil;
139 winOutlines: THTopWindow = nil;
142 procedure createOptionsWindow ();
143 var
144 llb: THCtlCBListBox;
145 begin
146 llb := THCtlCBListBox.Create(0, 0);
147 llb.appendItem('map grid', @showGrid);
148 llb.appendItem('cursor position on map', @showMapCurPos);
149 llb.appendItem('monster info', @showMonsInfo);
150 llb.appendItem('monster LOS to player', @showMonsLOS2Plr);
151 llb.appendItem('monster cells (SLOW!)', @showAllMonsCells);
152 llb.appendItem('', nil);
153 llb.appendItem('layers window', @showLayersWindow);
154 llb.appendItem('outline window', @showOutlineWindow);
155 winOptions := THTopWindow.Create('Holmes Options', 100, 100);
156 winOptions.escClose := true;
157 winOptions.appendChild(llb);
158 end;
161 procedure createLayersWindow ();
162 var
163 llb: THCtlCBListBox;
164 begin
165 llb := THCtlCBListBox.Create(0, 0);
166 llb.appendItem('background', @g_rlayer_back);
167 llb.appendItem('steps', @g_rlayer_step);
168 llb.appendItem('walls', @g_rlayer_wall);
169 llb.appendItem('doors', @g_rlayer_door);
170 llb.appendItem('acid1', @g_rlayer_acid1);
171 llb.appendItem('acid2', @g_rlayer_acid2);
172 llb.appendItem('water', @g_rlayer_water);
173 llb.appendItem('foreground', @g_rlayer_fore);
174 winLayers := THTopWindow.Create('visible', 10, 10);
175 winLayers.escClose := true;
176 winLayers.appendChild(llb);
177 end;
180 procedure createOutlinesWindow ();
181 var
182 llb: THCtlCBListBox;
183 begin
184 llb := THCtlCBListBox.Create(0, 0);
185 llb.appendItem('background', @g_ol_rlayer_back);
186 llb.appendItem('steps', @g_ol_rlayer_step);
187 llb.appendItem('walls', @g_ol_rlayer_wall);
188 llb.appendItem('doors', @g_ol_rlayer_door);
189 llb.appendItem('acid1', @g_ol_rlayer_acid1);
190 llb.appendItem('acid2', @g_ol_rlayer_acid2);
191 llb.appendItem('water', @g_ol_rlayer_water);
192 llb.appendItem('foreground', @g_ol_rlayer_fore);
193 llb.appendItem('', nil);
194 llb.appendItem('fill walls', @g_ol_fill_walls);
195 llb.appendItem('', nil);
196 llb.appendItem('slow''n''nice', @g_ol_nice);
197 winOutlines := THTopWindow.Create('outlines', 100, 10);
198 winOutlines.escClose := true;
199 winOutlines.appendChild(llb);
200 end;
203 // ////////////////////////////////////////////////////////////////////////// //
204 procedure g_Holmes_VidModeChanged ();
205 begin
206 e_WriteLog(Format('Holmes: videomode changed: %dx%d', [gScreenWidth, gScreenHeight]), MSG_NOTIFY);
207 // texture space is possibly lost here, idc
208 curtexid := 0;
209 font6texid := 0;
210 font8texid := 0;
211 prfont6texid := 0;
212 prfont8texid := 0;
213 //createCursorTexture();
214 end;
216 procedure g_Holmes_WindowFocused ();
217 begin
218 msB := 0;
219 kbS := 0;
220 end;
222 procedure g_Holmes_WindowBlured ();
223 begin
224 end;
227 // ////////////////////////////////////////////////////////////////////////// //
228 var
229 vpSet: Boolean = false;
230 vpx, vpy: Integer;
231 vpw, vph: Integer;
232 laserSet: Boolean = false;
233 laserX0, laserY0, laserX1, laserY1: Integer;
234 monMarkedUID: Integer = -1;
237 procedure g_Holmes_plrView (viewPortX, viewPortY, viewPortW, viewPortH: Integer);
238 begin
239 vpSet := true;
240 vpx := viewPortX;
241 vpy := viewPortY;
242 vpw := viewPortW;
243 vph := viewPortH;
244 end;
246 procedure g_Holmes_plrLaser (ax0, ay0, ax1, ay1: Integer);
247 begin
248 laserSet := true;
249 laserX0 := ax0;
250 laserY0 := ay0;
251 laserX1 := ax1;
252 laserY1 := ay1;
253 laserSet := laserSet; // shut up, fpc!
254 end;
257 function pmsCurMapX (): Integer; inline; begin result := msX+vpx; end;
258 function pmsCurMapY (): Integer; inline; begin result := msY+vpy; end;
261 procedure plrDebugMouse (var ev: THMouseEvent);
263 function wallToggle (pan: TPanel; tag: Integer): Boolean;
264 begin
265 result := false; // don't stop
266 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);
267 if ((kbS and THKeyEvent.ModAlt) <> 0) then
268 begin
269 if pan.Enabled then g_Map_DisableWall(pan.arrIdx) else g_Map_EnableWall(pan.arrIdx);
270 end;
271 end;
273 function monsAtDump (mon: TMonster; tag: Integer): Boolean;
274 begin
275 result := false; // don't stop
276 e_WriteLog(Format('monster #%d; UID=%d', [mon.arrIdx, mon.UID]), MSG_NOTIFY);
277 monMarkedUID := mon.UID;
278 //if pan.Enabled then g_Map_DisableWall(pan.arrIdx) else g_Map_EnableWall(pan.arrIdx);
279 end;
281 function monsInCell (mon: TMonster; tag: Integer): Boolean;
282 begin
283 result := false; // don't stop
284 e_WriteLog(Format('monster #%d (UID:%u) (proxyid:%d)', [mon.arrIdx, mon.UID, mon.proxyId]), MSG_NOTIFY);
285 end;
287 begin
288 //e_WriteLog(Format('mouse: x=%d; y=%d; but=%d; bstate=%d', [msx, msy, but, bstate]), MSG_NOTIFY);
289 if (gPlayer1 = nil) or not vpSet then exit;
290 if (ev.kind <> THMouseEvent.Press) then exit;
292 e_WriteLog(Format('mev: %d', [Integer(ev.kind)]), MSG_NOTIFY);
294 if (ev.but = THMouseEvent.Left) then
295 begin
296 if ((kbS and THKeyEvent.ModShift) <> 0) then
297 begin
298 // dump monsters in cell
299 e_WriteLog('===========================', MSG_NOTIFY);
300 monsGrid.forEachInCell(pmsCurMapX, pmsCurMapY, monsInCell);
301 e_WriteLog('---------------------------', MSG_NOTIFY);
302 end
303 else
304 begin
305 // toggle wall
306 e_WriteLog('=== TOGGLE WALL ===', MSG_NOTIFY);
307 mapGrid.forEachAtPoint(pmsCurMapX, pmsCurMapY, wallToggle, (GridTagWall or GridTagDoor));
308 e_WriteLog('--- toggle wall ---', MSG_NOTIFY);
309 end;
310 exit;
311 end;
313 if (ev.but = THMouseEvent.Right) then
314 begin
315 monMarkedUID := -1;
316 e_WriteLog('===========================', MSG_NOTIFY);
317 monsGrid.forEachAtPoint(pmsCurMapX, pmsCurMapY, monsAtDump);
318 e_WriteLog('---------------------------', MSG_NOTIFY);
319 exit;
320 end;
321 end;
324 var
325 edgeBmp: array of Byte = nil;
328 procedure drawOutlines ();
329 var
330 r, g, b: Integer;
332 procedure clearEdgeBmp ();
333 begin
334 SetLength(edgeBmp, (gWinSizeX+4)*(gWinSizeY+4));
335 FillChar(edgeBmp[0], Length(edgeBmp)*sizeof(edgeBmp[0]), 0);
336 end;
338 procedure drawPanel (pan: TPanel);
339 var
340 sx, len, y0, y1: Integer;
341 begin
342 if (pan = nil) or (pan.Width < 1) or (pan.Height < 1) then exit;
343 if (pan.X+pan.Width <= vpx-1) or (pan.Y+pan.Height <= vpy-1) then exit;
344 if (pan.X >= vpx+vpw+1) or (pan.Y >= vpy+vph+1) then exit;
345 if g_ol_nice or g_ol_fill_walls then
346 begin
347 sx := pan.X-(vpx-1);
348 len := pan.Width;
349 if (len > gWinSizeX+4) then len := gWinSizeX+4;
350 if (sx < 0) then begin len += sx; sx := 0; end;
351 if (sx+len > gWinSizeX+4) then len := gWinSizeX+4-sx;
352 if (len < 1) then exit;
353 assert(sx >= 0);
354 assert(sx+len <= gWinSizeX+4);
355 y0 := pan.Y-(vpy-1);
356 y1 := y0+pan.Height;
357 if (y0 < 0) then y0 := 0;
358 if (y1 > gWinSizeY+4) then y1 := gWinSizeY+4;
359 while (y0 < y1) do
360 begin
361 FillChar(edgeBmp[y0*(gWinSizeX+4)+sx], len*sizeof(edgeBmp[0]), 1);
362 Inc(y0);
363 end;
364 end
365 else
366 begin
367 drawRect(pan.X, pan.Y, pan.Width, pan.Height, r, g, b);
368 end;
369 end;
371 var
372 lsx: Integer = -1;
373 lex: Integer = -1;
374 lsy: Integer = -1;
376 procedure flushLine ();
377 begin
378 if (lsy > 0) and (lsx > 0) then
379 begin
380 if (lex = lsx) then
381 begin
382 glBegin(GL_POINTS);
383 glVertex2f(lsx-1+vpx+0.37, lsy-1+vpy+0.37);
384 glEnd();
385 end
386 else
387 begin
388 glBegin(GL_LINES);
389 glVertex2f(lsx-1+vpx+0.37, lsy-1+vpy+0.37);
390 glVertex2f(lex-0+vpx+0.37, lsy-1+vpy+0.37);
391 glEnd();
392 end;
393 end;
394 lsx := -1;
395 lex := -1;
396 end;
398 procedure startLine (y: Integer);
399 begin
400 flushLine();
401 lsy := y;
402 end;
404 procedure putPixel (x: Integer);
405 begin
406 if (x < 1) then exit;
407 if (lex+1 <> x) then flushLine();
408 if (lsx < 0) then lsx := x;
409 lex := x;
410 end;
412 procedure drawEdges ();
413 var
414 x, y: Integer;
415 a: PByte;
416 begin
417 glDisable(GL_BLEND);
418 glDisable(GL_TEXTURE_2D);
419 glLineWidth(1);
420 glPointSize(1);
421 glDisable(GL_LINE_SMOOTH);
422 glDisable(GL_POLYGON_SMOOTH);
423 glColor4f(r/255.0, g/255.0, b/255.0, 1.0);
424 for y := 1 to vph do
425 begin
426 a := @edgeBmp[y*(gWinSizeX+4)+1];
427 startLine(y);
428 for x := 1 to vpw do
429 begin
430 if (a[0] <> 0) then
431 begin
432 if (a[-1] = 0) or (a[1] = 0) or (a[-(gWinSizeX+4)] = 0) or (a[gWinSizeX+4] = 0) or
433 (a[-(gWinSizeX+4)-1] = 0) or (a[-(gWinSizeX+4)+1] = 0) or
434 (a[gWinSizeX+4-1] = 0) or (a[gWinSizeX+4+1] = 0) then
435 begin
436 putPixel(x);
437 end;
438 end;
439 Inc(a);
440 end;
441 flushLine();
442 end;
443 end;
445 procedure drawFilledWalls ();
446 var
447 x, y: Integer;
448 a: PByte;
449 begin
450 glDisable(GL_BLEND);
451 glDisable(GL_TEXTURE_2D);
452 glLineWidth(1);
453 glPointSize(1);
454 glDisable(GL_LINE_SMOOTH);
455 glDisable(GL_POLYGON_SMOOTH);
456 glColor4f(r/255.0, g/255.0, b/255.0, 1.0);
457 for y := 1 to vph do
458 begin
459 a := @edgeBmp[y*(gWinSizeX+4)+1];
460 startLine(y);
461 for x := 1 to vpw do
462 begin
463 if (a[0] <> 0) then putPixel(x);
464 Inc(a);
465 end;
466 flushLine();
467 end;
468 end;
470 procedure doWallsOld (parr: array of TPanel; ptype: Word; ar, ag, ab: Integer);
471 var
472 f: Integer;
473 pan: TPanel;
474 begin
475 r := ar;
476 g := ag;
477 b := ab;
478 if g_ol_nice or g_ol_fill_walls then clearEdgeBmp();
479 for f := 0 to High(parr) do
480 begin
481 pan := parr[f];
482 if (pan = nil) or not pan.Enabled or (pan.Width < 1) or (pan.Height < 1) then continue;
483 if ((pan.PanelType and ptype) = 0) then continue;
484 drawPanel(pan);
485 end;
486 if g_ol_nice then drawEdges();
487 if g_ol_fill_walls then drawFilledWalls();
488 end;
490 var
491 xptag: Word;
493 function doWallCB (pan: TPanel; tag: Integer): Boolean;
494 begin
495 result := false; // don't stop
496 //if (pan = nil) or not pan.Enabled or (pan.Width < 1) or (pan.Height < 1) then exit;
497 if ((pan.PanelType and xptag) = 0) then exit;
498 drawPanel(pan);
499 end;
501 procedure doWalls (parr: array of TPanel; ptype: Word; ar, ag, ab: Integer);
502 begin
503 r := ar;
504 g := ag;
505 b := ab;
506 xptag := ptype;
507 if ((ptype and (PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR)) <> 0) then ptype := GridTagWall or GridTagDoor
508 else panelTypeToTag(ptype);
509 if g_ol_nice or g_ol_fill_walls then clearEdgeBmp();
510 mapGrid.forEachInAABB(vpx-1, vpy-1, vpw+2, vph+2, doWallCB, ptype);
511 if g_ol_nice then drawEdges();
512 if g_ol_fill_walls then drawFilledWalls();
513 end;
515 begin
516 if g_ol_rlayer_back then doWallsOld(gRenderBackgrounds, PANEL_BACK, 255, 127, 0);
517 if g_ol_rlayer_step then doWallsOld(gSteps, PANEL_STEP, 192, 192, 192);
518 if g_ol_rlayer_wall then doWallsOld(gWalls, PANEL_WALL, 255, 255, 255);
519 if g_ol_rlayer_door then doWallsOld(gWalls, PANEL_OPENDOOR or PANEL_CLOSEDOOR, 0, 255, 0);
520 if g_ol_rlayer_acid1 then doWallsOld(gAcid1, PANEL_ACID1, 255, 0, 0);
521 if g_ol_rlayer_acid2 then doWallsOld(gAcid2, PANEL_ACID2, 198, 198, 0);
522 if g_ol_rlayer_water then doWallsOld(gWater, PANEL_WATER, 0, 255, 255);
523 if g_ol_rlayer_fore then doWallsOld(gRenderForegrounds, PANEL_FORE, 210, 210, 210);
524 end;
527 procedure plrDebugDraw ();
529 procedure drawTileGrid ();
530 var
531 x, y: Integer;
532 begin
533 for y := 0 to (mapGrid.gridHeight div mapGrid.tileSize) do
534 begin
535 drawLine(mapGrid.gridX0, mapGrid.gridY0+y*mapGrid.tileSize, mapGrid.gridX0+mapGrid.gridWidth, mapGrid.gridY0+y*mapGrid.tileSize, 96, 96, 96, 255);
536 end;
538 for x := 0 to (mapGrid.gridWidth div mapGrid.tileSize) do
539 begin
540 drawLine(mapGrid.gridX0+x*mapGrid.tileSize, mapGrid.gridY0, mapGrid.gridX0+x*mapGrid.tileSize, mapGrid.gridY0+y*mapGrid.gridHeight, 96, 96, 96, 255);
541 end;
542 end;
544 procedure hilightCell (cx, cy: Integer);
545 begin
546 fillRect(cx, cy, monsGrid.tileSize, monsGrid.tileSize, 0, 128, 0, 64);
547 end;
549 procedure hilightCell1 (cx, cy: Integer);
550 begin
551 //e_WriteLog(Format('h1: (%d,%d)', [cx, cy]), MSG_NOTIFY);
552 fillRect(cx, cy, monsGrid.tileSize, monsGrid.tileSize, 255, 255, 0, 92);
553 end;
555 function hilightWallTrc (pan: TPanel; tag: Integer; x, y, prevx, prevy: Integer): Boolean;
556 begin
557 result := false; // don't stop
558 if (pan = nil) then exit; // cell completion, ignore
559 //e_WriteLog(Format('h1: (%d,%d)', [cx, cy]), MSG_NOTIFY);
560 fillRect(pan.X, pan.Y, pan.Width, pan.Height, 0, 128, 128, 64);
561 end;
563 function monsCollector (mon: TMonster; tag: Integer): Boolean;
564 var
565 ex, ey: Integer;
566 mx, my, mw, mh: Integer;
567 begin
568 result := false;
569 mon.getMapBox(mx, my, mw, mh);
570 e_DrawQuad(mx, my, mx+mw-1, my+mh-1, 255, 255, 0, 96);
571 if lineAABBIntersects(laserX0, laserY0, laserX1, laserY1, mx, my, mw, mh, ex, ey) then
572 begin
573 e_DrawPoint(8, ex, ey, 0, 255, 0);
574 end;
575 end;
577 procedure drawMonsterInfo (mon: TMonster);
578 var
579 mx, my, mw, mh: Integer;
581 procedure drawMonsterTargetLine ();
582 var
583 emx, emy, emw, emh: Integer;
584 enemy: TMonster;
585 eplr: TPlayer;
586 ex, ey: Integer;
587 begin
588 if (g_GetUIDType(mon.MonsterTargetUID) = UID_PLAYER) then
589 begin
590 eplr := g_Player_Get(mon.MonsterTargetUID);
591 if (eplr <> nil) then eplr.getMapBox(emx, emy, emw, emh) else exit;
592 end
593 else if (g_GetUIDType(mon.MonsterTargetUID) = UID_MONSTER) then
594 begin
595 enemy := g_Monsters_ByUID(mon.MonsterTargetUID);
596 if (enemy <> nil) then enemy.getMapBox(emx, emy, emw, emh) else exit;
597 end
598 else
599 begin
600 exit;
601 end;
602 mon.getMapBox(mx, my, mw, mh);
603 drawLine(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, 255, 0, 0, 255);
604 if (g_Map_traceToNearestWall(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, @ex, @ey) <> nil) then
605 begin
606 drawLine(mx+mw div 2, my+mh div 2, ex, ey, 0, 255, 0, 255);
607 end;
608 end;
610 procedure drawLOS2Plr ();
611 var
612 emx, emy, emw, emh: Integer;
613 eplr: TPlayer;
614 ex, ey: Integer;
615 begin
616 eplr := gPlayers[0];
617 if (eplr = nil) then exit;
618 eplr.getMapBox(emx, emy, emw, emh);
619 mon.getMapBox(mx, my, mw, mh);
620 drawLine(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, 255, 0, 0, 255);
621 {$IF DEFINED(D2F_DEBUG)}
622 //mapGrid.dbgRayTraceTileHitCB := hilightCell1;
623 {$ENDIF}
624 if (g_Map_traceToNearestWall(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, @ex, @ey) <> nil) then
625 //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
626 begin
627 drawLine(mx+mw div 2, my+mh div 2, ex, ey, 0, 255, 0, 255);
628 end;
629 {$IF DEFINED(D2F_DEBUG)}
630 //mapGrid.dbgRayTraceTileHitCB := nil;
631 {$ENDIF}
632 end;
634 begin
635 if (mon = nil) then exit;
636 mon.getMapBox(mx, my, mw, mh);
637 //mx += mw div 2;
639 monsGrid.forEachBodyCell(mon.proxyId, hilightCell);
641 if showMonsInfo then
642 begin
643 //fillRect(mx-4, my-7*8-6, 110, 7*8+6, 0, 0, 94, 250);
644 darkenRect(mx-4, my-7*8-6, 110, 7*8+6, 128);
645 my -= 8;
646 my -= 2;
648 // type
649 drawText6(mx, my, Format('%s(U:%u)', [monsTypeToString(mon.MonsterType), mon.UID]), 255, 127, 0); my -= 8;
650 // beh
651 drawText6(mx, my, Format('Beh: %s', [monsBehToString(mon.MonsterBehaviour)]), 255, 127, 0); my -= 8;
652 // state
653 drawText6(mx, my, Format('State:%s (%d)', [monsStateToString(mon.MonsterState), mon.MonsterSleep]), 255, 127, 0); my -= 8;
654 // health
655 drawText6(mx, my, Format('Health:%d', [mon.MonsterHealth]), 255, 127, 0); my -= 8;
656 // ammo
657 drawText6(mx, my, Format('Ammo:%d', [mon.MonsterAmmo]), 255, 127, 0); my -= 8;
658 // target
659 drawText6(mx, my, Format('TgtUID:%u', [mon.MonsterTargetUID]), 255, 127, 0); my -= 8;
660 drawText6(mx, my, Format('TgtTime:%d', [mon.MonsterTargetTime]), 255, 127, 0); my -= 8;
661 end;
663 drawMonsterTargetLine();
664 if showMonsLOS2Plr then drawLOS2Plr();
666 property MonsterRemoved: Boolean read FRemoved write FRemoved;
667 property MonsterPain: Integer read FPain write FPain;
668 property MonsterAnim: Byte read FCurAnim write FCurAnim;
670 end;
672 function highlightAllMonsterCells (mon: TMonster): Boolean;
673 begin
674 result := false; // don't stop
675 monsGrid.forEachBodyCell(mon.proxyId, hilightCell);
676 end;
678 var
679 mon: TMonster;
680 mx, my, mw, mh: Integer;
681 begin
682 if (gPlayer1 = nil) then exit;
684 glEnable(GL_SCISSOR_TEST);
685 glScissor(0, gWinSizeY-gPlayerScreenSize.Y-1, vpw, vph);
687 glPushMatrix();
688 glTranslatef(-vpx, -vpy, 0);
690 if (showGrid) then drawTileGrid();
691 drawOutlines();
693 if (laserSet) then g_Mons_AlongLine(laserX0, laserY0, laserX1, laserY1, monsCollector, true);
695 if (monMarkedUID <> -1) then
696 begin
697 mon := g_Monsters_ByUID(monMarkedUID);
698 if (mon <> nil) then
699 begin
700 mon.getMapBox(mx, my, mw, mh);
701 e_DrawQuad(mx, my, mx+mw-1, my+mh-1, 255, 0, 0, 30);
702 drawMonsterInfo(mon);
703 end;
704 end;
706 if showAllMonsCells then g_Mons_ForEach(highlightAllMonsterCells);
708 glPopMatrix();
710 glDisable(GL_SCISSOR_TEST);
712 if showMapCurPos then drawText8(4, gWinSizeY-10, Format('mappos:(%d,%d)', [pmsCurMapX, pmsCurMapY]), 255, 255, 0);
713 end;
716 // ////////////////////////////////////////////////////////////////////////// //
717 function g_Holmes_MouseEvent (var ev: THMouseEvent): Boolean;
718 begin
719 result := true;
720 msX := ev.x;
721 msY := ev.y;
722 msB := ev.bstate;
723 kbS := ev.kstate;
724 msB := msB;
725 if not uiMouseEvent(ev) then plrDebugMouse(ev);
726 end;
729 function g_Holmes_KeyEvent (var ev: THKeyEvent): Boolean;
730 var
731 mon: TMonster;
732 pan: TPanel;
733 x, y, w, h: Integer;
734 ex, ey: Integer;
735 dx, dy: Integer;
737 procedure dummyWallTrc (cx, cy: Integer);
738 begin
739 end;
741 begin
742 result := false;
743 msB := ev.bstate;
744 kbS := ev.kstate;
745 case ev.scan of
746 SDL_SCANCODE_LCTRL, SDL_SCANCODE_RCTRL,
747 SDL_SCANCODE_LALT, SDL_SCANCODE_RALT,
748 SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT:
749 result := true;
750 end;
751 if uiKeyEvent(ev) then begin result := true; exit; end;
752 // press
753 if (ev.kind = THKeyEvent.Press) then
754 begin
755 // M-M: one monster think step
756 if (ev.scan = SDL_SCANCODE_M) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
757 begin
758 result := true;
759 gmon_debug_think := false;
760 gmon_debug_one_think_step := true; // do one step
761 exit;
762 end;
763 // M-I: toggle monster info
764 if (ev.scan = SDL_SCANCODE_I) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
765 begin
766 result := true;
767 showMonsInfo := not showMonsInfo;
768 exit;
769 end;
770 // M-L: toggle monster LOS to player
771 if (ev.scan = SDL_SCANCODE_L) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
772 begin
773 result := true;
774 showMonsLOS2Plr := not showMonsLOS2Plr;
775 exit;
776 end;
777 // M-G: toggle "show all cells occupied by monsters"
778 if (ev.scan = SDL_SCANCODE_G) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
779 begin
780 result := true;
781 showAllMonsCells := not showAllMonsCells;
782 exit;
783 end;
784 // M-A: wake up monster
785 if (ev.scan = SDL_SCANCODE_A) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
786 begin
787 result := true;
788 if (monMarkedUID <> -1) then
789 begin
790 mon := g_Monsters_ByUID(monMarkedUID);
791 if (mon <> nil) then mon.WakeUp();
792 end;
793 exit;
794 end;
795 // C-T: teleport player
796 if (ev.scan = SDL_SCANCODE_T) and ((ev.kstate and THKeyEvent.ModCtrl) <> 0) then
797 begin
798 result := true;
799 //e_WriteLog(Format('TELEPORT: (%d,%d)', [pmsCurMapX, pmsCurMapY]), MSG_NOTIFY);
800 if (gPlayers[0] <> nil) then
801 begin
802 gPlayers[0].getMapBox(x, y, w, h);
803 gPlayers[0].TeleportTo(pmsCurMapX-w div 2, pmsCurMapY-h div 2, true, 69); // 69: don't change dir
804 end;
805 exit;
806 end;
807 // C-P: show cursor position on the map
808 if (ev.scan = SDL_SCANCODE_P) and ((ev.kstate and THKeyEvent.ModCtrl) <> 0) then
809 begin
810 result := true;
811 showMapCurPos := not showMapCurPos;
812 exit;
813 end;
814 // C-G: toggle grid
815 if (ev.scan = SDL_SCANCODE_G) and ((ev.kstate and THKeyEvent.ModCtrl) <> 0) then
816 begin
817 result := true;
818 showGrid := not showGrid;
819 exit;
820 end;
821 // C-L: toggle layers window
822 if (ev.scan = SDL_SCANCODE_L) and ((ev.kstate and THKeyEvent.ModCtrl) <> 0) then
823 begin
824 result := true;
825 showLayersWindow := not showLayersWindow;
826 exit;
827 end;
828 // C-O: toggle outlines window
829 if (ev.scan = SDL_SCANCODE_O) and ((ev.kstate and THKeyEvent.ModCtrl) <> 0) then
830 begin
831 result := true;
832 showOutlineWindow := not showOutlineWindow;
833 exit;
834 end;
835 // F1: toggle options window
836 if (ev.scan = SDL_SCANCODE_F1) and (ev.kstate = 0) then
837 begin
838 result := true;
839 if (winOptions = nil) then createOptionsWindow();
840 if not uiVisibleWindow(winOptions) then uiAddWindow(winOptions) else uiRemoveWindow(winOptions);
841 exit;
842 end;
843 // C-UP, C-DOWN, C-LEFT, C-RIGHT: trace 10 pixels from cursor in the respective direction
844 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
845 ((ev.kstate and THKeyEvent.ModCtrl) <> 0) then
846 begin
847 result := true;
848 dx := pmsCurMapX;
849 dy := pmsCurMapY;
850 case ev.scan of
851 SDL_SCANCODE_UP: dy -= 120;
852 SDL_SCANCODE_DOWN: dy += 120;
853 SDL_SCANCODE_LEFT: dx -= 120;
854 SDL_SCANCODE_RIGHT: dx += 120;
855 end;
856 {$IF DEFINED(D2F_DEBUG)}
857 //mapGrid.dbgRayTraceTileHitCB := dummyWallTrc;
858 mapGrid.dbgShowTraceLog := true;
859 {$ENDIF}
860 pan := g_Map_traceToNearest(pmsCurMapX, pmsCurMapY, dx, dy, (GridTagWall or GridTagDoor or GridTagStep or GridTagAcid1 or GridTagAcid2 or GridTagWater), @ex, @ey);
861 {$IF DEFINED(D2F_DEBUG)}
862 //mapGrid.dbgRayTraceTileHitCB := nil;
863 mapGrid.dbgShowTraceLog := false;
864 {$ENDIF}
865 e_LogWritefln('v-trace: (%d,%d)-(%d,%d); end=(%d,%d); hit=%d', [pmsCurMapX, pmsCurMapY, dx, dy, ex, ey, (pan <> nil)]);
866 exit;
867 end;
868 end;
869 end;
872 // ////////////////////////////////////////////////////////////////////////// //
873 procedure g_Holmes_Draw ();
874 begin
875 if (oldShowLayersWindow <> showLayersWindow) then
876 begin
877 oldShowLayersWindow := showLayersWindow;
878 if showLayersWindow then
879 begin
880 if (winLayers = nil) then createLayersWindow();
881 uiAddWindow(winLayers);
882 end
883 else
884 begin
885 uiRemoveWindow(winLayers);
886 end;
887 end
888 else
889 begin
890 showLayersWindow := uiVisibleWindow(winLayers);
891 oldShowLayersWindow := showLayersWindow;
892 end;
894 if (oldShowOutlineWindow <> showOutlineWindow) then
895 begin
896 oldShowOutlineWindow := showOutlineWindow;
897 if showOutlineWindow then
898 begin
899 if (winOutlines = nil) then createOutlinesWindow();
900 uiAddWindow(winOutlines);
901 end
902 else
903 begin
904 uiRemoveWindow(winOutlines);
905 end;
906 end
907 else
908 begin
909 showOutlineWindow := uiVisibleWindow(winOutlines);
910 oldShowOutlineWindow := showOutlineWindow;
911 end;
913 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
914 glDisable(GL_STENCIL_TEST);
915 glDisable(GL_BLEND);
916 glDisable(GL_SCISSOR_TEST);
917 glDisable(GL_TEXTURE_2D);
919 if gGameOn then
920 begin
921 plrDebugDraw();
922 end;
924 laserSet := false;
925 end;
928 procedure g_Holmes_DrawUI ();
929 begin
930 uiDraw();
931 drawCursor();
932 end;
935 end.