DEADSOFTWARE

game: disable gfx for server
[d2df-sdl.git] / src / game / g_phys.pas
index e92e49b1a6a9ba171d187a2acc2aa4e167df4fe8..3ff102b6e00b3220b8ce658b4cc7666726468820 100644 (file)
@@ -1,9 +1,8 @@
-(* 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
@@ -18,8 +17,7 @@ unit g_phys;
 
 interface
 
-uses
-  e_graphics;
+  uses g_base;
 
 type
   PObj = ^TObj;
@@ -32,6 +30,9 @@ type
     // 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
@@ -50,7 +51,8 @@ const
   MOVE_BLOCK      = 128;
 
 procedure g_Obj_Init(Obj: PObj); inline;
-function  g_Obj_Move(Obj: PObj; Fallable: Boolean; Splash: Boolean; ClimbSlopes: Boolean = False): Word;
+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;
@@ -60,9 +62,12 @@ 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;
 
@@ -71,14 +76,23 @@ var
 
 implementation
 
-uses
-  g_map, g_basic, Math, g_player, g_console, SysUtils,
-  g_sound, g_gfx, MAPDEF, g_monsters, g_game, utils;
+  uses
+    {$IFDEF ENABLE_GFX}
+      g_gfx,
+    {$ENDIF}
+    g_map, g_basic, Math, g_player, g_console, SysUtils,
+    g_sound, 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; inline;
 begin
@@ -90,6 +104,13 @@ begin
                                    PANEL_STEP, False);
 end;
 
+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,
@@ -208,11 +229,13 @@ begin
       g_Sound_PlayExAt('SOUND_GAME_BULK2', Obj^.X, Obj^.Y);
   end;
 
-  g_GFX_Water(Obj^.X+Obj^.Rect.X+(Obj^.Rect.Width div 2),
-              Obj^.Y+Obj^.Rect.Y+(Obj^.Rect.Height div 2),
-              Min(5*(abs(Obj^.Vel.X)+abs(Obj^.Vel.Y)), 50),
-              -Obj^.Vel.X, -Obj^.Vel.Y,
-              Obj^.Rect.Width, 16, Color);
+  {$IFDEF ENABLE_GFX}
+    g_GFX_Water(Obj^.X+Obj^.Rect.X+(Obj^.Rect.Width div 2),
+                Obj^.Y+Obj^.Rect.Y+(Obj^.Rect.Height div 2),
+                Min(5*(abs(Obj^.Vel.X)+abs(Obj^.Vel.Y)), 50),
+                -Obj^.Vel.X, -Obj^.Vel.Y,
+                Obj^.Rect.Width, 16, Color);
+  {$ENDIF}
 end;
 
 
@@ -257,7 +280,7 @@ var
       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
@@ -299,9 +322,8 @@ var
       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
@@ -358,13 +380,19 @@ begin
 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
@@ -385,7 +413,7 @@ 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);
@@ -451,17 +479,45 @@ begin
   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;
 
   // Óìåíüøàåì ïðèáàâêó ê ñêîðîñòè
@@ -568,6 +624,52 @@ 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;