DEADSOFTWARE

game: disable corpses for server
authorDeaDDooMER <deaddoomer@deadsoftware.ru>
Sat, 29 Jan 2022 14:56:47 +0000 (17:56 +0300)
committerDeaDDooMER <deaddoomer@deadsoftware.ru>
Sat, 29 Jan 2022 14:56:47 +0000 (17:56 +0300)
17 files changed:
src/game/Doom2DF.lpr
src/game/g_console.pas
src/game/g_corpses.pas [new file with mode: 0644]
src/game/g_game.pas
src/game/g_gibs.pas
src/game/g_menu.pas
src/game/g_monsters.pas
src/game/g_netmsg.pas
src/game/g_options.pas
src/game/g_panel.pas
src/game/g_player.pas
src/game/g_saveload.pas
src/game/g_shells.pas
src/game/g_weapons.pas
src/game/opengl/r_game.pas
src/game/opengl/r_player.pas
src/shared/a_modes.inc

index ec2bdfae15a8e46ee34278d1ae5bf357d767c914..28e3b6228ad0c23374b538de64b5130d14508934 100644 (file)
@@ -132,6 +132,9 @@ uses
   {$IFDEF ENABLE_SHELLS}
     g_shells in 'g_shells.pas',
   {$ENDIF}
+  {$IFDEF ENABLE_CORPSES}
+    g_corpses in 'g_corpses.pas',
+  {$ENDIF}
   g_items in 'g_items.pas',
   g_map in 'g_map.pas',
   g_monsters in 'g_monsters.pas',
index 99335bab48ac31344207cbba3491b32521c31b24..687a412661b7b6fa3ea0b4eeb9b684c26981f734 100644 (file)
@@ -115,6 +115,9 @@ uses
   {$IFDEF ENABLE_SHELLS}
     g_shells,
   {$ENDIF}
+  {$IFDEF ENABLE_CORPSES}
+    g_corpses,
+  {$ENDIF}
   g_textures, e_input, g_game, g_player, g_items,
   SysUtils, g_basic, g_options, Math, e_res,
   g_language, g_net, g_netmsg, e_log, conbuf;
@@ -2015,7 +2018,9 @@ begin
   {$IFDEF ENABLE_GIBS}
     WriteLn(f, 'g_max_gibs ', g_Gibs_GetMax());
   {$ENDIF}
-  WriteLn(f, 'g_max_corpses ', g_Corpses_GetMax());
+  {$IFDEF ENABLE_CORPSES}
+    WriteLn(f, 'g_max_corpses ', g_Corpses_GetMax());
+  {$ENDIF}
   WriteLn(f, 'sv_intertime ', gDefInterTime);
 
   // gameplay settings
