a88ca21c9da246a2f61e2f76ff17160cc032681e
1 (* Copyright (C) DooM 2D:Forever Developers
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.
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.
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/>.
16 {$INCLUDE ../shared/a_modes.inc}
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
52 procedure g_Obj_Init(Obj
: PObj
);
53 function g_Obj_Move(Obj
: PObj
; Fallable
: Boolean; Splash
: Boolean; ClimbSlopes
: Boolean = False): Word;
54 function g_Obj_Collide(Obj1
, Obj2
: PObj
): Boolean; overload
;
55 function g_Obj_Collide(X
, Y
: Integer; Width
, Height
: Word; Obj
: PObj
): Boolean; overload
;
56 function g_Obj_CollidePoint(X
, Y
: Integer; Obj
: PObj
): Boolean;
57 function g_Obj_CollideLevel(Obj
: PObj
; XInc
, YInc
: Integer): Boolean;
58 function g_Obj_CollideStep(Obj
: PObj
; XInc
, YInc
: Integer): Boolean;
59 function g_Obj_CollideWater(Obj
: PObj
; XInc
, YInc
: Integer): Boolean;
60 function g_Obj_CollideLiquid(Obj
: PObj
; XInc
, YInc
: Integer): Boolean;
61 function g_Obj_CollidePanel(Obj
: PObj
; XInc
, YInc
: Integer; PanelType
: Word): Boolean;
62 function g_Obj_StayOnStep(Obj
: PObj
): Boolean;
63 procedure g_Obj_Push(Obj
: PObj
; VelX
, VelY
: Integer);
64 procedure g_Obj_PushA(Obj
: PObj
; Vel
: Integer; Angle
: SmallInt);
65 procedure g_Obj_SetSpeed(Obj
: PObj
; s
: Integer);
66 function z_dec(a
, b
: Integer): Integer; inline;
67 function z_fdec(a
, b
: Double): Double; inline;
70 gMon
: Boolean = False;
75 g_map
, g_basic
, Math
, g_player
, g_console
, SysUtils
,
76 g_sound
, g_gfx
, MAPDEF
, g_monsters
, g_game
, BinEditor
, utils
;
80 SmoothSlopeFrames
= 4;
83 function g_Obj_StayOnStep(Obj
: PObj
): Boolean;
85 Result
:= not g_Map_CollidePanel(Obj
^.X
+Obj
^.Rect
.X
, Obj
^.Y
+Obj
^.Rect
.Y
+Obj
^.Rect
.Height
-1,
88 and g_Map_CollidePanel(Obj
^.X
+Obj
^.Rect
.X
, Obj
^.Y
+Obj
^.Rect
.Y
+Obj
^.Rect
.Height
,
93 function CollideLiquid(Obj
: PObj
; XInc
, YInc
: Integer): Boolean;
95 Result
:= g_Map_CollidePanel(Obj
^.X
+Obj
^.Rect
.X
+XInc
, Obj
^.Y
+Obj
^.Rect
.Y
+YInc
,
96 Obj
^.Rect
.Width
, Obj
^.Rect
.Height
*2 div 3,
97 PANEL_WATER
or PANEL_ACID1
or PANEL_ACID2
, False);
100 function CollideLift(Obj
: PObj
; XInc
, YInc
: Integer): Integer;
102 if g_Map_CollidePanel(Obj
^.X
+Obj
^.Rect
.X
+XInc
, Obj
^.Y
+Obj
^.Rect
.Y
+YInc
,
103 Obj
^.Rect
.Width
, Obj
^.Rect
.Height
,
104 PANEL_LIFTUP
, False) then
106 else if g_Map_CollidePanel(Obj
^.X
+Obj
^.Rect
.X
+XInc
, Obj
^.Y
+Obj
^.Rect
.Y
+YInc
,
107 Obj
^.Rect
.Width
, Obj
^.Rect
.Height
,
108 PANEL_LIFTDOWN
, False) then
114 function CollideHorLift(Obj
: PObj
; XInc
, YInc
: Integer): Integer;
116 left
, right
: Boolean;
118 left
:= g_Map_CollidePanel(Obj
^.X
+Obj
^.Rect
.X
+XInc
, Obj
^.Y
+Obj
^.Rect
.Y
+YInc
,
119 Obj
^.Rect
.Width
, Obj
^.Rect
.Height
,
120 PANEL_LIFTLEFT
, False);
121 right
:= g_Map_CollidePanel(Obj
^.X
+Obj
^.Rect
.X
+XInc
, Obj
^.Y
+Obj
^.Rect
.Y
+YInc
,
122 Obj
^.Rect
.Width
, Obj
^.Rect
.Height
,
123 PANEL_LIFTRIGHT
, False);
124 if left
and not right
then
126 else if right
and not left
then
132 function CollidePlayers(_Obj
: PObj
; XInc
, YInc
: Integer): Boolean;
137 if (gPlayers
= nil) then exit
;
138 for plr
in gPlayers
do
140 if (plr
= nil) then continue
;
141 if not plr
.Live
then continue
;
144 if g_Collide(GameX
+PLAYER_RECT
.X
, GameY
+PLAYER_RECT
.Y
,
145 PLAYER_RECT
.Width
, PLAYER_RECT
.Height
,
146 _Obj
^.X
+_Obj
^.Rect
.X
+XInc
, _Obj
^.Y
+_Obj
^.Rect
.Y
+YInc
,
147 _Obj
^.Rect
.Width
, _Obj
^.Rect
.Height
) then
156 function Blocked(Obj
: PObj
; XInc
, YInc
: Integer): Boolean;
158 Result
:= g_Map_CollidePanel(Obj
^.X
+Obj
^.Rect
.X
+XInc
, Obj
^.Y
+Obj
.Rect
.Y
+YInc
,
159 Obj
^.Rect
.Width
, Obj
^.Rect
.Height
,
160 PANEL_BLOCKMON
, False);
163 function g_Obj_CollideLevel(Obj
: PObj
; XInc
, YInc
: Integer): Boolean;
165 Result
:= g_Map_CollidePanel(Obj
^.X
+Obj
^.Rect
.X
+XInc
, Obj
^.Y
+Obj
.Rect
.Y
+YInc
,
166 Obj
^.Rect
.Width
, Obj
^.Rect
.Height
,
170 function g_Obj_CollideStep(Obj
: PObj
; XInc
, YInc
: Integer): Boolean;
172 Result
:= g_Map_CollidePanel(Obj
^.X
+Obj
^.Rect
.X
+XInc
, Obj
^.Y
+Obj
.Rect
.Y
+YInc
,
173 Obj
^.Rect
.Width
, Obj
^.Rect
.Height
,
177 function g_Obj_CollideWater(Obj
: PObj
; XInc
, YInc
: Integer): Boolean;
179 Result
:= g_Map_CollidePanel(Obj
^.X
+Obj
^.Rect
.X
+XInc
, Obj
^.Y
+Obj
.Rect
.Y
+YInc
,
180 Obj
^.Rect
.Width
, Obj
^.Rect
.Height
,
184 function g_Obj_CollideLiquid(Obj
: PObj
; XInc
, YInc
: Integer): Boolean;
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_WATER
or PANEL_ACID1
or PANEL_ACID2
, False);
191 function g_Obj_CollidePanel(Obj
: PObj
; XInc
, YInc
: Integer; PanelType
: Word): Boolean;
193 Result
:= g_Map_CollidePanel(Obj
^.X
+Obj
^.Rect
.X
+XInc
, Obj
^.Y
+Obj
.Rect
.Y
+YInc
,
194 Obj
^.Rect
.Width
, Obj
^.Rect
.Height
,
198 procedure g_Obj_Splash(Obj
: PObj
; Color
: Byte);
202 MaxVel
:= nmax(abs(Obj
^.Vel
.X
), abs(Obj
^.Vel
.Y
));
206 g_Sound_PlayExAt('SOUND_GAME_BULK1', Obj
^.X
, Obj
^.Y
)
208 g_Sound_PlayExAt('SOUND_GAME_BULK2', Obj
^.X
, Obj
^.Y
);
211 g_GFX_Water(Obj
^.X
+Obj
^.Rect
.X
+(Obj
^.Rect
.Width
div 2),
212 Obj
^.Y
+Obj
^.Rect
.Y
+(Obj
^.Rect
.Height
div 2),
213 Min(5*(abs(Obj
^.Vel
.X
)+abs(Obj
^.Vel
.Y
)), 50),
214 -Obj
^.Vel
.X
, -Obj
^.Vel
.Y
,
215 Obj
^.Rect
.Width
, 16, Color
);
219 function move (Obj
: PObj
; dx
, dy
: Integer; ClimbSlopes
: Boolean): Word;
225 procedure slope (s
: Integer);
230 while g_Obj_CollideLevel(Obj
, sx
, 0) and (i
< 4) do
238 Obj
.slopeUpLeft
+= i
*(-s
);
239 Obj
.slopeFramesLeft
:= SmoothSlopeFrames
;
243 function movex (): Boolean;
247 // Åñëè ìîíñòðó øàãíóòü â ñòîðîíó, à òàì áëîêìîí
248 if gMon
and ((st
and MOVE_BLOCK
) = 0) then
250 if Blocked(Obj
, sx
, 0) then st
:= st
or MOVE_BLOCK
;
253 // Åñëè øàãíóòü â ñòîðîíó, à òàì ñòåíà => øàãàòü íåëüçÿ
254 if g_Obj_CollideLevel(Obj
, sx
, 0) then
256 if ClimbSlopes
and (abs(dy
) < 2) then
259 if (not g_Obj_CollideLevel(Obj
, sx
, -12)) and // çàáèðàåìñÿ íà 12 ïèêñåëåé âëåâî/âïðàâî
260 g_Obj_CollidePanel(Obj
, 0, 1, PANEL_WALL
or PANEL_STEP
) then // òîëüêî åñëè åñòü çåìëÿ ïîä íîãàìè
267 st
:= st
or MOVE_HITWALL
;
272 st
:= st
or MOVE_HITWALL
;
275 else // Òàì ñòåíû íåò
277 if CollideLiquid(Obj
, sx
, 0) then
278 begin // Åñëè øàãíóòü â ñòîðîíó, à òàì òåïåðü æèäêîñòü
279 if ((st
and MOVE_INWATER
) = 0) then st
:= st
or MOVE_HITWATER
;
281 else // Åñëè øàãíóòü â ñòîðîíó, à òàì óæå íåò æèäêîñòè
283 if ((st
and MOVE_INWATER
) <> 0) then st
:= st
or MOVE_HITAIR
;
292 function movey (): Boolean;
296 // Åñëè ìîíñòðó øàãíóòü ïî âåðòèêàëè, à òàì áëîêìîí
297 if gMon
and ((st
and MOVE_BLOCK
) = 0) then
299 if Blocked(Obj
, 0, sy
) then st
:= st
or MOVE_BLOCK
;
302 // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì ñòåíà => øàãàòü íåëüçÿ
303 // Èëè åñëè øàãíóòü âíèç, à òàì ñòóïåíü => øàãàòü íåëüçÿ
304 if g_Obj_CollideLevel(Obj
, 0, sy
) or ((sy
> 0) and g_Obj_StayOnStep(Obj
)) then
307 st
:= st
or MOVE_HITLAND
309 st
:= st
or MOVE_HITCEIL
;
311 else // Òàì ñòåíû íåò. È ñòóïåíè ñíèçó òîæå íåò
313 if CollideLiquid(Obj
, 0, sy
) then
314 begin // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì òåïåðü æèäêîñòü
315 if ((st
and MOVE_INWATER
) = 0) then st
:= st
or MOVE_HITWATER
;
317 else // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì óæå íåò æèäêîñòè
319 if ((st
and MOVE_INWATER
) <> 0) then st
:= st
or MOVE_HITAIR
;
331 // Îáúåêò â æèäêîñòè?
332 if CollideLiquid(Obj
, 0, 0) then st
:= st
or MOVE_INWATER
;
334 // Ìîíñòð â áëîêìîíå?
337 if Blocked(Obj
, 0, 0) then st
:= st
or MOVE_BLOCK
;
340 // Äâèãàòüñÿ íå íàäî?
341 if (dx
= 0) and (dy
= 0) then begin result
:= st
; exit
; end;
343 sx
:= g_basic
.Sign(dx
);
344 sy
:= g_basic
.Sign(dy
);
348 for i
:= 1 to dx
do if not movex() then break
;
349 for i
:= 1 to dy
do if not movey() then break
;
355 procedure g_Obj_Init (Obj
: PObj
);
357 ZeroMemory(Obj
, SizeOf(TObj
));
361 function g_Obj_Move (Obj
: PObj
; Fallable
: Boolean; Splash
: Boolean; ClimbSlopes
: Boolean=False): Word;
363 xv
, yv
, dx
, dy
: Integer;
371 // Ëèìèòû íà ñêîðîñòü è óñêîðåíèå
372 Obj
^.Vel
.X
:= nclamp(Obj
^.Vel
.X
, -LIMIT_VEL
, LIMIT_VEL
);
373 Obj
^.Vel
.Y
:= nclamp(Obj
^.Vel
.Y
, -LIMIT_VEL
, LIMIT_VEL
);
374 Obj
^.Accel
.X
:= nclamp(Obj
^.Accel
.X
, -LIMIT_ACCEL
, LIMIT_ACCEL
);
375 Obj
^.Accel
.Y
:= nclamp(Obj
^.Accel
.Y
, -LIMIT_ACCEL
, LIMIT_ACCEL
);
377 if Obj^.Vel.X < -LIMIT_VEL then Obj^.Vel.X := -LIMIT_VEL
378 else if Obj^.Vel.X > LIMIT_VEL then Obj^.Vel.X := LIMIT_VEL;
379 if Obj^.Vel.Y < -LIMIT_VEL then Obj^.Vel.Y := -LIMIT_VEL
380 else if Obj^.Vel.Y > LIMIT_VEL then Obj^.Vel.Y := LIMIT_VEL;
381 if Obj^.Accel.X < -LIMIT_ACCEL then Obj^.Accel.X := -LIMIT_ACCEL
382 else if Obj^.Accel.X > LIMIT_ACCEL then Obj^.Accel.X := LIMIT_ACCEL;
383 if Obj^.Accel.Y < -LIMIT_ACCEL then Obj^.Accel.Y := -LIMIT_ACCEL
384 else if Obj^.Accel.Y > LIMIT_ACCEL then Obj^.Accel.Y := LIMIT_ACCEL;
387 // Âûëåòåë çà íèæíþþ ãðàíèöó êàðòû?
388 if (Obj
^.Y
> gMapInfo
.Height
+128) then begin result
:= MOVE_FALLOUT
; Obj
.slopeUpLeft
:= 0; Obj
.slopeFramesLeft
:= 0; exit
; end;
390 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
391 c
:= (gTime
mod (GAME_TICK
*2) <> 0);
394 if {not c and} (Obj
.slopeUpLeft
> 0) then
396 if (Obj
.slopeFramesLeft
< 1) then
398 //conwritefln('SLOPE DONE: slopeUpLeft=%s', [Obj.slopeUpLeft]);
399 Obj
.slopeUpLeft
:= 0; // oops
403 slopeStep
:= Obj
.slopeUpLeft
div Obj
.slopeFramesLeft
;
404 if (slopeStep
< 1) then slopeStep
:= 1;
405 //conwritefln('SLOPE STEP: slopeUpLeft=%s; slopeFramesLeft=%s; slopeStep=%d', [Obj.slopeUpLeft, Obj.slopeFramesLeft, slopeStep]);
406 Dec(Obj
.slopeFramesLeft
);
407 Obj
.slopeUpLeft
-= slopeStep
;
408 if (Obj
.slopeUpLeft
< 1) then
410 Obj
.slopeUpLeft
:= 0;
411 Obj
.slopeFramesLeft
:= 0;
416 if c
then goto _move
;
418 case CollideLift(Obj
, 0, 0) of
421 Obj
^.Vel
.Y
-= 1; // Ëèôò ââåðõ
422 if (Obj
^.Vel
.Y
< -5) then Obj
^.Vel
.Y
+= 1;
426 if (Obj
^.Vel
.Y
> 5) then Obj
^.Vel
.Y
-= 1;
427 Obj
^.Vel
.Y
+= 1; // Ãðàâèòàöèÿ èëè ëèôò âíèç
431 if Fallable
then Obj
^.Vel
.Y
+= 1; // Ãðàâèòàöèÿ
432 if (Obj
^.Vel
.Y
> MAX_YV
) then Obj
^.Vel
.Y
-= 1;
436 case CollideHorLift(Obj
, 0, 0) of
440 if (Obj
^.Vel
.X
< -9) then Obj
^.Vel
.X
+= 3;
445 if (Obj
^.Vel
.X
> 9) then Obj
^.Vel
.X
-= 3;
447 // 0 is not needed here
451 inwater
:= CollideLiquid(Obj
, 0, 0);
454 xv
:= abs(Obj
^.Vel
.X
)+1;
455 if (xv
> 5) then Obj
^.Vel
.X
:= z_dec(Obj
^.Vel
.X
, (xv
div 2)-2);
457 yv
:= abs(Obj
^.Vel
.Y
)+1;
458 if (yv
> 5) then Obj
^.Vel
.Y
:= z_dec(Obj
^.Vel
.Y
, (yv
div 2)-2);
460 xv
:= abs(Obj
^.Accel
.X
)+1;
461 if (xv
> 5) then Obj
^.Accel
.X
:= z_dec(Obj
^.Accel
.X
, (xv
div 2)-2);
463 yv
:= abs(Obj
^.Accel
.Y
)+1;
464 if (yv
> 5) then Obj
^.Accel
.Y
:= z_dec(Obj
^.Accel
.Y
, (yv
div 2)-2);
467 // Óìåíüøàåì ïðèáàâêó ê ñêîðîñòè
468 Obj
^.Accel
.X
:= z_dec(Obj
^.Accel
.X
, 1);
469 Obj
^.Accel
.Y
:= z_dec(Obj
^.Accel
.Y
, 1);
473 xv
:= Obj
^.Vel
.X
+Obj
^.Accel
.X
;
474 yv
:= Obj
^.Vel
.Y
+Obj
^.Accel
.Y
;
479 result
:= move(Obj
, dx
, dy
, ClimbSlopes
);
481 // Áðûçãè (åñëè íóæíû)
484 if WordBool(Result
and MOVE_HITWATER
) then
486 wtx
:= g_Map_CollideLiquid_Texture(Obj
^.X
+Obj
^.Rect
.X
, Obj
^.Y
+Obj
^.Rect
.Y
,
487 Obj
^.Rect
.Width
, Obj
^.Rect
.Height
*2 div 3);
489 LongWord(TEXTURE_SPECIAL_WATER
): g_Obj_Splash(Obj
, 3);
490 LongWord(TEXTURE_SPECIAL_ACID1
): g_Obj_Splash(Obj
, 2);
491 LongWord(TEXTURE_SPECIAL_ACID2
): g_Obj_Splash(Obj
, 1);
492 LongWord(TEXTURE_NONE
): begin end;
493 else g_Obj_Splash(Obj
, 0);
498 // Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
501 // Âðåçàëèñü â ñòåíó - ñòîï
502 if ((Result
and MOVE_HITWALL
) <> 0) then
508 // Âðåçàëèñü â ïîë èëè ïîòîëîê - ñòîï
509 if ((Result
and (MOVE_HITCEIL
or MOVE_HITLAND
)) <> 0) then
517 function g_Obj_Collide(Obj1
, Obj2
: PObj
): Boolean;
519 Result
:= g_Collide(Obj1
^.X
+Obj1
^.Rect
.X
, Obj1
^.Y
+Obj1
^.Rect
.Y
,
520 Obj1
^.Rect
.Width
, Obj1
^.Rect
.Height
,
521 Obj2
^.X
+Obj2
^.Rect
.X
, Obj2
^.Y
+Obj2
^.Rect
.Y
,
522 Obj2
^.Rect
.Width
, Obj2
^.Rect
.Height
);
525 function g_Obj_Collide(X
, Y
: Integer; Width
, Height
: Word; Obj
: PObj
): Boolean;
527 Result
:= g_Collide(X
, Y
,
529 Obj
^.X
+Obj
^.Rect
.X
, Obj
^.Y
+Obj
^.Rect
.Y
,
530 Obj
^.Rect
.Width
, Obj
^.Rect
.Height
);
533 function g_Obj_CollidePoint(X
, Y
: Integer; Obj
: PObj
): Boolean;
535 Result
:= g_CollidePoint(X
, Y
, Obj
^.X
+Obj
^.Rect
.X
, Obj
^.Y
+Obj
^.Rect
.Y
,
536 Obj
^.Rect
.Width
, Obj
^.Rect
.Height
);
539 procedure g_Obj_Push(Obj
: PObj
; VelX
, VelY
: Integer);
541 Obj
^.Vel
.X
:= Obj
^.Vel
.X
+ VelX
;
542 Obj
^.Vel
.Y
:= Obj
^.Vel
.Y
+ VelY
;
545 procedure g_Obj_PushA(Obj
: PObj
; Vel
: Integer; Angle
: SmallInt);
550 SinCos(DegToRad(-Angle
), s
, c
);
552 Obj
^.Vel
.X
:= Obj
^.Vel
.X
+ Round(Vel
*c
);
553 Obj
^.Vel
.Y
:= Obj
^.Vel
.Y
+ Round(Vel
*s
);
556 procedure g_Obj_SetSpeed(Obj
: PObj
; s
: Integer);
563 m
:= Max(abs(vx
), abs(vy
));
567 Obj
^.Vel
.X
:= (vx
*s
) div m
;
568 Obj
^.Vel
.Y
:= (vy
*s
) div m
;
572 // Ïðèáëèæàåì a ê 0 íà b åäèíèö:
573 function z_dec (a
, b
: Integer): Integer; inline;
575 if (abs(a
) < b
) then result
:= 0
576 else if (a
> 0) then result
:= a
-b
577 else if (a
< 0) then result
:= a
+b
578 else result
:= 0; // a = 0
582 // Ïðèáëèæàåì a ê 0.0 íà b åäèíèö:
583 function z_fdec (a
, b
: Double): Double; inline;
585 if (abs(a
) < b
) then result
:= 0.0
586 else if (a
> 0.0) then result
:= a
-b
587 else if (a
< 0.0) then result
:= a
+b
588 else result
:= 0.0; // a = 0.0