DEADSOFTWARE

fixed grid updates; another station now working (kinda)
[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;
107 showAllMonsCells: Boolean = false;
110 // ////////////////////////////////////////////////////////////////////////// //
111 {$INCLUDE g_holmes.inc}
113 // ////////////////////////////////////////////////////////////////////////// //
114 procedure g_Holmes_VidModeChanged ();
115 begin
116 e_WriteLog(Format('Inspector: videomode changed: %dx%d', [gScreenWidth, gScreenHeight]), MSG_NOTIFY);
117 // texture space is possibly lost here, idc
118 curtexid := 0;
119 font6texid := 0;
120 font8texid := 0;
121 prfont6texid := 0;
122 prfont8texid := 0;
123 //createCursorTexture();
124 end;
126 procedure g_Holmes_WindowFocused ();
127 begin
128 msB := 0;
129 kbS := 0;
130 end;
132 procedure g_Holmes_WindowBlured ();
133 begin
134 end;
137 // ////////////////////////////////////////////////////////////////////////// //
138 var
139 vpSet: Boolean = false;
140 vpx, vpy: Integer;
141 vpw, vph: Integer;
142 laserSet: Boolean = false;
143 laserX0, laserY0, laserX1, laserY1: Integer;
144 monMarkedUID: Integer = -1;
146 procedure g_Holmes_plrView (viewPortX, viewPortY, viewPortW, viewPortH: Integer);
147 begin
148 vpSet := true;
149 vpx := viewPortX;
150 vpy := viewPortY;
151 vpw := viewPortW;
152 vph := viewPortH;
153 end;
155 procedure g_Holmes_plrLaser (ax0, ay0, ax1, ay1: Integer);
156 begin
157 laserSet := true;
158 laserX0 := ax0;
159 laserY0 := ay0;
160 laserX1 := ax1;
161 laserY1 := ay1;
162 laserSet := laserSet; // shut up, fpc!
163 end;
166 function pmsCurMapX (): Integer; inline; begin result := msX+vpx; end;
167 function pmsCurMapY (): Integer; inline; begin result := msY+vpy; end;
170 procedure plrDebugMouse (var ev: THMouseEvent);
172 function wallToggle (pan: TPanel; tag: Integer): Boolean;
173 begin
174 result := false; // don't stop
175 e_WriteLog(Format('wall #%d(%d); enabled=%d (%d)', [pan.arrIdx, pan.proxyId, Integer(pan.Enabled), Integer(mapGrid.proxyEnabled[pan.proxyId])]), MSG_NOTIFY);
176 if pan.Enabled then g_Map_DisableWall(pan.arrIdx) else g_Map_EnableWall(pan.arrIdx);
177 end;
179 function monsAtDump (mon: TMonster; tag: Integer): Boolean;
180 begin
181 result := false; // don't stop
182 e_WriteLog(Format('monster #%d; UID=%d', [mon.arrIdx, mon.UID]), MSG_NOTIFY);
183 monMarkedUID := mon.UID;
184 //if pan.Enabled then g_Map_DisableWall(pan.arrIdx) else g_Map_EnableWall(pan.arrIdx);
185 end;
187 function monsInCell (mon: TMonster; tag: Integer): Boolean;
188 begin
189 result := false; // don't stop
190 e_WriteLog(Format('monster #%d (UID:%u) (proxyid:%d)', [mon.arrIdx, mon.UID, mon.proxyId]), MSG_NOTIFY);
191 end;
193 begin
194 //e_WriteLog(Format('mouse: x=%d; y=%d; but=%d; bstate=%d', [msx, msy, but, bstate]), MSG_NOTIFY);
195 if (gPlayer1 = nil) or not vpSet then exit;
196 if (ev.kind <> THMouseEvent.Press) then exit;
198 e_WriteLog(Format('mev: %d', [Integer(ev.kind)]), MSG_NOTIFY);
200 if (ev.but = THMouseEvent.Left) then
201 begin
202 if ((kbS and THKeyEvent.ModShift) <> 0) then
203 begin
204 // dump monsters in cell
205 e_WriteLog('===========================', MSG_NOTIFY);
206 monsGrid.forEachInCell(pmsCurMapX, pmsCurMapY, monsInCell);
207 e_WriteLog('---------------------------', MSG_NOTIFY);
208 end
209 else
210 begin
211 // toggle wall
212 e_WriteLog('=== TOGGLE WALL ===', MSG_NOTIFY);
213 mapGrid.forEachAtPoint(pmsCurMapX, pmsCurMapY, wallToggle, (GridTagWall or GridTagDoor));
214 e_WriteLog('--- toggle wall ---', MSG_NOTIFY);
215 end;
216 exit;
217 end;
219 if (ev.but = THMouseEvent.Right) then
220 begin
221 monMarkedUID := -1;
222 e_WriteLog('===========================', MSG_NOTIFY);
223 monsGrid.forEachAtPoint(pmsCurMapX, pmsCurMapY, monsAtDump);
224 e_WriteLog('---------------------------', MSG_NOTIFY);
225 exit;
226 end;
227 end;
230 procedure plrDebugDraw ();
232 procedure drawTileGrid ();
233 var
234 x, y: Integer;
235 begin
236 y := mapGrid.gridY0;
237 while (y < mapGrid.gridY0+mapGrid.gridHeight) do
238 begin
239 x := mapGrid.gridX0;
240 while (x < mapGrid.gridX0+mapGrid.gridWidth) do
241 begin
242 if (x+mapGrid.tileSize > vpx) and (y+mapGrid.tileSize > vpy) and
243 (x < vpx+vpw) and (y < vpy+vph) then
244 begin
245 //e_DrawQuad(x, y, x+mapGrid.tileSize-1, y+mapGrid.tileSize-1, 96, 96, 96, 96);
246 drawRect(x, y, mapGrid.tileSize, mapGrid.tileSize, 96, 96, 96, 255);
247 end;
248 Inc(x, mapGrid.tileSize);
249 end;
250 Inc(y, mapGrid.tileSize);
251 end;
252 end;
254 procedure hilightCell (cx, cy: Integer);
255 begin
256 fillRect(cx, cy, monsGrid.tileSize, monsGrid.tileSize, 0, 128, 0, 64);
257 end;
259 procedure hilightCell1 (cx, cy: Integer);
260 begin
261 //e_WriteLog(Format('h1: (%d,%d)', [cx, cy]), MSG_NOTIFY);
262 fillRect(cx, cy, monsGrid.tileSize, monsGrid.tileSize, 255, 255, 0, 92);
263 end;
265 function hilightWallTrc (pan: TPanel; tag: Integer; x, y, prevx, prevy: Integer): Boolean;
266 begin
267 result := false; // don't stop
268 if (pan = nil) then exit; // cell completion, ignore
269 //e_WriteLog(Format('h1: (%d,%d)', [cx, cy]), MSG_NOTIFY);
270 fillRect(pan.X, pan.Y, pan.Width, pan.Height, 0, 128, 128, 64);
271 end;
273 function monsCollector (mon: TMonster; tag: Integer): Boolean;
274 var
275 ex, ey: Integer;
276 mx, my, mw, mh: Integer;
277 begin
278 result := false;
279 mon.getMapBox(mx, my, mw, mh);
280 e_DrawQuad(mx, my, mx+mw-1, my+mh-1, 255, 255, 0, 96);
281 if lineAABBIntersects(laserX0, laserY0, laserX1, laserY1, mx, my, mw, mh, ex, ey) then
282 begin
283 e_DrawPoint(8, ex, ey, 0, 255, 0);
284 end;
285 end;
287 procedure drawMonsterInfo (mon: TMonster);
288 var
289 mx, my, mw, mh: Integer;
291 procedure drawMonsterTargetLine ();
292 var
293 emx, emy, emw, emh: Integer;
294 enemy: TMonster;
295 eplr: TPlayer;
296 ex, ey: Integer;
297 begin
298 if (g_GetUIDType(mon.MonsterTargetUID) = UID_PLAYER) then
299 begin
300 eplr := g_Player_Get(mon.MonsterTargetUID);
301 if (eplr <> nil) then eplr.getMapBox(emx, emy, emw, emh) else exit;
302 end
303 else if (g_GetUIDType(mon.MonsterTargetUID) = UID_MONSTER) then
304 begin
305 enemy := g_Monsters_ByUID(mon.MonsterTargetUID);
306 if (enemy <> nil) then enemy.getMapBox(emx, emy, emw, emh) else exit;
307 end
308 else
309 begin
310 exit;
311 end;
312 mon.getMapBox(mx, my, mw, mh);
313 drawLine(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, 255, 0, 0, 255);
314 if (g_Map_traceToNearestWall(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, @ex, @ey) <> nil) then
315 begin
316 drawLine(mx+mw div 2, my+mh div 2, ex, ey, 0, 255, 0, 255);
317 end;
318 end;
320 procedure drawLOS2Plr ();
321 var
322 emx, emy, emw, emh: Integer;
323 eplr: TPlayer;
324 ex, ey: Integer;
325 begin
326 eplr := gPlayers[0];
327 if (eplr = nil) then exit;
328 eplr.getMapBox(emx, emy, emw, emh);
329 mon.getMapBox(mx, my, mw, mh);
330 drawLine(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, 255, 0, 0, 255);
331 {$IF DEFINED(D2F_DEBUG)}
332 //mapGrid.dbgRayTraceTileHitCB := hilightCell1;
333 {$ENDIF}
334 if (g_Map_traceToNearestWall(mx+mw div 2, my+mh div 2, emx+emw div 2, emy+emh div 2, @ex, @ey) <> nil) then
335 //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
336 begin
337 drawLine(mx+mw div 2, my+mh div 2, ex, ey, 0, 255, 0, 255);
338 end;
339 {$IF DEFINED(D2F_DEBUG)}
340 //mapGrid.dbgRayTraceTileHitCB := nil;
341 {$ENDIF}
342 end;
344 begin
345 if (mon = nil) then exit;
346 mon.getMapBox(mx, my, mw, mh);
347 //mx += mw div 2;
349 monsGrid.forEachBodyCell(mon.proxyId, hilightCell);
351 if showMonsInfo then
352 begin
353 //fillRect(mx-4, my-7*8-6, 110, 7*8+6, 0, 0, 94, 250);
354 shadeRect(mx-4, my-7*8-6, 110, 7*8+6, 128);
355 my -= 8;
356 my -= 2;
358 // type
359 drawText6(mx, my, Format('%s(U:%u)', [monsTypeToString(mon.MonsterType), mon.UID]), 255, 127, 0); my -= 8;
360 // beh
361 drawText6(mx, my, Format('Beh: %s', [monsBehToString(mon.MonsterBehaviour)]), 255, 127, 0); my -= 8;
362 // state
363 drawText6(mx, my, Format('State:%s (%d)', [monsStateToString(mon.MonsterState), mon.MonsterSleep]), 255, 127, 0); my -= 8;
364 // health
365 drawText6(mx, my, Format('Health:%d', [mon.MonsterHealth]), 255, 127, 0); my -= 8;
366 // ammo
367 drawText6(mx, my, Format('Ammo:%d', [mon.MonsterAmmo]), 255, 127, 0); my -= 8;
368 // target
369 drawText6(mx, my, Format('TgtUID:%u', [mon.MonsterTargetUID]), 255, 127, 0); my -= 8;
370 drawText6(mx, my, Format('TgtTime:%d', [mon.MonsterTargetTime]), 255, 127, 0); my -= 8;
371 end;
373 drawMonsterTargetLine();
374 if showMonsLOS2Plr then drawLOS2Plr();
376 property MonsterRemoved: Boolean read FRemoved write FRemoved;
377 property MonsterPain: Integer read FPain write FPain;
378 property MonsterAnim: Byte read FCurAnim write FCurAnim;
380 end;
382 function highlightAllMonsterCells (mon: TMonster): Boolean;
383 begin
384 result := false; // don't stop
385 monsGrid.forEachBodyCell(mon.proxyId, hilightCell);
386 end;
388 var
389 mon: TMonster;
390 mx, my, mw, mh: Integer;
391 begin
392 //e_DrawPoint(4, plrMouseX, plrMouseY, 255, 0, 255);
393 if (gPlayer1 = nil) then exit;
395 //e_WriteLog(Format('(%d,%d)-(%d,%d)', [laserX0, laserY0, laserX1, laserY1]), MSG_NOTIFY);
397 glPushMatrix();
398 glTranslatef(-vpx, -vpy, 0);
400 drawTileGrid();
402 g_Mons_AlongLine(laserX0, laserY0, laserX1, laserY1, monsCollector, true);
404 if (monMarkedUID <> -1) then
405 begin
406 mon := g_Monsters_ByUID(monMarkedUID);
407 if (mon <> nil) then
408 begin
409 mon.getMapBox(mx, my, mw, mh);
410 e_DrawQuad(mx, my, mx+mw-1, my+mh-1, 255, 0, 0, 30);
411 drawMonsterInfo(mon);
412 end;
413 end;
415 if showAllMonsCells then g_Mons_ForEach(highlightAllMonsterCells);
417 //e_DrawPoint(16, laserX0, laserY0, 255, 255, 255);
419 glPopMatrix();
420 end;
423 // ////////////////////////////////////////////////////////////////////////// //
424 function g_Holmes_mouseEvent (var ev: THMouseEvent): Boolean;
425 begin
426 result := true;
427 msX := ev.x;
428 msY := ev.y;
429 msB := ev.bstate;
430 kbS := ev.kstate;
431 msB := msB;
432 plrDebugMouse(ev);
433 end;
436 function g_Holmes_KeyEvent (var ev: THKeyEvent): Boolean;
437 var
438 mon: TMonster;
439 x, y, w, h: Integer;
440 begin
441 result := false;
442 msB := ev.bstate;
443 kbS := ev.kstate;
444 case ev.scan of
445 SDL_SCANCODE_LCTRL, SDL_SCANCODE_RCTRL,
446 SDL_SCANCODE_LALT, SDL_SCANCODE_RALT,
447 SDL_SCANCODE_LSHIFT, SDL_SCANCODE_RSHIFT:
448 result := true;
449 end;
450 // press
451 if (ev.kind = THKeyEvent.Press) then
452 begin
453 // M-M: one monster think step
454 if (ev.scan = SDL_SCANCODE_M) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
455 begin
456 result := true;
457 gmon_debug_think := false;
458 gmon_debug_one_think_step := true; // do one step
459 exit;
460 end;
461 // M-I: toggle monster info
462 if (ev.scan = SDL_SCANCODE_I) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
463 begin
464 result := true;
465 showMonsInfo := not showMonsInfo;
466 exit;
467 end;
468 // M-L: toggle monster LOS to player
469 if (ev.scan = SDL_SCANCODE_L) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
470 begin
471 result := true;
472 showMonsLOS2Plr := not showMonsLOS2Plr;
473 exit;
474 end;
475 // M-G: toggle "show all cells occupied by monsters"
476 if (ev.scan = SDL_SCANCODE_G) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
477 begin
478 result := true;
479 showAllMonsCells := not showAllMonsCells;
480 exit;
481 end;
482 // M-A: wake up monster
483 if (ev.scan = SDL_SCANCODE_A) and ((ev.kstate and THKeyEvent.ModAlt) <> 0) then
484 begin
485 result := true;
486 if (monMarkedUID <> -1) then
487 begin
488 mon := g_Monsters_ByUID(monMarkedUID);
489 if (mon <> nil) then mon.WakeUp();
490 end;
491 exit;
492 end;
493 // C-T: teleport player
494 if (ev.scan = SDL_SCANCODE_T) and ((ev.kstate and THKeyEvent.ModCtrl) <> 0) then
495 begin
496 result := true;
497 //e_WriteLog(Format('TELEPORT: (%d,%d)', [pmsCurMapX, pmsCurMapY]), MSG_NOTIFY);
498 if (gPlayers[0] <> nil) then
499 begin
500 gPlayers[0].getMapBox(x, y, w, h);
501 gPlayers[0].TeleportTo(pmsCurMapX-w div 2, pmsCurMapY-h div 2, true, 69); // 69: don't change dir
502 end;
503 exit;
504 end;
505 end;
506 end;
509 // ////////////////////////////////////////////////////////////////////////// //
510 procedure g_Holmes_Draw ();
511 begin
512 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // modify color buffer
513 glDisable(GL_STENCIL_TEST);
514 glDisable(GL_BLEND);
515 glDisable(GL_SCISSOR_TEST);
516 glDisable(GL_TEXTURE_2D);
518 if gGameOn then
519 begin
520 plrDebugDraw();
521 end;
523 //drawText6Prop(10, 10, 'Hi there, I''m Holmes!', 255, 255, 0);
524 //drawText8Prop(10, 20, 'Hi there, I''m Holmes!', 255, 255, 0);
526 drawCursor();
528 laserSet := false;
529 end;
532 end.