diff --git a/src/game/g_corpses.pas b/src/game/g_corpses.pas
new file mode 100644 (file)
index 0000000..d4c6c50
--- /dev/null
@@ -0,0 +1,396 @@
+(* 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, 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
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *)
+{$INCLUDE ../shared/a_modes.inc}
+unit g_corpses;
+
+interface
+
+  uses Classes, g_phys, g_player, g_playermodel, g_base;
+
+  const
+    CORPSE_STATE_REMOVEME = 0;
+    CORPSE_STATE_NORMAL   = 1;
+    CORPSE_STATE_MESS     = 2;
+
+    PLAYER_CORPSERECT: TRectWH = (X:15; Y:48; Width:34; Height:16);
+
+    DefaultCorpsesMax = 20;
+
+  type
+    TCorpse = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
+    private
+      FMess:          Boolean;
+      FState:         Byte;
+      FDamage:        Byte;
+      FObj:           TObj;
+      FPlayerUID:     Word;
+      FModel:   TPlayerModel;
+
+    public
+      constructor Create(X, Y: Integer; ModelName: String; aMess: Boolean);
+      destructor  Destroy(); override;
+      procedure   Damage(Value: Word; SpawnerUID: Word; vx, vy: Integer);
+      procedure   Update();
+      procedure   SaveState (st: TStream);
+      procedure   LoadState (st: TStream);
+
+      procedure getMapBox (out x, y, w, h: Integer); inline;
+      procedure moveBy (dx, dy: Integer); inline;
+
+      procedure positionChanged ();  inline; //WARNING! call this after entity position was changed, or coldet will not work right!
+
+      function ObjPtr (): PObj; inline;
+
+      property Obj: TObj read FObj; // copies object
+      property State: Byte read FState;
+      property Mess: Boolean read FMess;
+      property Model: TPlayerModel read FModel;
+      property PlayerUID: Word read FPlayerUID;
+    end;
+
+  var
+    gCorpses: Array of TCorpse;
+
+  procedure g_Corpses_SetMax (Count: Word);
+  function g_Corpses_GetMax (): Word;
+
+  function g_Corpses_Create (Player: TPlayer): Integer;
+  procedure g_Corpses_RemoveAll;
+  procedure g_Corpses_Update;
+
+  {$IFNDEF HEADLESS}
+    function g_Corpses_GetCameraObj (Player: TPlayer): TObj;
+  {$ENDIF}
+
+implementation
+
+  uses
+    {$IFDEF ENABLE_GFX}
+      g_gfx,
+    {$ENDIF}
+    {$IFDEF ENABLE_GIBS}
+      g_gibs,
+    {$ENDIF}
+    Math,
+    utils, g_saveload, xstreams,
+    g_game, g_textures, g_map
+  ;
+
+  var
+    MaxCorpses: Word = DefaultCorpsesMax;
+
+  constructor TCorpse.Create (X, Y: Integer; ModelName: String; aMess: Boolean);
+  begin
+    g_Obj_Init(@FObj);
+    FObj.X := X;
+    FObj.Y := Y;
+    FObj.Rect := PLAYER_CORPSERECT;
+    FMess := aMess;
+    FModel := g_PlayerModel_Get(ModelName);
+    if FMess then
+    begin
+      FState := CORPSE_STATE_MESS;
+      FModel.ChangeAnimation(A_DIE2);
+    end
+    else
+    begin
+      FState := CORPSE_STATE_NORMAL;
+      FModel.ChangeAnimation(A_DIE1);
+    end;
+  end;
+
+  destructor TCorpse.Destroy;
+  begin
+    FModel.Free;
+    inherited;
+  end;
+
+  function TCorpse.ObjPtr (): PObj; inline;
+  begin
+    Result := @FObj;
+  end;
+
+  procedure TCorpse.positionChanged; inline;
+  begin
+  end;
+
+  procedure TCorpse.moveBy (dx, dy: Integer); inline;
+  begin
+    if (dx <> 0) or (dy <> 0) then
+    begin
+      FObj.X += dx;
+      FObj.Y += dy;
+      positionChanged;
+    end;
+  end;
+
+  procedure TCorpse.getMapBox (out x, y, w, h: Integer); inline;
+  begin
+    x := FObj.X+PLAYER_CORPSERECT.X;
+    y := FObj.Y+PLAYER_CORPSERECT.Y;
+    w := PLAYER_CORPSERECT.Width;
+    h := PLAYER_CORPSERECT.Height;
+  end;
+
+  procedure TCorpse.Damage (Value: Word; SpawnerUID: Word; vx, vy: Integer);
+    {$IFDEF ENABLE_GFX}
+      var Blood: TModelBlood;
+    {$ENDIF}
+  begin
+    if FState = CORPSE_STATE_REMOVEME then
+      Exit;
+    FDamage := FDamage + Value;
+
+{$IFDEF ENABLE_GIBS}
+    if FDamage > 150 then
+    begin
+      if FModel <> nil then
+      begin
+        FState := CORPSE_STATE_REMOVEME;
+        g_Gibs_Create(
+          FObj.X + FObj.Rect.X + (FObj.Rect.Width div 2),
+          FObj.Y + FObj.Rect.Y + (FObj.Rect.Height div 2),
+          FModel.id,
+          FModel.Color
+        );
+        // Звук мяса от трупа:
+        FModel.PlaySound(MODELSOUND_DIE, 5, FObj.X, FObj.Y);
+        // Зловещий смех:
+        if (gBodyKillEvent <> -1) and gDelayedEvents[gBodyKillEvent].Pending then
+          gDelayedEvents[gBodyKillEvent].Pending := False;
+        gBodyKillEvent := g_Game_DelayEvent(DE_BODYKILL, 1050, SpawnerUID);
+        FModel.Free;
+        FModel := nil;
+      end
+    end
+    else
+{$ENDIF}
+    begin
+      FObj.Vel.X := FObj.Vel.X + vx;
+      FObj.Vel.Y := FObj.Vel.Y + vy;
+      {$IFDEF ENABLE_GFX}
+        Blood := FModel.GetBlood();
+        g_GFX_Blood(FObj.X+PLAYER_CORPSERECT.X+(PLAYER_CORPSERECT.Width div 2),
+                    FObj.Y+PLAYER_CORPSERECT.Y+(PLAYER_CORPSERECT.Height div 2),
+                    Value, vx, vy, 16, (PLAYER_CORPSERECT.Height*2) div 3,
+                    Blood.R, Blood.G, Blood.B, Blood.Kind);
+      {$ENDIF}
+    end;
+  end;
+
+  procedure TCorpse.Update;
+    var st: Word;
+  begin
+    if FState = CORPSE_STATE_REMOVEME then
+      Exit;
+
+    FObj.oldX := FObj.X;
+    FObj.oldY := FObj.Y;
+    if gTime mod (GAME_TICK*2) <> 0 then
+    begin
+      g_Obj_Move(@FObj, True, True, True);
+      positionChanged(); // this updates spatial accelerators
+      Exit;
+    end;
+
+    // Сопротивление воздуха для трупа:
+    FObj.Vel.X := z_dec(FObj.Vel.X, 1);
+
+    st := g_Obj_Move(@FObj, True, True, True);
+    positionChanged; // this updates spatial accelerators
+
+    if WordBool(st and MOVE_FALLOUT) then
+      FState := CORPSE_STATE_REMOVEME
+    else if FModel <> nil then
+      FModel.Update;
+  end;
+
+  procedure TCorpse.SaveState (st: TStream);
+    var anim: Boolean;
+  begin
+    assert(st <> nil);
+
+    // Сигнатура трупа
+    utils.writeSign(st, 'CORP');
+    utils.writeInt(st, Byte(0));
+    // Состояние
+    utils.writeInt(st, Byte(FState));
+    // Накопленный урон
+    utils.writeInt(st, Byte(FDamage));
+    // Цвет
+    utils.writeInt(st, Byte(FModel.Color.R));
+    utils.writeInt(st, Byte(FModel.Color.G));
+    utils.writeInt(st, Byte(FModel.Color.B));
+    // Объект трупа
+    Obj_SaveState(st, @FObj);
+    utils.writeInt(st, Word(FPlayerUID));
+    // animation
+    anim := (FModel <> nil);
+    utils.writeBool(st, anim);
+    if anim then FModel.AnimState.SaveState(st, 0, False);
+    // animation for mask (same as animation, compat with older saves)
+    anim := (FModel <> nil);
+    utils.writeBool(st, anim);
+    if anim then FModel.AnimState.SaveState(st, 0, False);
+  end;
+
+  procedure TCorpse.LoadState (st: TStream);
+    var anim, blending: Boolean; r, g, b, alpha: Byte; stub: TAnimationState;
+  begin
+    assert(st <> nil);
+
+    // Сигнатура трупа
+    if not utils.checkSign(st, 'CORP') then raise XStreamError.Create('invalid corpse signature');
+    if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid corpse version');
+    // Состояние
+    FState := utils.readByte(st);
+    // Накопленный урон
+    FDamage := utils.readByte(st);
+    // Цвет
+    r := utils.readByte(st);
+    g := utils.readByte(st);
+    b := utils.readByte(st);
+    FModel.SetColor(r, g, b);
+    // Объект трупа
+    Obj_LoadState(@FObj, st);
+    FPlayerUID := utils.readWord(st);
+    // animation
+    stub := TAnimationState.Create(False, 0, 0);
+    anim := utils.readBool(st);
+    if anim then
+    begin
+      stub.LoadState(st, alpha, blending);
+      FModel.AnimState.CurrentFrame := Min(stub.CurrentFrame, FModel.AnimState.Length);
+    end
+    else
+    begin
+      FModel.Free;
+      FModel := nil
+    end;
+    // animation for mask (same as animation, compat with older saves)
+    anim := utils.readBool(st);
+    if anim then stub.LoadState(st, alpha, blending);
+    stub.Free;
+  end;
+
+  procedure g_Corpses_SetMax (Count: Word);
+  begin
+    MaxCorpses := Count;
+    SetLength(gCorpses, Count);
+  end;
+
+  function g_Corpses_GetMax (): Word;
+  begin
+    Result := MaxCorpses;
+  end;
+
+  function  g_Corpses_Create (Player: TPlayer): Integer;
+    var i: Integer; find_id: DWORD; ok: Boolean;
+  begin
+    Result := -1;
+    if Player.alive then
+      Exit;
+    // Разрываем связь с прежним трупом:
+    i := Player.FCorpse;
+    if (i >= 0) and (i < Length(gCorpses)) then
+    begin
+      if (gCorpses[i] <> nil) and (gCorpses[i].FPlayerUID = Player.UID) then
+        gCorpses[i].FPlayerUID := 0;
+    end;
+
+    if Player.Obj.Y >= gMapInfo.Height+128 then
+      Exit;
+
+{$IFDEF ENABLE_GIBS}
+    if (Player.Health < -50) and (gGibsCount > 0) then
+    begin
+      g_Gibs_Create(Player.Obj.X + PLAYER_RECT_CX, Player.Obj.Y + PLAYER_RECT_CY, Player.Model.id, Player.Model.Color);
+    end
+    else
+{$ENDIF}
+    begin
+      if (gCorpses = nil) or (Length(gCorpses) = 0) then
+        Exit;
+      ok := False;
+      for find_id := 0 to High(gCorpses) do
+        if gCorpses[find_id] = nil then
+        begin
+          ok := True;
+          Break;
+        end;
+      if not ok then
+        find_id := Random(Length(gCorpses));
+      gCorpses[find_id] := TCorpse.Create(Player.Obj.X, Player.Obj.Y, Player.Model.GetName(), Player.Health < -20);
+      gCorpses[find_id].FModel.Color := Player.Model.Color;
+      gCorpses[find_id].FObj.Vel := Player.Obj.Vel;
+      gCorpses[find_id].FObj.Accel := Player.Obj.Accel;
+      gCorpses[find_id].FPlayerUID := Player.UID;
+      Result := find_id;
+    end
+  end;
+
+  procedure g_Corpses_Update;
+    var i: Integer;
+  begin
+    if gCorpses <> nil then
+    begin
+      for i := 0 to High(gCorpses) do
+      begin
+        if gCorpses[i] <> nil then
+        begin
+          if gCorpses[i].State = CORPSE_STATE_REMOVEME then
+          begin
+            gCorpses[i].Free();
+            gCorpses[i] := nil;
+          end
+          else
+            gCorpses[i].Update();
+        end;
+      end;
+    end;
+  end;
+
+  procedure g_Corpses_RemoveAll;
+    var i: Integer;
+  begin
+    if gCorpses <> nil then
+      for i := 0 to High(gCorpses) do
+        gCorpses[i].Free();
+    gCorpses := nil;
+    SetLength(gCorpses, MaxCorpses);
+  end;
+
+{$IFNDEF HEADLESS}
+  function g_Corpses_GetCameraObj (Player: TPlayer): TObj;
+  begin
+    {$IFDEF ENABLE_CORPSES}
+      if (not Player.Alive) and (not Player.Spectator) and
+         (Player.Corpse >= 0) and (Player.Corpse < Length(gCorpses)) and
+         (gCorpses[Player.Corpse] <> nil) and (gCorpses[Player.Corpse].PlayerUID = Player.UID) then
+      begin
+        gCorpses[Player.Corpse].FObj.slopeUpLeft := Player.Obj.slopeUpLeft;
+        Result := gCorpses[Player.Corpse].Obj;
+      end
+      else
+      begin
+        Result := Player.Obj;
+      end;
+    {$ELSE}
+      Result := Player.Obj;
+    {$ENDIF}
+  end;
+{$ENDIF}
+
+end.
index 8d47073d2a4eb41ae01a75517b34faf41d31ce59..6812d516e78746f006bdd531fa270a63995378e8 100644 (file)
@@ -457,6 +457,9 @@ uses
   {$IFDEF ENABLE_SHELLS}
     g_shells,
   {$ENDIF}
+  {$IFDEF ENABLE_CORPSES}
+    g_corpses,
+  {$ENDIF}
   {$IFNDEF HEADLESS}
     r_render, g_system,
   {$ENDIF}
@@ -1477,7 +1480,16 @@ begin
 
   g_Map_Free(freeTextures);
   g_Player_Free();
-  g_Player_RemoveAllCorpses();
+
+  {$IFDEF ENABLE_GIBS}
+    g_Gibs_RemoveAll;
+  {$ENDIF}
+  {$IFDEF ENALBE_SHELLS}
+    g_Shells_RemoveAll;
+  {$ENDIF}
+  {$IFDEF ENABLE_CORPSES}
+    g_Corpses_RemoveAll;
+  {$ENDIF}
 
   gGameSettings.GameType := GT_NONE;
   if gGameSettings.GameMode = GM_SINGLE then
@@ -2187,7 +2199,9 @@ begin
     {$IFDEF ENABLE_GIBS}
       g_Gibs_Update;
     {$ENDIF}
-    g_Player_UpdatePhysicalObjects();
+    {$IFDEF ENABLE_CORPSES}
+      g_Corpses_Update;
+    {$ENDIF}
     {$IFDEF ENABLE_SHELLS}
       g_Shells_Update;
     {$ENDIF}
@@ -3284,7 +3298,16 @@ var
   nws: AnsiString;
 begin
   g_Map_Free((Map <> gCurrentMapFileName) and (oldMapPath <> gCurrentMapFileName));
