saveload: fix read/write unexisting value
[d2df-sdl.git] / src / game / g_phys.pas
blob84a9cba28e38a8813cc83a6b8474fab9d98ec8df
1 (* Copyright (C) Doom 2D: Forever Developers
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, version 3 of the License ONLY.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 {$INCLUDE ../shared/a_modes.inc}
16 unit g_phys;
18 interface
20 uses
21 e_graphics;
23 type
24 PObj = ^TObj;
25 TObj = record
26 X, Y: Integer;
27 Rect: TRectWH;
28 Vel: TPoint2i;
29 Accel: TPoint2i;
30 // going up the slope will set this, and renderer will adjust the position
31 // this is purely visual change, it won't affect anything else
32 slopeUpLeft: Integer; // left to go
33 slopeFramesLeft: Integer; // frames left to go
34 // for frame interpolation
35 oldX, oldY: Integer;
36 procedure lerp(t: Single; out fX, fY: Integer);
37 end;
39 const
40 MAX_YV = 30;
41 LIMIT_VEL = 16384;
42 LIMIT_ACCEL = 1024;
44 MOVE_NONE = 0;
45 MOVE_HITWALL = 1;
46 MOVE_HITCEIL = 2;
47 MOVE_HITLAND = 4;
48 MOVE_FALLOUT = 8;
49 MOVE_INWATER = 16;
50 MOVE_HITWATER = 32;
51 MOVE_HITAIR = 64;
52 MOVE_BLOCK = 128;
54 procedure g_Obj_Init(Obj: PObj); inline;
55 function g_Obj_Move(Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False; asProjectile: Boolean=false): Word;
56 function g_Obj_Move_Projectile (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False): Word;
57 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; inline; overload;
58 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; inline; overload;
59 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean; inline;
60 function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
61 function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
62 function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
63 function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
64 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean; inline;
65 function g_Obj_StayOnStep(Obj: PObj): Boolean; inline;
66 function g_Obj_CanMoveY(Obj: PObj; YInc: Integer): Boolean; inline;
67 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer); inline;
68 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt); inline;
69 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer); inline;
70 function g_Obj_GetSpeedDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline; // `false`: zero speed
71 function g_Obj_GetAccelDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline; // `false`: zero speed
72 function z_dec(a, b: Integer): Integer; inline;
73 function z_fdec(a, b: Double): Double; inline;
75 var
76 gMon: Boolean = False;
78 implementation
80 uses
81 {$IFDEF ENABLE_SOUND}
82 g_sound,
83 {$ENDIF}
84 g_map, g_basic, Math, g_player, g_console, SysUtils,
85 g_gfx, MAPDEF, g_monsters, g_game, utils;
88 const
89 SmoothSlopeFrames = 4;
91 procedure TObj.lerp(t: Single; out fX, fY: Integer);
92 begin
93 fX := nlerp(oldX, X, t);
94 fY := nlerp(oldY, Y, t);
95 end;
97 function g_Obj_StayOnStep(Obj: PObj): Boolean; inline;
98 begin
99 Result := not g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height-1,
100 Obj^.Rect.Width, 1,
101 PANEL_STEP, False)
102 and g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height,
103 Obj^.Rect.Width, 1,
104 PANEL_STEP, False);
105 end;
107 function g_Obj_CanMoveY(Obj: PObj; YInc: Integer): Boolean; inline;
108 begin
109 // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì ñòåíà => øàãàòü íåëüçÿ
110 // Èëè åñëè øàãíóòü âíèç, à òàì ñòóïåíü => øàãàòü íåëüçÿ
111 Result := not(g_Obj_CollideLevel(Obj, 0, YInc) or ((YInc > 0) and g_Obj_StayOnStep(Obj)));
112 end;
114 function CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
115 begin
116 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
117 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3,
118 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
119 end;
121 function CollideLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
122 begin
123 if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
124 Obj^.Rect.Width, Obj^.Rect.Height,
125 PANEL_LIFTUP, False) then
126 Result := -1
127 else if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
128 Obj^.Rect.Width, Obj^.Rect.Height,
129 PANEL_LIFTDOWN, False) then
130 Result := 1
131 else
132 Result := 0;
133 end;
135 function CollideHorLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
137 left, right: Boolean;
138 begin
139 left := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
140 Obj^.Rect.Width, Obj^.Rect.Height,
141 PANEL_LIFTLEFT, False);
142 right := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
143 Obj^.Rect.Width, Obj^.Rect.Height,
144 PANEL_LIFTRIGHT, False);
145 if left and not right then
146 Result := -1
147 else if right and not left then
148 Result := 1
149 else
150 Result := 0;
151 end;
153 function CollidePlayers(_Obj: PObj; XInc, YInc: Integer): Boolean;
155 plr: TPlayer;
156 begin
157 result := false;
158 if (gPlayers = nil) then exit;
159 for plr in gPlayers do
160 begin
161 if (plr = nil) then continue;
162 if not plr.alive then continue;
163 with plr do
164 begin
165 if g_Collide(GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
166 PLAYER_RECT.Width, PLAYER_RECT.Height,
167 _Obj^.X+_Obj^.Rect.X+XInc, _Obj^.Y+_Obj^.Rect.Y+YInc,
168 _Obj^.Rect.Width, _Obj^.Rect.Height) then
169 begin
170 result := true;
171 exit;
172 end;
173 end;
174 end;
175 end;
177 function Blocked(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
178 begin
179 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
180 Obj^.Rect.Width, Obj^.Rect.Height,
181 PANEL_BLOCKMON, False);
182 end;
184 function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
185 begin
186 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
187 Obj^.Rect.Width, Obj^.Rect.Height,
188 PANEL_WALL, False);
189 end;
191 function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
192 begin
193 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
194 Obj^.Rect.Width, Obj^.Rect.Height,
195 PANEL_STEP, False);
196 end;
198 function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
199 begin
200 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
201 Obj^.Rect.Width, Obj^.Rect.Height,
202 PANEL_WATER, False);
203 end;
205 function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
206 begin
207 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
208 Obj^.Rect.Width, Obj^.Rect.Height,
209 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
210 end;
212 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean; inline;
213 begin
214 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
215 Obj^.Rect.Width, Obj^.Rect.Height,
216 PanelType, False);
217 end;
219 procedure g_Obj_Splash(Obj: PObj; Color: Byte);
220 {$IFDEF ENABLE_SOUND}
221 var MaxVel: Integer;
222 {$ENDIF}
223 begin
224 {$IFDEF ENABLE_SOUND}
225 MaxVel := nmax(abs(Obj^.Vel.X), abs(Obj^.Vel.Y));
226 if MaxVel > 4 then
227 begin
228 if MaxVel < 10 then
229 g_Sound_PlayExAt('SOUND_GAME_BULK1', Obj^.X, Obj^.Y)
230 else
231 g_Sound_PlayExAt('SOUND_GAME_BULK2', Obj^.X, Obj^.Y);
232 end;
233 {$ENDIF}
235 g_GFX_Water(Obj^.X+Obj^.Rect.X+(Obj^.Rect.Width div 2),
236 Obj^.Y+Obj^.Rect.Y+(Obj^.Rect.Height div 2),
237 Min(5*(abs(Obj^.Vel.X)+abs(Obj^.Vel.Y)), 50),
238 -Obj^.Vel.X, -Obj^.Vel.Y,
239 Obj^.Rect.Width, 16, Color);
240 end;
243 function move (Obj: PObj; dx, dy: Integer; ClimbSlopes: Boolean): Word;
245 i: Integer;
246 sx, sy: ShortInt;
247 st: Word;
249 procedure slope (s: Integer);
251 i: Integer;
252 begin
253 i := 0;
254 while g_Obj_CollideLevel(Obj, sx, 0) and (i < 4) do
255 begin
256 Obj^.Y += s;
257 Inc(i);
258 end;
259 Obj^.X += sx;
260 if (s < 0) then
261 begin
262 Obj.slopeUpLeft += i*(-s);
263 Obj.slopeFramesLeft := SmoothSlopeFrames;
264 end;
265 end;
267 function movex (): Boolean;
268 begin
269 result := false;
271 // Åñëè ìîíñòðó øàãíóòü â ñòîðîíó, à òàì áëîêìîí
272 if gMon and ((st and MOVE_BLOCK) = 0) then
273 begin
274 if Blocked(Obj, sx, 0) then st := st or MOVE_BLOCK;
275 end;
277 // Åñëè øàãíóòü â ñòîðîíó, à òàì ñòåíà => øàãàòü íåëüçÿ
278 if g_Obj_CollideLevel(Obj, sx, 0) then
279 begin
280 if ClimbSlopes and (abs(dy) < 2) then
281 begin
282 result := true;
283 if (not g_Obj_CollideLevel(Obj, sx, -12)) and // çàáèðàåìñÿ íà 12 ïèêñåëåé âëåâî/âïðàâî
284 (sy >= 0) and (not g_Obj_CanMoveY(Obj, sy)) then // òîëüêî åñëè åñòü çåìëÿ ïîä íîãàìè
285 begin
286 slope(-1);
288 else
289 begin
290 result := false;
291 st := st or MOVE_HITWALL;
292 end;
294 else
295 begin
296 st := st or MOVE_HITWALL;
297 end;
299 else // Òàì ñòåíû íåò
300 begin
301 if CollideLiquid(Obj, sx, 0) then
302 begin // Åñëè øàãíóòü â ñòîðîíó, à òàì òåïåðü æèäêîñòü
303 if ((st and MOVE_INWATER) = 0) then st := st or MOVE_HITWATER;
305 else // Åñëè øàãíóòü â ñòîðîíó, à òàì óæå íåò æèäêîñòè
306 begin
307 if ((st and MOVE_INWATER) <> 0) then st := st or MOVE_HITAIR;
308 end;
310 // Øàã
311 Obj^.X += sx;
312 result := true;
313 end;
314 end;
316 function movey (): Boolean;
317 begin
318 result := false;
320 // Åñëè ìîíñòðó øàãíóòü ïî âåðòèêàëè, à òàì áëîêìîí
321 if gMon and ((st and MOVE_BLOCK) = 0) then
322 begin
323 if Blocked(Obj, 0, sy) then st := st or MOVE_BLOCK;
324 end;
326 // Åñëè øàãàòü íåëüçÿ
327 if not g_Obj_CanMoveY(Obj, sy) then
328 begin
329 if sy > 0 then
330 st := st or MOVE_HITLAND
331 else
332 st := st or MOVE_HITCEIL;
334 else // Òàì ñòåíû íåò. È ñòóïåíè ñíèçó òîæå íåò
335 begin
336 if CollideLiquid(Obj, 0, sy) then
337 begin // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì òåïåðü æèäêîñòü
338 if ((st and MOVE_INWATER) = 0) then st := st or MOVE_HITWATER;
340 else // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì óæå íåò æèäêîñòè
341 begin
342 if ((st and MOVE_INWATER) <> 0) then st := st or MOVE_HITAIR;
343 end;
345 // Øàã
346 Obj^.Y += sy;
347 result := true;
348 end;
349 end;
351 begin
352 st := MOVE_NONE;
354 // Îáúåêò â æèäêîñòè?
355 if CollideLiquid(Obj, 0, 0) then st := st or MOVE_INWATER;
357 // Ìîíñòð â áëîêìîíå?
358 if gMon then
359 begin
360 if Blocked(Obj, 0, 0) then st := st or MOVE_BLOCK;
361 end;
363 // Äâèãàòüñÿ íå íàäî?
364 if (dx = 0) and (dy = 0) then begin result := st; exit; end;
366 sx := g_basic.Sign(dx);
367 sy := g_basic.Sign(dy);
368 dx := abs(dx);
369 dy := abs(dy);
371 for i := 1 to dx do if not movex() then break;
372 for i := 1 to dy do if not movey() then break;
374 result := st;
375 end;
378 procedure g_Obj_Init (Obj: PObj); inline;
379 begin
380 ZeroMemory(Obj, SizeOf(TObj));
381 end;
384 function g_Obj_Move_Projectile (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False): Word;
385 begin
386 result := g_Obj_Move(Obj, Fallable, Splash, ClimbSlopes, true);
387 end;
389 function g_Obj_Move (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False; asProjectile: Boolean=false): Word;
391 xv, yv, dx, dy: Integer;
392 inwater: Boolean;
393 c: Boolean;
394 wtx: DWORD;
395 slopeStep: Integer;
396 dirx, diry, speed: Double;
397 label
398 _move;
399 begin
400 // Ëèìèòû íà ñêîðîñòü è óñêîðåíèå
401 Obj^.Vel.X := nclamp(Obj^.Vel.X, -LIMIT_VEL, LIMIT_VEL);
402 Obj^.Vel.Y := nclamp(Obj^.Vel.Y, -LIMIT_VEL, LIMIT_VEL);
403 Obj^.Accel.X := nclamp(Obj^.Accel.X, -LIMIT_ACCEL, LIMIT_ACCEL);
404 Obj^.Accel.Y := nclamp(Obj^.Accel.Y, -LIMIT_ACCEL, LIMIT_ACCEL);
406 if Obj^.Vel.X < -LIMIT_VEL then Obj^.Vel.X := -LIMIT_VEL
407 else if Obj^.Vel.X > LIMIT_VEL then Obj^.Vel.X := LIMIT_VEL;
408 if Obj^.Vel.Y < -LIMIT_VEL then Obj^.Vel.Y := -LIMIT_VEL
409 else if Obj^.Vel.Y > LIMIT_VEL then Obj^.Vel.Y := LIMIT_VEL;
410 if Obj^.Accel.X < -LIMIT_ACCEL then Obj^.Accel.X := -LIMIT_ACCEL
411 else if Obj^.Accel.X > LIMIT_ACCEL then Obj^.Accel.X := LIMIT_ACCEL;
412 if Obj^.Accel.Y < -LIMIT_ACCEL then Obj^.Accel.Y := -LIMIT_ACCEL
413 else if Obj^.Accel.Y > LIMIT_ACCEL then Obj^.Accel.Y := LIMIT_ACCEL;
416 // Âûëåòåë çà íèæíþþ ãðàíèöó êàðòû?
417 if (Obj^.Y > Integer(gMapInfo.Height)+128) then begin result := MOVE_FALLOUT; Obj.slopeUpLeft := 0; Obj.slopeFramesLeft := 0; exit; end;
419 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
420 c := (gTime mod (GAME_TICK*2) <> 0);
422 // smoothed slopes
423 if {not c and} (Obj.slopeUpLeft > 0) then
424 begin
425 if (Obj.slopeFramesLeft < 1) then
426 begin
427 //conwritefln('SLOPE DONE: slopeUpLeft=%s', [Obj.slopeUpLeft]);
428 Obj.slopeUpLeft := 0; // oops
430 else
431 begin
432 slopeStep := Obj.slopeUpLeft div Obj.slopeFramesLeft;
433 if (slopeStep < 1) then slopeStep := 1;
434 //conwritefln('SLOPE STEP: slopeUpLeft=%s; slopeFramesLeft=%s; slopeStep=%d', [Obj.slopeUpLeft, Obj.slopeFramesLeft, slopeStep]);
435 Dec(Obj.slopeFramesLeft);
436 Obj.slopeUpLeft -= slopeStep;
437 if (Obj.slopeUpLeft < 1) then
438 begin
439 Obj.slopeUpLeft := 0;
440 Obj.slopeFramesLeft := 0;
441 end;
442 end;
443 end;
445 if c then goto _move;
447 case CollideLift(Obj, 0, 0) of
448 -1: //up
449 begin
450 Obj^.Vel.Y -= 1; // Ëèôò ââåðõ
451 if (Obj^.Vel.Y < -5) then Obj^.Vel.Y += 1;
452 end;
453 1: //down
454 begin
455 if (Obj^.Vel.Y > 5) then Obj^.Vel.Y -= 1;
456 Obj^.Vel.Y += 1; // Ãðàâèòàöèÿ èëè ëèôò âíèç
457 end;
458 0: //???
459 begin
460 if Fallable then Obj^.Vel.Y += 1; // Ãðàâèòàöèÿ
461 if (Obj^.Vel.Y > MAX_YV) then Obj^.Vel.Y -= 1;
462 end;
463 end;
465 case CollideHorLift(Obj, 0, 0) of
466 -1: //left
467 begin
468 Obj^.Vel.X -= 3;
469 if (Obj^.Vel.X < -9) then Obj^.Vel.X += 3;
470 end;
471 1: //right
472 begin
473 Obj^.Vel.X += 3;
474 if (Obj^.Vel.X > 9) then Obj^.Vel.X -= 3;
475 end;
476 // 0 is not needed here
477 end;
479 // Â âîäå?
480 inwater := CollideLiquid(Obj, 0, 0);
481 if inwater then
482 begin
483 if asProjectile then
484 begin
485 //writeln('velocity=(', Obj^.Vel.X, ',', Obj^.Vel.Y, '); acceleration=(', Obj^.Accel.X, ',', Obj^.Accel.Y, ')');
486 if (g_Obj_GetSpeedDirF(Obj, dirx, diry, speed)) then
487 begin
488 //writeln('SPEED: ', speed);
489 if (speed > 5) then
490 begin
491 speed := speed/1.4;
492 Obj^.Vel.X := round(dirx*speed);
493 Obj^.Vel.Y := round(diry*speed);
494 end;
495 end;
497 // acceleration
498 if (g_Obj_GetAccelDirF(Obj, dirx, diry, speed)) then
499 begin
500 if (speed > 5) then
501 begin
502 speed := speed/1.4;
503 Obj^.Accel.X := round(dirx*speed);
504 Obj^.Accel.Y := round(diry*speed);
505 end;
506 end;
508 else
509 begin
510 // velocity
511 xv := abs(Obj^.Vel.X)+1;
512 if (xv > 5) then Obj^.Vel.X := z_dec(Obj^.Vel.X, (xv div 2)-2);
513 yv := abs(Obj^.Vel.Y)+1;
514 if (yv > 5) then Obj^.Vel.Y := z_dec(Obj^.Vel.Y, (yv div 2)-2);
516 // acceleration
517 xv := abs(Obj^.Accel.X)+1;
518 if (xv > 5) then Obj^.Accel.X := z_dec(Obj^.Accel.X, (xv div 2)-2);
519 yv := abs(Obj^.Accel.Y)+1;
520 if (yv > 5) then Obj^.Accel.Y := z_dec(Obj^.Accel.Y, (yv div 2)-2);
521 end;
522 end;
524 // Óìåíüøàåì ïðèáàâêó ê ñêîðîñòè
525 Obj^.Accel.X := z_dec(Obj^.Accel.X, 1);
526 Obj^.Accel.Y := z_dec(Obj^.Accel.Y, 1);
528 _move:
530 xv := Obj^.Vel.X+Obj^.Accel.X;
531 yv := Obj^.Vel.Y+Obj^.Accel.Y;
533 dx := xv;
534 dy := yv;
536 result := move(Obj, dx, dy, ClimbSlopes);
538 // Áðûçãè (åñëè íóæíû)
539 if Splash then
540 begin
541 if WordBool(Result and MOVE_HITWATER) then
542 begin
543 wtx := g_Map_CollideLiquid_Texture(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
544 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3);
545 case wtx of
546 LongWord(TEXTURE_SPECIAL_WATER): g_Obj_Splash(Obj, 3);
547 LongWord(TEXTURE_SPECIAL_ACID1): g_Obj_Splash(Obj, 2);
548 LongWord(TEXTURE_SPECIAL_ACID2): g_Obj_Splash(Obj, 1);
549 LongWord(TEXTURE_NONE): begin end;
550 else g_Obj_Splash(Obj, 0);
551 end;
552 end;
553 end;
555 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
556 if c then exit;
558 // Âðåçàëèñü â ñòåíó - ñòîï
559 if ((Result and MOVE_HITWALL) <> 0) then
560 begin
561 Obj^.Vel.X := 0;
562 Obj^.Accel.X := 0;
563 end;
565 // Âðåçàëèñü â ïîë èëè ïîòîëîê - ñòîï
566 if ((Result and (MOVE_HITCEIL or MOVE_HITLAND)) <> 0) then
567 begin
568 Obj^.Vel.Y := 0;
569 Obj^.Accel.Y := 0;
570 end;
571 end;
574 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; inline;
575 begin
576 Result := g_Collide(Obj1^.X+Obj1^.Rect.X, Obj1^.Y+Obj1^.Rect.Y,
577 Obj1^.Rect.Width, Obj1^.Rect.Height,
578 Obj2^.X+Obj2^.Rect.X, Obj2^.Y+Obj2^.Rect.Y,
579 Obj2^.Rect.Width, Obj2^.Rect.Height);
580 end;
582 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; inline;
583 begin
584 Result := g_Collide(X, Y,
585 Width, Height,
586 Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
587 Obj^.Rect.Width, Obj^.Rect.Height);
588 end;
590 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean; inline;
591 begin
592 Result := g_CollidePoint(X, Y, Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
593 Obj^.Rect.Width, Obj^.Rect.Height);
594 end;
596 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer); inline;
597 begin
598 Obj^.Vel.X := Obj^.Vel.X + VelX;
599 Obj^.Vel.Y := Obj^.Vel.Y + VelY;
600 end;
602 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt); inline;
604 s, c: Extended;
606 begin
607 SinCos(DegToRad(-Angle), s, c);
609 Obj^.Vel.X := Obj^.Vel.X + Round(Vel*c);
610 Obj^.Vel.Y := Obj^.Vel.Y + Round(Vel*s);
611 end;
613 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer); inline;
615 m, vx, vy: Integer;
616 begin
617 vx := Obj^.Vel.X;
618 vy := Obj^.Vel.Y;
620 m := Max(abs(vx), abs(vy));
621 if m = 0 then
622 m := 1;
624 Obj^.Vel.X := (vx*s) div m;
625 Obj^.Vel.Y := (vy*s) div m;
626 end;
628 // `false`: zero speed
629 function g_Obj_GetSpeedDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline;
631 len, vx, vy: Double;
632 begin
633 if (Obj^.Vel.X = 0) and (Obj^.Vel.Y = 0) then
634 begin
635 dirx := 0;
636 diry := 0;
637 speed := 0;
638 result := false;
639 exit;
640 end;
642 vx := Obj^.Vel.X;
643 vy := Obj^.Vel.Y;
644 len := sqrt(vx*vx+vy*vy);
645 dirx := vx/len;
646 diry := vy/len;
647 speed := len;
648 result := true;
649 end;
651 // `false`: zero acceleratin
652 function g_Obj_GetAccelDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline;
654 len, vx, vy: Double;
655 begin
656 if (Obj^.Accel.X = 0) and (Obj^.Accel.Y = 0) then
657 begin
658 dirx := 0;
659 diry := 0;
660 speed := 0;
661 result := false;
662 exit;
663 end;
665 vx := Obj^.Accel.X;
666 vy := Obj^.Accel.Y;
667 len := sqrt(vx*vx+vy*vy);
668 dirx := vx/len;
669 diry := vy/len;
670 speed := len;
671 result := true;
672 end;
675 // Ïðèáëèæàåì a ê 0 íà b åäèíèö:
676 function z_dec (a, b: Integer): Integer; inline;
677 begin
678 if (abs(a) < b) then result := 0
679 else if (a > 0) then result := a-b
680 else if (a < 0) then result := a+b
681 else result := 0; // a = 0
682 end;
685 // Ïðèáëèæàåì a ê 0.0 íà b åäèíèö:
686 function z_fdec (a, b: Double): Double; inline;
687 begin
688 if (abs(a) < b) then result := 0.0
689 else if (a > 0.0) then result := a-b
690 else if (a < 0.0) then result := a+b
691 else result := 0.0; // a = 0.0
692 end;
695 end.