DEADSOFTWARE

Physics: Fix climb ladders on the fly
[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): 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 z_dec(a, b: Integer): Integer; inline;
68 function z_fdec(a, b: Double): Double; inline;
70 var
71 gMon: Boolean = False;
73 implementation
75 uses
76 g_map, g_basic, Math, g_player, g_console, SysUtils,
77 g_sound, g_gfx, MAPDEF, g_monsters, g_game, utils;
80 const
81 SmoothSlopeFrames = 4;
84 function g_Obj_StayOnStep(Obj: PObj): Boolean; inline;
85 begin
86 Result := not g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height-1,
87 Obj^.Rect.Width, 1,
88 PANEL_STEP, False)
89 and g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height,
90 Obj^.Rect.Width, 1,
91 PANEL_STEP, False);
92 end;
94 function g_Obj_CanMoveY(Obj: PObj; YInc: Integer): Boolean; inline;
95 begin
96 // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì ñòåíà => øàãàòü íåëüçÿ
97 // Èëè åñëè øàãíóòü âíèç, à òàì ñòóïåíü => øàãàòü íåëüçÿ
98 Result := not(g_Obj_CollideLevel(Obj, 0, YInc) or ((YInc > 0) and g_Obj_StayOnStep(Obj)));
99 end;
101 function CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
102 begin
103 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
104 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3,
105 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
106 end;
108 function CollideLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
109 begin
110 if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
111 Obj^.Rect.Width, Obj^.Rect.Height,
112 PANEL_LIFTUP, False) then
113 Result := -1
114 else if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
115 Obj^.Rect.Width, Obj^.Rect.Height,
116 PANEL_LIFTDOWN, False) then
117 Result := 1
118 else
119 Result := 0;
120 end;
122 function CollideHorLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
123 var
124 left, right: Boolean;
125 begin
126 left := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
127 Obj^.Rect.Width, Obj^.Rect.Height,
128 PANEL_LIFTLEFT, False);
129 right := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
130 Obj^.Rect.Width, Obj^.Rect.Height,
131 PANEL_LIFTRIGHT, False);
132 if left and not right then
133 Result := -1
134 else if right and not left then
135 Result := 1
136 else
137 Result := 0;
138 end;
140 function CollidePlayers(_Obj: PObj; XInc, YInc: Integer): Boolean;
141 var
142 plr: TPlayer;
143 begin
144 result := false;
145 if (gPlayers = nil) then exit;
146 for plr in gPlayers do
147 begin
148 if (plr = nil) then continue;
149 if not plr.alive then continue;
150 with plr do
151 begin
152 if g_Collide(GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
153 PLAYER_RECT.Width, PLAYER_RECT.Height,
154 _Obj^.X+_Obj^.Rect.X+XInc, _Obj^.Y+_Obj^.Rect.Y+YInc,
155 _Obj^.Rect.Width, _Obj^.Rect.Height) then
156 begin
157 result := true;
158 exit;
159 end;
160 end;
161 end;
162 end;
164 function Blocked(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
165 begin
166 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
167 Obj^.Rect.Width, Obj^.Rect.Height,
168 PANEL_BLOCKMON, False);
169 end;
171 function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
172 begin
173 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
174 Obj^.Rect.Width, Obj^.Rect.Height,
175 PANEL_WALL, False);
176 end;
178 function g_Obj_CollideStep(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_STEP, False);
183 end;
185 function g_Obj_CollideWater(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_WATER, False);
190 end;
192 function g_Obj_CollideLiquid(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_WATER or PANEL_ACID1 or PANEL_ACID2, False);
197 end;
199 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): 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 PanelType, False);
204 end;
206 procedure g_Obj_Splash(Obj: PObj; Color: Byte);
207 var
208 MaxVel: Integer;
209 begin
210 MaxVel := nmax(abs(Obj^.Vel.X), abs(Obj^.Vel.Y));
211 if MaxVel > 4 then
212 begin
213 if MaxVel < 10 then
214 g_Sound_PlayExAt('SOUND_GAME_BULK1', Obj^.X, Obj^.Y)
215 else
216 g_Sound_PlayExAt('SOUND_GAME_BULK2', Obj^.X, Obj^.Y);
217 end;
219 g_GFX_Water(Obj^.X+Obj^.Rect.X+(Obj^.Rect.Width div 2),
220 Obj^.Y+Obj^.Rect.Y+(Obj^.Rect.Height div 2),
221 Min(5*(abs(Obj^.Vel.X)+abs(Obj^.Vel.Y)), 50),
222 -Obj^.Vel.X, -Obj^.Vel.Y,
223 Obj^.Rect.Width, 16, Color);
224 end;
227 function move (Obj: PObj; dx, dy: Integer; ClimbSlopes: Boolean): Word;
228 var
229 i: Integer;
230 sx, sy: ShortInt;
231 st: Word;
233 procedure slope (s: Integer);
234 var
235 i: Integer;
236 begin
237 i := 0;
238 while g_Obj_CollideLevel(Obj, sx, 0) and (i < 4) do
239 begin
240 Obj^.Y += s;
241 Inc(i);
242 end;
243 Obj^.X += sx;
244 if (s < 0) then
245 begin
246 Obj.slopeUpLeft += i*(-s);
247 Obj.slopeFramesLeft := SmoothSlopeFrames;
248 end;
249 end;
251 function movex (): Boolean;
252 begin
253 result := false;
255 // Åñëè ìîíñòðó øàãíóòü â ñòîðîíó, à òàì áëîêìîí
256 if gMon and ((st and MOVE_BLOCK) = 0) then
257 begin
258 if Blocked(Obj, sx, 0) then st := st or MOVE_BLOCK;
259 end;
261 // Åñëè øàãíóòü â ñòîðîíó, à òàì ñòåíà => øàãàòü íåëüçÿ
262 if g_Obj_CollideLevel(Obj, sx, 0) then
263 begin
264 if ClimbSlopes and (abs(dy) < 2) then
265 begin
266 result := true;
267 if (not g_Obj_CollideLevel(Obj, sx, -12)) and // çàáèðàåìñÿ íà 12 ïèêñåëåé âëåâî/âïðàâî
268 (sy >= 0) and (not g_Obj_CanMoveY(Obj, sy)) then // òîëüêî åñëè åñòü çåìëÿ ïîä íîãàìè
269 begin
270 slope(-1);
271 end
272 else
273 begin
274 result := false;
275 st := st or MOVE_HITWALL;
276 end;
277 end
278 else
279 begin
280 st := st or MOVE_HITWALL;
281 end;
282 end
283 else // Òàì ñòåíû íåò
284 begin
285 if CollideLiquid(Obj, sx, 0) then
286 begin // Åñëè øàãíóòü â ñòîðîíó, à òàì òåïåðü æèäêîñòü
287 if ((st and MOVE_INWATER) = 0) then st := st or MOVE_HITWATER;
288 end
289 else // Åñëè øàãíóòü â ñòîðîíó, à òàì óæå íåò æèäêîñòè
290 begin
291 if ((st and MOVE_INWATER) <> 0) then st := st or MOVE_HITAIR;
292 end;
294 // Øàã
295 Obj^.X += sx;
296 result := true;
297 end;
298 end;
300 function movey (): Boolean;
301 begin
302 result := false;
304 // Åñëè ìîíñòðó øàãíóòü ïî âåðòèêàëè, à òàì áëîêìîí
305 if gMon and ((st and MOVE_BLOCK) = 0) then
306 begin
307 if Blocked(Obj, 0, sy) then st := st or MOVE_BLOCK;
308 end;
310 // Åñëè øàãàòü íåëüçÿ
311 if not g_Obj_CanMoveY(Obj, sy) then
312 begin
313 if sy > 0 then
314 st := st or MOVE_HITLAND
315 else
316 st := st or MOVE_HITCEIL;
317 end
318 else // Òàì ñòåíû íåò. È ñòóïåíè ñíèçó òîæå íåò
319 begin
320 if CollideLiquid(Obj, 0, sy) then
321 begin // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì òåïåðü æèäêîñòü
322 if ((st and MOVE_INWATER) = 0) then st := st or MOVE_HITWATER;
323 end
324 else // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì óæå íåò æèäêîñòè
325 begin
326 if ((st and MOVE_INWATER) <> 0) then st := st or MOVE_HITAIR;
327 end;
329 // Øàã
330 Obj^.Y += sy;
331 result := true;
332 end;
333 end;
335 begin
336 st := MOVE_NONE;
338 // Îáúåêò â æèäêîñòè?
339 if CollideLiquid(Obj, 0, 0) then st := st or MOVE_INWATER;
341 // Ìîíñòð â áëîêìîíå?
342 if gMon then
343 begin
344 if Blocked(Obj, 0, 0) then st := st or MOVE_BLOCK;
345 end;
347 // Äâèãàòüñÿ íå íàäî?
348 if (dx = 0) and (dy = 0) then begin result := st; exit; end;
350 sx := g_basic.Sign(dx);
351 sy := g_basic.Sign(dy);
352 dx := abs(dx);
353 dy := abs(dy);
355 for i := 1 to dx do if not movex() then break;
356 for i := 1 to dy do if not movey() then break;
358 result := st;
359 end;
362 procedure g_Obj_Init (Obj: PObj); inline;
363 begin
364 ZeroMemory(Obj, SizeOf(TObj));
365 end;
368 function g_Obj_Move (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False): Word;
369 var
370 xv, yv, dx, dy: Integer;
371 inwater: Boolean;
372 c: Boolean;
373 wtx: DWORD;
374 slopeStep: Integer;
375 label
376 _move;
377 begin
378 // Ëèìèòû íà ñêîðîñòü è óñêîðåíèå
379 Obj^.Vel.X := nclamp(Obj^.Vel.X, -LIMIT_VEL, LIMIT_VEL);
380 Obj^.Vel.Y := nclamp(Obj^.Vel.Y, -LIMIT_VEL, LIMIT_VEL);
381 Obj^.Accel.X := nclamp(Obj^.Accel.X, -LIMIT_ACCEL, LIMIT_ACCEL);
382 Obj^.Accel.Y := nclamp(Obj^.Accel.Y, -LIMIT_ACCEL, LIMIT_ACCEL);
384 if Obj^.Vel.X < -LIMIT_VEL then Obj^.Vel.X := -LIMIT_VEL
385 else if Obj^.Vel.X > LIMIT_VEL then Obj^.Vel.X := LIMIT_VEL;
386 if Obj^.Vel.Y < -LIMIT_VEL then Obj^.Vel.Y := -LIMIT_VEL
387 else if Obj^.Vel.Y > LIMIT_VEL then Obj^.Vel.Y := LIMIT_VEL;
388 if Obj^.Accel.X < -LIMIT_ACCEL then Obj^.Accel.X := -LIMIT_ACCEL
389 else if Obj^.Accel.X > LIMIT_ACCEL then Obj^.Accel.X := LIMIT_ACCEL;
390 if Obj^.Accel.Y < -LIMIT_ACCEL then Obj^.Accel.Y := -LIMIT_ACCEL
391 else if Obj^.Accel.Y > LIMIT_ACCEL then Obj^.Accel.Y := LIMIT_ACCEL;
394 // Âûëåòåë çà íèæíþþ ãðàíèöó êàðòû?
395 if (Obj^.Y > gMapInfo.Height+128) then begin result := MOVE_FALLOUT; Obj.slopeUpLeft := 0; Obj.slopeFramesLeft := 0; exit; end;
397 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
398 c := (gTime mod (GAME_TICK*2) <> 0);
400 // smoothed slopes
401 if {not c and} (Obj.slopeUpLeft > 0) then
402 begin
403 if (Obj.slopeFramesLeft < 1) then
404 begin
405 //conwritefln('SLOPE DONE: slopeUpLeft=%s', [Obj.slopeUpLeft]);
406 Obj.slopeUpLeft := 0; // oops
407 end
408 else
409 begin
410 slopeStep := Obj.slopeUpLeft div Obj.slopeFramesLeft;
411 if (slopeStep < 1) then slopeStep := 1;
412 //conwritefln('SLOPE STEP: slopeUpLeft=%s; slopeFramesLeft=%s; slopeStep=%d', [Obj.slopeUpLeft, Obj.slopeFramesLeft, slopeStep]);
413 Dec(Obj.slopeFramesLeft);
414 Obj.slopeUpLeft -= slopeStep;
415 if (Obj.slopeUpLeft < 1) then
416 begin
417 Obj.slopeUpLeft := 0;
418 Obj.slopeFramesLeft := 0;
419 end;
420 end;
421 end;
423 if c then goto _move;
425 case CollideLift(Obj, 0, 0) of
426 -1: //up
427 begin
428 Obj^.Vel.Y -= 1; // Ëèôò ââåðõ
429 if (Obj^.Vel.Y < -5) then Obj^.Vel.Y += 1;
430 end;
431 1: //down
432 begin
433 if (Obj^.Vel.Y > 5) then Obj^.Vel.Y -= 1;
434 Obj^.Vel.Y += 1; // Ãðàâèòàöèÿ èëè ëèôò âíèç
435 end;
436 0: //???
437 begin
438 if Fallable then Obj^.Vel.Y += 1; // Ãðàâèòàöèÿ
439 if (Obj^.Vel.Y > MAX_YV) then Obj^.Vel.Y -= 1;
440 end;
441 end;
443 case CollideHorLift(Obj, 0, 0) of
444 -1: //left
445 begin
446 Obj^.Vel.X -= 3;
447 if (Obj^.Vel.X < -9) then Obj^.Vel.X += 3;
448 end;
449 1: //right
450 begin
451 Obj^.Vel.X += 3;
452 if (Obj^.Vel.X > 9) then Obj^.Vel.X -= 3;
453 end;
454 // 0 is not needed here
455 end;
457 // Â âîäå?
458 inwater := CollideLiquid(Obj, 0, 0);
459 if inwater then
460 begin
461 xv := abs(Obj^.Vel.X)+1;
462 if (xv > 5) then Obj^.Vel.X := z_dec(Obj^.Vel.X, (xv div 2)-2);
464 yv := abs(Obj^.Vel.Y)+1;
465 if (yv > 5) then Obj^.Vel.Y := z_dec(Obj^.Vel.Y, (yv div 2)-2);
467 xv := abs(Obj^.Accel.X)+1;
468 if (xv > 5) then Obj^.Accel.X := z_dec(Obj^.Accel.X, (xv div 2)-2);
470 yv := abs(Obj^.Accel.Y)+1;
471 if (yv > 5) then Obj^.Accel.Y := z_dec(Obj^.Accel.Y, (yv div 2)-2);
472 end;
474 // Óìåíüøàåì ïðèáàâêó ê ñêîðîñòè
475 Obj^.Accel.X := z_dec(Obj^.Accel.X, 1);
476 Obj^.Accel.Y := z_dec(Obj^.Accel.Y, 1);
478 _move:
480 xv := Obj^.Vel.X+Obj^.Accel.X;
481 yv := Obj^.Vel.Y+Obj^.Accel.Y;
483 dx := xv;
484 dy := yv;
486 result := move(Obj, dx, dy, ClimbSlopes);
488 // Áðûçãè (åñëè íóæíû)
489 if Splash then
490 begin
491 if WordBool(Result and MOVE_HITWATER) then
492 begin
493 wtx := g_Map_CollideLiquid_Texture(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
494 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3);
495 case wtx of
496 LongWord(TEXTURE_SPECIAL_WATER): g_Obj_Splash(Obj, 3);
497 LongWord(TEXTURE_SPECIAL_ACID1): g_Obj_Splash(Obj, 2);
498 LongWord(TEXTURE_SPECIAL_ACID2): g_Obj_Splash(Obj, 1);
499 LongWord(TEXTURE_NONE): begin end;
500 else g_Obj_Splash(Obj, 0);
501 end;
502 end;
503 end;
505 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
506 if c then exit;
508 // Âðåçàëèñü â ñòåíó - ñòîï
509 if ((Result and MOVE_HITWALL) <> 0) then
510 begin
511 Obj^.Vel.X := 0;
512 Obj^.Accel.X := 0;
513 end;
515 // Âðåçàëèñü â ïîë èëè ïîòîëîê - ñòîï
516 if ((Result and (MOVE_HITCEIL or MOVE_HITLAND)) <> 0) then
517 begin
518 Obj^.Vel.Y := 0;
519 Obj^.Accel.Y := 0;
520 end;
521 end;
524 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; inline;
525 begin
526 Result := g_Collide(Obj1^.X+Obj1^.Rect.X, Obj1^.Y+Obj1^.Rect.Y,
527 Obj1^.Rect.Width, Obj1^.Rect.Height,
528 Obj2^.X+Obj2^.Rect.X, Obj2^.Y+Obj2^.Rect.Y,
529 Obj2^.Rect.Width, Obj2^.Rect.Height);
530 end;
532 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; inline;
533 begin
534 Result := g_Collide(X, Y,
535 Width, Height,
536 Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
537 Obj^.Rect.Width, Obj^.Rect.Height);
538 end;
540 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean; inline;
541 begin
542 Result := g_CollidePoint(X, Y, Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
543 Obj^.Rect.Width, Obj^.Rect.Height);
544 end;
546 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer); inline;
547 begin
548 Obj^.Vel.X := Obj^.Vel.X + VelX;
549 Obj^.Vel.Y := Obj^.Vel.Y + VelY;
550 end;
552 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt); inline;
553 var
554 s, c: Extended;
556 begin
557 SinCos(DegToRad(-Angle), s, c);
559 Obj^.Vel.X := Obj^.Vel.X + Round(Vel*c);
560 Obj^.Vel.Y := Obj^.Vel.Y + Round(Vel*s);
561 end;
563 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer); inline;
564 var
565 m, vx, vy: Integer;
566 begin
567 vx := Obj^.Vel.X;
568 vy := Obj^.Vel.Y;
570 m := Max(abs(vx), abs(vy));
571 if m = 0 then
572 m := 1;
574 Obj^.Vel.X := (vx*s) div m;
575 Obj^.Vel.Y := (vy*s) div m;
576 end;
579 // Ïðèáëèæàåì a ê 0 íà b åäèíèö:
580 function z_dec (a, b: Integer): Integer; inline;
581 begin
582 if (abs(a) < b) then result := 0
583 else if (a > 0) then result := a-b
584 else if (a < 0) then result := a+b
585 else result := 0; // a = 0
586 end;
589 // Ïðèáëèæàåì a ê 0.0 íà b åäèíèö:
590 function z_fdec (a, b: Double): Double; inline;
591 begin
592 if (abs(a) < b) then result := 0.0
593 else if (a > 0.0) then result := a-b
594 else if (a < 0.0) then result := a+b
595 else result := 0.0; // a = 0.0
596 end;
599 end.