-  g_Player_RemoveAllCorpses();
+
+  {$IFDEF ENABLE_GIBS}
+    g_Gibs_RemoveAll;
+  {$ENDIF}
+  {$IFDEF ENALBE_SHELLS}
+    g_Shells_RemoveAll;
+  {$ENDIF}
+  {$IFDEF ENABLE_CORPSES}
+    g_Corpses_RemoveAll;
+  {$ENDIF}
 
   if (not g_Game_IsClient) and
      (gSwitchGameMode <> gGameSettings.GameMode) and
@@ -3609,7 +3632,16 @@ begin
     Exit;
   end;
 
-  g_Player_RemoveAllCorpses;
+  {$IFDEF ENABLE_GIBS}
+    g_Gibs_RemoveAll;
+  {$ENDIF}
+  {$IFDEF ENALBE_SHELLS}
+    g_Shells_RemoveAll;
+  {$ENDIF}
+  {$IFDEF ENABLE_CORPSES}
+    g_Corpses_RemoveAll;
+  {$ENDIF}
+
   g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
   if g_Game_IsNet then
     MH_SEND_GameEvent(NET_EV_LMS_START);
@@ -4026,12 +4058,18 @@ begin
   begin
     if Length(p) = 2 then
     begin
-      a := Max(0, StrToInt(p[1]));
-      g_Corpses_SetMax(a)
+      {$IFDEF ENABLE_CORPSES}
+        a := Max(0, StrToInt(p[1]));
+        g_Corpses_SetMax(a)
+      {$ENDIF}
     end
     else if Length(p) = 1 then
     begin
-      e_LogWritefln('%s', [g_Corpses_GetMax()])
+      {$IFDEF ENABLE_CORPSES}
+        e_LogWritefln('%s', [g_Corpses_GetMax()])
+      {$ELSE}
+        e_LogWritefln('%s', [0])
+      {$ENDIF}
     end
     else
     begin
index 627764b6142c5de86389c69a42c482f2f7278fc3..804126414c3eb35090d7cda8ac5f2fdc19eac5f4 100644 (file)
@@ -46,6 +46,7 @@ interface
   function g_Gibs_GetMax (): Word;
 
   procedure g_Gibs_Create (fX, fY, mid: Integer; fColor: TRGB);
+  procedure g_Gibs_RemoveAll;
   procedure g_Gibs_Update;
 
 implementation
@@ -198,6 +199,14 @@ implementation
     end;
   end;
 
+  procedure g_Gibs_RemoveAll;
+    var i: Integer;
+  begin
+    i := g_Gibs_GetMax();
+    g_Gibs_SetMax(0);
+    g_Gibs_SetMax(i);
+  end;
+
   procedure g_Gibs_Update;
     var i: Integer; vel: TPoint2i; mr: Word;
   begin
index d88c79f45a04d95bc68515b1f47430661f7f9af0..33a2477065a524de5aa0754756445aeba646212e 100644 (file)
@@ -48,6 +48,9 @@ uses
   {$IFDEF ENABLE_SHELLS}
     g_shells,
   {$ENDIF}
+  {$IFDEF ENABLE_CORPSES}
+    g_corpses,
+  {$ENDIF}
   g_gui, r_textures, r_graphics, g_game, g_map,
   g_base, g_basic, g_console, g_sound, g_player, g_options, g_weapons,
   e_log, SysUtils, CONFIG, g_playermodel, DateUtils,
@@ -150,7 +153,9 @@ begin
   {$IFDEF ENABLE_GIBS}
     g_Gibs_SetMax(TGUIScroll(menu.GetControl('scGibsMax')).Value*25);
   {$ENDIF}
-  g_Corpses_SetMax(TGUIScroll(menu.GetControl('scCorpsesMax')).Value*5);
+  {$IFDEF ENABLE_CORPSES}
+    g_Corpses_SetMax(TGUIScroll(menu.GetControl('scCorpsesMax')).Value*5);
+  {$ENDIF}
 
   {$IFDEF ENABLE_GIBS}
     case TGUISwitch(menu.GetControl('swGibsCount')).ItemIndex of
@@ -552,7 +557,9 @@ begin
   {$IFDEF ENABLE_GIBS}
     TGUIScroll(menu.GetControl('scGibsMax')).Value := g_Gibs_GetMax() div 25;
   {$ENDIF}
-  TGUIScroll(menu.GetControl('scCorpsesMax')).Value := g_Corpses_GetMax() div 5;
+  {$IFDEF ENABLE_CORPSES}
+    TGUIScroll(menu.GetControl('scCorpsesMax')).Value := g_Corpses_GetMax() div 5;
+  {$ENDIF}
   TGUISwitch(menu.GetControl('swBloodCount')).ItemIndex := gBloodCount;
 
   with TGUISwitch(menu.GetControl('swScreenFlash')) do
index fbb75a56f1929a1b0b66cc734f6aa29f522287fb..9877ece2b6bb4a7ce11584deff04a3dd9f287204 100644 (file)
@@ -537,6 +537,9 @@ uses
   {$IFDEF ENABLE_SHELLS}
     g_shells,
   {$ENDIF}
+  {$IFDEF ENABLE_CORPSES}
+    g_corpses,
+  {$ENDIF}
   e_log, g_sound, g_player, g_game,
   g_weapons, g_triggers, g_items, g_options,
   g_console, g_map, Math, wadreader,
@@ -2070,14 +2073,20 @@ begin
 end;
 
 procedure TMonster.Update();
-var
-  a, b, sx, sy, wx, wy, oldvelx: Integer;
-  st: Word;
-  o, co: TObj;
-  fall: Boolean;
-  mon: TMonster;
-  mit: PMonster;
-  it: TMonsterGrid.Iter;
+  {$IFDEF ENABLE_CORPSES}
+    var co: TObj;
+  {$ENDIF}
+  {$IF DEFINED(ENABLE_GIBS) OR DEFINED(ENABLE_CORPSES)}
+    var b: Integer;
+  {$ENDIF}
+  var
+    a, sx, sy, wx, wy, oldvelx: Integer;
+    st: Word;
+    o: TObj;
+    fall: Boolean;
+    mon: TMonster;
+    mit: PMonster;
+    it: TMonsterGrid.Iter;
 label
   _end;
 begin
@@ -2570,25 +2579,27 @@ begin
                 end;
               end;
             {$ENDIF}
-          // Боссы могут пинать трупы:
-            if (FMonsterType in [MONSTER_CYBER, MONSTER_SPIDER, MONSTER_ROBO]) and
-               (FObj.Vel.X <> 0) and (gCorpses <> nil) then
-            begin
-              b := Abs(FObj.Vel.X);
-              if b > 1 then b := b * (Random(8 div b) + 1);
-              for a := 0 to High(gCorpses) do
-                if (gCorpses[a] <> nil) and (gCorpses[a].State > 0) then
-                begin
-                  co := gCorpses[a].Obj;
-                  if g_Obj_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y+FObj.Rect.Height-4,
-                                   FObj.Rect.Width, 8, @co) and (Random(3) = 0) then
-                    // Пинаем трупы
-                    if FObj.Vel.X < 0 then
-                      gCorpses[a].Damage(b*2, FUID, -b, Random(7)) // налево
-                    else
-                      gCorpses[a].Damage(b*2, FUID, b, Random(7)); // направо
-                end;
-            end;
+            {$IFDEF ENABLE_CORPSES}
+              // Боссы могут пинать трупы:
+              if (FMonsterType in [MONSTER_CYBER, MONSTER_SPIDER, MONSTER_ROBO]) and
+                 (FObj.Vel.X <> 0) and (gCorpses <> nil) then
+              begin
+                b := Abs(FObj.Vel.X);
+                if b > 1 then b := b * (Random(8 div b) + 1);
+                for a := 0 to High(gCorpses) do
+                  if (gCorpses[a] <> nil) and (gCorpses[a].State > 0) then
+                  begin
+                    co := gCorpses[a].Obj;
+                    if g_Obj_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y+FObj.Rect.Height-4,
+                                     FObj.Rect.Width, 8, @co) and (Random(3) = 0) then
+                      // Пинаем трупы
+                      if FObj.Vel.X < 0 then
+                        gCorpses[a].Damage(b*2, FUID, -b, Random(7)) // налево
+                      else
+                        gCorpses[a].Damage(b*2, FUID, b, Random(7)); // направо
+                  end;
+              end;
+            {$ENDIF}
           // Если цель высоко, то, возможно, прыгаем:
             if sy < -40 then
               if g_Obj_CollideLevel(@FObj, 0, 1) or g_Obj_StayOnStep(@FObj) then
@@ -3114,11 +3125,14 @@ begin
 end;
 
 procedure TMonster.ClientUpdate();
