diff --git a/src/game/g_phys.pas b/src/game/g_phys.pas
index a88ca21c9da246a2f61e2f76ff17160cc032681e..af569ea1e1784185f70f13aa5ae47f0ef955fa1d 100644 (file)
--- a/src/game/g_phys.pas
+++ b/src/game/g_phys.pas
-(* Copyright (C) DooM 2D:Forever Developers
+(* Copyright (C) Doom 2D: Forever Developers
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
+ * the Free Software Foundation, version 3 of the License ONLY.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
// this is purely visual change, it won't affect anything else
slopeUpLeft: Integer; // left to go
slopeFramesLeft: Integer; // frames left to go
+ // for frame interpolation
+ oldX, oldY: Integer;
+ procedure lerp(t: Single; out fX, fY: Integer);
end;
const
MOVE_HITAIR = 64;
MOVE_BLOCK = 128;
-procedure g_Obj_Init(Obj: PObj);
-function g_Obj_Move(Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean = False): Word;
-function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; overload;
-function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; overload;
-function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean;
-function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean;
-function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean;
-function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean;
-function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean;
-function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean;
-function g_Obj_StayOnStep(Obj: PObj): Boolean;
-procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer);
-procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt);
-procedure g_Obj_SetSpeed(Obj: PObj; s: Integer);
+procedure g_Obj_Init(Obj: PObj); inline;
+function g_Obj_Move(Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False; asProjectile: Boolean=false): Word;
+function g_Obj_Move_Projectile (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False): Word;
+function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; inline; overload;
+function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; inline; overload;
+function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean; inline;
+function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
+function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
+function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
+function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
+function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean; inline;
+function g_Obj_StayOnStep(Obj: PObj): Boolean; inline;
+function g_Obj_CanMoveY(Obj: PObj; YInc: Integer): Boolean; inline;
+procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer); inline;
+procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt); inline;
+procedure g_Obj_SetSpeed(Obj: PObj; s: Integer); inline;
+function g_Obj_GetSpeedDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline; // `false`: zero speed
+function g_Obj_GetAccelDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline; // `false`: zero speed
function z_dec(a, b: Integer): Integer; inline;
function z_fdec(a, b: Double): Double; inline;
uses
g_map, g_basic, Math, g_player, g_console, SysUtils,
- g_sound, g_gfx, MAPDEF, g_monsters, g_game, BinEditor, utils;
+ g_sound, g_gfx, MAPDEF, g_monsters, g_game, utils;
const
SmoothSlopeFrames = 4;
+procedure TObj.lerp(t: Single; out fX, fY: Integer);
+begin
+ fX := nlerp(oldX, X, t);
+ fY := nlerp(oldY, Y, t);
+end;
-function g_Obj_StayOnStep(Obj: PObj): Boolean;
+function g_Obj_StayOnStep(Obj: PObj): Boolean; inline;
begin
Result := not g_Map_CollidePanel(Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y+Obj^.Rect.Height-1,
Obj^.Rect.Width, 1,
PANEL_STEP, False);
end;
-function CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean;
+function g_Obj_CanMoveY(Obj: PObj; YInc: Integer): Boolean; inline;
+begin
+ // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì ñòåíà => øàãàòü íåëüçÿ
+ // Èëè åñëè øàãíóòü âíèç, à òàì ñòóïåíü => øàãàòü íåëüçÿ
+ Result := not(g_Obj_CollideLevel(Obj, 0, YInc) or ((YInc > 0) and g_Obj_StayOnStep(Obj)));
+end;
+
+function CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
begin
Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
Obj^.Rect.Width, Obj^.Rect.Height*2 div 3,
PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
end;
-function CollideLift(Obj: PObj; XInc, YInc: Integer): Integer;
+function CollideLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
begin
if g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj^.Rect.Y+YInc,
Obj^.Rect.Width, Obj^.Rect.Height,
Result := 0;
end;
-function CollideHorLift(Obj: PObj; XInc, YInc: Integer): Integer;
+function CollideHorLift(Obj: PObj; XInc, YInc: Integer): Integer; inline;
var
left, right: Boolean;
begin
for plr in gPlayers do
begin
if (plr = nil) then continue;
- if not plr.Live then continue;
+ if not plr.alive then continue;
with plr do
begin
if g_Collide(GameX+PLAYER_RECT.X, GameY+PLAYER_RECT.Y,
end;
end;
-function Blocked(Obj: PObj; XInc, YInc: Integer): Boolean;
+function Blocked(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
begin
Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
Obj^.Rect.Width, Obj^.Rect.Height,
PANEL_BLOCKMON, False);
end;
-function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean;
+function g_Obj_CollideLevel(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
begin
Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
Obj^.Rect.Width, Obj^.Rect.Height,
PANEL_WALL, False);
end;
-function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean;
+function g_Obj_CollideStep(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
begin
Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
Obj^.Rect.Width, Obj^.Rect.Height,
PANEL_STEP, False);
end;
-function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean;
+function g_Obj_CollideWater(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
begin
Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
Obj^.Rect.Width, Obj^.Rect.Height,
PANEL_WATER, False);
end;
-function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean;
+function g_Obj_CollideLiquid(Obj: PObj; XInc, YInc: Integer): Boolean; inline;
begin
Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
Obj^.Rect.Width, Obj^.Rect.Height,
PANEL_WATER or PANEL_ACID1 or PANEL_ACID2, False);
end;
-function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean;
+function g_Obj_CollidePanel(Obj: PObj; XInc, YInc: Integer; PanelType: Word): Boolean; inline;
begin
Result := g_Map_CollidePanel(Obj^.X+Obj^.Rect.X+XInc, Obj^.Y+Obj.Rect.Y+YInc,
Obj^.Rect.Width, Obj^.Rect.Height,
begin
result := true;
if (not g_Obj_CollideLevel(Obj, sx, -12)) and // çàáèðàåìñÿ íà 12 ïèêñåëåé âëåâî/âïðàâî
- g_Obj_CollidePanel(Obj, 0, 1, PANEL_WALL or PANEL_STEP) then // òîëüêî åñëè åñòü çåìëÿ ïîä íîãàìè
+ (sy >= 0) and (not g_Obj_CanMoveY(Obj, sy)) then // òîëüêî åñëè åñòü çåìëÿ ïîä íîãàìè
begin
slope(-1);
end
if Blocked(Obj, 0, sy) then st := st or MOVE_BLOCK;
end;
- // Åñëè øàãíóòü â ïî âåðòèêàëè, à òàì ñòåíà => øàãàòü íåëüçÿ
- // Èëè åñëè øàãíóòü âíèç, à òàì ñòóïåíü => øàãàòü íåëüçÿ
- if g_Obj_CollideLevel(Obj, 0, sy) or ((sy > 0) and g_Obj_StayOnStep(Obj)) then
+ // Åñëè øàãàòü íåëüçÿ
+ if not g_Obj_CanMoveY(Obj, sy) then
begin
if sy > 0 then
st := st or MOVE_HITLAND
end;
-procedure g_Obj_Init (Obj: PObj);
+procedure g_Obj_Init (Obj: PObj); inline;
begin
ZeroMemory(Obj, SizeOf(TObj));
end;
-function g_Obj_Move (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False): Word;
+function g_Obj_Move_Projectile (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False): Word;
+begin
+ result := g_Obj_Move(Obj, Fallable, Splash, ClimbSlopes, true);
+end;
+
+function g_Obj_Move (Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean=False; asProjectile: Boolean=false): Word;
var
xv, yv, dx, dy: Integer;
inwater: Boolean;
c: Boolean;
wtx: DWORD;
slopeStep: Integer;
+ dirx, diry, speed: Double;
label
_move;
begin
}
// Âûëåòåë çà íèæíþþ ãðàíèöó êàðòû?
- if (Obj^.Y > gMapInfo.Height+128) then begin result := MOVE_FALLOUT; Obj.slopeUpLeft := 0; Obj.slopeFramesLeft := 0; exit; end;
+ if (Obj^.Y > Integer(gMapInfo.Height)+128) then begin result := MOVE_FALLOUT; Obj.slopeUpLeft := 0; Obj.slopeFramesLeft := 0; exit; end;
// Ìåíÿåì ñêîðîñòü è óñêîðåíèå òîëüêî ïî ÷åòíûì êàäðàì
c := (gTime mod (GAME_TICK*2) <> 0);
inwater := CollideLiquid(Obj, 0, 0);
if inwater then
begin
- xv := abs(Obj^.Vel.X)+1;
- if (xv > 5) then Obj^.Vel.X := z_dec(Obj^.Vel.X, (xv div 2)-2);
-
- yv := abs(Obj^.Vel.Y)+1;
- if (yv > 5) then Obj^.Vel.Y := z_dec(Obj^.Vel.Y, (yv div 2)-2);
-
- xv := abs(Obj^.Accel.X)+1;
- if (xv > 5) then Obj^.Accel.X := z_dec(Obj^.Accel.X, (xv div 2)-2);
+ if asProjectile then
+ begin
+ //writeln('velocity=(', Obj^.Vel.X, ',', Obj^.Vel.Y, '); acceleration=(', Obj^.Accel.X, ',', Obj^.Accel.Y, ')');
+ if (g_Obj_GetSpeedDirF(Obj, dirx, diry, speed)) then
+ begin
+ //writeln('SPEED: ', speed);
+ if (speed > 5) then
+ begin
+ speed := speed/1.4;
+ Obj^.Vel.X := round(dirx*speed);
+ Obj^.Vel.Y := round(diry*speed);
+ end;
+ end;
- yv := abs(Obj^.Accel.Y)+1;
- if (yv > 5) then Obj^.Accel.Y := z_dec(Obj^.Accel.Y, (yv div 2)-2);
+ // acceleration
+ if (g_Obj_GetAccelDirF(Obj, dirx, diry, speed)) then
+ begin
+ if (speed > 5) then
+ begin
+ speed := speed/1.4;
+ Obj^.Accel.X := round(dirx*speed);
+ Obj^.Accel.Y := round(diry*speed);
+ end;
+ end;
+ end
+ else
+ begin
+ // velocity
+ xv := abs(Obj^.Vel.X)+1;
+ if (xv > 5) then Obj^.Vel.X := z_dec(Obj^.Vel.X, (xv div 2)-2);
+ yv := abs(Obj^.Vel.Y)+1;
+ if (yv > 5) then Obj^.Vel.Y := z_dec(Obj^.Vel.Y, (yv div 2)-2);
+
+ // acceleration
+ xv := abs(Obj^.Accel.X)+1;
+ if (xv > 5) then Obj^.Accel.X := z_dec(Obj^.Accel.X, (xv div 2)-2);
+ yv := abs(Obj^.Accel.Y)+1;
+ if (yv > 5) then Obj^.Accel.Y := z_dec(Obj^.Accel.Y, (yv div 2)-2);
+ end;
end;
// Óìåíüøàåì ïðèáàâêó ê ñêîðîñòè
end;
-function g_Obj_Collide(Obj1, Obj2: PObj): Boolean;
+function g_Obj_Collide(Obj1, Obj2: PObj): Boolean; inline;
begin
Result := g_Collide(Obj1^.X+Obj1^.Rect.X, Obj1^.Y+Obj1^.Rect.Y,
Obj1^.Rect.Width, Obj1^.Rect.Height,
Obj2^.Rect.Width, Obj2^.Rect.Height);
end;
-function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean;
+function g_Obj_Collide(X, Y: Integer; Width, Height: Word; Obj: PObj): Boolean; inline;
begin
Result := g_Collide(X, Y,
Width, Height,
Obj^.Rect.Width, Obj^.Rect.Height);
end;
-function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean;
+function g_Obj_CollidePoint(X, Y: Integer; Obj: PObj): Boolean; inline;
begin
Result := g_CollidePoint(X, Y, Obj^.X+Obj^.Rect.X, Obj^.Y+Obj^.Rect.Y,
Obj^.Rect.Width, Obj^.Rect.Height);
end;
-procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer);
+procedure g_Obj_Push(Obj: PObj; VelX, VelY: Integer); inline;
begin
Obj^.Vel.X := Obj^.Vel.X + VelX;
Obj^.Vel.Y := Obj^.Vel.Y + VelY;
end;
-procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt);
+procedure g_Obj_PushA(Obj: PObj; Vel: Integer; Angle: SmallInt); inline;
var
s, c: Extended;
Obj^.Vel.Y := Obj^.Vel.Y + Round(Vel*s);
end;
-procedure g_Obj_SetSpeed(Obj: PObj; s: Integer);
+procedure g_Obj_SetSpeed(Obj: PObj; s: Integer); inline;
var
m, vx, vy: Integer;
begin
Obj^.Vel.Y := (vy*s) div m;
end;
+// `false`: zero speed
+function g_Obj_GetSpeedDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline;
+var
+ len, vx, vy: Double;
+begin
+ if (Obj^.Vel.X = 0) and (Obj^.Vel.Y = 0) then
+ begin
+ dirx := 0;
+ diry := 0;
+ speed := 0;
+ result := false;
+ exit;
+ end;
+
+ vx := Obj^.Vel.X;
+ vy := Obj^.Vel.Y;
+ len := sqrt(vx*vx+vy*vy);
+ dirx := vx/len;
+ diry := vy/len;
+ speed := len;
+ result := true;
+end;
+
+// `false`: zero acceleratin
+function g_Obj_GetAccelDirF(Obj: PObj; var dirx, diry, speed: Double): Boolean; inline;
+var
+ len, vx, vy: Double;
+begin
+ if (Obj^.Accel.X = 0) and (Obj^.Accel.Y = 0) then
+ begin
+ dirx := 0;
+ diry := 0;
+ speed := 0;
+ result := false;
+ exit;
+ end;
+
+ vx := Obj^.Accel.X;
+ vy := Obj^.Accel.Y;
+ len := sqrt(vx*vx+vy*vy);
+ dirx := vx/len;
+ diry := vy/len;
+ speed := len;
+ result := true;
+end;
+
// Ïðèáëèæàåì a ê 0 íà b åäèíèö:
function z_dec (a, b: Integer): Integer; inline;