DEADSOFTWARE

game: disable gfx for server
[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 g_base;
22 type
23 PObj = ^TObj;
24 TObj = record
25 X, Y: Integer;
26 Rect: TRectWH;
27 Vel: TPoint2i;
28 Accel: TPoint2i;
29 // going up the slope will set this, and renderer will adjust the position
30 // this is purely visual change, it won't affect anything else
31 slopeUpLeft: Integer; // left to go
32 slopeFramesLeft: Integer; // frames left to go
33 // for frame interpolation
34 oldX, oldY: Integer;
35 procedure lerp(t: Single; out fX, fY: Integer);
36 end;
38 const
39 MAX_YV = 30;
40 LIMIT_VEL = 16384;
41 LIMIT_ACCEL = 1024;
43 MOVE_NONE = 0;
44 MOVE_HITWALL = 1;
45 MOVE_HITCEIL = 2;
46 MOVE_HITLAND = 4;
47 MOVE_FALLOUT = 8;
48 MOVE_INWATER = 16;
49 MOVE_HITWATER = 32;
50 MOVE_HITAIR = 64;
51 MOVE_BLOCK = 128;
53 procedure g_Obj_Init(Obj: PObj); inline;
54 function g_Obj_Move(Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False; asProjectile: Boolean=false): Word;
55 function g_Obj_Move_Projectile (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False): Word;
56 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; inline; overload;
57 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; inline; overload;
58 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean; inline;
59 function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
60 function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
61 function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
62 function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
63 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean; inline;
64 function g_Obj_StayOnStep(Obj: PObj): Boolean; inline;
65 function g_Obj_CanMoveY(Obj: PObj; YInc: Integer): Boolean; inline;
66 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer); inline;
67 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt); inline;
68 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer); inline;
69 function g_Obj_GetSpeedDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline; // `false`: zero speed
70 function g_Obj_GetAccelDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline; // `false`: zero speed
71 function z_dec(a, b: Integer): Integer; inline;
72 function z_fdec(a, b: Double): Double; inline;
74 var
75 gMon: Boolean = False;
77 implementation
79 uses
80 {$IFDEF ENABLE_GFX}
81 g_gfx,
82 {$ENDIF}
83 g_map, g_basic, Math, g_player, g_console, SysUtils,
84 g_sound, MAPDEF, g_monsters, g_game, utils
85 ;
88 const
89 SmoothSlopeFrames = 4;
91 procedure TObj.lerp(t: Single; out fX, fY: Integer);
92 begin
93 fX := nlerp(oldX, X, t);
94 fY := nlerp(oldY, Y, t);
95 end;
97 function g_Obj_StayOnStep(Obj: PObj): Boolean; inline;
98 begin
99 Result := not g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height-1,
100 Obj^.Rect.Width, 1,
101 PANEL_STEP, False)
102 and g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height,
103 Obj^.Rect.Width, 1,
104 PANEL_STEP, False);
105 end;
107 function g_Obj_CanMoveY(Obj: PObj; YInc: Integer): Boolean; inline;
108 begin
109 // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì ñòåíà => øàãàòü íåëüçÿ
110 // Èëè åñëè øàãíóòü âíèç, à òàì ñòóïåíü => øàãàòü íåëüçÿ
111 Result := not(g_Obj_CollideLevel(Obj, 0, YInc) or ((YInc > 0) and g_Obj_StayOnStep(Obj)));
112 end;
114 function CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
115 begin
116 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
117 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3,
118 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
119 end;
121 function CollideLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
122 begin
123 if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
124 Obj^.Rect.Width, Obj^.Rect.Height,
125 PANEL_LIFTUP, False) then
126 Result := -1
127 else if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
128 Obj^.Rect.Width, Obj^.Rect.Height,
129 PANEL_LIFTDOWN, False) then
130 Result := 1
131 else
132 Result := 0;
133 end;
135 function CollideHorLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
136 var
137 left, right: Boolean;
138 begin
139 left := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
140 Obj^.Rect.Width, Obj^.Rect.Height,
141 PANEL_LIFTLEFT, False);
142 right := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
143 Obj^.Rect.Width, Obj^.Rect.Height,
144 PANEL_LIFTRIGHT, False);
145 if left and not right then
146 Result := -1
147 else if right and not left then
148 Result := 1
149 else
150 Result := 0;
151 end;
153 function CollidePlayers(_Obj: PObj; XInc, YInc: Integer): Boolean;
154 var
155 plr: TPlayer;
156 begin
157 result := false;
158 if (gPlayers = nil) then exit;
159 for plr in gPlayers do
160 begin
161 if (plr = nil) then continue;
162 if not plr.alive then continue;
163 with plr do
164 begin
165 if g_Collide(GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
166 PLAYER_RECT.Width, PLAYER_RECT.Height,
167 _Obj^.X+_Obj^.Rect.X+XInc, _Obj^.Y+_Obj^.Rect.Y+YInc,
168 _Obj^.Rect.Width, _Obj^.Rect.Height) then
169 begin
170 result := true;
171 exit;
172 end;
173 end;
174 end;
175 end;
177 function Blocked(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
178 begin
179 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
180 Obj^.Rect.Width, Obj^.Rect.Height,
181 PANEL_BLOCKMON, False);
182 end;
184 function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
185 begin
186 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
187 Obj^.Rect.Width, Obj^.Rect.Height,
188 PANEL_WALL, False);
189 end;
191 function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
192 begin
193 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
194 Obj^.Rect.Width, Obj^.Rect.Height,
195 PANEL_STEP, False);
196 end;
198 function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
199 begin
200 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
201 Obj^.Rect.Width, Obj^.Rect.Height,
202 PANEL_WATER, False);
203 end;
205 function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
206 begin
207 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
208 Obj^.Rect.Width, Obj^.Rect.Height,
209 PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
210 end;
212 function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean; inline;
213 begin
214 Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
215 Obj^.Rect.Width, Obj^.Rect.Height,
216 PanelType, False);
217 end;
219 procedure g_Obj_Splash(Obj: PObj; Color: Byte);
220 var
221 MaxVel: Integer;
222 begin
223 MaxVel := nmax(abs(Obj^.Vel.X), abs(Obj^.Vel.Y));
224 if MaxVel > 4 then
225 begin
226 if MaxVel < 10 then
227 g_Sound_PlayExAt('SOUND_GAME_BULK1', Obj^.X, Obj^.Y)
228 else
229 g_Sound_PlayExAt('SOUND_GAME_BULK2', Obj^.X, Obj^.Y);
230 end;
232 {$IFDEF ENABLE_GFX}
233 g_GFX_Water(Obj^.X+Obj^.Rect.X+(Obj^.Rect.Width div 2),
234 Obj^.Y+Obj^.Rect.Y+(Obj^.Rect.Height div 2),
235 Min(5*(abs(Obj^.Vel.X)+abs(Obj^.Vel.Y)), 50),
236 -Obj^.Vel.X, -Obj^.Vel.Y,
237 Obj^.Rect.Width, 16, Color);
238 {$ENDIF}
239 end;
242 function move (Obj: PObj; dx, dy: Integer; ClimbSlopes: Boolean): Word;
243 var
244 i: Integer;
245 sx, sy: ShortInt;
246 st: Word;
248 procedure slope (s: Integer);
249 var
250 i: Integer;
251 begin
252 i := 0;
253 while g_Obj_CollideLevel(Obj, sx, 0) and (i < 4) do
254 begin
255 Obj^.Y += s;
256 Inc(i);
257 end;
258 Obj^.X += sx;
259 if (s < 0) then
260 begin
261 Obj.slopeUpLeft += i*(-s);
262 Obj.slopeFramesLeft := SmoothSlopeFrames;
263 end;
264 end;
266 function movex (): Boolean;
267 begin
268 result := false;
270 // Åñëè ìîíñòðó øàãíóòü â ñòîðîíó, à òàì áëîêìîí
271 if gMon and ((st and MOVE_BLOCK) = 0) then
272 begin
273 if Blocked(Obj, sx, 0) then st := st or MOVE_BLOCK;
274 end;
276 // Åñëè øàãíóòü â ñòîðîíó, à òàì ñòåíà => øàãàòü íåëüçÿ
277 if g_Obj_CollideLevel(Obj, sx, 0) then
278 begin
279 if ClimbSlopes and (abs(dy) < 2) then
280 begin
281 result := true;
282 if (not g_Obj_CollideLevel(Obj, sx, -12)) and // çàáèðàåìñÿ íà 12 ïèêñåëåé âëåâî/âïðàâî
283 (sy >= 0) and (not g_Obj_CanMoveY(Obj, sy)) then // òîëüêî åñëè åñòü çåìëÿ ïîä íîãàìè
284 begin
285 slope(-1);
286 end
287 else
288 begin
289 result := false;
290 st := st or MOVE_HITWALL;
291 end;
292 end
293 else
294 begin
295 st := st or MOVE_HITWALL;
296 end;
297 end
298 else // Òàì ñòåíû íåò
299 begin
300 if CollideLiquid(Obj, sx, 0) then
301 begin // Åñëè øàãíóòü â ñòîðîíó, à òàì òåïåðü æèäêîñòü
302 if ((st and MOVE_INWATER) = 0) then st := st or MOVE_HITWATER;
303 end
304 else // Åñëè øàãíóòü â ñòîðîíó, à òàì óæå íåò æèäêîñòè
305 begin
306 if ((st and MOVE_INWATER) <> 0) then st := st or MOVE_HITAIR;
307 end;
309 // Øàã
310 Obj^.X += sx;
311 result := true;
312 end;
313 end;
315 function movey (): Boolean;
316 begin
317 result := false;
319 // Åñëè ìîíñòðó øàãíóòü ïî âåðòèêàëè, à òàì áëîêìîí
320 if gMon and ((st and MOVE_BLOCK) = 0) then
321 begin
322 if Blocked(Obj, 0, sy) then st := st or MOVE_BLOCK;
323 end;
325 // Åñëè øàãàòü íåëüçÿ
326 if not g_Obj_CanMoveY(Obj, sy) then
327 begin
328 if sy > 0 then
329 st := st or MOVE_HITLAND
330 else
331 st := st or MOVE_HITCEIL;
332 end
333 else // Òàì ñòåíû íåò. È ñòóïåíè ñíèçó òîæå íåò
334 begin
335 if CollideLiquid(Obj, 0, sy) then
336 begin // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì òåïåðü æèäêîñòü
337 if ((st and MOVE_INWATER) = 0) then st := st or MOVE_HITWATER;
338 end
339 else // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì óæå íåò æèäêîñòè
340 begin
341 if ((st and MOVE_INWATER) <> 0) then st := st or MOVE_HITAIR;
342 end;
344 // Øàã
345 Obj^.Y += sy;
346 result := true;
347 end;
348 end;
350 begin
351 st := MOVE_NONE;
353 // Îáúåêò â æèäêîñòè?
354 if CollideLiquid(Obj, 0, 0) then st := st or MOVE_INWATER;
356 // Ìîíñòð â áëîêìîíå?
357 if gMon then
358 begin
359 if Blocked(Obj, 0, 0) then st := st or MOVE_BLOCK;
360 end;
362 // Äâèãàòüñÿ íå íàäî?
363 if (dx = 0) and (dy = 0) then begin result := st; exit; end;
365 sx := g_basic.Sign(dx);
366 sy := g_basic.Sign(dy);
367 dx := abs(dx);
368 dy := abs(dy);
370 for i := 1 to dx do if not movex() then break;
371 for i := 1 to dy do if not movey() then break;
373 result := st;
374 end;
377 procedure g_Obj_Init (Obj: PObj); inline;
378 begin
379 ZeroMemory(Obj, SizeOf(TObj));
380 end;
383 function g_Obj_Move_Projectile (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False): Word;
384 begin
385 result := g_Obj_Move(Obj, Fallable, Splash, ClimbSlopes, true);
386 end;
388 function g_Obj_Move (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False; asProjectile: Boolean=false): Word;
389 var
390 xv, yv, dx, dy: Integer;
391 inwater: Boolean;
392 c: Boolean;
393 wtx: DWORD;
394 slopeStep: Integer;
395 dirx, diry, speed: Double;
396 label
397 _move;
398 begin
399 // Ëèìèòû íà ñêîðîñòü è óñêîðåíèå
400 Obj^.Vel.X := nclamp(Obj^.Vel.X, -LIMIT_VEL, LIMIT_VEL);
401 Obj^.Vel.Y := nclamp(Obj^.Vel.Y, -LIMIT_VEL, LIMIT_VEL);
402 Obj^.Accel.X := nclamp(Obj^.Accel.X, -LIMIT_ACCEL, LIMIT_ACCEL);
403 Obj^.Accel.Y := nclamp(Obj^.Accel.Y, -LIMIT_ACCEL, LIMIT_ACCEL);
405 if Obj^.Vel.X < -LIMIT_VEL then Obj^.Vel.X := -LIMIT_VEL
406 else if Obj^.Vel.X > LIMIT_VEL then Obj^.Vel.X := LIMIT_VEL;
407 if Obj^.Vel.Y < -LIMIT_VEL then Obj^.Vel.Y := -LIMIT_VEL
408 else if Obj^.Vel.Y > LIMIT_VEL then Obj^.Vel.Y := LIMIT_VEL;
409 if Obj^.Accel.X < -LIMIT_ACCEL then Obj^.Accel.X := -LIMIT_ACCEL
410 else if Obj^.Accel.X > LIMIT_ACCEL then Obj^.Accel.X := LIMIT_ACCEL;
411 if Obj^.Accel.Y < -LIMIT_ACCEL then Obj^.Accel.Y := -LIMIT_ACCEL
412 else if Obj^.Accel.Y > LIMIT_ACCEL then Obj^.Accel.Y := LIMIT_ACCEL;
415 // Âûëåòåë çà íèæíþþ ãðàíèöó êàðòû?
416 if (Obj^.Y > Integer(gMapInfo.Height)+128) then begin result := MOVE_FALLOUT; Obj.slopeUpLeft := 0; Obj.slopeFramesLeft := 0; exit; end;
418 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
419 c := (gTime mod (GAME_TICK*2) <> 0);
421 // smoothed slopes
422 if {not c and} (Obj.slopeUpLeft > 0) then
423 begin
424 if (Obj.slopeFramesLeft < 1) then
425 begin
426 //conwritefln('SLOPE DONE: slopeUpLeft=%s', [Obj.slopeUpLeft]);
427 Obj.slopeUpLeft := 0; // oops
428 end
429 else
430 begin
431 slopeStep := Obj.slopeUpLeft div Obj.slopeFramesLeft;
432 if (slopeStep < 1) then slopeStep := 1;
433 //conwritefln('SLOPE STEP: slopeUpLeft=%s; slopeFramesLeft=%s; slopeStep=%d', [Obj.slopeUpLeft, Obj.slopeFramesLeft, slopeStep]);
434 Dec(Obj.slopeFramesLeft);
435 Obj.slopeUpLeft -= slopeStep;
436 if (Obj.slopeUpLeft < 1) then
437 begin
438 Obj.slopeUpLeft := 0;
439 Obj.slopeFramesLeft := 0;
440 end;
441 end;
442 end;
444 if c then goto _move;
446 case CollideLift(Obj, 0, 0) of
447 -1: //up
448 begin
449 Obj^.Vel.Y -= 1; // Ëèôò ââåðõ
450 if (Obj^.Vel.Y < -5) then Obj^.Vel.Y += 1;
451 end;
452 1: //down
453 begin
454 if (Obj^.Vel.Y > 5) then Obj^.Vel.Y -= 1;
455 Obj^.Vel.Y += 1; // Ãðàâèòàöèÿ èëè ëèôò âíèç
456 end;
457 0: //???
458 begin
459 if Fallable then Obj^.Vel.Y += 1; // Ãðàâèòàöèÿ
460 if (Obj^.Vel.Y > MAX_YV) then Obj^.Vel.Y -= 1;
461 end;
462 end;
464 case CollideHorLift(Obj, 0, 0) of
465 -1: //left
466 begin
467 Obj^.Vel.X -= 3;
468 if (Obj^.Vel.X < -9) then Obj^.Vel.X += 3;
469 end;
470 1: //right
471 begin
472 Obj^.Vel.X += 3;
473 if (Obj^.Vel.X > 9) then Obj^.Vel.X -= 3;
474 end;
475 // 0 is not needed here
476 end;
478 // Â âîäå?
479 inwater := CollideLiquid(Obj, 0, 0);
480 if inwater then
481 begin
482 if asProjectile then
483 begin
484 //writeln('velocity=(', Obj^.Vel.X, ',', Obj^.Vel.Y, '); acceleration=(', Obj^.Accel.X, ',', Obj^.Accel.Y, ')');
485 if (g_Obj_GetSpeedDirF(Obj, dirx, diry, speed)) then
486 begin
487 //writeln('SPEED: ', speed);
488 if (speed > 5) then
489 begin
490 speed := speed/1.4;
491 Obj^.Vel.X := round(dirx*speed);
492 Obj^.Vel.Y := round(diry*speed);
493 end;
494 end;
496 // acceleration
497 if (g_Obj_GetAccelDirF(Obj, dirx, diry, speed)) then
498 begin
499 if (speed > 5) then
500 begin
501 speed := speed/1.4;
502 Obj^.Accel.X := round(dirx*speed);
503 Obj^.Accel.Y := round(diry*speed);
504 end;
505 end;
506 end
507 else
508 begin
509 // velocity
510 xv := abs(Obj^.Vel.X)+1;
511 if (xv > 5) then Obj^.Vel.X := z_dec(Obj^.Vel.X, (xv div 2)-2);
512 yv := abs(Obj^.Vel.Y)+1;
513 if (yv > 5) then Obj^.Vel.Y := z_dec(Obj^.Vel.Y, (yv div 2)-2);
515 // acceleration
516 xv := abs(Obj^.Accel.X)+1;
517 if (xv > 5) then Obj^.Accel.X := z_dec(Obj^.Accel.X, (xv div 2)-2);
518 yv := abs(Obj^.Accel.Y)+1;
519 if (yv > 5) then Obj^.Accel.Y := z_dec(Obj^.Accel.Y, (yv div 2)-2);
520 end;
521 end;
523 // Óìåíüøàåì ïðèáàâêó ê ñêîðîñòè
524 Obj^.Accel.X := z_dec(Obj^.Accel.X, 1);
525 Obj^.Accel.Y := z_dec(Obj^.Accel.Y, 1);
527 _move:
529 xv := Obj^.Vel.X+Obj^.Accel.X;
530 yv := Obj^.Vel.Y+Obj^.Accel.Y;
532 dx := xv;
533 dy := yv;
535 result := move(Obj, dx, dy, ClimbSlopes);
537 // Áðûçãè (åñëè íóæíû)
538 if Splash then
539 begin
540 if WordBool(Result and MOVE_HITWATER) then
541 begin
542 wtx := g_Map_CollideLiquid_Texture(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
543 Obj^.Rect.Width, Obj^.Rect.Height*2 div 3);
544 case wtx of
545 LongWord(TEXTURE_SPECIAL_WATER): g_Obj_Splash(Obj, 3);
546 LongWord(TEXTURE_SPECIAL_ACID1): g_Obj_Splash(Obj, 2);
547 LongWord(TEXTURE_SPECIAL_ACID2): g_Obj_Splash(Obj, 1);
548 LongWord(TEXTURE_NONE): begin end;
549 else g_Obj_Splash(Obj, 0);
550 end;
551 end;
552 end;
554 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
555 if c then exit;
557 // Âðåçàëèñü â ñòåíó - ñòîï
558 if ((Result and MOVE_HITWALL) <> 0) then
559 begin
560 Obj^.Vel.X := 0;
561 Obj^.Accel.X := 0;
562 end;
564 // Âðåçàëèñü â ïîë èëè ïîòîëîê - ñòîï
565 if ((Result and (MOVE_HITCEIL or MOVE_HITLAND)) <> 0) then
566 begin
567 Obj^.Vel.Y := 0;
568 Obj^.Accel.Y := 0;
569 end;
570 end;
573 function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; inline;
574 begin
575 Result := g_Collide(Obj1^.X+Obj1^.Rect.X, Obj1^.Y+Obj1^.Rect.Y,
576 Obj1^.Rect.Width, Obj1^.Rect.Height,
577 Obj2^.X+Obj2^.Rect.X, Obj2^.Y+Obj2^.Rect.Y,
578 Obj2^.Rect.Width, Obj2^.Rect.Height);
579 end;
581 function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; inline;
582 begin
583 Result := g_Collide(X, Y,
584 Width, Height,
585 Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
586 Obj^.Rect.Width, Obj^.Rect.Height);
587 end;
589 function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean; inline;
590 begin
591 Result := g_CollidePoint(X, Y, Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
592 Obj^.Rect.Width, Obj^.Rect.Height);
593 end;
595 procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer); inline;
596 begin
597 Obj^.Vel.X := Obj^.Vel.X + VelX;
598 Obj^.Vel.Y := Obj^.Vel.Y + VelY;
599 end;
601 procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt); inline;
602 var
603 s, c: Extended;
605 begin
606 SinCos(DegToRad(-Angle), s, c);
608 Obj^.Vel.X := Obj^.Vel.X + Round(Vel*c);
609 Obj^.Vel.Y := Obj^.Vel.Y + Round(Vel*s);
610 end;
612 procedure g_Obj_SetSpeed(Obj: PObj; s: Integer); inline;
613 var
614 m, vx, vy: Integer;
615 begin
616 vx := Obj^.Vel.X;
617 vy := Obj^.Vel.Y;
619 m := Max(abs(vx), abs(vy));
620 if m = 0 then
621 m := 1;
623 Obj^.Vel.X := (vx*s) div m;
624 Obj^.Vel.Y := (vy*s) div m;
625 end;
627 // `false`: zero speed
628 function g_Obj_GetSpeedDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline;
629 var
630 len, vx, vy: Double;
631 begin
632 if (Obj^.Vel.X = 0) and (Obj^.Vel.Y = 0) then
633 begin
634 dirx := 0;
635 diry := 0;
636 speed := 0;
637 result := false;
638 exit;
639 end;
641 vx := Obj^.Vel.X;
642 vy := Obj^.Vel.Y;
643 len := sqrt(vx*vx+vy*vy);
644 dirx := vx/len;
645 diry := vy/len;
646 speed := len;
647 result := true;
648 end;
650 // `false`: zero acceleratin
651 function g_Obj_GetAccelDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline;
652 var
653 len, vx, vy: Double;
654 begin
655 if (Obj^.Accel.X = 0) and (Obj^.Accel.Y = 0) then
656 begin
657 dirx := 0;
658 diry := 0;
659 speed := 0;
660 result := false;
661 exit;
662 end;
664 vx := Obj^.Accel.X;
665 vy := Obj^.Accel.Y;
666 len := sqrt(vx*vx+vy*vy);
667 dirx := vx/len;
668 diry := vy/len;
669 speed := len;
670 result := true;
671 end;
674 // Ïðèáëèæàåì a ê 0 íà b åäèíèö:
675 function z_dec (a, b: Integer): Integer; inline;
676 begin
677 if (abs(a) < b) then result := 0
678 else if (a > 0) then result := a-b
679 else if (a < 0) then result := a+b
680 else result := 0; // a = 0
681 end;
684 // Ïðèáëèæàåì a ê 0.0 íà b åäèíèö:
685 function z_fdec (a, b: Double): Double; inline;
686 begin
687 if (abs(a) < b) then result := 0.0
688 else if (a > 0.0) then result := a-b
689 else if (a < 0.0) then result := a+b
690 else result := 0.0; // a = 0.0
691 end;
694 end.