-var
-  a, b, sx, sy, oldvelx: Integer;
-  st: Word;
-  o, co: TObj;
-  fall: Boolean;
+  {$IFDEF ENABLE_CORPSES}
+    var a, b: Integer; co: TObj;
+  {$ENDIF}
+  var
+    sx, sy, oldvelx: Integer;
+    st: Word;
+    o: TObj;
+    fall: Boolean;
 label
   _end;
 begin
@@ -3470,25 +3484,27 @@ begin
                 end;
               end;
             {$ENDIF}
-          // Боссы могут пинать трупы:
-            if (FMonsterType in [MONSTER_CYBER, MONSTER_SPIDER, MONSTER_ROBO]) and
-               (FObj.Vel.X <> 0) and (gCorpses <> nil) then
-            begin
-              b := Abs(FObj.Vel.X);
-              if b > 1 then b := b * (Random(8 div b) + 1);
-              for a := 0 to High(gCorpses) do
-                if (gCorpses[a] <> nil) and (gCorpses[a].State > 0) then
-                begin
-                  co := gCorpses[a].Obj;
-                  if g_Obj_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y+FObj.Rect.Height-4,
-                                   FObj.Rect.Width, 8, @co) and (Random(3) = 0) then
-                    // Пинаем трупы
-                    if FObj.Vel.X < 0 then
-                      gCorpses[a].Damage(b*2, FUID, -b, Random(7)) // налево
-                    else
-                      gCorpses[a].Damage(b*2, FUID, b, Random(7)); // направо
-                end;
-            end;
+            {$IFDEF ENABLE_CORPSES}
+              // Боссы могут пинать трупы:
+              if (FMonsterType in [MONSTER_CYBER, MONSTER_SPIDER, MONSTER_ROBO]) and
+                 (FObj.Vel.X <> 0) and (gCorpses <> nil) then
+              begin
+                b := Abs(FObj.Vel.X);
+                if b > 1 then b := b * (Random(8 div b) + 1);
+                for a := 0 to High(gCorpses) do
+                  if (gCorpses[a] <> nil) and (gCorpses[a].State > 0) then
+                  begin
+                    co := gCorpses[a].Obj;
+                    if g_Obj_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y+FObj.Rect.Height-4,
+                                     FObj.Rect.Width, 8, @co) and (Random(3) = 0) then
+                      // Пинаем трупы
+                      if FObj.Vel.X < 0 then
+                        gCorpses[a].Damage(b*2, FUID, -b, Random(7)) // налево
+                      else
+                        gCorpses[a].Damage(b*2, FUID, b, Random(7)); // направо
+                  end;
+              end;
+            {$ENDIF}
           end;
 
         FSleep := FSleep + 1;
index 775a7ae0c06afeb392244540d9917605fb199097..b719a8f1575f00ef170ebb6c6410e46a8947c7df 100644 (file)
@@ -282,9 +282,15 @@ implementation
     {$IFDEF ENABLE_GFX}
       g_gfx,
     {$ENDIF}
+    {$IFDEF ENABLE_GIBS}
+      g_gibs,
+    {$ENDIF}
     {$IFDEF ENABLE_SHELLS}
       g_shells,
     {$ENDIF}
+    {$IFDEF ENABLE_CORPSES}
+      g_corpses,
+    {$ENDIF}
     Math, ENet, e_input, e_log, g_base, g_basic,
     g_textures, g_sound, g_console, g_options,
     g_game, g_player, g_map, g_panel, g_items, g_weapons, g_phys,
@@ -1956,7 +1962,15 @@ begin
 
     NET_EV_LMS_START:
     begin
-      g_Player_RemoveAllCorpses;
+      {$IFDEF ENABLE_GIBS}
+        g_Gibs_RemoveAll;
+      {$ENDIF}
+      {$IFDEF ENALBE_SHELLS}
+        g_Shells_RemoveAll;
+      {$ENDIF}
+      {$IFDEF ENABLE_CORPSES}
+        g_Corpses_RemoveAll;
+      {$ENDIF}
       gLMSRespawn := LMS_RESPAWN_NONE;
       g_Game_Message(_lc[I_MESSAGE_LMS_START], 144);
     end;
index 83bae9679f92697cceb072d3ed4625f75bbd46a0..936f2b8cd8ad49444d168b110690315eb4a780d4 100644 (file)
@@ -134,6 +134,9 @@ uses
   {$IFDEF ENABLE_SHELLS}
     g_shells,
   {$ENDIF}
+  {$IFDEF ENABLE_CORPSES}
+    g_corpses,
+  {$ENDIF}
   e_log, e_input, g_console, g_sound, g_player, Math,
   g_map, g_net, g_netmaster, SysUtils, CONFIG, g_game,
   g_items, wadreader, envvars;
@@ -292,7 +295,9 @@ begin
   {$IFDEF ENABLE_SHELLS}
     g_Shells_SetMax(DefaultShellMax);
   {$ENDIF}
-  g_Corpses_SetMax(20);
+  {$IFDEF ENABLE_CORPSES}
+    g_Corpses_SetMax(DefaultCorpsesMax);
+  {$ENDIF}
   {$IFDEF ENABLE_GIBS}
     g_Gibs_SetMax(DefaultGibsMax);
     gGibsCount := DefaultGibsCount;
index f9b7371f213c2f8b4b380e02ec61bd7b03b6672f..61d341ebb2d82982f2c786ad463b2463a1ded98d 100644 (file)
@@ -223,6 +223,9 @@ implementation
     {$IFDEF ENABLE_GIBS}
       g_gibs,
     {$ENDIF}
+    {$IFDEF ENABLE_CORPSES}
+      g_corpses,
+    {$ENDIF}
     g_basic, g_map, g_game, g_weapons, g_triggers,
     g_console, g_language, g_monsters, g_player, g_grid, e_log, geom, utils, xstreams
   ;
@@ -580,10 +583,14 @@ var
   {$IFDEF ENABLE_GIBS}
     gib: PGib;
   {$ENDIF}
-  cor: TCorpse;
+  {$IFDEF ENABLE_CORPSES}
+    cor: TCorpse;
+  {$ENDIF}
+  {$IF DEFINED(ENABLE_GIBS) OR DEFINED(ENABLE_CORPSES)}
+    ontop: Boolean;
+  {$ENDIF}
   mon: TMonster;
   mpfrid: LongWord;
-  ontop: Boolean;
   actMoveTrig: Boolean;
   actSizeTrig: Boolean;
 begin
@@ -704,19 +711,21 @@ begin
           end;
         {$ENDIF}
 
-        // move and push corpses
-        for f := 0 to High(gCorpses) do
-        begin
-          cor := gCorpses[f];
-          if (cor = nil) then continue;
-          cor.getMapBox(px, py, pw, ph);
-          if not g_Collide(px, py, pw, ph, cx0, cy0, cw, ch) then continue;
-          if tryMPlatMove(px, py, pw, ph, pdx, pdy, squash, @ontop) then
+        {$IFDEF ENABLE_CORPSES}
+          // move and push corpses
+          for f := 0 to High(gCorpses) do
           begin
-            // set new position
-            cor.moveBy(pdx, pdy); // this will call `positionChanged()` for us
+            cor := gCorpses[f];
+            if (cor = nil) then continue;
+            cor.getMapBox(px, py, pw, ph);
+            if not g_Collide(px, py, pw, ph, cx0, cy0, cw, ch) then continue;
+            if tryMPlatMove(px, py, pw, ph, pdx, pdy, squash, @ontop) then
+            begin
+              // set new position
+              cor.moveBy(pdx, pdy); // this will call `positionChanged()` for us
+            end;
           end;
-        end;
+        {$ENDIF}
 
         // collect monsters
         monCheckListUsed := 0;
index aab6b385b7658f3650a1880468bea002a799d92e..991b0d8799b91c6528ec9c49bbf63ab2b39f8e36 100644 (file)
@@ -76,14 +76,9 @@ const
 
   ANGLE_NONE        = Low(SmallInt);
 
-  CORPSE_STATE_REMOVEME = 0;
-  CORPSE_STATE_NORMAL   = 1;
-  CORPSE_STATE_MESS     = 2;
-
   PLAYER_RECT: TRectWH = (X:15; Y:12; Width:34; Height:52);
   PLAYER_RECT_CX       = 15+(34 div 2);
   PLAYER_RECT_CY       = 12+(52 div 2);
-  PLAYER_CORPSERECT: TRectWH = (X:15; Y:48; Width:34; Height:16);
 
   PLAYER_HP_SOFT  = 100;
   PLAYER_HP_LIMIT = 200;
@@ -248,8 +243,6 @@ type
 
     procedure doDamage (v: Integer);
 
-    function refreshCorpse(): Boolean;
-
   public
     FDamageBuffer:   Integer;
 
@@ -281,7 +274,10 @@ type
     FSpawnInvul: Integer;
     FHandicap:  Integer;
     FWaitForFirstSpawn: Boolean; // set to `true` in server, used to spawn a player on first full state request
-    FCorpse:    Integer;
+
+    {$IFDEF ENABLE_CORPSES}
+      FCorpse:    Integer;
+    {$ENDIF}
 
     // debug: viewport offset
     viewPortX, viewPortY, viewPortW, viewPortH: Integer;
