Game: Maintain player direction for death animations
[d2df-sdl.git] / src / game / g_phys.pas
blob195b538237af7b75906eb56c0d98962ecac71415
1 (* Copyright (C) 2016 - The Doom2D.org team & involved community members <http://www.doom2d.org>.
2 * This file is part of Doom2D Forever.
4 * This program is free software: you can redistribute it and/or modify it under the terms of
5 * the GNU General Public License as published by the Free Software Foundation, version 3 of
6 * the License ONLY.
8 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 * See the GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License along with this program.
13 * If not, see <http://www.gnu.org/licenses/>.
16 {$INCLUDE ../shared/a_modes.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 // going up the slope will set this, and renderer will adjust the position
32 // this is purely visual change, it won't affect anything else
33 slopeUpLeft: Integer; // left to go
34 slopeFramesLeft: Integer; // frames left to go
35 // for frame interpolation
36 oldX, oldY: Integer;
37 procedure lerp(t: Single; out fX, fY: Integer);
38 end;
40 const
41 MAX_YV = 30;
42 LIMIT_VEL = 16384;
43 LIMIT_ACCEL = 1024;
45 MOVE_NONE = 0;
46 MOVE_HITWALL = 1;
47 MOVE_HITCEIL = 2;
48 MOVE_HITLAND = 4;
49 MOVE_FALLOUT = 8;
50 MOVE_INWATER = 16;
51 MOVE_HITWATER = 32;
52 MOVE_HITAIR = 64;
53 MOVE_BLOCK = 128;
55 procedure g_Obj_Init(Obj: PObj); inline;
56 function g_Obj_Move(Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False; asProjectile: Boolean=false): Word;
57 function g_Obj_Move_Projectile (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False): Word;
58 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; inline; overload;
59 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; inline; overload;
60 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean; inline;
61 function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
62 function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
63 function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
64 function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
65 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean; inline;
66 function g_Obj_StayOnStep(Obj: PObj): Boolean; inline;
67 function g_Obj_CanMoveY(Obj: PObj; YInc: Integer): Boolean; inline;
68 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer); inline;
69 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt); inline;
70 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer); inline;
71 function g_Obj_GetSpeedDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline; // `false`: zero speed
72 function g_Obj_GetAccelDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline; // `false`: zero speed
73 function z_dec(a, b: Integer): Integer; inline;
74 function z_fdec(a, b: Double): Double; inline;
76 var
77 gMon: Boolean = False;
79 implementation
81 uses
82 {$IFDEF ENABLE_SOUND}
83 g_sound,
84 {$ENDIF}
85 g_map, g_basic, Math, g_player, g_console, SysUtils,
86 g_gfx, MAPDEF, g_monsters, g_game, utils;
89 const
90 SmoothSlopeFrames = 4;
92 procedure TObj.lerp(t: Single; out fX, fY: Integer);
93 begin
94 fX := nlerp(oldX, X, t);
95 fY := nlerp(oldY, Y, t);
96 end;
98 function g_Obj_StayOnStep(Obj: PObj): Boolean; inline;
99 begin
100 Result := not g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height-1,
101 Obj^.Rect.Width, 1,
102 PANEL_STEP, False)
103 and g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height,
104 Obj^.Rect.Width, 1,
105 PANEL_STEP, False);
106 end;
108 function g_Obj_CanMoveY(Obj: PObj; YInc: Integer): Boolean; inline;
109 begin
110 // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì ñòåíà => øàãàòü íåëüçÿ
111 // Èëè åñëè øàãíóòü âíèç, à òàì ñòóïåíü => øàãàòü íåëüçÿ
112 Result := not(g_Obj_CollideLevel(Obj, 0, YInc) or ((YInc > 0) and g_Obj_StayOnStep(Obj)));
113 end;
115 function CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
116 begin
117 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
118 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3,
119 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
120 end;
122 function CollideLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
123 begin
124 if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
125 Obj^.Rect.Width, Obj^.Rect.Height,
126 PANEL_LIFTUP, False) then
127 Result := -1
128 else if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
129 Obj^.Rect.Width, Obj^.Rect.Height,
130 PANEL_LIFTDOWN, False) then
131 Result := 1
132 else
133 Result := 0;
134 end;
136 function CollideHorLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
138 left, right: Boolean;
139 begin
140 left := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
141 Obj^.Rect.Width, Obj^.Rect.Height,
142 PANEL_LIFTLEFT, False);
143 right := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
144 Obj^.Rect.Width, Obj^.Rect.Height,
145 PANEL_LIFTRIGHT, False);
146 if left and not right then
147 Result := -1
148 else if right and not left then
149 Result := 1
150 else
151 Result := 0;
152 end;
154 function CollidePlayers(_Obj: PObj; XInc, YInc: Integer): Boolean;
156 plr: TPlayer;
157 begin
158 result := false;
159 if (gPlayers = nil) then exit;
160 for plr in gPlayers do
161 begin
162 if (plr = nil) then continue;
163 if not plr.alive then continue;
164 with plr do
165 begin
166 if g_Collide(GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
167 PLAYER_RECT.Width, PLAYER_RECT.Height,
168 _Obj^.X+_Obj^.Rect.X+XInc, _Obj^.Y+_Obj^.Rect.Y+YInc,
169 _Obj^.Rect.Width, _Obj^.Rect.Height) then
170 begin
171 result := true;
172 exit;
173 end;
174 end;
175 end;
176 end;
178 function Blocked(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
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_BLOCKMON, False);
183 end;
185 function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
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 PANEL_WALL, False);
190 end;
192 function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
193 begin
194 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
195 Obj^.Rect.Width, Obj^.Rect.Height,
196 PANEL_STEP, False);
197 end;
199 function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
200 begin
201 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
202 Obj^.Rect.Width, Obj^.Rect.Height,
203 PANEL_WATER, False);
204 end;
206 function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
207 begin
208 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
209 Obj^.Rect.Width, Obj^.Rect.Height,
210 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
211 end;
213 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean; inline;
214 begin
215 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
216 Obj^.Rect.Width, Obj^.Rect.Height,
217 PanelType, False);
218 end;
220 procedure g_Obj_Splash(Obj: PObj; Color: Byte);
221 {$IFDEF ENABLE_SOUND}
222 var MaxVel: Integer;
223 {$ENDIF}
224 begin
225 {$IFDEF ENABLE_SOUND}
226 MaxVel := nmax(abs(Obj^.Vel.X), abs(Obj^.Vel.Y));
227 if MaxVel > 4 then
228 begin
229 if MaxVel < 10 then
230 g_Sound_PlayExAt('SOUND_GAME_BULK1', Obj^.X, Obj^.Y)
231 else
232 g_Sound_PlayExAt('SOUND_GAME_BULK2', Obj^.X, Obj^.Y);
233 end;
234 {$ENDIF}
236 g_GFX_Water(Obj^.X+Obj^.Rect.X+(Obj^.Rect.Width div 2),
237 Obj^.Y+Obj^.Rect.Y+(Obj^.Rect.Height div 2),
238 Min(5*(abs(Obj^.Vel.X)+abs(Obj^.Vel.Y)), 50),
239 -Obj^.Vel.X, -Obj^.Vel.Y,
240 Obj^.Rect.Width, 16, Color);
241 end;
244 function move (Obj: PObj; dx, dy: Integer; ClimbSlopes: Boolean): Word;
246 i: Integer;
247 sx, sy: ShortInt;
248 st: Word;
250 procedure slope (s: Integer);
252 i: Integer;
253 begin
254 i := 0;
255 while g_Obj_CollideLevel(Obj, sx, 0) and (i < 4) do
256 begin
257 Obj^.Y += s;
258 Inc(i);
259 end;
260 Obj^.X += sx;
261 if (s < 0) then
262 begin
263 Obj.slopeUpLeft += i*(-s);
264 Obj.slopeFramesLeft := SmoothSlopeFrames;
265 end;
266 end;
268 function movex (): Boolean;
269 begin
270 result := false;
272 // Åñëè ìîíñòðó øàãíóòü â ñòîðîíó, à òàì áëîêìîí
273 if gMon and ((st and MOVE_BLOCK) = 0) then
274 begin
275 if Blocked(Obj, sx, 0) then st := st or MOVE_BLOCK;
276 end;
278 // Åñëè øàãíóòü â ñòîðîíó, à òàì ñòåíà => øàãàòü íåëüçÿ
279 if g_Obj_CollideLevel(Obj, sx, 0) then
280 begin
281 if ClimbSlopes and (abs(dy) < 2) then
282 begin
283 result := true;
284 if (not g_Obj_CollideLevel(Obj, sx, -12)) and // çàáèðàåìñÿ íà 12 ïèêñåëåé âëåâî/âïðàâî
285 (sy >= 0) and (not g_Obj_CanMoveY(Obj, sy)) then // òîëüêî åñëè åñòü çåìëÿ ïîä íîãàìè
286 begin
287 slope(-1);
289 else
290 begin
291 result := false;
292 st := st or MOVE_HITWALL;
293 end;
295 else
296 begin
297 st := st or MOVE_HITWALL;
298 end;
300 else // Òàì ñòåíû íåò
301 begin
302 if CollideLiquid(Obj, sx, 0) then
303 begin // Åñëè øàãíóòü â ñòîðîíó, à òàì òåïåðü æèäêîñòü
304 if ((st and MOVE_INWATER) = 0) then st := st or MOVE_HITWATER;
306 else // Åñëè øàãíóòü â ñòîðîíó, à òàì óæå íåò æèäêîñòè
307 begin
308 if ((st and MOVE_INWATER) <> 0) then st := st or MOVE_HITAIR;
309 end;
311 // Øàã
312 Obj^.X += sx;
313 result := true;
314 end;
315 end;
317 function movey (): Boolean;
318 begin
319 result := false;
321 // Åñëè ìîíñòðó øàãíóòü ïî âåðòèêàëè, à òàì áëîêìîí
322 if gMon and ((st and MOVE_BLOCK) = 0) then
323 begin
324 if Blocked(Obj, 0, sy) then st := st or MOVE_BLOCK;
325 end;
327 // Åñëè øàãàòü íåëüçÿ
328 if not g_Obj_CanMoveY(Obj, sy) then
329 begin
330 if sy > 0 then
331 st := st or MOVE_HITLAND
332 else
333 st := st or MOVE_HITCEIL;
335 else // Òàì ñòåíû íåò. È ñòóïåíè ñíèçó òîæå íåò
336 begin
337 if CollideLiquid(Obj, 0, sy) then
338 begin // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì òåïåðü æèäêîñòü
339 if ((st and MOVE_INWATER) = 0) then st := st or MOVE_HITWATER;
341 else // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì óæå íåò æèäêîñòè
342 begin
343 if ((st and MOVE_INWATER) <> 0) then st := st or MOVE_HITAIR;
344 end;
346 // Øàã
347 Obj^.Y += sy;
348 result := true;
349 end;
350 end;
352 begin
353 st := MOVE_NONE;
355 // Îáúåêò â æèäêîñòè?
356 if CollideLiquid(Obj, 0, 0) then st := st or MOVE_INWATER;
358 // Ìîíñòð â áëîêìîíå?
359 if gMon then
360 begin
361 if Blocked(Obj, 0, 0) then st := st or MOVE_BLOCK;
362 end;
364 // Äâèãàòüñÿ íå íàäî?
365 if (dx = 0) and (dy = 0) then begin result := st; exit; end;
367 sx := g_basic.Sign(dx);
368 sy := g_basic.Sign(dy);
369 dx := abs(dx);
370 dy := abs(dy);
372 for i := 1 to dx do if not movex() then break;
373 for i := 1 to dy do if not movey() then break;
375 result := st;
376 end;
379 procedure g_Obj_Init (Obj: PObj); inline;
380 begin
381 ZeroMemory(Obj, SizeOf(TObj));
382 end;
385 function g_Obj_Move_Projectile (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False): Word;
386 begin
387 result := g_Obj_Move(Obj, Fallable, Splash, ClimbSlopes, true);
388 end;
390 function g_Obj_Move (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False; asProjectile: Boolean=false): Word;
392 xv, yv, dx, dy: Integer;
393 inwater: Boolean;
394 c: Boolean;
395 wtx: DWORD;
396 slopeStep: Integer;
397 dirx, diry, speed: Double;
398 label
399 _move;
400 begin
401 // Ëèìèòû íà ñêîðîñòü è óñêîðåíèå
402 Obj^.Vel.X := nclamp(Obj^.Vel.X, -LIMIT_VEL, LIMIT_VEL);
403 Obj^.Vel.Y := nclamp(Obj^.Vel.Y, -LIMIT_VEL, LIMIT_VEL);
404 Obj^.Accel.X := nclamp(Obj^.Accel.X, -LIMIT_ACCEL, LIMIT_ACCEL);
405 Obj^.Accel.Y := nclamp(Obj^.Accel.Y, -LIMIT_ACCEL, LIMIT_ACCEL);
407 if Obj^.Vel.X < -LIMIT_VEL then Obj^.Vel.X := -LIMIT_VEL
408 else if Obj^.Vel.X > LIMIT_VEL then Obj^.Vel.X := LIMIT_VEL;
409 if Obj^.Vel.Y < -LIMIT_VEL then Obj^.Vel.Y := -LIMIT_VEL
410 else if Obj^.Vel.Y > LIMIT_VEL then Obj^.Vel.Y := LIMIT_VEL;
411 if Obj^.Accel.X < -LIMIT_ACCEL then Obj^.Accel.X := -LIMIT_ACCEL
412 else if Obj^.Accel.X > LIMIT_ACCEL then Obj^.Accel.X := LIMIT_ACCEL;
413 if Obj^.Accel.Y < -LIMIT_ACCEL then Obj^.Accel.Y := -LIMIT_ACCEL
414 else if Obj^.Accel.Y > LIMIT_ACCEL then Obj^.Accel.Y := LIMIT_ACCEL;
417 // Âûëåòåë çà íèæíþþ ãðàíèöó êàðòû?
418 if (Obj^.Y > Integer(gMapInfo.Height)+128) then begin result := MOVE_FALLOUT; Obj.slopeUpLeft := 0; Obj.slopeFramesLeft := 0; exit; end;
420 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
421 c := (gTime mod (GAME_TICK*2) <> 0);
423 // smoothed slopes
424 if {not c and} (Obj.slopeUpLeft > 0) then
425 begin
426 if (Obj.slopeFramesLeft < 1) then
427 begin
428 //conwritefln('SLOPE DONE: slopeUpLeft=%s', [Obj.slopeUpLeft]);
429 Obj.slopeUpLeft := 0; // oops
431 else
432 begin
433 slopeStep := Obj.slopeUpLeft div Obj.slopeFramesLeft;
434 if (slopeStep < 1) then slopeStep := 1;
435 //conwritefln('SLOPE STEP: slopeUpLeft=%s; slopeFramesLeft=%s; slopeStep=%d', [Obj.slopeUpLeft, Obj.slopeFramesLeft, slopeStep]);
436 Dec(Obj.slopeFramesLeft);
437 Obj.slopeUpLeft -= slopeStep;
438 if (Obj.slopeUpLeft < 1) then
439 begin
440 Obj.slopeUpLeft := 0;
441 Obj.slopeFramesLeft := 0;
442 end;
443 end;
444 end;
446 if c then goto _move;
448 case CollideLift(Obj, 0, 0) of
449 -1: //up
450 begin
451 Obj^.Vel.Y -= 1; // Ëèôò ââåðõ
452 if (Obj^.Vel.Y < -5) then Obj^.Vel.Y += 1;
453 end;
454 1: //down
455 begin
456 if (Obj^.Vel.Y > 5) then Obj^.Vel.Y -= 1;
457 Obj^.Vel.Y += 1; // Ãðàâèòàöèÿ èëè ëèôò âíèç
458 end;
459 0: //???
460 begin
461 if Fallable then Obj^.Vel.Y += 1; // Ãðàâèòàöèÿ
462 if (Obj^.Vel.Y > MAX_YV) then Obj^.Vel.Y -= 1;
463 end;
464 end;
466 case CollideHorLift(Obj, 0, 0) of
467 -1: //left
468 begin
469 Obj^.Vel.X -= 3;
470 if (Obj^.Vel.X < -9) then Obj^.Vel.X += 3;
471 end;
472 1: //right
473 begin
474 Obj^.Vel.X += 3;
475 if (Obj^.Vel.X > 9) then Obj^.Vel.X -= 3;
476 end;
477 // 0 is not needed here
478 end;
480 // Â âîäå?
481 inwater := CollideLiquid(Obj, 0, 0);
482 if inwater then
483 begin
484 if asProjectile then
485 begin
486 //writeln('velocity=(', Obj^.Vel.X, ',', Obj^.Vel.Y, '); acceleration=(', Obj^.Accel.X, ',', Obj^.Accel.Y, ')');
487 if (g_Obj_GetSpeedDirF(Obj, dirx, diry, speed)) then
488 begin
489 //writeln('SPEED: ', speed);
490 if (speed > 5) then
491 begin
492 speed := speed/1.4;
493 Obj^.Vel.X := round(dirx*speed);
494 Obj^.Vel.Y := round(diry*speed);
495 end;
496 end;
498 // acceleration
499 if (g_Obj_GetAccelDirF(Obj, dirx, diry, speed)) then
500 begin
501 if (speed > 5) then
502 begin
503 speed := speed/1.4;
504 Obj^.Accel.X := round(dirx*speed);
505 Obj^.Accel.Y := round(diry*speed);
506 end;
507 end;
509 else
510 begin
511 // velocity
512 xv := abs(Obj^.Vel.X)+1;
513 if (xv > 5) then Obj^.Vel.X := z_dec(Obj^.Vel.X, (xv div 2)-2);
514 yv := abs(Obj^.Vel.Y)+1;
515 if (yv > 5) then Obj^.Vel.Y := z_dec(Obj^.Vel.Y, (yv div 2)-2);
517 // acceleration
518 xv := abs(Obj^.Accel.X)+1;
519 if (xv > 5) then Obj^.Accel.X := z_dec(Obj^.Accel.X, (xv div 2)-2);
520 yv := abs(Obj^.Accel.Y)+1;
521 if (yv > 5) then Obj^.Accel.Y := z_dec(Obj^.Accel.Y, (yv div 2)-2);
522 end;
523 end;
525 // Óìåíüøàåì ïðèáàâêó ê ñêîðîñòè
526 Obj^.Accel.X := z_dec(Obj^.Accel.X, 1);
527 Obj^.Accel.Y := z_dec(Obj^.Accel.Y, 1);
529 _move:
531 xv := Obj^.Vel.X+Obj^.Accel.X;
532 yv := Obj^.Vel.Y+Obj^.Accel.Y;
534 dx := xv;
535 dy := yv;
537 result := move(Obj, dx, dy, ClimbSlopes);
539 // Áðûçãè (åñëè íóæíû)
540 if Splash then
541 begin
542 if WordBool(Result and MOVE_HITWATER) then
543 begin
544 wtx := g_Map_CollideLiquid_Texture(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
545 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3);
546 case wtx of
547 LongWord(TEXTURE_SPECIAL_WATER): g_Obj_Splash(Obj, 3);
548 LongWord(TEXTURE_SPECIAL_ACID1): g_Obj_Splash(Obj, 2);
549 LongWord(TEXTURE_SPECIAL_ACID2): g_Obj_Splash(Obj, 1);
550 LongWord(TEXTURE_NONE): begin end;
551 else g_Obj_Splash(Obj, 0);
552 end;
553 end;
554 end;
556 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
557 if c then exit;
559 // Âðåçàëèñü â ñòåíó - ñòîï
560 if ((Result and MOVE_HITWALL) <> 0) then
561 begin
562 Obj^.Vel.X := 0;
563 Obj^.Accel.X := 0;
564 end;
566 // Âðåçàëèñü â ïîë èëè ïîòîëîê - ñòîï
567 if ((Result and (MOVE_HITCEIL or MOVE_HITLAND)) <> 0) then
568 begin
569 Obj^.Vel.Y := 0;
570 Obj^.Accel.Y := 0;
571 end;
572 end;
575 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; inline;
576 begin
577 Result := g_Collide(Obj1^.X+Obj1^.Rect.X, Obj1^.Y+Obj1^.Rect.Y,
578 Obj1^.Rect.Width, Obj1^.Rect.Height,
579 Obj2^.X+Obj2^.Rect.X, Obj2^.Y+Obj2^.Rect.Y,
580 Obj2^.Rect.Width, Obj2^.Rect.Height);
581 end;
583 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; inline;
584 begin
585 Result := g_Collide(X, Y,
586 Width, Height,
587 Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
588 Obj^.Rect.Width, Obj^.Rect.Height);
589 end;
591 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean; inline;
592 begin
593 Result := g_CollidePoint(X, Y, Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
594 Obj^.Rect.Width, Obj^.Rect.Height);
595 end;
597 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer); inline;
598 begin
599 Obj^.Vel.X := Obj^.Vel.X + VelX;
600 Obj^.Vel.Y := Obj^.Vel.Y + VelY;
601 end;
603 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt); inline;
605 s, c: Extended;
607 begin
608 SinCos(DegToRad(-Angle), s, c);
610 Obj^.Vel.X := Obj^.Vel.X + Round(Vel*c);
611 Obj^.Vel.Y := Obj^.Vel.Y + Round(Vel*s);
612 end;
614 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer); inline;
616 m, vx, vy: Integer;
617 begin
618 vx := Obj^.Vel.X;
619 vy := Obj^.Vel.Y;
621 m := Max(abs(vx), abs(vy));
622 if m = 0 then
623 m := 1;
625 Obj^.Vel.X := (vx*s) div m;
626 Obj^.Vel.Y := (vy*s) div m;
627 end;
629 // `false`: zero speed
630 function g_Obj_GetSpeedDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline;
632 len, vx, vy: Double;
633 begin
634 if (Obj^.Vel.X = 0) and (Obj^.Vel.Y = 0) then
635 begin
636 dirx := 0;
637 diry := 0;
638 speed := 0;
639 result := false;
640 exit;
641 end;
643 vx := Obj^.Vel.X;
644 vy := Obj^.Vel.Y;
645 len := sqrt(vx*vx+vy*vy);
646 dirx := vx/len;
647 diry := vy/len;
648 speed := len;
649 result := true;
650 end;
652 // `false`: zero acceleratin
653 function g_Obj_GetAccelDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline;
655 len, vx, vy: Double;
656 begin
657 if (Obj^.Accel.X = 0) and (Obj^.Accel.Y = 0) then
658 begin
659 dirx := 0;
660 diry := 0;
661 speed := 0;
662 result := false;
663 exit;
664 end;
666 vx := Obj^.Accel.X;
667 vy := Obj^.Accel.Y;
668 len := sqrt(vx*vx+vy*vy);
669 dirx := vx/len;
670 diry := vy/len;
671 speed := len;
672 result := true;
673 end;
676 // Ïðèáëèæàåì a ê 0 íà b åäèíèö:
677 function z_dec (a, b: Integer): Integer; inline;
678 begin
679 if (abs(a) < b) then result := 0
680 else if (a > 0) then result := a-b
681 else if (a < 0) then result := a+b
682 else result := 0; // a = 0
683 end;
686 // Ïðèáëèæàåì a ê 0.0 íà b åäèíèö:
687 function z_fdec (a, b: Double): Double; inline;
688 begin
689 if (abs(a) < b) then result := 0.0
690 else if (a > 0.0) then result := a-b
691 else if (a < 0.0) then result := a+b
692 else result := 0.0; // a = 0.0
693 end;
696 end.