DEADSOFTWARE

whitelist new cvars
[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 end;
36 const
37 MAX_YV = 30;
38 LIMIT_VEL = 16384;
39 LIMIT_ACCEL = 1024;
41 MOVE_NONE = 0;
42 MOVE_HITWALL = 1;
43 MOVE_HITCEIL = 2;
44 MOVE_HITLAND = 4;
45 MOVE_FALLOUT = 8;
46 MOVE_INWATER = 16;
47 MOVE_HITWATER = 32;
48 MOVE_HITAIR = 64;
49 MOVE_BLOCK = 128;
51 procedure g_Obj_Init(Obj: PObj); inline;
52 function g_Obj_Move(Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False; asProjectile: Boolean=false): Word;
53 function g_Obj_Move_Projectile (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False): Word;
54 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; inline; overload;
55 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; inline; overload;
56 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean; inline;
57 function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
58 function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
59 function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
60 function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
61 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean; inline;
62 function g_Obj_StayOnStep(Obj: PObj): Boolean; inline;
63 function g_Obj_CanMoveY(Obj: PObj; YInc: Integer): Boolean; inline;
64 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer); inline;
65 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt); inline;
66 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer); inline;
67 function g_Obj_GetSpeedDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline; // `false`: zero speed
68 function g_Obj_GetAccelDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline; // `false`: zero speed
69 function z_dec(a, b: Integer): Integer; inline;
70 function z_fdec(a, b: Double): Double; inline;
72 var
73 gMon: Boolean = False;
75 implementation
77 uses
78 g_map, g_basic, Math, g_player, g_console, SysUtils,
79 g_sound, g_gfx, MAPDEF, g_monsters, g_game, utils;
82 const
83 SmoothSlopeFrames = 4;
86 function g_Obj_StayOnStep(Obj: PObj): Boolean; inline;
87 begin
88 Result := not g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height-1,
89 Obj^.Rect.Width, 1,
90 PANEL_STEP, False)
91 and g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height,
92 Obj^.Rect.Width, 1,
93 PANEL_STEP, False);
94 end;
96 function g_Obj_CanMoveY(Obj: PObj; YInc: Integer): Boolean; inline;
97 begin
98 // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì ñòåíà => øàãàòü íåëüçÿ
99 // Èëè åñëè øàãíóòü âíèç, à òàì ñòóïåíü => øàãàòü íåëüçÿ
100 Result := not(g_Obj_CollideLevel(Obj, 0, YInc) or ((YInc > 0) and g_Obj_StayOnStep(Obj)));
101 end;
103 function CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
104 begin
105 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
106 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3,
107 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
108 end;
110 function CollideLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
111 begin
112 if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
113 Obj^.Rect.Width, Obj^.Rect.Height,
114 PANEL_LIFTUP, False) then
115 Result := -1
116 else if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
117 Obj^.Rect.Width, Obj^.Rect.Height,
118 PANEL_LIFTDOWN, False) then
119 Result := 1
120 else
121 Result := 0;
122 end;
124 function CollideHorLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
125 var
126 left, right: Boolean;
127 begin
128 left := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
129 Obj^.Rect.Width, Obj^.Rect.Height,
130 PANEL_LIFTLEFT, False);
131 right := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
132 Obj^.Rect.Width, Obj^.Rect.Height,
133 PANEL_LIFTRIGHT, False);
134 if left and not right then
135 Result := -1
136 else if right and not left then
137 Result := 1
138 else
139 Result := 0;
140 end;
142 function CollidePlayers(_Obj: PObj; XInc, YInc: Integer): Boolean;
143 var
144 plr: TPlayer;
145 begin
146 result := false;
147 if (gPlayers = nil) then exit;
148 for plr in gPlayers do
149 begin
150 if (plr = nil) then continue;
151 if not plr.alive then continue;
152 with plr do
153 begin
154 if g_Collide(GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
155 PLAYER_RECT.Width, PLAYER_RECT.Height,
156 _Obj^.X+_Obj^.Rect.X+XInc, _Obj^.Y+_Obj^.Rect.Y+YInc,
157 _Obj^.Rect.Width, _Obj^.Rect.Height) then
158 begin
159 result := true;
160 exit;
161 end;
162 end;
163 end;
164 end;
166 function Blocked(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
167 begin
168 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
169 Obj^.Rect.Width, Obj^.Rect.Height,
170 PANEL_BLOCKMON, False);
171 end;
173 function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
174 begin
175 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
176 Obj^.Rect.Width, Obj^.Rect.Height,
177 PANEL_WALL, False);
178 end;
180 function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
181 begin
182 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
183 Obj^.Rect.Width, Obj^.Rect.Height,
184 PANEL_STEP, False);
185 end;
187 function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
188 begin
189 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
190 Obj^.Rect.Width, Obj^.Rect.Height,
191 PANEL_WATER, False);
192 end;
194 function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
195 begin
196 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
197 Obj^.Rect.Width, Obj^.Rect.Height,
198 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
199 end;
201 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean; inline;
202 begin
203 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
204 Obj^.Rect.Width, Obj^.Rect.Height,
205 PanelType, False);
206 end;
208 procedure g_Obj_Splash(Obj: PObj; Color: Byte);
209 var
210 MaxVel: Integer;
211 begin
212 MaxVel := nmax(abs(Obj^.Vel.X), abs(Obj^.Vel.Y));
213 if MaxVel > 4 then
214 begin
215 if MaxVel < 10 then
216 g_Sound_PlayExAt('SOUND_GAME_BULK1', Obj^.X, Obj^.Y)
217 else
218 g_Sound_PlayExAt('SOUND_GAME_BULK2', Obj^.X, Obj^.Y);
219 end;
221 g_GFX_Water(Obj^.X+Obj^.Rect.X+(Obj^.Rect.Width div 2),
222 Obj^.Y+Obj^.Rect.Y+(Obj^.Rect.Height div 2),
223 Min(5*(abs(Obj^.Vel.X)+abs(Obj^.Vel.Y)), 50),
224 -Obj^.Vel.X, -Obj^.Vel.Y,
225 Obj^.Rect.Width, 16, Color);
226 end;
229 function move (Obj: PObj; dx, dy: Integer; ClimbSlopes: Boolean): Word;
230 var
231 i: Integer;
232 sx, sy: ShortInt;
233 st: Word;
235 procedure slope (s: Integer);
236 var
237 i: Integer;
238 begin
239 i := 0;
240 while g_Obj_CollideLevel(Obj, sx, 0) and (i < 4) do
241 begin
242 Obj^.Y += s;
243 Inc(i);
244 end;
245 Obj^.X += sx;
246 if (s < 0) then
247 begin
248 Obj.slopeUpLeft += i*(-s);
249 Obj.slopeFramesLeft := SmoothSlopeFrames;
250 end;
251 end;
253 function movex (): Boolean;
254 begin
255 result := false;
257 // Åñëè ìîíñòðó øàãíóòü â ñòîðîíó, à òàì áëîêìîí
258 if gMon and ((st and MOVE_BLOCK) = 0) then
259 begin
260 if Blocked(Obj, sx, 0) then st := st or MOVE_BLOCK;
261 end;
263 // Åñëè øàãíóòü â ñòîðîíó, à òàì ñòåíà => øàãàòü íåëüçÿ
264 if g_Obj_CollideLevel(Obj, sx, 0) then
265 begin
266 if ClimbSlopes and (abs(dy) < 2) then
267 begin
268 result := true;
269 if (not g_Obj_CollideLevel(Obj, sx, -12)) and // çàáèðàåìñÿ íà 12 ïèêñåëåé âëåâî/âïðàâî
270 (sy >= 0) and (not g_Obj_CanMoveY(Obj, sy)) then // òîëüêî åñëè åñòü çåìëÿ ïîä íîãàìè
271 begin
272 slope(-1);
273 end
274 else
275 begin
276 result := false;
277 st := st or MOVE_HITWALL;
278 end;
279 end
280 else
281 begin
282 st := st or MOVE_HITWALL;
283 end;
284 end
285 else // Òàì ñòåíû íåò
286 begin
287 if CollideLiquid(Obj, sx, 0) then
288 begin // Åñëè øàãíóòü â ñòîðîíó, à òàì òåïåðü æèäêîñòü
289 if ((st and MOVE_INWATER) = 0) then st := st or MOVE_HITWATER;
290 end
291 else // Åñëè øàãíóòü â ñòîðîíó, à òàì óæå íåò æèäêîñòè
292 begin
293 if ((st and MOVE_INWATER) <> 0) then st := st or MOVE_HITAIR;
294 end;
296 // Øàã
297 Obj^.X += sx;
298 result := true;
299 end;
300 end;
302 function movey (): Boolean;
303 begin
304 result := false;
306 // Åñëè ìîíñòðó øàãíóòü ïî âåðòèêàëè, à òàì áëîêìîí
307 if gMon and ((st and MOVE_BLOCK) = 0) then
308 begin
309 if Blocked(Obj, 0, sy) then st := st or MOVE_BLOCK;
310 end;
312 // Åñëè øàãàòü íåëüçÿ
313 if not g_Obj_CanMoveY(Obj, sy) then
314 begin
315 if sy > 0 then
316 st := st or MOVE_HITLAND
317 else
318 st := st or MOVE_HITCEIL;
319 end
320 else // Òàì ñòåíû íåò. È ñòóïåíè ñíèçó òîæå íåò
321 begin
322 if CollideLiquid(Obj, 0, sy) then
323 begin // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì òåïåðü æèäêîñòü
324 if ((st and MOVE_INWATER) = 0) then st := st or MOVE_HITWATER;
325 end
326 else // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì óæå íåò æèäêîñòè
327 begin
328 if ((st and MOVE_INWATER) <> 0) then st := st or MOVE_HITAIR;
329 end;
331 // Øàã
332 Obj^.Y += sy;
333 result := true;
334 end;
335 end;
337 begin
338 st := MOVE_NONE;
340 // Îáúåêò â æèäêîñòè?
341 if CollideLiquid(Obj, 0, 0) then st := st or MOVE_INWATER;
343 // Ìîíñòð â áëîêìîíå?
344 if gMon then
345 begin
346 if Blocked(Obj, 0, 0) then st := st or MOVE_BLOCK;
347 end;
349 // Äâèãàòüñÿ íå íàäî?
350 if (dx = 0) and (dy = 0) then begin result := st; exit; end;
352 sx := g_basic.Sign(dx);
353 sy := g_basic.Sign(dy);
354 dx := abs(dx);
355 dy := abs(dy);
357 for i := 1 to dx do if not movex() then break;
358 for i := 1 to dy do if not movey() then break;
360 result := st;
361 end;
364 procedure g_Obj_Init (Obj: PObj); inline;
365 begin
366 ZeroMemory(Obj, SizeOf(TObj));
367 end;
370 function g_Obj_Move_Projectile (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False): Word;
371 begin
372 result := g_Obj_Move(Obj, Fallable, Splash, ClimbSlopes, true);
373 end;
375 function g_Obj_Move (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False; asProjectile: Boolean=false): Word;
376 var
377 xv, yv, dx, dy: Integer;
378 inwater: Boolean;
379 c: Boolean;
380 wtx: DWORD;
381 slopeStep: Integer;
382 dirx, diry, speed: Double;
383 label
384 _move;
385 begin
386 // Ëèìèòû íà ñêîðîñòü è óñêîðåíèå
387 Obj^.Vel.X := nclamp(Obj^.Vel.X, -LIMIT_VEL, LIMIT_VEL);
388 Obj^.Vel.Y := nclamp(Obj^.Vel.Y, -LIMIT_VEL, LIMIT_VEL);
389 Obj^.Accel.X := nclamp(Obj^.Accel.X, -LIMIT_ACCEL, LIMIT_ACCEL);
390 Obj^.Accel.Y := nclamp(Obj^.Accel.Y, -LIMIT_ACCEL, LIMIT_ACCEL);
392 if Obj^.Vel.X < -LIMIT_VEL then Obj^.Vel.X := -LIMIT_VEL
393 else if Obj^.Vel.X > LIMIT_VEL then Obj^.Vel.X := LIMIT_VEL;
394 if Obj^.Vel.Y < -LIMIT_VEL then Obj^.Vel.Y := -LIMIT_VEL
395 else if Obj^.Vel.Y > LIMIT_VEL then Obj^.Vel.Y := LIMIT_VEL;
396 if Obj^.Accel.X < -LIMIT_ACCEL then Obj^.Accel.X := -LIMIT_ACCEL
397 else if Obj^.Accel.X > LIMIT_ACCEL then Obj^.Accel.X := LIMIT_ACCEL;
398 if Obj^.Accel.Y < -LIMIT_ACCEL then Obj^.Accel.Y := -LIMIT_ACCEL
399 else if Obj^.Accel.Y > LIMIT_ACCEL then Obj^.Accel.Y := LIMIT_ACCEL;
402 // Âûëåòåë çà íèæíþþ ãðàíèöó êàðòû?
403 if (Obj^.Y > Integer(gMapInfo.Height)+128) then begin result := MOVE_FALLOUT; Obj.slopeUpLeft := 0; Obj.slopeFramesLeft := 0; exit; end;
405 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
406 c := (gTime mod (GAME_TICK*2) <> 0);
408 // smoothed slopes
409 if {not c and} (Obj.slopeUpLeft > 0) then
410 begin
411 if (Obj.slopeFramesLeft < 1) then
412 begin
413 //conwritefln('SLOPE DONE: slopeUpLeft=%s', [Obj.slopeUpLeft]);
414 Obj.slopeUpLeft := 0; // oops
415 end
416 else
417 begin
418 slopeStep := Obj.slopeUpLeft div Obj.slopeFramesLeft;
419 if (slopeStep < 1) then slopeStep := 1;
420 //conwritefln('SLOPE STEP: slopeUpLeft=%s; slopeFramesLeft=%s; slopeStep=%d', [Obj.slopeUpLeft, Obj.slopeFramesLeft, slopeStep]);
421 Dec(Obj.slopeFramesLeft);
422 Obj.slopeUpLeft -= slopeStep;
423 if (Obj.slopeUpLeft < 1) then
424 begin
425 Obj.slopeUpLeft := 0;
426 Obj.slopeFramesLeft := 0;
427 end;
428 end;
429 end;
431 if c then goto _move;
433 case CollideLift(Obj, 0, 0) of
434 -1: //up
435 begin
436 Obj^.Vel.Y -= 1; // Ëèôò ââåðõ
437 if (Obj^.Vel.Y < -5) then Obj^.Vel.Y += 1;
438 end;
439 1: //down
440 begin
441 if (Obj^.Vel.Y > 5) then Obj^.Vel.Y -= 1;
442 Obj^.Vel.Y += 1; // Ãðàâèòàöèÿ èëè ëèôò âíèç
443 end;
444 0: //???
445 begin
446 if Fallable then Obj^.Vel.Y += 1; // Ãðàâèòàöèÿ
447 if (Obj^.Vel.Y > MAX_YV) then Obj^.Vel.Y -= 1;
448 end;
449 end;
451 case CollideHorLift(Obj, 0, 0) of
452 -1: //left
453 begin
454 Obj^.Vel.X -= 3;
455 if (Obj^.Vel.X < -9) then Obj^.Vel.X += 3;
456 end;
457 1: //right
458 begin
459 Obj^.Vel.X += 3;
460 if (Obj^.Vel.X > 9) then Obj^.Vel.X -= 3;
461 end;
462 // 0 is not needed here
463 end;
465 // Â âîäå?
466 inwater := CollideLiquid(Obj, 0, 0);
467 if inwater then
468 begin
469 if asProjectile then
470 begin
471 //writeln('velocity=(', Obj^.Vel.X, ',', Obj^.Vel.Y, '); acceleration=(', Obj^.Accel.X, ',', Obj^.Accel.Y, ')');
472 if (g_Obj_GetSpeedDirF(Obj, dirx, diry, speed)) then
473 begin
474 //writeln('SPEED: ', speed);
475 if (speed > 5) then
476 begin
477 speed := speed/1.4;
478 Obj^.Vel.X := round(dirx*speed);
479 Obj^.Vel.Y := round(diry*speed);
480 end;
481 end;
483 // acceleration
484 if (g_Obj_GetAccelDirF(Obj, dirx, diry, speed)) then
485 begin
486 if (speed > 5) then
487 begin
488 speed := speed/1.4;
489 Obj^.Accel.X := round(dirx*speed);
490 Obj^.Accel.Y := round(diry*speed);
491 end;
492 end;
493 end
494 else
495 begin
496 // velocity
497 xv := abs(Obj^.Vel.X)+1;
498 if (xv > 5) then Obj^.Vel.X := z_dec(Obj^.Vel.X, (xv div 2)-2);
499 yv := abs(Obj^.Vel.Y)+1;
500 if (yv > 5) then Obj^.Vel.Y := z_dec(Obj^.Vel.Y, (yv div 2)-2);
502 // acceleration
503 xv := abs(Obj^.Accel.X)+1;
504 if (xv > 5) then Obj^.Accel.X := z_dec(Obj^.Accel.X, (xv div 2)-2);
505 yv := abs(Obj^.Accel.Y)+1;
506 if (yv > 5) then Obj^.Accel.Y := z_dec(Obj^.Accel.Y, (yv div 2)-2);
507 end;
508 end;
510 // Óìåíüøàåì ïðèáàâêó ê ñêîðîñòè
511 Obj^.Accel.X := z_dec(Obj^.Accel.X, 1);
512 Obj^.Accel.Y := z_dec(Obj^.Accel.Y, 1);
514 _move:
516 xv := Obj^.Vel.X+Obj^.Accel.X;
517 yv := Obj^.Vel.Y+Obj^.Accel.Y;
519 dx := xv;
520 dy := yv;
522 result := move(Obj, dx, dy, ClimbSlopes);
524 // Áðûçãè (åñëè íóæíû)
525 if Splash then
526 begin
527 if WordBool(Result and MOVE_HITWATER) then
528 begin
529 wtx := g_Map_CollideLiquid_Texture(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
530 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3);
531 case wtx of
532 LongWord(TEXTURE_SPECIAL_WATER): g_Obj_Splash(Obj, 3);
533 LongWord(TEXTURE_SPECIAL_ACID1): g_Obj_Splash(Obj, 2);
534 LongWord(TEXTURE_SPECIAL_ACID2): g_Obj_Splash(Obj, 1);
535 LongWord(TEXTURE_NONE): begin end;
536 else g_Obj_Splash(Obj, 0);
537 end;
538 end;
539 end;
541 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
542 if c then exit;
544 // Âðåçàëèñü â ñòåíó - ñòîï
545 if ((Result and MOVE_HITWALL) <> 0) then
546 begin
547 Obj^.Vel.X := 0;
548 Obj^.Accel.X := 0;
549 end;
551 // Âðåçàëèñü â ïîë èëè ïîòîëîê - ñòîï
552 if ((Result and (MOVE_HITCEIL or MOVE_HITLAND)) <> 0) then
553 begin
554 Obj^.Vel.Y := 0;
555 Obj^.Accel.Y := 0;
556 end;
557 end;
560 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; inline;
561 begin
562 Result := g_Collide(Obj1^.X+Obj1^.Rect.X, Obj1^.Y+Obj1^.Rect.Y,
563 Obj1^.Rect.Width, Obj1^.Rect.Height,
564 Obj2^.X+Obj2^.Rect.X, Obj2^.Y+Obj2^.Rect.Y,
565 Obj2^.Rect.Width, Obj2^.Rect.Height);
566 end;
568 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; inline;
569 begin
570 Result := g_Collide(X, Y,
571 Width, Height,
572 Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
573 Obj^.Rect.Width, Obj^.Rect.Height);
574 end;
576 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean; inline;
577 begin
578 Result := g_CollidePoint(X, Y, Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
579 Obj^.Rect.Width, Obj^.Rect.Height);
580 end;
582 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer); inline;
583 begin
584 Obj^.Vel.X := Obj^.Vel.X + VelX;
585 Obj^.Vel.Y := Obj^.Vel.Y + VelY;
586 end;
588 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt); inline;
589 var
590 s, c: Extended;
592 begin
593 SinCos(DegToRad(-Angle), s, c);
595 Obj^.Vel.X := Obj^.Vel.X + Round(Vel*c);
596 Obj^.Vel.Y := Obj^.Vel.Y + Round(Vel*s);
597 end;
599 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer); inline;
600 var
601 m, vx, vy: Integer;
602 begin
603 vx := Obj^.Vel.X;
604 vy := Obj^.Vel.Y;
606 m := Max(abs(vx), abs(vy));
607 if m = 0 then
608 m := 1;
610 Obj^.Vel.X := (vx*s) div m;
611 Obj^.Vel.Y := (vy*s) div m;
612 end;
614 // `false`: zero speed
615 function g_Obj_GetSpeedDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline;
616 var
617 len, vx, vy: Double;
618 begin
619 if (Obj^.Vel.X = 0) and (Obj^.Vel.Y = 0) then
620 begin
621 dirx := 0;
622 diry := 0;
623 speed := 0;
624 result := false;
625 exit;
626 end;
628 vx := Obj^.Vel.X;
629 vy := Obj^.Vel.Y;
630 len := sqrt(vx*vx+vy*vy);
631 dirx := vx/len;
632 diry := vy/len;
633 speed := len;
634 result := true;
635 end;
637 // `false`: zero acceleratin
638 function g_Obj_GetAccelDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline;
639 var
640 len, vx, vy: Double;
641 begin
642 if (Obj^.Accel.X = 0) and (Obj^.Accel.Y = 0) then
643 begin
644 dirx := 0;
645 diry := 0;
646 speed := 0;
647 result := false;
648 exit;
649 end;
651 vx := Obj^.Accel.X;
652 vy := Obj^.Accel.Y;
653 len := sqrt(vx*vx+vy*vy);
654 dirx := vx/len;
655 diry := vy/len;
656 speed := len;
657 result := true;
658 end;
661 // Ïðèáëèæàåì a ê 0 íà b åäèíèö:
662 function z_dec (a, b: Integer): Integer; inline;
663 begin
664 if (abs(a) < b) then result := 0
665 else if (a > 0) then result := a-b
666 else if (a < 0) then result := a+b
667 else result := 0; // a = 0
668 end;
671 // Ïðèáëèæàåì a ê 0.0 íà b åäèíèö:
672 function z_fdec (a, b: Double): Double; inline;
673 begin
674 if (abs(a) < b) then result := 0.0
675 else if (a > 0.0) then result := a-b
676 else if (a < 0.0) then result := a+b
677 else result := 0.0; // a = 0.0
678 end;
681 end.