DEADSOFTWARE

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