DEADSOFTWARE

added actor rendering interpolation; fixed vsync on startup
[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, version 3 of the License ONLY.
6 *
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.
11 *
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/>.
14 *)
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 g_map, g_basic, Math, g_player, g_console, SysUtils,
82 g_sound, g_gfx, MAPDEF, g_monsters, g_game, utils;
85 const
86 SmoothSlopeFrames = 4;
88 procedure TObj.lerp(t: Single; out fX, fY: Integer);
89 begin
90 fX := nlerp(oldX, X, t);
91 fY := nlerp(oldY, Y, t);
92 end;
94 function g_Obj_StayOnStep(Obj: PObj): Boolean; inline;
95 begin
96 Result := not g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height-1,
97 Obj^.Rect.Width, 1,
98 PANEL_STEP, False)
99 and g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height,
100 Obj^.Rect.Width, 1,
101 PANEL_STEP, False);
102 end;
104 function g_Obj_CanMoveY(Obj: PObj; YInc: Integer): Boolean; inline;
105 begin
106 // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì ñòåíà => øàãàòü íåëüçÿ
107 // Èëè åñëè øàãíóòü âíèç, à òàì ñòóïåíü => øàãàòü íåëüçÿ
108 Result := not(g_Obj_CollideLevel(Obj, 0, YInc) or ((YInc > 0) and g_Obj_StayOnStep(Obj)));
109 end;
111 function CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
112 begin
113 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
114 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3,
115 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
116 end;
118 function CollideLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
119 begin
120 if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
121 Obj^.Rect.Width, Obj^.Rect.Height,
122 PANEL_LIFTUP, False) then
123 Result := -1
124 else 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_LIFTDOWN, False) then
127 Result := 1
128 else
129 Result := 0;
130 end;
132 function CollideHorLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
133 var
134 left, right: Boolean;
135 begin
136 left := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
137 Obj^.Rect.Width, Obj^.Rect.Height,
138 PANEL_LIFTLEFT, False);
139 right := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
140 Obj^.Rect.Width, Obj^.Rect.Height,
141 PANEL_LIFTRIGHT, False);
142 if left and not right then
143 Result := -1
144 else if right and not left then
145 Result := 1
146 else
147 Result := 0;
148 end;
150 function CollidePlayers(_Obj: PObj; XInc, YInc: Integer): Boolean;
151 var
152 plr: TPlayer;
153 begin
154 result := false;
155 if (gPlayers = nil) then exit;
156 for plr in gPlayers do
157 begin
158 if (plr = nil) then continue;
159 if not plr.alive then continue;
160 with plr do
161 begin
162 if g_Collide(GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
163 PLAYER_RECT.Width, PLAYER_RECT.Height,
164 _Obj^.X+_Obj^.Rect.X+XInc, _Obj^.Y+_Obj^.Rect.Y+YInc,
165 _Obj^.Rect.Width, _Obj^.Rect.Height) then
166 begin
167 result := true;
168 exit;
169 end;
170 end;
171 end;
172 end;
174 function Blocked(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
175 begin
176 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
177 Obj^.Rect.Width, Obj^.Rect.Height,
178 PANEL_BLOCKMON, False);
179 end;
181 function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
182 begin
183 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
184 Obj^.Rect.Width, Obj^.Rect.Height,
185 PANEL_WALL, False);
186 end;
188 function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
189 begin
190 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
191 Obj^.Rect.Width, Obj^.Rect.Height,
192 PANEL_STEP, False);
193 end;
195 function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
196 begin
197 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
198 Obj^.Rect.Width, Obj^.Rect.Height,
199 PANEL_WATER, False);
200 end;
202 function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
203 begin
204 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
205 Obj^.Rect.Width, Obj^.Rect.Height,
206 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
207 end;
209 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean; inline;
210 begin
211 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
212 Obj^.Rect.Width, Obj^.Rect.Height,
213 PanelType, False);
214 end;
216 procedure g_Obj_Splash(Obj: PObj; Color: Byte);
217 var
218 MaxVel: Integer;
219 begin
220 MaxVel := nmax(abs(Obj^.Vel.X), abs(Obj^.Vel.Y));
221 if MaxVel > 4 then
222 begin
223 if MaxVel < 10 then
224 g_Sound_PlayExAt('SOUND_GAME_BULK1', Obj^.X, Obj^.Y)
225 else
226 g_Sound_PlayExAt('SOUND_GAME_BULK2', Obj^.X, Obj^.Y);
227 end;
229 g_GFX_Water(Obj^.X+Obj^.Rect.X+(Obj^.Rect.Width div 2),
230 Obj^.Y+Obj^.Rect.Y+(Obj^.Rect.Height div 2),
231 Min(5*(abs(Obj^.Vel.X)+abs(Obj^.Vel.Y)), 50),
232 -Obj^.Vel.X, -Obj^.Vel.Y,
233 Obj^.Rect.Width, 16, Color);
234 end;
237 function move (Obj: PObj; dx, dy: Integer; ClimbSlopes: Boolean): Word;
238 var
239 i: Integer;
240 sx, sy: ShortInt;
241 st: Word;
243 procedure slope (s: Integer);
244 var
245 i: Integer;
246 begin
247 i := 0;
248 while g_Obj_CollideLevel(Obj, sx, 0) and (i < 4) do
249 begin
250 Obj^.Y += s;
251 Inc(i);
252 end;
253 Obj^.X += sx;
254 if (s < 0) then
255 begin
256 Obj.slopeUpLeft += i*(-s);
257 Obj.slopeFramesLeft := SmoothSlopeFrames;
258 end;
259 end;
261 function movex (): Boolean;
262 begin
263 result := false;
265 // Åñëè ìîíñòðó øàãíóòü â ñòîðîíó, à òàì áëîêìîí
266 if gMon and ((st and MOVE_BLOCK) = 0) then
267 begin
268 if Blocked(Obj, sx, 0) then st := st or MOVE_BLOCK;
269 end;
271 // Åñëè øàãíóòü â ñòîðîíó, à òàì ñòåíà => øàãàòü íåëüçÿ
272 if g_Obj_CollideLevel(Obj, sx, 0) then
273 begin
274 if ClimbSlopes and (abs(dy) < 2) then
275 begin
276 result := true;
277 if (not g_Obj_CollideLevel(Obj, sx, -12)) and // çàáèðàåìñÿ íà 12 ïèêñåëåé âëåâî/âïðàâî
278 (sy >= 0) and (not g_Obj_CanMoveY(Obj, sy)) then // òîëüêî åñëè åñòü çåìëÿ ïîä íîãàìè
279 begin
280 slope(-1);
281 end
282 else
283 begin
284 result := false;
285 st := st or MOVE_HITWALL;
286 end;
287 end
288 else
289 begin
290 st := st or MOVE_HITWALL;
291 end;
292 end
293 else // Òàì ñòåíû íåò
294 begin
295 if CollideLiquid(Obj, sx, 0) then
296 begin // Åñëè øàãíóòü â ñòîðîíó, à òàì òåïåðü æèäêîñòü
297 if ((st and MOVE_INWATER) = 0) then st := st or MOVE_HITWATER;
298 end
299 else // Åñëè øàãíóòü â ñòîðîíó, à òàì óæå íåò æèäêîñòè
300 begin
301 if ((st and MOVE_INWATER) <> 0) then st := st or MOVE_HITAIR;
302 end;
304 // Øàã
305 Obj^.X += sx;
306 result := true;
307 end;
308 end;
310 function movey (): Boolean;
311 begin
312 result := false;
314 // Åñëè ìîíñòðó øàãíóòü ïî âåðòèêàëè, à òàì áëîêìîí
315 if gMon and ((st and MOVE_BLOCK) = 0) then
316 begin
317 if Blocked(Obj, 0, sy) then st := st or MOVE_BLOCK;
318 end;
320 // Åñëè øàãàòü íåëüçÿ
321 if not g_Obj_CanMoveY(Obj, sy) then
322 begin
323 if sy > 0 then
324 st := st or MOVE_HITLAND
325 else
326 st := st or MOVE_HITCEIL;
327 end
328 else // Òàì ñòåíû íåò. È ñòóïåíè ñíèçó òîæå íåò
329 begin
330 if CollideLiquid(Obj, 0, sy) then
331 begin // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì òåïåðü æèäêîñòü
332 if ((st and MOVE_INWATER) = 0) then st := st or MOVE_HITWATER;
333 end
334 else // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì óæå íåò æèäêîñòè
335 begin
336 if ((st and MOVE_INWATER) <> 0) then st := st or MOVE_HITAIR;
337 end;
339 // Øàã
340 Obj^.Y += sy;
341 result := true;
342 end;
343 end;
345 begin
346 st := MOVE_NONE;
348 // Îáúåêò â æèäêîñòè?
349 if CollideLiquid(Obj, 0, 0) then st := st or MOVE_INWATER;
351 // Ìîíñòð â áëîêìîíå?
352 if gMon then
353 begin
354 if Blocked(Obj, 0, 0) then st := st or MOVE_BLOCK;
355 end;
357 // Äâèãàòüñÿ íå íàäî?
358 if (dx = 0) and (dy = 0) then begin result := st; exit; end;
360 sx := g_basic.Sign(dx);
361 sy := g_basic.Sign(dy);
362 dx := abs(dx);
363 dy := abs(dy);
365 for i := 1 to dx do if not movex() then break;
366 for i := 1 to dy do if not movey() then break;
368 result := st;
369 end;
372 procedure g_Obj_Init (Obj: PObj); inline;
373 begin
374 ZeroMemory(Obj, SizeOf(TObj));
375 end;
378 function g_Obj_Move_Projectile (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False): Word;
379 begin
380 result := g_Obj_Move(Obj, Fallable, Splash, ClimbSlopes, true);
381 end;
383 function g_Obj_Move (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False; asProjectile: Boolean=false): Word;
384 var
385 xv, yv, dx, dy: Integer;
386 inwater: Boolean;
387 c: Boolean;
388 wtx: DWORD;
389 slopeStep: Integer;
390 dirx, diry, speed: Double;
391 label
392 _move;
393 begin
394 // Ëèìèòû íà ñêîðîñòü è óñêîðåíèå
395 Obj^.Vel.X := nclamp(Obj^.Vel.X, -LIMIT_VEL, LIMIT_VEL);
396 Obj^.Vel.Y := nclamp(Obj^.Vel.Y, -LIMIT_VEL, LIMIT_VEL);
397 Obj^.Accel.X := nclamp(Obj^.Accel.X, -LIMIT_ACCEL, LIMIT_ACCEL);
398 Obj^.Accel.Y := nclamp(Obj^.Accel.Y, -LIMIT_ACCEL, LIMIT_ACCEL);
400 if Obj^.Vel.X < -LIMIT_VEL then Obj^.Vel.X := -LIMIT_VEL
401 else if Obj^.Vel.X > LIMIT_VEL then Obj^.Vel.X := LIMIT_VEL;
402 if Obj^.Vel.Y < -LIMIT_VEL then Obj^.Vel.Y := -LIMIT_VEL
403 else if Obj^.Vel.Y > LIMIT_VEL then Obj^.Vel.Y := LIMIT_VEL;
404 if Obj^.Accel.X < -LIMIT_ACCEL then Obj^.Accel.X := -LIMIT_ACCEL
405 else if Obj^.Accel.X > LIMIT_ACCEL then Obj^.Accel.X := LIMIT_ACCEL;
406 if Obj^.Accel.Y < -LIMIT_ACCEL then Obj^.Accel.Y := -LIMIT_ACCEL
407 else if Obj^.Accel.Y > LIMIT_ACCEL then Obj^.Accel.Y := LIMIT_ACCEL;
410 // Âûëåòåë çà íèæíþþ ãðàíèöó êàðòû?
411 if (Obj^.Y > Integer(gMapInfo.Height)+128) then begin result := MOVE_FALLOUT; Obj.slopeUpLeft := 0; Obj.slopeFramesLeft := 0; exit; end;
413 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
414 c := (gTime mod (GAME_TICK*2) <> 0);
416 // smoothed slopes
417 if {not c and} (Obj.slopeUpLeft > 0) then
418 begin
419 if (Obj.slopeFramesLeft < 1) then
420 begin
421 //conwritefln('SLOPE DONE: slopeUpLeft=%s', [Obj.slopeUpLeft]);
422 Obj.slopeUpLeft := 0; // oops
423 end
424 else
425 begin
426 slopeStep := Obj.slopeUpLeft div Obj.slopeFramesLeft;
427 if (slopeStep < 1) then slopeStep := 1;
428 //conwritefln('SLOPE STEP: slopeUpLeft=%s; slopeFramesLeft=%s; slopeStep=%d', [Obj.slopeUpLeft, Obj.slopeFramesLeft, slopeStep]);
429 Dec(Obj.slopeFramesLeft);
430 Obj.slopeUpLeft -= slopeStep;
431 if (Obj.slopeUpLeft < 1) then
432 begin
433 Obj.slopeUpLeft := 0;
434 Obj.slopeFramesLeft := 0;
435 end;
436 end;
437 end;
439 if c then goto _move;
441 case CollideLift(Obj, 0, 0) of
442 -1: //up
443 begin
444 Obj^.Vel.Y -= 1; // Ëèôò ââåðõ
445 if (Obj^.Vel.Y < -5) then Obj^.Vel.Y += 1;
446 end;
447 1: //down
448 begin
449 if (Obj^.Vel.Y > 5) then Obj^.Vel.Y -= 1;
450 Obj^.Vel.Y += 1; // Ãðàâèòàöèÿ èëè ëèôò âíèç
451 end;
452 0: //???
453 begin
454 if Fallable then Obj^.Vel.Y += 1; // Ãðàâèòàöèÿ
455 if (Obj^.Vel.Y > MAX_YV) then Obj^.Vel.Y -= 1;
456 end;
457 end;
459 case CollideHorLift(Obj, 0, 0) of
460 -1: //left
461 begin
462 Obj^.Vel.X -= 3;
463 if (Obj^.Vel.X < -9) then Obj^.Vel.X += 3;
464 end;
465 1: //right
466 begin
467 Obj^.Vel.X += 3;
468 if (Obj^.Vel.X > 9) then Obj^.Vel.X -= 3;
469 end;
470 // 0 is not needed here
471 end;
473 // Â âîäå?
474 inwater := CollideLiquid(Obj, 0, 0);
475 if inwater then
476 begin
477 if asProjectile then
478 begin
479 //writeln('velocity=(', Obj^.Vel.X, ',', Obj^.Vel.Y, '); acceleration=(', Obj^.Accel.X, ',', Obj^.Accel.Y, ')');
480 if (g_Obj_GetSpeedDirF(Obj, dirx, diry, speed)) then
481 begin
482 //writeln('SPEED: ', speed);
483 if (speed > 5) then
484 begin
485 speed := speed/1.4;
486 Obj^.Vel.X := round(dirx*speed);
487 Obj^.Vel.Y := round(diry*speed);
488 end;
489 end;
491 // acceleration
492 if (g_Obj_GetAccelDirF(Obj, dirx, diry, speed)) then
493 begin
494 if (speed > 5) then
495 begin
496 speed := speed/1.4;
497 Obj^.Accel.X := round(dirx*speed);
498 Obj^.Accel.Y := round(diry*speed);
499 end;
500 end;
501 end
502 else
503 begin
504 // velocity
505 xv := abs(Obj^.Vel.X)+1;
506 if (xv > 5) then Obj^.Vel.X := z_dec(Obj^.Vel.X, (xv div 2)-2);
507 yv := abs(Obj^.Vel.Y)+1;
508 if (yv > 5) then Obj^.Vel.Y := z_dec(Obj^.Vel.Y, (yv div 2)-2);
510 // acceleration
511 xv := abs(Obj^.Accel.X)+1;
512 if (xv > 5) then Obj^.Accel.X := z_dec(Obj^.Accel.X, (xv div 2)-2);
513 yv := abs(Obj^.Accel.Y)+1;
514 if (yv > 5) then Obj^.Accel.Y := z_dec(Obj^.Accel.Y, (yv div 2)-2);
515 end;
516 end;
518 // Óìåíüøàåì ïðèáàâêó ê ñêîðîñòè
519 Obj^.Accel.X := z_dec(Obj^.Accel.X, 1);
520 Obj^.Accel.Y := z_dec(Obj^.Accel.Y, 1);
522 _move:
524 xv := Obj^.Vel.X+Obj^.Accel.X;
525 yv := Obj^.Vel.Y+Obj^.Accel.Y;
527 dx := xv;
528 dy := yv;
530 result := move(Obj, dx, dy, ClimbSlopes);
532 // Áðûçãè (åñëè íóæíû)
533 if Splash then
534 begin
535 if WordBool(Result and MOVE_HITWATER) then
536 begin
537 wtx := g_Map_CollideLiquid_Texture(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
538 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3);
539 case wtx of
540 LongWord(TEXTURE_SPECIAL_WATER): g_Obj_Splash(Obj, 3);
541 LongWord(TEXTURE_SPECIAL_ACID1): g_Obj_Splash(Obj, 2);
542 LongWord(TEXTURE_SPECIAL_ACID2): g_Obj_Splash(Obj, 1);
543 LongWord(TEXTURE_NONE): begin end;
544 else g_Obj_Splash(Obj, 0);
545 end;
546 end;
547 end;
549 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
550 if c then exit;
552 // Âðåçàëèñü â ñòåíó - ñòîï
553 if ((Result and MOVE_HITWALL) <> 0) then
554 begin
555 Obj^.Vel.X := 0;
556 Obj^.Accel.X := 0;
557 end;
559 // Âðåçàëèñü â ïîë èëè ïîòîëîê - ñòîï
560 if ((Result and (MOVE_HITCEIL or MOVE_HITLAND)) <> 0) then
561 begin
562 Obj^.Vel.Y := 0;
563 Obj^.Accel.Y := 0;
564 end;
565 end;
568 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; inline;
569 begin
570 Result := g_Collide(Obj1^.X+Obj1^.Rect.X, Obj1^.Y+Obj1^.Rect.Y,
571 Obj1^.Rect.Width, Obj1^.Rect.Height,
572 Obj2^.X+Obj2^.Rect.X, Obj2^.Y+Obj2^.Rect.Y,
573 Obj2^.Rect.Width, Obj2^.Rect.Height);
574 end;
576 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; inline;
577 begin
578 Result := g_Collide(X, Y,
579 Width, Height,
580 Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
581 Obj^.Rect.Width, Obj^.Rect.Height);
582 end;
584 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean; inline;
585 begin
586 Result := g_CollidePoint(X, Y, Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
587 Obj^.Rect.Width, Obj^.Rect.Height);
588 end;
590 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer); inline;
591 begin
592 Obj^.Vel.X := Obj^.Vel.X + VelX;
593 Obj^.Vel.Y := Obj^.Vel.Y + VelY;
594 end;
596 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt); inline;
597 var
598 s, c: Extended;
600 begin
601 SinCos(DegToRad(-Angle), s, c);
603 Obj^.Vel.X := Obj^.Vel.X + Round(Vel*c);
604 Obj^.Vel.Y := Obj^.Vel.Y + Round(Vel*s);
605 end;
607 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer); inline;
608 var
609 m, vx, vy: Integer;
610 begin
611 vx := Obj^.Vel.X;
612 vy := Obj^.Vel.Y;
614 m := Max(abs(vx), abs(vy));
615 if m = 0 then
616 m := 1;
618 Obj^.Vel.X := (vx*s) div m;
619 Obj^.Vel.Y := (vy*s) div m;
620 end;
622 // `false`: zero speed
623 function g_Obj_GetSpeedDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline;
624 var
625 len, vx, vy: Double;
626 begin
627 if (Obj^.Vel.X = 0) and (Obj^.Vel.Y = 0) then
628 begin
629 dirx := 0;
630 diry := 0;
631 speed := 0;
632 result := false;
633 exit;
634 end;
636 vx := Obj^.Vel.X;
637 vy := Obj^.Vel.Y;
638 len := sqrt(vx*vx+vy*vy);
639 dirx := vx/len;
640 diry := vy/len;
641 speed := len;
642 result := true;
643 end;
645 // `false`: zero acceleratin
646 function g_Obj_GetAccelDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline;
647 var
648 len, vx, vy: Double;
649 begin
650 if (Obj^.Accel.X = 0) and (Obj^.Accel.Y = 0) then
651 begin
652 dirx := 0;
653 diry := 0;
654 speed := 0;
655 result := false;
656 exit;
657 end;
659 vx := Obj^.Accel.X;
660 vy := Obj^.Accel.Y;
661 len := sqrt(vx*vx+vy*vy);
662 dirx := vx/len;
663 diry := vy/len;
664 speed := len;
665 result := true;
666 end;
669 // Ïðèáëèæàåì a ê 0 íà b åäèíèö:
670 function z_dec (a, b: Integer): Integer; inline;
671 begin
672 if (abs(a) < b) then result := 0
673 else if (a > 0) then result := a-b
674 else if (a < 0) then result := a+b
675 else result := 0; // a = 0
676 end;
679 // Ïðèáëèæàåì a ê 0.0 íà b åäèíèö:
680 function z_fdec (a, b: Double): Double; inline;
681 begin
682 if (abs(a) < b) then result := 0.0
683 else if (a > 0.0) then result := a-b
684 else if (a < 0.0) then result := a+b
685 else result := 0.0; // a = 0.0
686 end;
689 end.