DEADSOFTWARE

put "{$MODE ...}" directive in each source file; removed trailing spaces, and convert...
[d2df-sdl.git] / src / game / g_phys.pas
1 {$MODE DELPHI}
2 unit g_phys;
4 interface
6 uses
7 e_graphics;
9 type
10 PObj = ^TObj;
11 TObj = record
12 X, Y: Integer;
13 Rect: TRectWH;
14 Vel: TPoint2i;
15 Accel: TPoint2i;
16 end;
18 const
19 MAX_YV = 30;
20 LIMIT_VEL = 16384;
21 LIMIT_ACCEL = 1024;
23 MOVE_NONE = 0;
24 MOVE_HITWALL = 1;
25 MOVE_HITCEIL = 2;
26 MOVE_HITLAND = 4;
27 MOVE_FALLOUT = 8;
28 MOVE_INWATER = 16;
29 MOVE_HITWATER = 32;
30 MOVE_HITAIR = 64;
31 MOVE_BLOCK = 128;
33 procedure g_Obj_Init(Obj: PObj);
34 function g_Obj_Move(Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean = False): Word;
35 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; overload;
36 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; overload;
37 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean;
38 function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean;
39 function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean;
40 function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean;
41 function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean;
42 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean;
43 function g_Obj_StayOnStep(Obj: PObj): Boolean;
44 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer);
45 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt);
46 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer);
47 function z_dec(a, b: Integer): Integer;
48 function z_fdec(a, b: Double): Double;
50 var
51 gMon: Boolean = False;
53 implementation
55 uses
56 g_map, g_basic, Math, g_player, g_console, SysUtils,
57 g_sound, g_gfx, MAPDEF, g_monsters, g_game, BinEditor;
59 function g_Obj_StayOnStep(Obj: PObj): Boolean;
60 begin
61 Result := not g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height-1,
62 Obj^.Rect.Width, 1,
63 PANEL_STEP, False)
64 and g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height,
65 Obj^.Rect.Width, 1,
66 PANEL_STEP, False);
67 end;
69 function CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean;
70 begin
71 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
72 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3,
73 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
74 end;
76 function CollideLift(Obj: PObj; XInc, YInc: Integer): Integer;
77 begin
78 if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
79 Obj^.Rect.Width, Obj^.Rect.Height,
80 PANEL_LIFTUP, False) then
81 Result := -1
82 else if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
83 Obj^.Rect.Width, Obj^.Rect.Height,
84 PANEL_LIFTDOWN, False) then
85 Result := 1
86 else
87 Result := 0;
88 end;
90 function CollideHorLift(Obj: PObj; XInc, YInc: Integer): Integer;
91 var
92 left, right: Boolean;
93 begin
94 left := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
95 Obj^.Rect.Width, Obj^.Rect.Height,
96 PANEL_LIFTLEFT, False);
97 right := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
98 Obj^.Rect.Width, Obj^.Rect.Height,
99 PANEL_LIFTRIGHT, False);
100 if left and not right then
101 Result := -1
102 else if right and not left then
103 Result := 1
104 else
105 Result := 0;
106 end;
108 function CollidePlayers(_Obj: PObj; XInc, YInc: Integer): Boolean;
109 var
110 a: Integer;
111 begin
112 Result := False;
114 if gPlayers = nil then
115 Exit;
117 for a := 0 to High(gPlayers) do
118 if gPlayers[a] <> nil then
119 with gPlayers[a] do
120 if Live and
121 g_Collide(GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
122 PLAYER_RECT.Width, PLAYER_RECT.Height,
123 _Obj^.X+_Obj^.Rect.X+XInc, _Obj^.Y+_Obj^.Rect.Y+YInc,
124 _Obj^.Rect.Width, _Obj^.Rect.Height) then
125 begin
126 Result := True;
127 Exit;
128 end;
129 end;
131 function CollideMonsters(Obj: PObj; XInc, YInc: Integer): Boolean;
132 var
133 a: Integer;
134 begin
135 Result := False;
137 if gMonsters = nil then
138 Exit;
140 for a := 0 to High(gMonsters) do
141 if gMonsters[a] <> nil then
142 if gMonsters[a].Live and
143 gMonsters[a].Collide(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
144 Obj^.Rect.Width, Obj^.Rect.Height) then
145 begin
146 Result := True;
147 Exit;
148 end;
149 end;
151 function Blocked(Obj: PObj; XInc, YInc: Integer): Boolean;
152 begin
153 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
154 Obj^.Rect.Width, Obj^.Rect.Height,
155 PANEL_BLOCKMON, False);
156 end;
158 function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean;
159 begin
160 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
161 Obj^.Rect.Width, Obj^.Rect.Height,
162 PANEL_WALL, False);
163 end;
165 function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean;
166 begin
167 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
168 Obj^.Rect.Width, Obj^.Rect.Height,
169 PANEL_STEP, False);
170 end;
172 function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean;
173 begin
174 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
175 Obj^.Rect.Width, Obj^.Rect.Height,
176 PANEL_WATER, False);
177 end;
179 function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean;
180 begin
181 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
182 Obj^.Rect.Width, Obj^.Rect.Height,
183 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
184 end;
186 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean;
187 begin
188 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
189 Obj^.Rect.Width, Obj^.Rect.Height,
190 PanelType, False);
191 end;
193 procedure g_Obj_Splash(Obj: PObj; Color: Byte);
194 var
195 MaxVel: Integer;
196 begin
197 MaxVel := Max(Abs(Obj^.Vel.X), Abs(Obj^.Vel.Y));
198 if MaxVel > 4 then begin
199 if MaxVel < 10 then
200 g_Sound_PlayExAt('SOUND_GAME_BULK1', Obj^.X, Obj^.Y)
201 else
202 g_Sound_PlayExAt('SOUND_GAME_BULK2', Obj^.X, Obj^.Y);
203 end;
205 g_GFX_Water(Obj^.X+Obj^.Rect.X+(Obj^.Rect.Width div 2),
206 Obj^.Y+Obj^.Rect.Y+(Obj^.Rect.Height div 2),
207 Min(5*(Abs(Obj^.Vel.X)+Abs(Obj^.Vel.Y)), 50),
208 -Obj^.Vel.X, -Obj^.Vel.Y,
209 Obj^.Rect.Width, 16, Color);
210 end;
212 function move(Obj: PObj; dx, dy: Integer; ClimbSlopes: Boolean): Word;
213 var
214 i: Integer;
215 sx, sy: ShortInt;
216 st: Word;
218 procedure slope(s: Integer);
219 var
220 i: Integer;
221 begin
222 i := 0;
223 while g_Obj_CollideLevel(Obj, sx, 0) and (i < 4) do
224 begin
225 Obj^.Y := Obj^.Y + s;
226 Inc(i);
227 end;
228 Obj^.X := Obj^.X + sx;
229 end;
231 function movex(): Boolean;
232 begin
233 Result := False;
235 // Åñëè ìîíñòðó øàãíóòü â ñòîðîíó, à òàì áëîêìîí:
236 if gMon and not WordBool(st and MOVE_BLOCK) then
237 if Blocked(Obj, sx, 0) then
238 st := st or MOVE_BLOCK;
240 // Åñëè øàãíóòü â ñòîðîíó, à òàì ñòåíà => øàãàòü íåëüçÿ:
241 if g_Obj_CollideLevel(Obj, sx, 0) then
242 begin
243 if ClimbSlopes and (Abs(dy) < 2) then
244 begin
245 Result := True;
246 if (not g_Obj_CollideLevel(Obj, sx, -12)) // çàáèðàåìñÿ íà 12 ïèêñåëåé âëåâî/âïðàâî
247 and g_Obj_CollidePanel(Obj, 0, 1, PANEL_WALL or PANEL_STEP) // òîëüêî åñëè åñòü çåìëÿ ïîä íîãàìè
248 then
249 slope(-1)
250 else
251 begin
252 Result := False;
253 st := st or MOVE_HITWALL;
254 end;
255 end
256 else
257 st := st or MOVE_HITWALL;
258 end
259 else // Òàì ñòåíû íåò
260 begin
261 if CollideLiquid(Obj, sx, 0) then
262 begin // Åñëè øàãíóòü â ñòîðîíó, à òàì òåïåðü æèäêîñòü
263 if not WordBool(st and MOVE_INWATER) then
264 st := st or MOVE_HITWATER;
265 end
266 else // Åñëè øàãíóòü â ñòîðîíó, à òàì óæå íåò æèäêîñòè
267 if WordBool(st and MOVE_INWATER) then
268 st := st or MOVE_HITAIR;
270 // Øàã:
271 Obj^.X := Obj^.X + sx;
272 Result := True;
273 end;
274 end;
276 function movey(): Boolean;
277 begin
278 Result := False;
280 // Åñëè ìîíñòðó øàãíóòü ïî âåðòèêàëè, à òàì áëîêìîí:
281 if gMon and not WordBool(st and MOVE_BLOCK) then
282 if Blocked(Obj, 0, sy) then
283 st := st or MOVE_BLOCK;
285 // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì ñòåíà => øàãàòü íåëüçÿ:
286 // Èëè åñëè øàãíóòü âíèç, à òàì ñòóïåíü => øàãàòü íåëüçÿ:
287 if g_Obj_CollideLevel(Obj, 0, sy) or
288 ((sy > 0) and g_Obj_StayOnStep(Obj)) then
289 begin
290 if sy > 0 then
291 st := st or MOVE_HITLAND
292 else
293 st := st or MOVE_HITCEIL;
294 end
295 else // Òàì ñòåíû íåò. È ñòóïåíè ñíèçó òîæå íåò
296 begin
297 if CollideLiquid(Obj, 0, sy) then
298 begin // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì òåïåðü æèäêîñòü
299 if not WordBool(st and MOVE_INWATER) then
300 st := st or MOVE_HITWATER;
301 end
302 else // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì óæå íåò æèäêîñòè
303 if WordBool(st and MOVE_INWATER) then
304 st := st or MOVE_HITAIR;
306 // Øàã:
307 Obj^.Y := Obj^.Y + sy;
309 Result := True;
310 end;
311 end;
313 begin
314 st := MOVE_NONE;
316 // Îáúåêò â æèäêîñòè:
317 if CollideLiquid(Obj, 0, 0) then
318 st := st or MOVE_INWATER;
320 // Ìîíñòð â áëîêìîíå:
321 if gMon then
322 if Blocked(Obj, 0, 0) then
323 st := st or MOVE_BLOCK;
325 // Äâèãàòüñÿ íå íàäî:
326 if (dx = 0) and (dy = 0) then
327 begin
328 Result := st;
329 Exit;
330 end;
332 sx := g_basic.Sign(dx);
333 sy := g_basic.Sign(dy);
334 dx := Abs(dx);
335 dy := Abs(dy);
337 for i := 1 to dx do
338 if not movex() then
339 Break;
341 for i := 1 to dy do
342 if not movey() then
343 Break;
345 Result := st;
346 end;
348 procedure g_Obj_Init(Obj: PObj);
349 begin
350 ZeroMemory(Obj, SizeOf(TObj));
351 end;
353 function g_Obj_Move(Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean = False): Word;
354 var
355 xv, yv, dx, dy: Integer;
356 inwater: Boolean;
357 c: Boolean;
358 wtx: DWORD;
359 label
360 _move;
361 begin
362 // Ëèìèòû íà ñêîðîñòü è óñêîðåíèå
363 if Obj^.Vel.X < -LIMIT_VEL then Obj^.Vel.X := -LIMIT_VEL
364 else if Obj^.Vel.X > LIMIT_VEL then Obj^.Vel.X := LIMIT_VEL;
365 if Obj^.Vel.Y < -LIMIT_VEL then Obj^.Vel.Y := -LIMIT_VEL
366 else if Obj^.Vel.Y > LIMIT_VEL then Obj^.Vel.Y := LIMIT_VEL;
367 if Obj^.Accel.X < -LIMIT_ACCEL then Obj^.Accel.X := -LIMIT_ACCEL
368 else if Obj^.Accel.X > LIMIT_ACCEL then Obj^.Accel.X := LIMIT_ACCEL;
369 if Obj^.Accel.Y < -LIMIT_ACCEL then Obj^.Accel.Y := -LIMIT_ACCEL
370 else if Obj^.Accel.Y > LIMIT_ACCEL then Obj^.Accel.Y := LIMIT_ACCEL;
372 // Âûëåòåë çà íèæíþþ ãðàíèöó êàðòû:
373 if Obj^.Y > gMapInfo.Height+128 then
374 begin
375 Result := MOVE_FALLOUT;
376 Exit;
377 end;
379 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì:
380 c := gTime mod (GAME_TICK*2) <> 0;
382 if c then
383 goto _move;
385 case CollideLift(Obj, 0, 0) of
386 -1: //up
387 begin
388 Obj^.Vel.Y := Obj^.Vel.Y - 1; // Ëèôò ââåðõ
389 if Obj^.Vel.Y < -5 then
390 Obj^.Vel.Y := Obj^.Vel.Y + 1;
391 end;
393 1: //down
394 begin
395 if Obj^.Vel.Y > 5 then
396 Obj^.Vel.Y := Obj^.Vel.Y - 1;
397 Obj^.Vel.Y := Obj^.Vel.Y + 1; // Ãðàâèòàöèÿ èëè ëèôò âíèç
398 end;
400 0:
401 begin
402 if Fallable then
403 Obj^.Vel.Y := Obj^.Vel.Y + 1; // Ãðàâèòàöèÿ
404 if Obj^.Vel.Y > MAX_YV then
405 Obj^.Vel.Y := Obj^.Vel.Y - 1;
406 end;
407 end;
409 case CollideHorLift(Obj, 0, 0) of
410 -1: //left
411 begin
412 Obj^.Vel.X := Obj^.Vel.X - 3; // Ëèôò ââåðõ
413 if Obj^.Vel.X < -9 then
414 Obj^.Vel.X := Obj^.Vel.X + 3;
415 end;
417 1: //right
418 begin
419 Obj^.Vel.X := Obj^.Vel.X + 3;
420 if Obj^.Vel.X > 9 then
421 Obj^.Vel.X := Obj^.Vel.X - 3;
422 end;
423 // 0 is not needed here
424 end;
426 inwater := CollideLiquid(Obj, 0, 0);
427 if inwater then
428 begin
429 xv := Abs(Obj^.Vel.X)+1;
430 if xv > 5 then
431 Obj^.Vel.X := z_dec(Obj^.Vel.X, (xv div 2)-2);
433 yv := Abs(Obj^.Vel.Y)+1;
434 if yv > 5 then
435 Obj^.Vel.Y := z_dec(Obj^.Vel.Y, (yv div 2)-2);
437 xv := Abs(Obj^.Accel.X)+1;
438 if xv > 5 then
439 Obj^.Accel.X := z_dec(Obj^.Accel.X, (xv div 2)-2);
441 yv := Abs(Obj^.Accel.Y)+1;
442 if yv > 5 then
443 Obj^.Accel.Y := z_dec(Obj^.Accel.Y, (yv div 2)-2);
444 end;
446 // Óìåíüøàåì ïðèáàâêó ê ñêîðîñòè:
447 Obj^.Accel.X := z_dec(Obj^.Accel.X, 1);
448 Obj^.Accel.Y := z_dec(Obj^.Accel.Y, 1);
450 _move:
452 xv := Obj^.Vel.X + Obj^.Accel.X;
453 yv := Obj^.Vel.Y + Obj^.Accel.Y;
455 dx := xv;
456 dy := yv;
458 Result := move(Obj, dx, dy, ClimbSlopes);
460 // Áðûçãè (åñëè íóæíû):
461 if Splash then
462 if WordBool(Result and MOVE_HITWATER) then
463 begin
464 wtx := g_Map_CollideLiquid_Texture(Obj^.X+Obj^.Rect.X,
465 Obj^.Y+Obj^.Rect.Y,
466 Obj^.Rect.Width,
467 Obj^.Rect.Height*2 div 3);
468 case wtx of
469 TEXTURE_SPECIAL_WATER:
470 g_Obj_Splash(Obj, 3);
471 TEXTURE_SPECIAL_ACID1:
472 g_Obj_Splash(Obj, 2);
473 TEXTURE_SPECIAL_ACID2:
474 g_Obj_Splash(Obj, 1);
475 TEXTURE_NONE:
477 else
478 g_Obj_Splash(Obj, 0);
479 end;
480 end;
482 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì:
483 if c then
484 Exit;
486 // Âðåçàëèñü â ñòåíó - ñòîï:
487 if WordBool(Result and MOVE_HITWALL) then
488 begin
489 Obj^.Vel.X := 0;
490 Obj^.Accel.X := 0;
491 end;
493 // Âðåçàëèñü â ïîë èëè ïîòîëîê - ñòîï:
494 if WordBool(Result and (MOVE_HITCEIL or MOVE_HITLAND)) then
495 begin
496 Obj^.Vel.Y := 0;
497 Obj^.Accel.Y := 0;
498 end;
499 end;
501 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean;
502 begin
503 Result := g_Collide(Obj1^.X+Obj1^.Rect.X, Obj1^.Y+Obj1^.Rect.Y,
504 Obj1^.Rect.Width, Obj1^.Rect.Height,
505 Obj2^.X+Obj2^.Rect.X, Obj2^.Y+Obj2^.Rect.Y,
506 Obj2^.Rect.Width, Obj2^.Rect.Height);
507 end;
509 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean;
510 begin
511 Result := g_Collide(X, Y,
512 Width, Height,
513 Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
514 Obj^.Rect.Width, Obj^.Rect.Height);
515 end;
517 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean;
518 begin
519 Result := g_CollidePoint(X, Y, Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
520 Obj^.Rect.Width, Obj^.Rect.Height);
521 end;
523 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer);
524 begin
525 Obj^.Vel.X := Obj^.Vel.X + VelX;
526 Obj^.Vel.Y := Obj^.Vel.Y + VelY;
527 end;
529 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt);
530 var
531 s, c: Extended;
533 begin
534 SinCos(DegToRad(-Angle), s, c);
536 Obj^.Vel.X := Obj^.Vel.X + Round(Vel*c);
537 Obj^.Vel.Y := Obj^.Vel.Y + Round(Vel*s);
538 end;
540 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer);
541 var
542 m, vx, vy: Integer;
543 begin
544 vx := Obj^.Vel.X;
545 vy := Obj^.Vel.Y;
547 m := Max(Abs(vx), Abs(vy));
548 if m = 0 then
549 m := 1;
551 Obj^.Vel.X := (vx*s) div m;
552 Obj^.Vel.Y := (vy*s) div m;
553 end;
555 function z_dec(a, b: Integer): Integer;
556 begin
557 // Ïðèáëèæàåì a ê 0 íà b åäèíèö:
558 if Abs(a) < b then
559 Result := 0
560 else
561 if a > 0 then
562 Result := a - b
563 else
564 if a < 0 then
565 Result := a + b
566 else // a = 0
567 Result := 0;
568 end;
570 function z_fdec(a, b: Double): Double;
571 begin
572 // Ïðèáëèæàåì a ê 0.0 íà b åäèíèö:
573 if Abs(a) < b then
574 Result := 0.0
575 else
576 if a > 0.0 then
577 Result := a - b
578 else
579 if a < 0.0 then
580 Result := a + b
581 else // a = 0.0
582 Result := 0.0;
583 end;
585 end.