@@ -353,8 +349,6 @@ type
     procedure getMapBox (out x, y, w, h: Integer); inline;
     procedure moveBy (dx, dy: Integer); inline;
 
-    function getCameraObj(): TObj;
-
     function GetAmmoByWeapon(Weapon: Byte): Word; // private state
 
   public
@@ -439,6 +433,10 @@ type
     // set this before assigning something to `eDamage`
     property eDamageType: Integer read mEDamageType write mEDamageType;
     property eDamage: Integer write doDamage;
+
+    {$IFDEF ENABLE_CORPSES}
+      property Corpse: Integer read FCorpse;
+    {$ENDIF}
   end;
 
   TDifficult = record
@@ -499,36 +497,6 @@ type
     procedure   LoadState (st: TStream); override;
   end;
 
-  TCorpse = class{$IFDEF USE_MEMPOOL}(TPoolObject){$ENDIF}
-  private
-    FMess:          Boolean;
-    FState:         Byte;
-    FDamage:        Byte;
-    FObj:           TObj;
-    FPlayerUID:     Word;
-    FModel:   TPlayerModel;
-
-  public
-    constructor Create(X, Y: Integer; ModelName: String; aMess: Boolean);
-    destructor  Destroy(); override;
-    procedure   Damage(Value: Word; SpawnerUID: Word; vx, vy: Integer);
-    procedure   Update();
-    procedure   SaveState (st: TStream);
-    procedure   LoadState (st: TStream);
-
-    procedure getMapBox (out x, y, w, h: Integer); inline;
-    procedure moveBy (dx, dy: Integer); inline;
-
-    procedure positionChanged ();  inline; //WARNING! call this after entity position was changed, or coldet will not work right!
-
-    function ObjPtr (): PObj; inline;
-
-    property    Obj: TObj read FObj; // copies object
-    property    State: Byte read FState;
-    property    Mess: Boolean read FMess;
-    property    Model: TPlayerModel read FModel;
-  end;
-
   TTeamStat = Array [TEAM_RED..TEAM_BLUE] of
     record
       Goals: SmallInt;
@@ -536,7 +504,6 @@ type
 
 var
   gPlayers: Array of TPlayer;
-  gCorpses: Array of TCorpse;
   gTeamStat: TTeamStat;
   gFly: Boolean = False;
   gAimLine: Boolean = False;
@@ -551,9 +518,6 @@ var
 
 function  Lerp(X, Y, Factor: Integer): Integer;
 
-procedure g_Corpses_SetMax(Count: Word);
-function  g_Corpses_GetMax(): Word;
-
 procedure g_Player_Init();
 procedure g_Player_Free();
 function  g_Player_Create(ModelName: String; Color: TRGB; Team: Byte; Bot: Boolean): Word;
@@ -568,11 +532,6 @@ function  g_Player_Get(UID: Word): TPlayer;
 function  g_Player_GetCount(): Byte;
 function  g_Player_GetStats(): TPlayerStatArray;
 function  g_Player_ValidName(Name: String): Boolean;
-function  g_Player_CreateCorpse(Player: TPlayer): Integer;
-procedure g_Player_UpdatePhysicalObjects();
-procedure g_Player_RemoveAllCorpses();
-procedure g_Player_Corpses_SaveState (st: TStream);
-procedure g_Player_Corpses_LoadState (st: TStream);
 procedure g_Player_ResetReady();
 procedure g_Bot_Add(Team, Difficult: Byte; Handicap: Integer = 100);
 procedure g_Bot_AddList(Team: Byte; lname: ShortString; num: Integer = -1; Handicap: Integer = 100);
@@ -600,6 +559,9 @@ uses
   {$IFDEF ENABLE_SHELLS}
     g_shells,
   {$ENDIF}
+  {$IFDEF ENABLE_CORPSES}
+    g_corpses,
+  {$ENDIF}
   e_log, g_map, g_items, g_console, Math,
   g_options, g_triggers, g_game, g_grid, e_res,
   wadreader, g_monsters, CONFIG, g_language,
@@ -673,7 +635,6 @@ const
   BOTLIST_FILENAME = 'botlist.txt';
 
 var
-  MaxCorpses: Word = 20;
   BotNames: Array of String;
   BotList: Array of TBotProfile;
   SavedStates: Array of TPlayerSavedState;
@@ -699,17 +660,6 @@ begin
   Result := g_Player_Get(UID1).FTeam = g_Player_Get(UID2).FTeam;
 end;
 
-procedure g_Corpses_SetMax(Count: Word);
-begin
-  MaxCorpses := Count;
-  SetLength(gCorpses, Count);
-end;
-
-function g_Corpses_GetMax(): Word;
-begin
-  Result := MaxCorpses;
-end;
-
 function g_Player_Create(ModelName: String; Color: TRGB; Team: Byte; Bot: Boolean): Word;
 var
   a: Integer;
@@ -1460,160 +1410,6 @@ begin
       end;
 end;
 
-function  g_Player_CreateCorpse(Player: TPlayer): Integer;
-var
-  i: Integer;
-  find_id: DWORD;
-  ok: Boolean;
-begin
-  Result := -1;
-
-  if Player.alive then
-    Exit;
-
-// Разрываем связь с прежним трупом:
-  i := Player.FCorpse;
-  if (i >= 0) and (i < Length(gCorpses)) then
-  begin
-    if (gCorpses[i] <> nil) and (gCorpses[i].FPlayerUID = Player.FUID) then
-      gCorpses[i].FPlayerUID := 0;
-  end;
-
-  if Player.FObj.Y >= gMapInfo.Height+128 then
-    Exit;
-
-  with Player do
-  begin
-{$IFDEF ENABLE_GIBS}
-      if (FHealth < -50) and (gGibsCount > 0) then
-      begin
-        g_Gibs_Create(FObj.X + PLAYER_RECT_CX, FObj.Y + PLAYER_RECT_CY, FModel.id, FModel.Color);
-      end
-      else
-{$ENDIF}
-      begin
-        if (gCorpses = nil) or (Length(gCorpses) = 0) then
-          Exit;
-
-        ok := False;
-        for find_id := 0 to High(gCorpses) do
-          if gCorpses[find_id] = nil then
-          begin
-            ok := True;
-            Break;
-          end;
-
-        if not ok then
-          find_id := Random(Length(gCorpses));
-
-        gCorpses[find_id] := TCorpse.Create(FObj.X, FObj.Y, FModel.GetName(), FHealth < -20);
-        gCorpses[find_id].FModel.Color := FModel.Color;
-        gCorpses[find_id].FObj.Vel := FObj.Vel;
-        gCorpses[find_id].FObj.Accel := FObj.Accel;
-        gCorpses[find_id].FPlayerUID := FUID;
-
-        Result := find_id;
-      end
-  end;
-end;
-
-procedure g_Player_UpdatePhysicalObjects();
-  var i: Integer;
-begin
-  if gCorpses <> nil then
-    for i := 0 to High(gCorpses) do
-      if gCorpses[i] <> nil then
-        if gCorpses[i].State = CORPSE_STATE_REMOVEME then
-          begin
-            gCorpses[i].Free();
-            gCorpses[i] := nil;
-          end
-        else
-          gCorpses[i].Update();
-end;
-
-procedure g_Player_RemoveAllCorpses();
-  var i: Integer;
-begin
-  {$IFDEF ENABLE_GIBS}
-    i := g_Gibs_GetMax();
-    g_Gibs_SetMax(0);
-    g_Gibs_SetMax(i);
-  {$ENDIF}
-  {$IFDEF ENABLE_SHELLS}
-    i := g_Shells_GetMax();
-    g_Shells_SetMax(0);
-    g_Shells_SetMax(i);
-  {$ENDIF}
-
-  if gCorpses <> nil then
-    for i := 0 to High(gCorpses) do
-      gCorpses[i].Free();
-
-  gCorpses := nil;
-  SetLength(gCorpses, MaxCorpses);
-end;
-
-procedure g_Player_Corpses_SaveState (st: TStream);
-var
-  count, i: Integer;
-begin
-  // Считаем количество существующих трупов
-  count := 0;
-  for i := 0 to High(gCorpses) do if (gCorpses[i] <> nil) then Inc(count);
-
-  // Количество трупов
-  utils.writeInt(st, LongInt(count));
-
-  if (count = 0) then exit;
-
-  // Сохраняем трупы
-  for i := 0 to High(gCorpses) do
-  begin
-    if gCorpses[i] <> nil then
-    begin
-      // Название модели
-      utils.writeStr(st, gCorpses[i].FModel.GetName());
-      // Тип смерти
-      utils.writeBool(st, gCorpses[i].Mess);
-      // Сохраняем данные трупа:
-      gCorpses[i].SaveState(st);
-    end;
-  end;
-end;
-
-
-procedure g_Player_Corpses_LoadState (st: TStream);
-var
-  count, i: Integer;
-  str: String;
-  b: Boolean;
-begin
-  assert(st <> nil);
-
-  g_Player_RemoveAllCorpses();
-
-  // Количество трупов:
-  count := utils.readLongInt(st);
-  if (count < 0) or (count > Length(gCorpses)) then raise XStreamError.Create('invalid number of corpses');
-
-  if (count = 0) then exit;
-
-  // Загружаем трупы
-  for i := 0 to count-1 do
-  begin
-    // Название модели:
-    str := utils.readStr(st);
-    // Тип смерти
-    b := utils.readBool(st);
-    // Создаем труп
-    gCorpses[i] := TCorpse.Create(0, 0, str, b);
-    // Загружаем данные трупа
-    gCorpses[i].LoadState(st);
-  end;
-end;
-
-
 { T P l a y e r : }
 
 function TPlayer.isValidViewPort (): Boolean; inline; begin result := (viewPortW > 0) and (viewPortH > 0); end;
