DEADSOFTWARE

added common file with compiler flags; cosmetic fix in g_monsters.pas
[d2df-sdl.git] / src / game / g_phys.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 g_amodes.inc}
17 unit g_phys;
19 interface
21 uses
22 e_graphics;
24 type
25 PObj = ^TObj;
26 TObj = record
27 X, Y: Integer;
28 Rect: TRectWH;
29 Vel: TPoint2i;
30 Accel: TPoint2i;
31 end;
33 const
34 MAX_YV = 30;
35 LIMIT_VEL = 16384;
36 LIMIT_ACCEL = 1024;
38 MOVE_NONE = 0;
39 MOVE_HITWALL = 1;
40 MOVE_HITCEIL = 2;
41 MOVE_HITLAND = 4;
42 MOVE_FALLOUT = 8;
43 MOVE_INWATER = 16;
44 MOVE_HITWATER = 32;
45 MOVE_HITAIR = 64;
46 MOVE_BLOCK = 128;
48 procedure g_Obj_Init(Obj: PObj);
49 function g_Obj_Move(Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean = False): Word;
50 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; overload;
51 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; overload;
52 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean;
53 function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean;
54 function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean;
55 function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean;
56 function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean;
57 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean;
58 function g_Obj_StayOnStep(Obj: PObj): Boolean;
59 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer);
60 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt);
61 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer);
62 function z_dec(a, b: Integer): Integer;
63 function z_fdec(a, b: Double): Double;
65 var
66 gMon: Boolean = False;
68 implementation
70 uses
71 g_map, g_basic, Math, g_player, g_console, SysUtils,
72 g_sound, g_gfx, MAPDEF, g_monsters, g_game, BinEditor;
74 function g_Obj_StayOnStep(Obj: PObj): Boolean;
75 begin
76 Result := not g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height-1,
77 Obj^.Rect.Width, 1,
78 PANEL_STEP, False)
79 and g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height,
80 Obj^.Rect.Width, 1,
81 PANEL_STEP, False);
82 end;
84 function CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean;
85 begin
86 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
87 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3,
88 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
89 end;
91 function CollideLift(Obj: PObj; XInc, YInc: Integer): Integer;
92 begin
93 if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
94 Obj^.Rect.Width, Obj^.Rect.Height,
95 PANEL_LIFTUP, False) then
96 Result := -1
97 else if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
98 Obj^.Rect.Width, Obj^.Rect.Height,
99 PANEL_LIFTDOWN, False) then
100 Result := 1
101 else
102 Result := 0;
103 end;
105 function CollideHorLift(Obj: PObj; XInc, YInc: Integer): Integer;
106 var
107 left, right: Boolean;
108 begin
109 left := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
110 Obj^.Rect.Width, Obj^.Rect.Height,
111 PANEL_LIFTLEFT, False);
112 right := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
113 Obj^.Rect.Width, Obj^.Rect.Height,
114 PANEL_LIFTRIGHT, False);
115 if left and not right then
116 Result := -1
117 else if right and not left then
118 Result := 1
119 else
120 Result := 0;
121 end;
123 function CollidePlayers(_Obj: PObj; XInc, YInc: Integer): Boolean;
124 var
125 a: Integer;
126 begin
127 Result := False;
129 if gPlayers = nil then
130 Exit;
132 for a := 0 to High(gPlayers) do
133 if gPlayers[a] <> nil then
134 with gPlayers[a] do
135 if Live and
136 g_Collide(GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
137 PLAYER_RECT.Width, PLAYER_RECT.Height,
138 _Obj^.X+_Obj^.Rect.X+XInc, _Obj^.Y+_Obj^.Rect.Y+YInc,
139 _Obj^.Rect.Width, _Obj^.Rect.Height) then
140 begin
141 Result := True;
142 Exit;
143 end;
144 end;
146 function CollideMonsters(Obj: PObj; XInc, YInc: Integer): Boolean;
147 var
148 a: Integer;
149 begin
150 Result := False;
152 if gMonsters = nil then
153 Exit;
155 for a := 0 to High(gMonsters) do
156 if gMonsters[a] <> nil then
157 if gMonsters[a].Live and
158 gMonsters[a].Collide(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
159 Obj^.Rect.Width, Obj^.Rect.Height) then
160 begin
161 Result := True;
162 Exit;
163 end;
164 end;
166 function Blocked(Obj: PObj; XInc, YInc: Integer): Boolean;
167 begin
168 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
169 Obj^.Rect.Width, Obj^.Rect.Height,
170 PANEL_BLOCKMON, False);
171 end;
173 function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean;
174 begin
175 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
176 Obj^.Rect.Width, Obj^.Rect.Height,
177 PANEL_WALL, False);
178 end;
180 function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean;
181 begin
182 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
183 Obj^.Rect.Width, Obj^.Rect.Height,
184 PANEL_STEP, False);
185 end;
187 function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean;
188 begin
189 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
190 Obj^.Rect.Width, Obj^.Rect.Height,
191 PANEL_WATER, False);
192 end;
194 function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean;
195 begin
196 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
197 Obj^.Rect.Width, Obj^.Rect.Height,
198 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
199 end;
201 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean;
202 begin
203 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
204 Obj^.Rect.Width, Obj^.Rect.Height,
205 PanelType, False);
206 end;
208 procedure g_Obj_Splash(Obj: PObj; Color: Byte);
209 var
210 MaxVel: Integer;
211 begin
212 MaxVel := Max(Abs(Obj^.Vel.X), Abs(Obj^.Vel.Y));
213 if MaxVel > 4 then begin
214 if MaxVel < 10 then
215 g_Sound_PlayExAt('SOUND_GAME_BULK1', Obj^.X, Obj^.Y)
216 else
217 g_Sound_PlayExAt('SOUND_GAME_BULK2', Obj^.X, Obj^.Y);
218 end;
220 g_GFX_Water(Obj^.X+Obj^.Rect.X+(Obj^.Rect.Width div 2),
221 Obj^.Y+Obj^.Rect.Y+(Obj^.Rect.Height div 2),
222 Min(5*(Abs(Obj^.Vel.X)+Abs(Obj^.Vel.Y)), 50),
223 -Obj^.Vel.X, -Obj^.Vel.Y,
224 Obj^.Rect.Width, 16, Color);
225 end;
227 function move(Obj: PObj; dx, dy: Integer; ClimbSlopes: Boolean): Word;
228 var
229 i: Integer;
230 sx, sy: ShortInt;
231 st: Word;
233 procedure slope(s: Integer);
234 var
235 i: Integer;
236 begin
237 i := 0;
238 while g_Obj_CollideLevel(Obj, sx, 0) and (i < 4) do
239 begin
240 Obj^.Y := Obj^.Y + s;
241 Inc(i);
242 end;
243 Obj^.X := Obj^.X + sx;
244 end;
246 function movex(): Boolean;
247 begin
248 Result := False;
250 // Åñëè ìîíñòðó øàãíóòü â ñòîðîíó, à òàì áëîêìîí:
251 if gMon and not WordBool(st and MOVE_BLOCK) then
252 if Blocked(Obj, sx, 0) then
253 st := st or MOVE_BLOCK;
255 // Åñëè øàãíóòü â ñòîðîíó, à òàì ñòåíà => øàãàòü íåëüçÿ:
256 if g_Obj_CollideLevel(Obj, sx, 0) then
257 begin
258 if ClimbSlopes and (Abs(dy) < 2) then
259 begin
260 Result := True;
261 if (not g_Obj_CollideLevel(Obj, sx, -12)) // çàáèðàåìñÿ íà 12 ïèêñåëåé âëåâî/âïðàâî
262 and g_Obj_CollidePanel(Obj, 0, 1, PANEL_WALL or PANEL_STEP) // òîëüêî åñëè åñòü çåìëÿ ïîä íîãàìè
263 then
264 slope(-1)
265 else
266 begin
267 Result := False;
268 st := st or MOVE_HITWALL;
269 end;
270 end
271 else
272 st := st or MOVE_HITWALL;
273 end
274 else // Òàì ñòåíû íåò
275 begin
276 if CollideLiquid(Obj, sx, 0) then
277 begin // Åñëè øàãíóòü â ñòîðîíó, à òàì òåïåðü æèäêîñòü
278 if not WordBool(st and MOVE_INWATER) then
279 st := st or MOVE_HITWATER;
280 end
281 else // Åñëè øàãíóòü â ñòîðîíó, à òàì óæå íåò æèäêîñòè
282 if WordBool(st and MOVE_INWATER) then
283 st := st or MOVE_HITAIR;
285 // Øàã:
286 Obj^.X := Obj^.X + sx;
287 Result := True;
288 end;
289 end;
291 function movey(): Boolean;
292 begin
293 Result := False;
295 // Åñëè ìîíñòðó øàãíóòü ïî âåðòèêàëè, à òàì áëîêìîí:
296 if gMon and not WordBool(st and MOVE_BLOCK) then
297 if Blocked(Obj, 0, sy) then
298 st := st or MOVE_BLOCK;
300 // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì ñòåíà => øàãàòü íåëüçÿ:
301 // Èëè åñëè øàãíóòü âíèç, à òàì ñòóïåíü => øàãàòü íåëüçÿ:
302 if g_Obj_CollideLevel(Obj, 0, sy) or
303 ((sy > 0) and g_Obj_StayOnStep(Obj)) then
304 begin
305 if sy > 0 then
306 st := st or MOVE_HITLAND
307 else
308 st := st or MOVE_HITCEIL;
309 end
310 else // Òàì ñòåíû íåò. È ñòóïåíè ñíèçó òîæå íåò
311 begin
312 if CollideLiquid(Obj, 0, sy) then
313 begin // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì òåïåðü æèäêîñòü
314 if not WordBool(st and MOVE_INWATER) then
315 st := st or MOVE_HITWATER;
316 end
317 else // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì óæå íåò æèäêîñòè
318 if WordBool(st and MOVE_INWATER) then
319 st := st or MOVE_HITAIR;
321 // Øàã:
322 Obj^.Y := Obj^.Y + sy;
324 Result := True;
325 end;
326 end;
328 begin
329 st := MOVE_NONE;
331 // Îáúåêò â æèäêîñòè:
332 if CollideLiquid(Obj, 0, 0) then
333 st := st or MOVE_INWATER;
335 // Ìîíñòð â áëîêìîíå:
336 if gMon then
337 if Blocked(Obj, 0, 0) then
338 st := st or MOVE_BLOCK;
340 // Äâèãàòüñÿ íå íàäî:
341 if (dx = 0) and (dy = 0) then
342 begin
343 Result := st;
344 Exit;
345 end;
347 sx := g_basic.Sign(dx);
348 sy := g_basic.Sign(dy);
349 dx := Abs(dx);
350 dy := Abs(dy);
352 for i := 1 to dx do
353 if not movex() then
354 Break;
356 for i := 1 to dy do
357 if not movey() then
358 Break;
360 Result := st;
361 end;
363 procedure g_Obj_Init(Obj: PObj);
364 begin
365 ZeroMemory(Obj, SizeOf(TObj));
366 end;
368 function g_Obj_Move(Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean = False): Word;
369 var
370 xv, yv, dx, dy: Integer;
371 inwater: Boolean;
372 c: Boolean;
373 wtx: DWORD;
374 label
375 _move;
376 begin
377 // Ëèìèòû íà ñêîðîñòü è óñêîðåíèå
378 if Obj^.Vel.X < -LIMIT_VEL then Obj^.Vel.X := -LIMIT_VEL
379 else if Obj^.Vel.X > LIMIT_VEL then Obj^.Vel.X := LIMIT_VEL;
380 if Obj^.Vel.Y < -LIMIT_VEL then Obj^.Vel.Y := -LIMIT_VEL
381 else if Obj^.Vel.Y > LIMIT_VEL then Obj^.Vel.Y := LIMIT_VEL;
382 if Obj^.Accel.X < -LIMIT_ACCEL then Obj^.Accel.X := -LIMIT_ACCEL
383 else if Obj^.Accel.X > LIMIT_ACCEL then Obj^.Accel.X := LIMIT_ACCEL;
384 if Obj^.Accel.Y < -LIMIT_ACCEL then Obj^.Accel.Y := -LIMIT_ACCEL
385 else if Obj^.Accel.Y > LIMIT_ACCEL then Obj^.Accel.Y := LIMIT_ACCEL;
387 // Âûëåòåë çà íèæíþþ ãðàíèöó êàðòû:
388 if Obj^.Y > gMapInfo.Height+128 then
389 begin
390 Result := MOVE_FALLOUT;
391 Exit;
392 end;
394 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì:
395 c := gTime mod (GAME_TICK*2) <> 0;
397 if c then
398 goto _move;
400 case CollideLift(Obj, 0, 0) of
401 -1: //up
402 begin
403 Obj^.Vel.Y := Obj^.Vel.Y - 1; // Ëèôò ââåðõ
404 if Obj^.Vel.Y < -5 then
405 Obj^.Vel.Y := Obj^.Vel.Y + 1;
406 end;
408 1: //down
409 begin
410 if Obj^.Vel.Y > 5 then
411 Obj^.Vel.Y := Obj^.Vel.Y - 1;
412 Obj^.Vel.Y := Obj^.Vel.Y + 1; // Ãðàâèòàöèÿ èëè ëèôò âíèç
413 end;
415 0:
416 begin
417 if Fallable then
418 Obj^.Vel.Y := Obj^.Vel.Y + 1; // Ãðàâèòàöèÿ
419 if Obj^.Vel.Y > MAX_YV then
420 Obj^.Vel.Y := Obj^.Vel.Y - 1;
421 end;
422 end;
424 case CollideHorLift(Obj, 0, 0) of
425 -1: //left
426 begin
427 Obj^.Vel.X := Obj^.Vel.X - 3; // Ëèôò ââåðõ
428 if Obj^.Vel.X < -9 then
429 Obj^.Vel.X := Obj^.Vel.X + 3;
430 end;
432 1: //right
433 begin
434 Obj^.Vel.X := Obj^.Vel.X + 3;
435 if Obj^.Vel.X > 9 then
436 Obj^.Vel.X := Obj^.Vel.X - 3;
437 end;
438 // 0 is not needed here
439 end;
441 inwater := CollideLiquid(Obj, 0, 0);
442 if inwater then
443 begin
444 xv := Abs(Obj^.Vel.X)+1;
445 if xv > 5 then
446 Obj^.Vel.X := z_dec(Obj^.Vel.X, (xv div 2)-2);
448 yv := Abs(Obj^.Vel.Y)+1;
449 if yv > 5 then
450 Obj^.Vel.Y := z_dec(Obj^.Vel.Y, (yv div 2)-2);
452 xv := Abs(Obj^.Accel.X)+1;
453 if xv > 5 then
454 Obj^.Accel.X := z_dec(Obj^.Accel.X, (xv div 2)-2);
456 yv := Abs(Obj^.Accel.Y)+1;
457 if yv > 5 then
458 Obj^.Accel.Y := z_dec(Obj^.Accel.Y, (yv div 2)-2);
459 end;
461 // Óìåíüøàåì ïðèáàâêó ê ñêîðîñòè:
462 Obj^.Accel.X := z_dec(Obj^.Accel.X, 1);
463 Obj^.Accel.Y := z_dec(Obj^.Accel.Y, 1);
465 _move:
467 xv := Obj^.Vel.X + Obj^.Accel.X;
468 yv := Obj^.Vel.Y + Obj^.Accel.Y;
470 dx := xv;
471 dy := yv;
473 Result := move(Obj, dx, dy, ClimbSlopes);
475 // Áðûçãè (åñëè íóæíû):
476 if Splash then
477 if WordBool(Result and MOVE_HITWATER) then
478 begin
479 wtx := g_Map_CollideLiquid_Texture(Obj^.X+Obj^.Rect.X,
480 Obj^.Y+Obj^.Rect.Y,
481 Obj^.Rect.Width,
482 Obj^.Rect.Height*2 div 3);
483 case wtx of
484 TEXTURE_SPECIAL_WATER:
485 g_Obj_Splash(Obj, 3);
486 TEXTURE_SPECIAL_ACID1:
487 g_Obj_Splash(Obj, 2);
488 TEXTURE_SPECIAL_ACID2:
489 g_Obj_Splash(Obj, 1);
490 TEXTURE_NONE:
492 else
493 g_Obj_Splash(Obj, 0);
494 end;
495 end;
497 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì:
498 if c then
499 Exit;
501 // Âðåçàëèñü â ñòåíó - ñòîï:
502 if WordBool(Result and MOVE_HITWALL) then
503 begin
504 Obj^.Vel.X := 0;
505 Obj^.Accel.X := 0;
506 end;
508 // Âðåçàëèñü â ïîë èëè ïîòîëîê - ñòîï:
509 if WordBool(Result and (MOVE_HITCEIL or MOVE_HITLAND)) then
510 begin
511 Obj^.Vel.Y := 0;
512 Obj^.Accel.Y := 0;
513 end;
514 end;
516 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean;
517 begin
518 Result := g_Collide(Obj1^.X+Obj1^.Rect.X, Obj1^.Y+Obj1^.Rect.Y,
519 Obj1^.Rect.Width, Obj1^.Rect.Height,
520 Obj2^.X+Obj2^.Rect.X, Obj2^.Y+Obj2^.Rect.Y,
521 Obj2^.Rect.Width, Obj2^.Rect.Height);
522 end;
524 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean;
525 begin
526 Result := g_Collide(X, Y,
527 Width, Height,
528 Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
529 Obj^.Rect.Width, Obj^.Rect.Height);
530 end;
532 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean;
533 begin
534 Result := g_CollidePoint(X, Y, Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
535 Obj^.Rect.Width, Obj^.Rect.Height);
536 end;
538 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer);
539 begin
540 Obj^.Vel.X := Obj^.Vel.X + VelX;
541 Obj^.Vel.Y := Obj^.Vel.Y + VelY;
542 end;
544 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt);
545 var
546 s, c: Extended;
548 begin
549 SinCos(DegToRad(-Angle), s, c);
551 Obj^.Vel.X := Obj^.Vel.X + Round(Vel*c);
552 Obj^.Vel.Y := Obj^.Vel.Y + Round(Vel*s);
553 end;
555 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer);
556 var
557 m, vx, vy: Integer;
558 begin
559 vx := Obj^.Vel.X;
560 vy := Obj^.Vel.Y;
562 m := Max(Abs(vx), Abs(vy));
563 if m = 0 then
564 m := 1;
566 Obj^.Vel.X := (vx*s) div m;
567 Obj^.Vel.Y := (vy*s) div m;
568 end;
570 function z_dec(a, b: Integer): Integer;
571 begin
572 // Ïðèáëèæàåì a ê 0 íà b åäèíèö:
573 if Abs(a) < b then
574 Result := 0
575 else
576 if a > 0 then
577 Result := a - b
578 else
579 if a < 0 then
580 Result := a + b
581 else // a = 0
582 Result := 0;
583 end;
585 function z_fdec(a, b: Double): Double;
586 begin
587 // Ïðèáëèæàåì a ê 0.0 íà b åäèíèö:
588 if Abs(a) < b then
589 Result := 0.0
590 else
591 if a > 0.0 then
592 Result := a - b
593 else
594 if a < 0.0 then
595 Result := a + b
596 else // a = 0.0
597 Result := 0.0;
598 end;
600 end.