DEADSOFTWARE

made projectiles slightly faster in liquids (it rougly matches "original" speed now)
[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, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *)
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 end;
37 const
38 MAX_YV = 30;
39 LIMIT_VEL = 16384;
40 LIMIT_ACCEL = 1024;
42 MOVE_NONE = 0;
43 MOVE_HITWALL = 1;
44 MOVE_HITCEIL = 2;
45 MOVE_HITLAND = 4;
46 MOVE_FALLOUT = 8;
47 MOVE_INWATER = 16;
48 MOVE_HITWATER = 32;
49 MOVE_HITAIR = 64;
50 MOVE_BLOCK = 128;
52 procedure g_Obj_Init(Obj: PObj); inline;
53 function g_Obj_Move(Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False; asProjectile: Boolean=false): Word;
54 function g_Obj_Move_Projectile (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False): Word;
55 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; inline; overload;
56 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; inline; overload;
57 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean; inline;
58 function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
59 function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
60 function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
61 function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
62 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean; inline;
63 function g_Obj_StayOnStep(Obj: PObj): Boolean; inline;
64 function g_Obj_CanMoveY(Obj: PObj; YInc: Integer): Boolean; inline;
65 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer); inline;
66 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt); inline;
67 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer); inline;
68 function g_Obj_GetSpeedDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline; // `false`: zero speed
69 function g_Obj_GetAccelDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline; // `false`: zero speed
70 function z_dec(a, b: Integer): Integer; inline;
71 function z_fdec(a, b: Double): Double; inline;
73 var
74 gMon: Boolean = False;
76 implementation
78 uses
79 g_map, g_basic, Math, g_player, g_console, SysUtils,
80 g_sound, g_gfx, MAPDEF, g_monsters, g_game, utils;
83 const
84 SmoothSlopeFrames = 4;
87 function g_Obj_StayOnStep(Obj: PObj): Boolean; inline;
88 begin
89 Result := not g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height-1,
90 Obj^.Rect.Width, 1,
91 PANEL_STEP, False)
92 and g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height,
93 Obj^.Rect.Width, 1,
94 PANEL_STEP, False);
95 end;
97 function g_Obj_CanMoveY(Obj: PObj; YInc: Integer): Boolean; inline;
98 begin
99 // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì ñòåíà => øàãàòü íåëüçÿ
100 // Èëè åñëè øàãíóòü âíèç, à òàì ñòóïåíü => øàãàòü íåëüçÿ
101 Result := not(g_Obj_CollideLevel(Obj, 0, YInc) or ((YInc > 0) and g_Obj_StayOnStep(Obj)));
102 end;
104 function CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
105 begin
106 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
107 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3,
108 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
109 end;
111 function CollideLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
112 begin
113 if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
114 Obj^.Rect.Width, Obj^.Rect.Height,
115 PANEL_LIFTUP, False) then
116 Result := -1
117 else if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
118 Obj^.Rect.Width, Obj^.Rect.Height,
119 PANEL_LIFTDOWN, False) then
120 Result := 1
121 else
122 Result := 0;
123 end;
125 function CollideHorLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
126 var
127 left, right: Boolean;
128 begin
129 left := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
130 Obj^.Rect.Width, Obj^.Rect.Height,
131 PANEL_LIFTLEFT, False);
132 right := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
133 Obj^.Rect.Width, Obj^.Rect.Height,
134 PANEL_LIFTRIGHT, False);
135 if left and not right then
136 Result := -1
137 else if right and not left then
138 Result := 1
139 else
140 Result := 0;
141 end;
143 function CollidePlayers(_Obj: PObj; XInc, YInc: Integer): Boolean;
144 var
145 plr: TPlayer;
146 begin
147 result := false;
148 if (gPlayers = nil) then exit;
149 for plr in gPlayers do
150 begin
151 if (plr = nil) then continue;
152 if not plr.alive then continue;
153 with plr do
154 begin
155 if g_Collide(GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
156 PLAYER_RECT.Width, PLAYER_RECT.Height,
157 _Obj^.X+_Obj^.Rect.X+XInc, _Obj^.Y+_Obj^.Rect.Y+YInc,
158 _Obj^.Rect.Width, _Obj^.Rect.Height) then
159 begin
160 result := true;
161 exit;
162 end;
163 end;
164 end;
165 end;
167 function Blocked(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
168 begin
169 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
170 Obj^.Rect.Width, Obj^.Rect.Height,
171 PANEL_BLOCKMON, False);
172 end;
174 function g_Obj_CollideLevel(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_WALL, False);
179 end;
181 function g_Obj_CollideStep(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_STEP, False);
186 end;
188 function g_Obj_CollideWater(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_WATER, False);
193 end;
195 function g_Obj_CollideLiquid(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 or PANEL_ACID1 or PANEL_ACID2, False);
200 end;
202 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): 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 PanelType, False);
207 end;
209 procedure g_Obj_Splash(Obj: PObj; Color: Byte);
210 var
211 MaxVel: Integer;
212 begin
213 MaxVel := nmax(abs(Obj^.Vel.X), abs(Obj^.Vel.Y));
214 if MaxVel > 4 then
215 begin
216 if MaxVel < 10 then
217 g_Sound_PlayExAt('SOUND_GAME_BULK1', Obj^.X, Obj^.Y)
218 else
219 g_Sound_PlayExAt('SOUND_GAME_BULK2', Obj^.X, Obj^.Y);
220 end;
222 g_GFX_Water(Obj^.X+Obj^.Rect.X+(Obj^.Rect.Width div 2),
223 Obj^.Y+Obj^.Rect.Y+(Obj^.Rect.Height div 2),
224 Min(5*(abs(Obj^.Vel.X)+abs(Obj^.Vel.Y)), 50),
225 -Obj^.Vel.X, -Obj^.Vel.Y,
226 Obj^.Rect.Width, 16, Color);
227 end;
230 function move (Obj: PObj; dx, dy: Integer; ClimbSlopes: Boolean): Word;
231 var
232 i: Integer;
233 sx, sy: ShortInt;
234 st: Word;
236 procedure slope (s: Integer);
237 var
238 i: Integer;
239 begin
240 i := 0;
241 while g_Obj_CollideLevel(Obj, sx, 0) and (i < 4) do
242 begin
243 Obj^.Y += s;
244 Inc(i);
245 end;
246 Obj^.X += sx;
247 if (s < 0) then
248 begin
249 Obj.slopeUpLeft += i*(-s);
250 Obj.slopeFramesLeft := SmoothSlopeFrames;
251 end;
252 end;
254 function movex (): Boolean;
255 begin
256 result := false;
258 // Åñëè ìîíñòðó øàãíóòü â ñòîðîíó, à òàì áëîêìîí
259 if gMon and ((st and MOVE_BLOCK) = 0) then
260 begin
261 if Blocked(Obj, sx, 0) then st := st or MOVE_BLOCK;
262 end;
264 // Åñëè øàãíóòü â ñòîðîíó, à òàì ñòåíà => øàãàòü íåëüçÿ
265 if g_Obj_CollideLevel(Obj, sx, 0) then
266 begin
267 if ClimbSlopes and (abs(dy) < 2) then
268 begin
269 result := true;
270 if (not g_Obj_CollideLevel(Obj, sx, -12)) and // çàáèðàåìñÿ íà 12 ïèêñåëåé âëåâî/âïðàâî
271 (sy >= 0) and (not g_Obj_CanMoveY(Obj, sy)) then // òîëüêî åñëè åñòü çåìëÿ ïîä íîãàìè
272 begin
273 slope(-1);
274 end
275 else
276 begin
277 result := false;
278 st := st or MOVE_HITWALL;
279 end;
280 end
281 else
282 begin
283 st := st or MOVE_HITWALL;
284 end;
285 end
286 else // Òàì ñòåíû íåò
287 begin
288 if CollideLiquid(Obj, sx, 0) then
289 begin // Åñëè øàãíóòü â ñòîðîíó, à òàì òåïåðü æèäêîñòü
290 if ((st and MOVE_INWATER) = 0) then st := st or MOVE_HITWATER;
291 end
292 else // Åñëè øàãíóòü â ñòîðîíó, à òàì óæå íåò æèäêîñòè
293 begin
294 if ((st and MOVE_INWATER) <> 0) then st := st or MOVE_HITAIR;
295 end;
297 // Øàã
298 Obj^.X += sx;
299 result := true;
300 end;
301 end;
303 function movey (): Boolean;
304 begin
305 result := false;
307 // Åñëè ìîíñòðó øàãíóòü ïî âåðòèêàëè, à òàì áëîêìîí
308 if gMon and ((st and MOVE_BLOCK) = 0) then
309 begin
310 if Blocked(Obj, 0, sy) then st := st or MOVE_BLOCK;
311 end;
313 // Åñëè øàãàòü íåëüçÿ
314 if not g_Obj_CanMoveY(Obj, sy) then
315 begin
316 if sy > 0 then
317 st := st or MOVE_HITLAND
318 else
319 st := st or MOVE_HITCEIL;
320 end
321 else // Òàì ñòåíû íåò. È ñòóïåíè ñíèçó òîæå íåò
322 begin
323 if CollideLiquid(Obj, 0, sy) then
324 begin // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì òåïåðü æèäêîñòü
325 if ((st and MOVE_INWATER) = 0) then st := st or MOVE_HITWATER;
326 end
327 else // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì óæå íåò æèäêîñòè
328 begin
329 if ((st and MOVE_INWATER) <> 0) then st := st or MOVE_HITAIR;
330 end;
332 // Øàã
333 Obj^.Y += sy;
334 result := true;
335 end;
336 end;
338 begin
339 st := MOVE_NONE;
341 // Îáúåêò â æèäêîñòè?
342 if CollideLiquid(Obj, 0, 0) then st := st or MOVE_INWATER;
344 // Ìîíñòð â áëîêìîíå?
345 if gMon then
346 begin
347 if Blocked(Obj, 0, 0) then st := st or MOVE_BLOCK;
348 end;
350 // Äâèãàòüñÿ íå íàäî?
351 if (dx = 0) and (dy = 0) then begin result := st; exit; end;
353 sx := g_basic.Sign(dx);
354 sy := g_basic.Sign(dy);
355 dx := abs(dx);
356 dy := abs(dy);
358 for i := 1 to dx do if not movex() then break;
359 for i := 1 to dy do if not movey() then break;
361 result := st;
362 end;
365 procedure g_Obj_Init (Obj: PObj); inline;
366 begin
367 ZeroMemory(Obj, SizeOf(TObj));
368 end;
371 function g_Obj_Move_Projectile (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False): Word;
372 begin
373 result := g_Obj_Move(Obj, Fallable, Splash, ClimbSlopes, true);
374 end;
376 function g_Obj_Move (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False; asProjectile: Boolean=false): Word;
377 var
378 xv, yv, dx, dy: Integer;
379 inwater: Boolean;
380 c: Boolean;
381 wtx: DWORD;
382 slopeStep: Integer;
383 dirx, diry, speed: Double;
384 label
385 _move;
386 begin
387 // Ëèìèòû íà ñêîðîñòü è óñêîðåíèå
388 Obj^.Vel.X := nclamp(Obj^.Vel.X, -LIMIT_VEL, LIMIT_VEL);
389 Obj^.Vel.Y := nclamp(Obj^.Vel.Y, -LIMIT_VEL, LIMIT_VEL);
390 Obj^.Accel.X := nclamp(Obj^.Accel.X, -LIMIT_ACCEL, LIMIT_ACCEL);
391 Obj^.Accel.Y := nclamp(Obj^.Accel.Y, -LIMIT_ACCEL, LIMIT_ACCEL);
393 if Obj^.Vel.X < -LIMIT_VEL then Obj^.Vel.X := -LIMIT_VEL
394 else if Obj^.Vel.X > LIMIT_VEL then Obj^.Vel.X := LIMIT_VEL;
395 if Obj^.Vel.Y < -LIMIT_VEL then Obj^.Vel.Y := -LIMIT_VEL
396 else if Obj^.Vel.Y > LIMIT_VEL then Obj^.Vel.Y := LIMIT_VEL;
397 if Obj^.Accel.X < -LIMIT_ACCEL then Obj^.Accel.X := -LIMIT_ACCEL
398 else if Obj^.Accel.X > LIMIT_ACCEL then Obj^.Accel.X := LIMIT_ACCEL;
399 if Obj^.Accel.Y < -LIMIT_ACCEL then Obj^.Accel.Y := -LIMIT_ACCEL
400 else if Obj^.Accel.Y > LIMIT_ACCEL then Obj^.Accel.Y := LIMIT_ACCEL;
403 // Âûëåòåë çà íèæíþþ ãðàíèöó êàðòû?
404 if (Obj^.Y > Integer(gMapInfo.Height)+128) then begin result := MOVE_FALLOUT; Obj.slopeUpLeft := 0; Obj.slopeFramesLeft := 0; exit; end;
406 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
407 c := (gTime mod (GAME_TICK*2) <> 0);
409 // smoothed slopes
410 if {not c and} (Obj.slopeUpLeft > 0) then
411 begin
412 if (Obj.slopeFramesLeft < 1) then
413 begin
414 //conwritefln('SLOPE DONE: slopeUpLeft=%s', [Obj.slopeUpLeft]);
415 Obj.slopeUpLeft := 0; // oops
416 end
417 else
418 begin
419 slopeStep := Obj.slopeUpLeft div Obj.slopeFramesLeft;
420 if (slopeStep < 1) then slopeStep := 1;
421 //conwritefln('SLOPE STEP: slopeUpLeft=%s; slopeFramesLeft=%s; slopeStep=%d', [Obj.slopeUpLeft, Obj.slopeFramesLeft, slopeStep]);
422 Dec(Obj.slopeFramesLeft);
423 Obj.slopeUpLeft -= slopeStep;
424 if (Obj.slopeUpLeft < 1) then
425 begin
426 Obj.slopeUpLeft := 0;
427 Obj.slopeFramesLeft := 0;
428 end;
429 end;
430 end;
432 if c then goto _move;
434 case CollideLift(Obj, 0, 0) of
435 -1: //up
436 begin
437 Obj^.Vel.Y -= 1; // Ëèôò ââåðõ
438 if (Obj^.Vel.Y < -5) then Obj^.Vel.Y += 1;
439 end;
440 1: //down
441 begin
442 if (Obj^.Vel.Y > 5) then Obj^.Vel.Y -= 1;
443 Obj^.Vel.Y += 1; // Ãðàâèòàöèÿ èëè ëèôò âíèç
444 end;
445 0: //???
446 begin
447 if Fallable then Obj^.Vel.Y += 1; // Ãðàâèòàöèÿ
448 if (Obj^.Vel.Y > MAX_YV) then Obj^.Vel.Y -= 1;
449 end;
450 end;
452 case CollideHorLift(Obj, 0, 0) of
453 -1: //left
454 begin
455 Obj^.Vel.X -= 3;
456 if (Obj^.Vel.X < -9) then Obj^.Vel.X += 3;
457 end;
458 1: //right
459 begin
460 Obj^.Vel.X += 3;
461 if (Obj^.Vel.X > 9) then Obj^.Vel.X -= 3;
462 end;
463 // 0 is not needed here
464 end;
466 // Â âîäå?
467 inwater := CollideLiquid(Obj, 0, 0);
468 if inwater then
469 begin
470 if asProjectile then
471 begin
472 //writeln('velocity=(', Obj^.Vel.X, ',', Obj^.Vel.Y, '); acceleration=(', Obj^.Accel.X, ',', Obj^.Accel.Y, ')');
473 if (g_Obj_GetSpeedDirF(Obj, dirx, diry, speed)) then
474 begin
475 //writeln('SPEED: ', speed);
476 if (speed > 5) then
477 begin
478 speed := speed/1.4;
479 Obj^.Vel.X := round(dirx*speed);
480 Obj^.Vel.Y := round(diry*speed);
481 end;
482 end;
484 // acceleration
485 if (g_Obj_GetAccelDirF(Obj, dirx, diry, speed)) then
486 begin
487 if (speed > 5) then
488 begin
489 speed := speed/1.4;
490 Obj^.Accel.X := round(dirx*speed);
491 Obj^.Accel.Y := round(diry*speed);
492 end;
493 end;
494 end
495 else
496 begin
497 // velocity
498 xv := abs(Obj^.Vel.X)+1;
499 if (xv > 5) then Obj^.Vel.X := z_dec(Obj^.Vel.X, (xv div 2)-2);
500 yv := abs(Obj^.Vel.Y)+1;
501 if (yv > 5) then Obj^.Vel.Y := z_dec(Obj^.Vel.Y, (yv div 2)-2);
503 // acceleration
504 xv := abs(Obj^.Accel.X)+1;
505 if (xv > 5) then Obj^.Accel.X := z_dec(Obj^.Accel.X, (xv div 2)-2);
506 yv := abs(Obj^.Accel.Y)+1;
507 if (yv > 5) then Obj^.Accel.Y := z_dec(Obj^.Accel.Y, (yv div 2)-2);
508 end;
509 end;
511 // Óìåíüøàåì ïðèáàâêó ê ñêîðîñòè
512 Obj^.Accel.X := z_dec(Obj^.Accel.X, 1);
513 Obj^.Accel.Y := z_dec(Obj^.Accel.Y, 1);
515 _move:
517 xv := Obj^.Vel.X+Obj^.Accel.X;
518 yv := Obj^.Vel.Y+Obj^.Accel.Y;
520 dx := xv;
521 dy := yv;
523 result := move(Obj, dx, dy, ClimbSlopes);
525 // Áðûçãè (åñëè íóæíû)
526 if Splash then
527 begin
528 if WordBool(Result and MOVE_HITWATER) then
529 begin
530 wtx := g_Map_CollideLiquid_Texture(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
531 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3);
532 case wtx of
533 LongWord(TEXTURE_SPECIAL_WATER): g_Obj_Splash(Obj, 3);
534 LongWord(TEXTURE_SPECIAL_ACID1): g_Obj_Splash(Obj, 2);
535 LongWord(TEXTURE_SPECIAL_ACID2): g_Obj_Splash(Obj, 1);
536 LongWord(TEXTURE_NONE): begin end;
537 else g_Obj_Splash(Obj, 0);
538 end;
539 end;
540 end;
542 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
543 if c then exit;
545 // Âðåçàëèñü â ñòåíó - ñòîï
546 if ((Result and MOVE_HITWALL) <> 0) then
547 begin
548 Obj^.Vel.X := 0;
549 Obj^.Accel.X := 0;
550 end;
552 // Âðåçàëèñü â ïîë èëè ïîòîëîê - ñòîï
553 if ((Result and (MOVE_HITCEIL or MOVE_HITLAND)) <> 0) then
554 begin
555 Obj^.Vel.Y := 0;
556 Obj^.Accel.Y := 0;
557 end;
558 end;
561 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; inline;
562 begin
563 Result := g_Collide(Obj1^.X+Obj1^.Rect.X, Obj1^.Y+Obj1^.Rect.Y,
564 Obj1^.Rect.Width, Obj1^.Rect.Height,
565 Obj2^.X+Obj2^.Rect.X, Obj2^.Y+Obj2^.Rect.Y,
566 Obj2^.Rect.Width, Obj2^.Rect.Height);
567 end;
569 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; inline;
570 begin
571 Result := g_Collide(X, Y,
572 Width, Height,
573 Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
574 Obj^.Rect.Width, Obj^.Rect.Height);
575 end;
577 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean; inline;
578 begin
579 Result := g_CollidePoint(X, Y, Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
580 Obj^.Rect.Width, Obj^.Rect.Height);
581 end;
583 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer); inline;
584 begin
585 Obj^.Vel.X := Obj^.Vel.X + VelX;
586 Obj^.Vel.Y := Obj^.Vel.Y + VelY;
587 end;
589 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt); inline;
590 var
591 s, c: Extended;
593 begin
594 SinCos(DegToRad(-Angle), s, c);
596 Obj^.Vel.X := Obj^.Vel.X + Round(Vel*c);
597 Obj^.Vel.Y := Obj^.Vel.Y + Round(Vel*s);
598 end;
600 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer); inline;
601 var
602 m, vx, vy: Integer;
603 begin
604 vx := Obj^.Vel.X;
605 vy := Obj^.Vel.Y;
607 m := Max(abs(vx), abs(vy));
608 if m = 0 then
609 m := 1;
611 Obj^.Vel.X := (vx*s) div m;
612 Obj^.Vel.Y := (vy*s) div m;
613 end;
615 // `false`: zero speed
616 function g_Obj_GetSpeedDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline;
617 var
618 len, vx, vy: Double;
619 begin
620 if (Obj^.Vel.X = 0) and (Obj^.Vel.Y = 0) then
621 begin
622 dirx := 0;
623 diry := 0;
624 speed := 0;
625 result := false;
626 exit;
627 end;
629 vx := Obj^.Vel.X;
630 vy := Obj^.Vel.Y;
631 len := sqrt(vx*vx+vy*vy);
632 dirx := vx/len;
633 diry := vy/len;
634 speed := len;
635 result := true;
636 end;
638 // `false`: zero acceleratin
639 function g_Obj_GetAccelDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline;
640 var
641 len, vx, vy: Double;
642 begin
643 if (Obj^.Accel.X = 0) and (Obj^.Accel.Y = 0) then
644 begin
645 dirx := 0;
646 diry := 0;
647 speed := 0;
648 result := false;
649 exit;
650 end;
652 vx := Obj^.Accel.X;
653 vy := Obj^.Accel.Y;
654 len := sqrt(vx*vx+vy*vy);
655 dirx := vx/len;
656 diry := vy/len;
657 speed := len;
658 result := true;
659 end;
662 // Ïðèáëèæàåì a ê 0 íà b åäèíèö:
663 function z_dec (a, b: Integer): Integer; inline;
664 begin
665 if (abs(a) < b) then result := 0
666 else if (a > 0) then result := a-b
667 else if (a < 0) then result := a+b
668 else result := 0; // a = 0
669 end;
672 // Ïðèáëèæàåì a ê 0.0 íà b åäèíèö:
673 function z_fdec (a, b: Double): Double; inline;
674 begin
675 if (abs(a) < b) then result := 0.0
676 else if (a > 0.0) then result := a-b
677 else if (a < 0.0) then result := a+b
678 else result := 0.0; // a = 0.0
679 end;
682 end.