DEADSOFTWARE

fixed bug in grid raytracer: use `const`, Luke!
[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 showMonsInfo: Boolean = false;
106 showMonsLOS2Plr: Boolean = false;
109 // ////////////////////////////////////////////////////////////////////////// //
110 {$INCLUDE g_holmes.inc}
112 // ////////////////////////////////////////////////////////////////////////// //
113 procedure g_Holmes_VidModeChanged ();
114 begin
115 e_WriteLog(Format('Inspector: videomode changed: %dx%d', [gScreenWidth, gScreenHeight]), MSG_NOTIFY);
116 // texture space is possibly lost here, idc
117 curtexid := 0;
118 font6texid := 0;
119 font8texid := 0;
120 prfont6texid := 0;
121 prfont8texid := 0;
122 //createCursorTexture();
123 end;
125 procedure g_Holmes_WindowFocused ();
126 begin
127 msB := 0;
128 kbS := 0;
129 end;
131 procedure g_Holmes_WindowBlured ();
132 begin
133 end;
136 // ////////////////////////////////////////////////////////////////////////// //
137 var
138 vpSet: Boolean = false;
139 vpx, vpy: Integer;
140 vpw, vph: Integer;
141 laserSet: Boolean = false;
142 laserX0, laserY0, laserX1, laserY1: Integer;
143 monMarkedUID: Integer = -1;
145 procedure g_Holmes_plrView (viewPortX, viewPortY, viewPortW, viewPortH: Integer);
146 begin
147 vpSet := true;
148 vpx := viewPortX;
149 vpy := viewPortY;
150 vpw := viewPortW;
151 vph := viewPortH;
152 end;
154 procedure g_Holmes_plrLaser (ax0, ay0, ax1, ay1: Integer);
155 begin
156 laserSet := true;
157 laserX0 := ax0;
158 laserY0 := ay0;
159 laserX1 := ax1;
160 laserY1 := ay1;
161 end;
164 function pmsCurMapX (): Integer; inline; begin result := msX+vpx; end;
165 function pmsCurMapY (): Integer; inline; begin result := msY+vpy; end;
168 procedure plrDebugMouse (var ev: THMouseEvent);
170 function wallToggle (pan: TPanel; tag: Integer): Boolean;
171 begin
172 result := false; // don't stop
173 if pan.Enabled then g_Map_DisableWall(pan.arrIdx) else g_Map_EnableWall(pan.arrIdx);
174 end;
176 function monsAtDump (mon: TMonster; tag: Integer): Boolean;
177 begin
178 result := false; // don't stop
179 e_WriteLog(Format('monster #%d; UID=%d', [mon.arrIdx, mon.UID]), MSG_NOTIFY);
180 monMarkedUID := mon.UID;
181 //if pan.Enabled then g_Map_DisableWall(pan.arrIdx) else g_Map_EnableWall(pan.arrIdx);
182 end;
184 function monsInCell (mon: TMonster; tag: Integer): Boolean;
185 begin
186 result := false; // don't stop
187 e_WriteLog(Format('monster #%d (UID:%u) (proxyid:%d)', [mon.arrIdx, mon.UID, mon.proxyId]), MSG_NOTIFY);
188 end;
190 begin
191 //e_WriteLog(Format('mouse: x=%d; y=%d; but=%d; bstate=%d', [msx, msy, but, bstate]), MSG_NOTIFY);
192 if (gPlayer1 = nil) then exit;
193 if (ev.kind <> THMouseEvent.Press) then exit;
195 if (ev.but = THMouseEvent.Left) then
196 begin
197 if ((kbS and THKeyEvent.ModShift) <> 0) then
198 begin
199 // dump monsters in cell
200 e_WriteLog('===========================', MSG_NOTIFY);
201 monsGrid.forEachInCell(pmsCurMapX, pmsCurMapY, monsInCell);
202 e_WriteLog('---------------------------', MSG_NOTIFY);
203 end
204 else
205 begin
206 // toggle wall
207 mapGrid.forEachAtPoint(pmsCurMapX, pmsCurMapY, wallToggle, (GridTagWall or GridTagDoor));
208 end;
209 exit;
210 end;
212 if (ev.but = THMouseEvent.Right) then
213 begin
214 monMarkedUID := -1;
215 e_WriteLog('===========================', MSG_NOTIFY);
216 monsGrid.forEachAtPoint(pmsCurMapX, pmsCurMapY, monsAtDump);
217 e_WriteLog('---------------------------', MSG_NOTIFY);
218 exit;
219 end;
220 end;
223 procedure plrDebugDraw ();
225 procedure drawTileGrid ();
226 var
227 x, y: Integer;
228 begin
229 y := mapGrid.gridY0;
230 while (y < mapGrid.gridY0+mapGrid.gridHeight) do
231 begin
232 x := mapGrid.gridX0;
233 while (x < mapGrid.gridX0+mapGrid.gridWidth) do
234 begin
235 if (x+mapGrid.tileSize > vpx) and (y+mapGrid.tileSize > vpy) and
236 (x < vpx+vpw) and (y < vpy+vph) then
237 begin
238 //e_DrawQuad(x, y, x+mapGrid.tileSize-1, y+mapGrid.tileSize-1, 96, 96, 96, 96);
239 drawRect(x, y, mapGrid.tileSize, mapGrid.tileSize, 96, 96, 96, 255);
240 end;
241 Inc(x, mapGrid.tileSize);
242 end;
243 Inc(y, mapGrid.tileSize);
244 end;
245 end;
247 procedure hilightCell (cx, cy: Integer);
248 begin
249 fillRect(cx, cy, monsGrid.tileSize, monsGrid.tileSize, 0, 128, 0, 64);
250 end;
252 procedure hilightCell1 (cx, cy: Integer);
253 begin
254 //e_WriteLog(Format('h1: (%d,%d)', [cx, cy]), MSG_NOTIFY);
255 fillRect(cx, cy, monsGrid.tileSize, monsGrid.tileSize, 255, 255, 0, 92);
256 end;
258 function hilightWallTrc (pan: TPanel; tag: Integer; x, y, prevx, prevy: Integer): Boolean;
259 begin
260 result := false; // don't stop
261 if (pan = nil) then exit; // cell completion, ignore
262 //e_WriteLog(Format('h1: (%d,%d)', [cx, cy]), MSG_NOTIFY);
263 fillRect(pan.X, pan.Y, pan.Width, pan.Height, 0, 128, 128, 64);
264 end;
266 function monsCollector (mon: TMonster; tag: Integer): Boolean;
267 var
268 ex, ey: Integer;
269 mx, my, mw, mh: Integer;
270 begin
271 result := false;
272 mon.getMapBox(mx, my, mw, mh);
273 e_DrawQuad(mx, my, mx+mw-1, my+mh-1, 255, 255, 0, 96);
274 if lineAABBIntersects(laserX0, laserY0, laserX1, laserY1, mx, my, mw, mh, ex, ey) then
275 begin
276 e_DrawPoint(8, ex, ey, 0, 255, 0);
277 end;
278 end;
280 procedure drawMonsterInfo (mon: TMonster);
281 var
282 mx, my, mw, mh: Integer;
284 procedure drawMonsterTargetLine ();
285 var
286 emx, emy, emw, emh: Integer;
287 enemy: TMonster;
288 eplr: TPlayer;
289 ex, ey: Integer;
290 begin
291 if (g_GetUIDType(mon.MonsterTargetUID) = UID_PLAYER) then
292 begin
293 eplr := g_Player_Get(mon.MonsterTargetUID);
294 if (eplr <> nil) then eplr.getMapBox(emx, emy, emw, emh) else exit;
295 end
296 else if (g_GetUIDType(mon.MonsterTargetUID) = UID_MONSTER) then
297 begin
298 enemy := g_Monsters_ByUID(mon.MonsterTargetUID);
299 if (enemy <> nil) then enemy.getMapBox(emx, emy, emw, emh) else exit;
300 end
301 else
302 begin
303 exit;
304 end;
305 mon.getMapBox(mx, my, mw, mh);
306 drawLine(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, 255, 0, 0, 255);
307 if (g_Map_traceToNearestWall(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, @ex, @ey) <> nil) then
308 begin
309 drawLine(mx+mw div 2, my+mh div 2, ex, ey, 0, 255, 0, 255);
310 end;
311 end;
313 procedure drawLOS2Plr ();
314 var
315 emx, emy, emw, emh: Integer;
316 eplr: TPlayer;
317 ex, ey: Integer;
318 begin
319 eplr := gPlayers[0];
320 if (eplr = nil) then exit;
321 eplr.getMapBox(emx, emy, emw, emh);
322 mon.getMapBox(mx, my, mw, mh);
323 drawLine(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, 255, 0, 0, 255);
324 {$IF DEFINED(D2F_DEBUG)}
325 //mapGrid.dbgRayTraceTileHitCB := hilightCell1;
326 {$ENDIF}
327 if (g_Map_traceToNearestWall(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, @ex, @ey) <> nil) then
328 //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
329 begin
330 drawLine(mx+mw div 2, my+mh div 2, ex, ey, 0, 255, 0, 255);
331 end;
332 {$IF DEFINED(D2F_DEBUG)}
333 //mapGrid.dbgRayTraceTileHitCB := nil;
334 {$ENDIF}
335 end;
337 begin
338 if (mon = nil) then exit;
339 mon.getMapBox(mx, my, mw, mh);
340 //mx += mw div 2;
342 monsGrid.forEachBodyCell(mon.proxyId, hilightCell);
344 if showMonsInfo then
345 begin
346 //fillRect(mx-4, my-7*8-6, 110, 7*8+6, 0, 0, 94, 250);
347 shadeRect(mx-4, my-7*8-6, 110, 7*8+6, 128);
348 my -= 8;
349 my -= 2;
351 // type
352 drawText6(mx, my, Format('%s(U:%u)', [monsTypeToString(mon.MonsterType), mon.UID]), 255, 127, 0); my -= 8;
353 // beh
354 drawText6(mx, my, Format('Beh: %s', [monsBehToString(mon.MonsterBehaviour)]), 255, 127, 0); my -= 8;
355 // state
356 drawText6(mx, my, Format('State:%s (%d)', [monsStateToString(mon.MonsterState), mon.MonsterSleep]), 255, 127, 0); my -= 8;
357 // health
358 drawText6(mx, my, Format('Health:%d', [mon.MonsterHealth]), 255, 127, 0); my -= 8;
359 // ammo
360 drawText6(mx, my, Format('Ammo:%d', [mon.MonsterAmmo]), 255, 127, 0); my -= 8;
361 // target
362 drawText6(mx, my, Format('TgtUID:%u', [mon.MonsterTargetUID]), 255, 127, 0); my -= 8;
363 drawText6(mx, my, Format('TgtTime:%d', [mon.MonsterTargetTime]), 255, 127, 0); my -= 8;
364 end;
366 drawMonsterTargetLine();
367 if showMonsLOS2Plr then drawLOS2Plr();
369 property MonsterRemoved: Boolean read FRemoved write FRemoved;
370 property MonsterPain: Integer read FPain write FPain;
371 property MonsterAnim: Byte read FCurAnim write FCurAnim;
373 end;
375 var
376 mon: TMonster;
377 mx, my, mw, mh: Integer;
378 begin
379 //e_DrawPoint(4, plrMouseX, plrMouseY, 255, 0, 255);
380 if (gPlayer1 = nil) then exit;
382 //e_WriteLog(Format('(%d,%d)-(%d,%d)', [laserX0, laserY0, laserX1, laserY1]), MSG_NOTIFY);
384 glPushMatrix();
385 glTranslatef(-vpx, -vpy, 0);
387 drawTileGrid();
389 g_Mons_AlongLine(laserX0, laserY0, laserX1, laserY1, monsCollector, true);
391 if (monMarkedUID <> -1) then
392 begin
393 mon := g_Monsters_ByUID(monMarkedUID);
394 if (mon <> nil) then
395 begin
396 mon.getMapBox(mx, my, mw, mh);
397 e_DrawQuad(mx, my, mx+mw-1, my+mh-1, 255, 0, 0, 30);
398 drawMonsterInfo(mon);
399 end;
400 end;
402 //e_DrawPoint(16, laserX0, laserY0, 255, 255, 255);
404 glPopMatrix();
405 end;
408 // ////////////////////////////////////////////////////////////////////////// //
409 function g_Holmes_mouseEvent (var ev: THMouseEvent): Boolean;
410 begin
411 result := true;
412 msX := ev.x;
413 msY := ev.y;
414 msB := ev.bstate;
415 kbS := ev.kstate;
416 plrDebugMouse(ev);
417 end;
420 function g_Holmes_KeyEvent (var ev: THKeyEvent): Boolean;
421 begin
422 result := false;
423 msB := ev.bstate;
424 kbS := ev.kstate;
425 case ev.scan of
426 SDL_SCANCODE_LCTRL, SDL_SCANCODE_RCTRL,
427 SDL_SCANCODE_LALT, SDL_SCANCODE_RALT,
428 SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT:
429 result := true;
430 end;
431 // press
432 if (ev.kind = THKeyEvent.Press) then
433 begin
434 // M-M: one monster think step
435 if (ev.scan = SDL_SCANCODE_M) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
436 begin
437 result := true;
438 gmon_debug_think := false;
439 gmon_debug_one_think_step := true; // do one step
440 exit;
441 end;
442 // M-I: toggle monster info
443 if (ev.scan = SDL_SCANCODE_I) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
444 begin
445 result := true;
446 showMonsInfo := not showMonsInfo;
447 exit;
448 end;
449 // M-L: toggle monster LOS to player
450 if (ev.scan = SDL_SCANCODE_L) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
451 begin
452 result := true;
453 showMonsLOS2Plr := not showMonsLOS2Plr;
454 exit;
455 end;
456 end;
457 end;
460 // ////////////////////////////////////////////////////////////////////////// //
461 procedure g_Holmes_Draw ();
462 begin
463 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
464 glDisable(GL_STENCIL_TEST);
465 glDisable(GL_BLEND);
466 glDisable(GL_SCISSOR_TEST);
467 glDisable(GL_TEXTURE_2D);
469 if gGameOn then
470 begin
471 plrDebugDraw();
472 end;
474 //drawText6Prop(10, 10, 'Hi there, I''m Holmes!', 255, 255, 0);
475 //drawText8Prop(10, 20, 'Hi there, I''m Holmes!', 255, 255, 0);
477 drawCursor();
478 end;
481 end.