@@ -1810,7 +1606,10 @@ begin
   FFirePainTime := 0;
   FFireAttacker := 0;
   FHandicap := 100;
-  FCorpse := -1;
+
+  {$IFDEF ENABLE_CORPSES}
+    FCorpse := -1;
+  {$ENDIF}
 
   FActualModelName := 'doomer';
 
@@ -2584,7 +2383,9 @@ begin
     DropFlag(KillType = K_FALLKILL);
   end;
 
-  FCorpse := g_Player_CreateCorpse(Self);
+  {$IFDEF ENABLE_CORPSES}
+    FCorpse := g_Corpses_Create(Self);
+  {$ENDIF}
 
   if Srv and (gGameSettings.MaxLives > 0) and FNoRespawn and
      (gLMSRespawn = LMS_RESPAWN_NONE) then
@@ -3407,7 +3208,9 @@ begin
   FDeath := 0;
   FSecrets := 0;
   FSpawnInvul := 0;
-  FCorpse := -1;
+  {$IFDEF ENABLE_CORPSES}
+    FCorpse := -1;
+  {$ENDIF}
   FReady := False;
   if FNoRespawn then
   begin
@@ -3522,7 +3325,10 @@ begin
   FPain := 0;
   FLastHit := 0;
   FSpawnInvul := 0;
-  FCorpse := -1;
+
+  {$IFDEF ENABLE_CORPSES}
+    FCorpse := -1;
+  {$ENDIF}
 
   if not g_Game_IsServer then
     Exit;
@@ -3707,7 +3513,10 @@ begin
   FPhysics := False;
   FWantsInGame := False;
   FSpawned := False;
-  FCorpse := -1;
+
+  {$IFDEF ENABLE_CORPSES}
+    FCorpse := -1;
+  {$ENDIF}
 
   if FNoRespawn then
   begin
@@ -3942,41 +3751,6 @@ begin
     Result := 1;
 end;
 
-function TPlayer.refreshCorpse(): Boolean;
-var
-  i: Integer;
-begin
-  Result := False;
-  FCorpse := -1;
-  if FAlive or FSpectator then
-    Exit;
-  if (gCorpses = nil) or (Length(gCorpses) = 0) then
-    Exit;
-  for i := 0 to High(gCorpses) do
-    if gCorpses[i] <> nil then
-      if gCorpses[i].FPlayerUID = FUID then
-      begin
-        Result := True;
-        FCorpse := i;
-        break;
-      end;
-end;
-
-function TPlayer.getCameraObj(): TObj;
-begin
-  if (not FAlive) and (not FSpectator) and
-     (FCorpse >= 0) and (FCorpse < Length(gCorpses)) and
-     (gCorpses[FCorpse] <> nil) and (gCorpses[FCorpse].FPlayerUID = FUID) then
-  begin
-    gCorpses[FCorpse].FObj.slopeUpLeft := FObj.slopeUpLeft;
-    Result := gCorpses[FCorpse].FObj;
-  end
-  else
-  begin
-    Result := FObj;
-  end;
-end;
-
 procedure TPlayer.PreUpdate();
 begin
   FSlopeOld := FObj.slopeUpLeft;
@@ -5476,213 +5250,6 @@ begin
   FJetSoundOff.Pause(Enable);
 end;
 
-{ T C o r p s e : }
-
-constructor TCorpse.Create(X, Y: Integer; ModelName: String; aMess: Boolean);
-begin
-  g_Obj_Init(@FObj);
-  FObj.X := X;
-  FObj.Y := Y;
-  FObj.Rect := PLAYER_CORPSERECT;
-  FMess := aMess;
-  FModel := g_PlayerModel_Get(ModelName);
-
-  if FMess then
-  begin
-    FState := CORPSE_STATE_MESS;
-    FModel.ChangeAnimation(A_DIE2);
-  end
-  else
-  begin
-    FState := CORPSE_STATE_NORMAL;
-    FModel.ChangeAnimation(A_DIE1);
-  end;
-end;
-
-destructor TCorpse.Destroy();
-begin
-  FModel.Free;
-  inherited;
-end;
-
-function TCorpse.ObjPtr (): PObj; inline; begin result := @FObj; end;
-
-procedure TCorpse.positionChanged (); inline; begin end;
-
-procedure TCorpse.moveBy (dx, dy: Integer); inline;
-begin
-  if (dx <> 0) or (dy <> 0) then
-  begin
-    FObj.X += dx;
-    FObj.Y += dy;
-    positionChanged();
-  end;
-end;
-
-
-procedure TCorpse.getMapBox (out x, y, w, h: Integer); inline;
-begin
-  x := FObj.X+PLAYER_CORPSERECT.X;
-  y := FObj.Y+PLAYER_CORPSERECT.Y;
-  w := PLAYER_CORPSERECT.Width;
-  h := PLAYER_CORPSERECT.Height;
-end;
-
-
-procedure TCorpse.Damage(Value: Word; SpawnerUID: Word; vx, vy: Integer);
-  {$IFDEF ENABLE_GFX}
-    var Blood: TModelBlood;
-  {$ENDIF}
-begin
-  if FState = CORPSE_STATE_REMOVEME then
-    Exit;
-
-  FDamage := FDamage + Value;
-
-{$IFDEF ENABLE_GIBS}
-  if FDamage > 150 then
-  begin
-    if FModel <> nil then
-    begin
-      FState := CORPSE_STATE_REMOVEME;
-
-      g_Gibs_Create(
-        FObj.X + FObj.Rect.X + (FObj.Rect.Width div 2),
-        FObj.Y + FObj.Rect.Y + (FObj.Rect.Height div 2),
-        FModel.id,
-        FModel.Color
-      );
-
-      // Звук мяса от трупа:
-      FModel.PlaySound(MODELSOUND_DIE, 5, FObj.X, FObj.Y);
-
-      // Зловещий смех:
-      if (gBodyKillEvent <> -1) and gDelayedEvents[gBodyKillEvent].Pending then
-        gDelayedEvents[gBodyKillEvent].Pending := False;
-      gBodyKillEvent := g_Game_DelayEvent(DE_BODYKILL, 1050, SpawnerUID);
-
-      FModel.Free;
-      FModel := nil;
-    end
-  end
-  else
-{$ENDIF}
-  begin
-    FObj.Vel.X := FObj.Vel.X + vx;
-    FObj.Vel.Y := FObj.Vel.Y + vy;
-    {$IFDEF ENABLE_GFX}
-      Blood := FModel.GetBlood();
-      g_GFX_Blood(FObj.X+PLAYER_CORPSERECT.X+(PLAYER_CORPSERECT.Width div 2),
-                  FObj.Y+PLAYER_CORPSERECT.Y+(PLAYER_CORPSERECT.Height div 2),
-                  Value, vx, vy, 16, (PLAYER_CORPSERECT.Height*2) div 3,
-                  Blood.R, Blood.G, Blood.B, Blood.Kind);
-    {$ENDIF}
-  end;
-end;
-
-procedure TCorpse.Update();
-var
-  st: Word;
-begin
-  if FState = CORPSE_STATE_REMOVEME then
-    Exit;
-
-  FObj.oldX := FObj.X;
-  FObj.oldY := FObj.Y;
-
-  if gTime mod (GAME_TICK*2) <> 0 then
-  begin
-    g_Obj_Move(@FObj, True, True, True);
-    positionChanged(); // this updates spatial accelerators
-    Exit;
-  end;
-
-// Сопротивление воздуха для трупа:
-  FObj.Vel.X := z_dec(FObj.Vel.X, 1);
-
-  st := g_Obj_Move(@FObj, True, True, True);
-  positionChanged(); // this updates spatial accelerators
-
-  if WordBool(st and MOVE_FALLOUT) then
-  begin
-    FState := CORPSE_STATE_REMOVEME;
-    Exit;
-  end;
-
-  if FModel <> nil then
-    FModel.Update;
-end;
-
-
-procedure TCorpse.SaveState (st: TStream);
-  var anim: Boolean;
-begin
-  assert(st <> nil);
-
-  // Сигнатура трупа
-  utils.writeSign(st, 'CORP');
-  utils.writeInt(st, Byte(0));
-  // Состояние
-  utils.writeInt(st, Byte(FState));
-  // Накопленный урон
-  utils.writeInt(st, Byte(FDamage));
-  // Цвет
-  utils.writeInt(st, Byte(FModel.Color.R));
-  utils.writeInt(st, Byte(FModel.Color.G));
-  utils.writeInt(st, Byte(FModel.Color.B));
-  // Объект трупа
-  Obj_SaveState(st, @FObj);
-  utils.writeInt(st, Word(FPlayerUID));
-  // animation
-  anim := (FModel <> nil);
-  utils.writeBool(st, anim);
-  if anim then FModel.AnimState.SaveState(st, 0, False);
-  // animation for mask (same as animation, compat with older saves)
-  anim := (FModel <> nil);
-  utils.writeBool(st, anim);
-  if anim then FModel.AnimState.SaveState(st, 0, False);
-end;
-
-
-procedure TCorpse.LoadState (st: TStream);
-  var anim, blending: Boolean; r, g, b, alpha: Byte; stub: TAnimationState;
-begin
-  assert(st <> nil);
-
-  // Сигнатура трупа
-  if not utils.checkSign(st, 'CORP') then raise XStreamError.Create('invalid corpse signature');
-  if (utils.readByte(st) <> 0) then raise XStreamError.Create('invalid corpse version');
-  // Состояние
-  FState := utils.readByte(st);
-  // Накопленный урон
-  FDamage := utils.readByte(st);
-  // Цвет
-  r := utils.readByte(st);
-  g := utils.readByte(st);
-  b := utils.readByte(st);
-  FModel.SetColor(r, g, b);
-  // Объект трупа
-  Obj_LoadState(@FObj, st);
-  FPlayerUID := utils.readWord(st);
-  // animation
-  stub := TAnimationState.Create(False, 0, 0);
-  anim := utils.readBool(st);
-  if anim then
-  begin
-    stub.LoadState(st, alpha, blending);
-    FModel.AnimState.CurrentFrame := Min(stub.CurrentFrame, FModel.AnimState.Length);
-  end
-  else
-  begin
-    FModel.Free;
-    FModel := nil
-  end;
-  // animation for mask (same as animation, compat with older saves)
-  anim := utils.readBool(st);
-  if anim then stub.LoadState(st, alpha, blending);
-  stub.Free;
-end;
-
 { T B o t : }
 
 constructor TBot.Create();
