From 9c48cca3ecf72ee2f326460c7fe895245544bdcf Mon Sep 17 00:00:00 2001 From: DeaDDooMER Date: Sat, 29 Jan 2022 17:56:47 +0300 Subject: [PATCH] game: disable corpses for server --- src/game/Doom2DF.lpr | 3 + src/game/g_console.pas | 7 +- src/game/g_corpses.pas | 396 ++++++++++++++++++++++++++++ src/game/g_game.pas | 52 +++- src/game/g_gibs.pas | 9 + src/game/g_menu.pas | 11 +- src/game/g_monsters.pas | 118 +++++---- src/game/g_netmsg.pas | 16 +- src/game/g_options.pas | 7 +- src/game/g_panel.pas | 35 ++- src/game/g_player.pas | 491 +++-------------------------------- src/game/g_saveload.pas | 70 +++++ src/game/g_shells.pas | 9 + src/game/g_weapons.pas | 122 +++++---- src/game/opengl/r_game.pas | 14 +- src/game/opengl/r_player.pas | 17 +- src/shared/a_modes.inc | 16 ++ 17 files changed, 801 insertions(+), 592 deletions(-) create mode 100644 src/game/g_corpses.pas diff --git a/src/game/Doom2DF.lpr b/src/game/Doom2DF.lpr index ec2bdfa..28e3b62 100644 --- a/src/game/Doom2DF.lpr +++ b/src/game/Doom2DF.lpr @@ -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', diff --git a/src/game/g_console.pas b/src/game/g_console.pas index 99335ba..687a412 100644 --- a/src/game/g_console.pas +++ b/src/game/g_console.pas @@ -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 index 0000000..d4c6c50 --- /dev/null +++ b/src/game/g_corpses.pas @@ -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 . + *) +{$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. diff --git a/src/game/g_game.pas b/src/game/g_game.pas index 8d47073..6812d51 100644 --- a/src/game/g_game.pas +++ b/src/game/g_game.pas @@ -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 diff --git a/src/game/g_gibs.pas b/src/game/g_gibs.pas index 627764b..8041264 100644 --- a/src/game/g_gibs.pas +++ b/src/game/g_gibs.pas @@ -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 diff --git a/src/game/g_menu.pas b/src/game/g_menu.pas index d88c79f..33a2477 100644 --- a/src/game/g_menu.pas +++ b/src/game/g_menu.pas @@ -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 diff --git a/src/game/g_monsters.pas b/src/game/g_monsters.pas index fbb75a5..9877ece 100644 --- a/src/game/g_monsters.pas +++ b/src/game/g_monsters.pas @@ -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; diff --git a/src/game/g_netmsg.pas b/src/game/g_netmsg.pas index 775a7ae..b719a8f 100644 --- a/src/game/g_netmsg.pas +++ b/src/game/g_netmsg.pas @@ -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; diff --git a/src/game/g_options.pas b/src/game/g_options.pas index 83bae96..936f2b8 100644 --- a/src/game/g_options.pas +++ b/src/game/g_options.pas @@ -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; diff --git a/src/game/g_panel.pas b/src/game/g_panel.pas index f9b7371..61d341e 100644 --- a/src/game/g_panel.pas +++ b/src/game/g_panel.pas @@ -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; diff --git a/src/game/g_player.pas b/src/game/g_player.pas index aab6b38..991b0d8 100644 --- a/src/game/g_player.pas +++ b/src/game/g_player.pas @@ -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(); diff --git a/src/game/g_saveload.pas b/src/game/g_saveload.pas index d603440..14539dd 100644 --- a/src/game/g_saveload.pas +++ b/src/game/g_saveload.pas @@ -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 diff --git a/src/game/g_shells.pas b/src/game/g_shells.pas index de0fc86..1ba5fdb 100644 --- a/src/game/g_shells.pas +++ b/src/game/g_shells.pas @@ -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 diff --git a/src/game/g_weapons.pas b/src/game/g_weapons.pas index 0353a85..beabbcf 100644 --- a/src/game/g_weapons.pas +++ b/src/game/g_weapons.pas @@ -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); diff --git a/src/game/opengl/r_game.pas b/src/game/opengl/r_game.pas index 58bbd63..286ce25 100644 --- a/src/game/opengl/r_game.pas +++ b/src/game/opengl/r_game.pas @@ -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); diff --git a/src/game/opengl/r_player.pas b/src/game/opengl/r_player.pas index c803b4c..49d2c0c 100644 --- a/src/game/opengl/r_player.pas +++ b/src/game/opengl/r_player.pas @@ -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); diff --git a/src/shared/a_modes.inc b/src/shared/a_modes.inc index 1ba7346..6789e6e 100644 --- a/src/shared/a_modes.inc +++ b/src/shared/a_modes.inc @@ -130,6 +130,11 @@ {$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)} @@ -187,6 +192,17 @@ {$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!} -- 2.29.2