index d603440d839241a9c3513f59140ae34d0ca755ba..14539ddd88f6ef4ba2dedcb505d08a6a39f0fc5c 100644 (file)
@@ -36,6 +36,12 @@ procedure Obj_LoadState (o: PObj; st: TStream);
 implementation
 
 uses
+  {$IFDEF ENABLE_GIBS}
+    g_gibs,
+  {$ENDIF}
+  {$IFDEF ENABLE_CORPSES}
+    g_corpses,
+  {$ENDIF}
   MAPDEF, utils, xstreams,
   g_game, g_items, g_map, g_monsters, g_triggers,
   g_basic, Math, wadreader,
@@ -160,6 +166,70 @@ begin
   end;
 end;
 
+procedure g_Player_Corpses_SaveState (st: TStream);
+  {$IFDEF ENABLE_CORPSES}
+    var i: Integer;
+  {$ENDIF}
+  var count: Integer;
+begin
+  count := 0;
+  {$IFDEF ENABLE_CORPSES}
+    for i := 0 to High(gCorpses) do
+      if (gCorpses[i] <> nil) then
+        Inc(count);
+  {$ENDIF}
+  utils.writeInt(st, LongInt(count));
+  {$IFDEF ENABLE_CORPSES}
+    if count > 0 then
+    begin
+      for i := 0 to High(gCorpses) do
+      begin
+        if gCorpses[i] <> nil then
+        begin
+          utils.writeStr(st, gCorpses[i].Model.GetName());
+          utils.writeBool(st, gCorpses[i].Mess);
+          gCorpses[i].SaveState(st);
+        end;
+      end;
+    end;
+  {$ENDIF}
+end;
+
+procedure g_Player_Corpses_LoadState (st: TStream);
+  {$IFDEF ENABLE_CORPSES}
+    var str: String; b: Boolean; i: Integer;
+  {$ENDIF}
+  var count: Integer;
+begin
+  assert(st <> nil);
+
+  {$IFDEF ENABLE_GIBS}
+    g_Gibs_RemoveAll;
+  {$ENDIF}
+  {$IFDEF ENALBE_SHELLS}
+    g_Shells_RemoveAll; // ???
+  {$ENDIF}
+  {$IFDEF ENABLE_CORPSES}
+    g_Corpses_RemoveAll;
+  {$ENDIF}
+
+  count := utils.readLongInt(st);
+
+  {$IFDEF ENABLE_CORPSES}
+    if (count < 0) or (count > Length(gCorpses)) then
+      raise XStreamError.Create('invalid number of corpses');
+    for i := 0 to count - 1 do
+    begin
+      str := utils.readStr(st);
+      b := utils.readBool(st);
+      gCorpses[i] := TCorpse.Create(0, 0, str, b);
+      gCorpses[i].LoadState(st);
+    end;
+  {$ELSE}
+    if count <> 0 then
+      raise XStreamError.Create('corpses not supported in this version');
+  {$ENDIF}
+end;
 
 function g_SaveGameTo (const filename: AnsiString; const aname: AnsiString; deleteOnError: Boolean=true): Boolean;
 var
index de0fc86c06380e51c2092bb0449ae670515b5025..1ba5fdb9d0d53130d8fad8475a1c574588d5c261 100644 (file)
@@ -49,6 +49,7 @@ interface
   function g_Shells_GetMax (): Word;
 
   procedure g_Shells_Create (fX, fY, dX, dY: Integer; T: Byte);
+  procedure g_Shells_RemoveAll;
   procedure g_Shells_Update;
 
 implementation
@@ -130,6 +131,14 @@ implementation
     end;
   end;
 
+  procedure g_Shells_RemoveAll;
+    var i: Integer;
+  begin
+    i := g_Shells_GetMax();
+    g_Shells_SetMax(0);
+    g_Shells_SetMax(i);
+  end;
+
   procedure g_Shells_SoundBounce(X, Y: Integer; T: Byte);
     var k: Integer;
   begin
index 0353a85a5809c7a9496770d8651ed676655f6326..beabbcfe8490cb53041553547c54150939f22944 100644 (file)
@@ -117,6 +117,9 @@ implementation
     {$IFDEF ENABLE_GIBS}
       g_gibs,
     {$ENDIF}
+    {$IFDEF ENABLE_CORPSES}
+      g_corpses,
+    {$ENDIF}
     Math, g_map, g_player, g_sound, g_panel,
     g_console, g_options, g_game,
     g_triggers, MAPDEF, e_log, g_monsters, g_saveload,
@@ -520,21 +523,29 @@ var
 begin
   //g_Sound_PlayEx('SOUND_WEAPON_EXPLODEBFG', 255);
 
-  h := High(gCorpses);
-
-  if gAdvCorpses and (h <> -1) then
-    for i := 0 to h do
-      if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
-        with gCorpses[i] do
-          if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
-                            Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
-              g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
-                            Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
+  {$IFDEF ENABLE_CORPSES}
+    h := High(gCorpses);
+    if gAdvCorpses and (h <> -1) then
+    begin
+      for i := 0 to h do
+      begin
+        if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
+        begin
+          with gCorpses[i] do
           begin
-            Damage(50, SpawnerUID, 0, 0);
-            g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
-                            Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2));
+            if (g_PatchLength(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
+                              Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) <= SHOT_BFG_RADIUS) and
+                g_TraceVector(X, Y, Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2),
+                              Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)) then
+            begin
+              Damage(50, SpawnerUID, 0, 0);
+              g_Weapon_BFGHit(Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2), Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2));
+            end;
           end;
+        end;
+      end;
+    end;
+  {$ENDIF}
 
   st := TEAM_NONE;
   pl := g_Player_Get(SpawnerUID);
@@ -778,8 +789,10 @@ begin
 end;
 
 function g_Weapon_Hit(obj: PObj; d: Integer; SpawnerUID: Word; t: Byte; HitCorpses: Boolean = True): Byte;
-var
-  i, h: Integer;
+  {$IFDEF ENABLE_CORPSES}
+    var i: Integer;
+  {$ENDIF}
+  var h: Integer;
 
   function PlayerHit(Team: Byte = 0): Boolean;
   var
@@ -858,21 +871,26 @@ var
 begin
   Result := 0;
 
-  if HitCorpses then
-  begin
-    h := High(gCorpses);
-
-    if gAdvCorpses and (h <> -1) then
-      for i := 0 to h do
-        if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) and
-           g_Obj_Collide(obj, @gCorpses[i].Obj) then
+  {$IFDEF ENABLE_CORPSES}
+    if HitCorpses then
+    begin
+      h := High(gCorpses);
+      if gAdvCorpses and (h <> -1) then
+      begin
+        for i := 0 to h do
         begin
-          // Ðàñïèëèâàåì òðóï:
-          gCorpses[i].Damage(d, SpawnerUID, (obj^.Vel.X+obj^.Accel.X) div 4,
-                                            (obj^.Vel.Y+obj^.Accel.Y) div 4);
-          Result := 1;
+          if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) and
+             g_Obj_Collide(obj, @gCorpses[i].Obj) then
+          begin
+            // Ðàñïèëèâàåì òðóï:
+            gCorpses[i].Damage(d, SpawnerUID, (obj^.Vel.X+obj^.Accel.X) div 4,
+                                              (obj^.Vel.Y+obj^.Accel.Y) div 4);
+            Result := 1;
+          end;
         end;
-  end;
+      end;
+    end;
+  {$ENDIF}
 
   case gGameSettings.GameMode of
     // Êàìïàíèÿ:
@@ -992,10 +1010,13 @@ var
     end;
   end;
 
-  var i, h, dx, dy, m, mm: Integer;
+  var i, h, dx, dy, mm: Integer;
   {$IFDEF ENABLE_GIBS}
     var _angle: SmallInt;
   {$ENDIF}
+  {$IF DEFINED(ENABLE_GIBS) OR DEFINED(ENABLE_CORPSES)}
+    var m: Integer;
+  {$ENDIF}
 begin
   result := false;
 
@@ -1032,30 +1053,33 @@ begin
   //g_Mons_ForEach(monsExCheck);
   g_Mons_ForEachAt(X-(rad+32), Y-(rad+32), (rad+32)*2, (rad+32)*2, monsExCheck);
 
-  h := High(gCorpses);
-
-  if gAdvCorpses and (h <> -1) then
-    for i := 0 to h do
-      if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
-        with gCorpses[i] do
+  {$IFDEF ENABLE_CORPSES}
+    h := High(gCorpses);
+    if gAdvCorpses and (h <> -1) then
+    begin
+      for i := 0 to h do
+      begin
+        if (gCorpses[i] <> nil) and (gCorpses[i].State <> CORPSE_STATE_REMOVEME) then
         begin
-          dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
-          dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
-
-          if dx > 1000 then dx := 1000;
-          if dy > 1000 then dy := 1000;
-
-          if dx*dx+dy*dy < r then
+          with gCorpses[i] do
           begin
-            m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y,
-                             Obj.Rect.Width, Obj.Rect.Height);
-
-            mm := Max(abs(dx), abs(dy));
-            if mm = 0 then mm := 1;
-
-            Damage(Round(100*(rad-m)/rad), SpawnerUID, (dx*10) div mm, (dy*10) div mm);
+            dx := Obj.X+Obj.Rect.X+(Obj.Rect.Width div 2)-X;
+            dy := Obj.Y+Obj.Rect.Y+(Obj.Rect.Height div 2)-Y;
+            if dx > 1000 then dx := 1000;
+            if dy > 1000 then dy := 1000;
+            if dx*dx+dy*dy < r then
+            begin
+              m := PointToRect(X, Y, Obj.X+Obj.Rect.X, Obj.Y+Obj.Rect.Y, Obj.Rect.Width, Obj.Rect.Height);
+              mm := Max(abs(dx), abs(dy));
+              if mm = 0 then
+                mm := 1;
+              Damage(Round(100*(rad-m)/rad), SpawnerUID, (dx*10) div mm, (dy*10) div mm);
+            end;
           end;
         end;
+      end;
+    end;
+  {$ENDIF}
 
   {$IFDEF ENABLE_GIBS}
     h := High(gGibs);
index 58bbd6305edcc9739acee071f1779a0f347aa2ad..286ce250b12e292e780e7e85a2b62872f07c1411 100644 (file)
@@ -47,6 +47,9 @@ implementation
     {$IFDEF ENABLE_GFX}
       g_gfx, r_gfx,
     {$ENDIF}
+    {$IFDEF ENABLE_CORPSES}
+      g_corpses,
+    {$ENDIF}
     SysUtils, Classes, Math,
     g_base, g_basic, r_graphics,
     g_system,
@@ -1554,7 +1557,9 @@ begin
   {$IFDEF ENABLE_GIBS}
     drawOther('gibs', @r_PlayerModel_DrawGibs);
   {$ENDIF}
-  drawOther('corpses', @r_Player_DrawCorpses);
+  {$IFDEF ENABLE_CORPSES}
+    drawOther('corpses', @r_Player_DrawCorpses);
+  {$ENDIF}
   drawPanelType('*wall', PANEL_WALL, g_rlayer_wall);
   drawOther('monsters', @r_Monsters_Draw);
   drawOther('itemdrop', @r_Items_DrawDrop);
@@ -1630,7 +1635,12 @@ begin
 
   glPushMatrix();
 
-  camObj := p.getCameraObj();
+  {$IFDEF ENABLE_CORPSES}
+    camObj := g_Corpses_GetCameraObj(p);
+  {$ELSE}
+    camObj := p.Obj;
+  {$ENDIF}
+
   camObj.lerp(gLerpFactor, fX, fY);
   px := fX + PLAYER_RECT_CX;
   py := fY + PLAYER_RECT_CY+nlerp(p.SlopeOld, camObj.slopeUpLeft, gLerpFactor);
index c803b4c9e582de6c48867123a557f63a43951f28..49d2c0c78c3cc7f382e3450c148d60acbc989f2f 100644 (file)
@@ -26,8 +26,6 @@ interface
   procedure r_Player_DrawDebug (p: TPlayer);
   procedure r_Player_DrawHealth;
 
-  procedure r_Player_DrawCorpses;
-
   procedure r_Player_Draw (p: TPlayer);
   procedure r_Player_DrawIndicator (p: TPlayer; Color: TRGB);
   procedure r_Player_DrawBubble (p: TPlayer);
@@ -41,6 +39,10 @@ interface
     procedure r_Player_DrawShells;
   {$ENDIF}
 
+  {$IFDEF ENABLE_CORPSES}
+    procedure r_Player_DrawCorpses;
+  {$ENDIF}
+
 implementation
 
   uses
@@ -53,6 +55,9 @@ implementation
     {$IFDEF ENABLE_SHELLS}
       g_shells,
     {$ENDIF}
+    {$IFDEF ENABLE_CORPSES}
+      g_corpses,
+    {$ENDIF}
     SysUtils, Classes, Math,
     MAPDEF, utils,
     g_basic, g_game, g_phys, g_map, g_language, g_weapons, g_items, g_net, g_options,
@@ -136,6 +141,7 @@ begin
     end;
 end;
 
+{$IFDEF ENABLE_CORPSES}
   procedure r_Player_DrawCorpse (p: TCorpse);
     var fX, fY: Integer;
   begin
@@ -154,6 +160,7 @@ end;
         if gCorpses[i] <> nil then
           r_Player_DrawCorpse(gCorpses[i])
   end;
+{$ENDIF}
 
 {$IFDEF ENABLE_SHELLS}
   procedure r_Player_DrawShells;
@@ -272,7 +279,11 @@ var
   Dot: Byte;
   CObj: TObj;
 begin
-  CObj := p.getCameraObj();
+  {$IFDEF ENABLE_CORPSES}
+    CObj := g_Corpses_GetCameraObj(p);
+  {$ELSE}
+    CObj := p.Obj;
+  {$ENDIF}
   CObj.lerp(gLerpFactor, fX, fY);
   // NB: _F_Obj.Rect is used to keep the bubble higher; this is not a mistake
   bubX := fX + p.Obj.Rect.X + IfThen(p.Direction = TDirection.D_LEFT, -4, 18);
index 1ba73469a5954abfbbf82d24cf504c9edde6ec3f..6789e6ee7ac17a5d783d10518add164132f5a4b3 100644 (file)
     {$UNDEF ENABLE_SHELLS}
     {$DEFINE DISABLE_SHELLS}
   {$ENDIF}
+  {$IFDEF ENABLE_CORPSES}
+    {$WARNING Corpses in headless mode has no sense. Disabled.}
+    {$UNDEF ENABLE_CORPSES}
+    {$DEFINE DISABLE_CORPSES}
+  {$ENDIF}
 {$ENDIF}
 
 {$IF DEFINED(ENABLE_MENU) AND DEFINED(DISABLE_MENU)}
   {$ENDIF}
 {$ENDIF}
 
+{$IF DEFINED(ENABLE_CORPSES) AND DEFINED(DISABLE_CORPSES)}
+  {$ERROR Select ENABLE_CORPSES or DISABLE_CORPSES}
+{$ELSEIF NOT DEFINED(ENABLE_CORPSES) AND NOT DEFINED(DISABLE_CORPSES)}
+  // default ENABLE/DISABLE corpses
+  {$IFDEF HEADLESS}
+    {$DEFINE DISABLE_CORPSES}
+  {$ELSE}
+    {$DEFINE ENABLE_CORPSES}
+  {$ENDIF}
+{$ENDIF}
+
 {$IF DEFINED(USE_SYSSTUB)}
   {$IF DEFINED(USE_SDL) OR DEFINED(USE_SDL2)}
     {$ERROR Only one system driver must be selected!}