From: DeaDDooMER Date: Tue, 8 Jun 2021 05:42:00 +0000 (+0300) Subject: render: separate player logic and drawing X-Git-Url: http://deadsoftware.ru/gitweb?a=commitdiff_plain;h=e12091dd9a87d838c40b0bf0956e40a967bb8dc9;p=d2df-sdl.git render: separate player logic and drawing --- diff --git a/src/game/Doom2DF.lpr b/src/game/Doom2DF.lpr index 8ee5396..5e81dcd 100644 --- a/src/game/Doom2DF.lpr +++ b/src/game/Doom2DF.lpr @@ -161,6 +161,7 @@ uses r_monsters in 'opengl/r_monsters.pas', r_netmaster in 'opengl/r_netmaster.pas', r_panel in 'opengl/r_panel.pas', + r_player in 'opengl/r_player.pas', r_weapons in 'opengl/r_weapons.pas', {$IFDEF USE_FMOD} diff --git a/src/game/g_player.pas b/src/game/g_player.pas index a7afb5c..f31bcee 100644 --- a/src/game/g_player.pas +++ b/src/game/g_player.pas @@ -104,6 +104,17 @@ const PLAYER1_DEF_COLOR: TRGB = (R:64; G:175; B:48); PLAYER2_DEF_COLOR: TRGB = (R:96; G:96; B:96); + AIR_DEF = 360; + AIR_MAX = 1091; + JET_MAX = 540; // ~30 sec + ANGLE_RIGHTUP = 55; + ANGLE_RIGHTDOWN = -35; + ANGLE_LEFTUP = 125; + ANGLE_LEFTDOWN = -145; + WEAPONPOINT: Array [TDirection] of TDFPoint = ((X:16; Y:32), (X:47; Y:32)); + TEAMCOLOR: Array [TEAM_RED..TEAM_BLUE] of TRGB = ((R:255; G:0; B:0), + (R:0; G:0; B:255)); + type TPlayerStat = record Num: Integer; @@ -222,7 +233,6 @@ type {procedure CollideItem();} procedure FlySmoke(Times: DWORD = 1); procedure OnFireFlame(Times: DWORD = 1); - function GetAmmoByWeapon(Weapon: Byte): Word; procedure SetAction(Action: Byte; Force: Boolean = False); procedure OnDamage(Angle: SmallInt); virtual; function firediry(): Integer; @@ -336,14 +346,6 @@ type procedure Spectate(NoMove: Boolean = False); procedure SwitchNoClip; procedure SoftReset(); - procedure Draw(); virtual; - procedure DrawPain(); - procedure DrawPickup(); - procedure DrawRulez(); - procedure DrawAim(); - procedure DrawIndicator(Color: TRGB); - procedure DrawBubble(); - procedure DrawGUI(); procedure PreUpdate(); procedure Update(); virtual; procedure RememberState(); @@ -371,6 +373,8 @@ type function getCameraObj(): TObj; + function GetAmmoByWeapon(Weapon: Byte): Word; // private state + public property Vel: TPoint2i read FObj.Vel; property Obj: TObj read FObj; @@ -411,6 +415,17 @@ type property JustTeleported: Boolean read FJustTeleported write FJustTeleported; property NetTime: LongWord read FNetTime write FNetTime; + (* internal state *) + property Angle_: SmallInt read FAngle; + property Spectator: Boolean read FSpectator; + property NoRespawn: Boolean read FNoRespawn; + property Berserk: Integer read FBerserk; + property Pain: Integer read FPain; + property Pickup: Integer read FPickup; + property PunchAnim: TAnimation read FPunchAnim write FPunchAnim; + property SpawnInvul: Integer read FSpawnInvul; + property Ghost: Boolean read FGhost; + published property eName: String read FName write FName; property eHealth: Integer read FHealth write FHealth; @@ -498,7 +513,6 @@ type procedure Respawn(Silent: Boolean; Force: Boolean = False); override; constructor Create(); override; destructor Destroy(); override; - procedure Draw(); override; function PickItem(ItemType: Byte; force: Boolean; var remove: Boolean): Boolean; override; function Heal(value: Word; Soft: Boolean): Boolean; override; procedure Update(); override; @@ -555,7 +569,6 @@ type destructor Destroy(); override; procedure Damage(Value: Word; SpawnerUID: Word; vx, vy: Integer); procedure Update(); - procedure Draw(); procedure SaveState (st: TStream); procedure LoadState (st: TStream); @@ -569,6 +582,11 @@ type property Obj: TObj read FObj; // copies object property State: Byte read FState; property Mess: Boolean read FMess; + + (* internal state *) + property Color: TRGB read FColor; + property Animation: TAnimation read FAnimation; + property AnimationMask: TAnimation read FAnimationMask; end; TTeamStat = Array [TEAM_RED..TEAM_BLUE] of @@ -615,9 +633,6 @@ procedure g_Player_Remove(UID: Word); procedure g_Player_ResetTeams(); procedure g_Player_PreUpdate(); procedure g_Player_UpdateAll(); -procedure g_Player_DrawAll(); -procedure g_Player_DrawDebug(p: TPlayer); -procedure g_Player_DrawHealth(); procedure g_Player_RememberAll(); procedure g_Player_ResetAll(Force, Silent: Boolean); function g_Player_Get(UID: Word): TPlayer; @@ -628,8 +643,6 @@ function g_Player_CreateCorpse(Player: TPlayer): Integer; procedure g_Player_CreateGibs(fX, fY: Integer; ModelName: String; fColor: TRGB); procedure g_Player_CreateShell(fX, fY, dX, dY: Integer; T: Byte); procedure g_Player_UpdatePhysicalObjects(); -procedure g_Player_DrawCorpses(); -procedure g_Player_DrawShells(); procedure g_Player_RemoveAllCorpses(); procedure g_Player_Corpses_SaveState (st: TStream); procedure g_Player_Corpses_LoadState (st: TStream); @@ -676,26 +689,16 @@ const TIME_RESPAWN1 = 1500; TIME_RESPAWN2 = 2000; TIME_RESPAWN3 = 3000; - AIR_DEF = 360; - AIR_MAX = 1091; - JET_MAX = 540; // ~30 sec PLAYER_SUIT_TIME = 30000; PLAYER_INVUL_TIME = 30000; PLAYER_INVIS_TIME = 35000; FRAG_COMBO_TIME = 3000; VEL_SW = 4; VEL_FLY = 6; - ANGLE_RIGHTUP = 55; - ANGLE_RIGHTDOWN = -35; - ANGLE_LEFTUP = 125; - ANGLE_LEFTDOWN = -145; PLAYER_HEADRECT: TRectWH = (X:24; Y:12; Width:20; Height:12); - WEAPONPOINT: Array [TDirection] of TDFPoint = ((X:16; Y:32), (X:47; Y:32)); BOT_MAXJUMP = 84; BOT_LONGDIST = 300; BOT_UNSAFEDIST = 128; - TEAMCOLOR: Array [TEAM_RED..TEAM_BLUE] of TRGB = ((R:255; G:0; B:0), - (R:0; G:0; B:255)); DIFFICULT_EASY: TDifficult = (DiagFire: 32; InvisFire: 32; DiagPrecision: 32; FlyPrecision: 32; Cover: 32; CloseJump: 32; WeaponPrior:(0,0,0,0,0,0,0,0,0,0,0); CloseWeaponPrior:(0,0,0,0,0,0,0,0,0,0,0)); @@ -832,7 +835,7 @@ begin ok := False; a := 0; -// Åñòü ëè ìåñòî â gPlayers: +// Есть ли место в gPlayers: if gPlayers <> nil then for a := 0 to High(gPlayers) do if gPlayers[a] = nil then @@ -841,14 +844,14 @@ begin Break; end; -// Íåò ìåñòà - ðàñøèðÿåì gPlayers: +// Нет места - расширяем gPlayers: if not ok then begin SetLength(gPlayers, Length(gPlayers)+1); a := High(gPlayers); end; -// Ñîçäàåì îáúåêò èãðîêà: +// Создаем объект игрока: if Bot then gPlayers[a] := TBot.Create() else @@ -860,7 +863,7 @@ begin if Bot and (g_Force_Model_Get() <> 0) then gPlayers[a].SetModel(g_Forced_Model_GetName()); -// Íåò ìîäåëè - ñîçäàíèå íå âîçìîæíî: +// Нет модели - создание не возможно: if gPlayers[a].FModel = nil then begin gPlayers[a].Free(); @@ -884,7 +887,7 @@ begin GM_COOP: gPlayers[a].FTeam := TEAM_COOP; end; -// Åñëè êîìàíäíàÿ èãðà - êðàñèì ìîäåëü â öâåò êîìàíäû: +// Если командная игра - красим модель в цвет команды: gPlayers[a].FColor := Color; if gPlayers[a].FTeam in [TEAM_RED, TEAM_BLUE] then gPlayers[a].FModel.Color := TEAMCOLOR[gPlayers[a].FTeam] @@ -977,15 +980,14 @@ var begin if not g_Game_IsServer then Exit; -// Íå äîáàâëÿåì áîòîâ åñëè ëèìèò óæå äîñòèãíóò if (g_Bot_GetCount() >= gMaxBots) then Exit; -// Ñïèñîê íàçâàíèé ìîäåëåé: +// Список названий моделей: m := g_PlayerModel_GetNames(); if m = nil then Exit; -// Êîìàíäà: +// Команда: if (gGameSettings.GameType = GT_SINGLE) or (gGameSettings.GameMode = GM_COOP) then Team := TEAM_COOP // COOP else @@ -994,7 +996,7 @@ begin else if Team = TEAM_NONE then // CTF / TDM begin - // Àâòîáàëàíñ êîìàíä: + // Автобаланс команд: tr := 0; tb := 0; @@ -1020,7 +1022,7 @@ begin Team := TEAM_BLUE; end; -// Âûáèðàåì áîòó èìÿ: +// Выбираем боту имя: _name := ''; if BotNames <> nil then for a := 0 to High(BotNames) do @@ -1030,17 +1032,17 @@ begin Break; end; -// Âûáèðàåì ñëó÷àéíóþ ìîäåëü: +// Выбираем случайную модель: _model := m[Random(Length(m))]; -// Ñîçäàåì áîòà: +// Создаем бота: with g_Player_Get(g_Player_Create(_model, _RGB(Min(Random(9)*32, 255), Min(Random(9)*32, 255), Min(Random(9)*32, 255)), Team, True)) as TBot do begin - // Åñëè èìåíè íåò, äåëàåì åãî èç UID áîòà + // Если имени нет, делаем его из UID бота if _name = '' then Name := Format('DFBOT%.5d', [UID]) else @@ -1077,15 +1079,14 @@ var begin if not g_Game_IsServer then Exit; -// Íå äîáàâëÿåì áîòîâ åñëè ëèìèò óæå äîñòèãíóò if (g_Bot_GetCount() >= gMaxBots) then Exit; -// Ñïèñîê íàçâàíèé ìîäåëåé: +// Список названий моделей: m := g_PlayerModel_GetNames(); if m = nil then Exit; -// Êîìàíäà: +// Команда: if (gGameSettings.GameType = GT_SINGLE) or (gGameSettings.GameMode = GM_COOP) then Team := TEAM_COOP // COOP else @@ -1095,7 +1096,7 @@ begin if Team = TEAM_NONE then Team := BotList[num].team; // CTF / TDM -// Âûáèðàåì íàñòðîéêè áîòà èç ñïèñêà ïî íîìåðó èëè èìåíè: +// Выбираем настройки бота из списка по номеру или имени: lName := AnsiLowerCase(lName); if (num < 0) or (num > Length(BotList)-1) then num := -1; @@ -1109,21 +1110,21 @@ begin if num = -1 then Exit; -// Èìÿ áîòà: +// Имя бота: _name := BotList[num].name; -// Çàíÿòî - âûáèðàåì ñëó÷àéíîå: +// Занято - выбираем случайное: if not g_Player_ValidName(_name) then repeat _name := Format('DFBOT%.2d', [Random(100)]); until g_Player_ValidName(_name); -// Ìîäåëü: +// Модель: _model := BotList[num].model; -// Íåò òàêîé - âûáèðàåì ñëó÷àéíóþ: +// Нет такой - выбираем случайную: if not InSArray(_model, m) then _model := m[Random(Length(m))]; -// Ñîçäàåì áîòà: +// Создаем бота: with g_Player_Get(g_Player_Create(_model, BotList[num].color, Team, True)) as TBot do begin Name := _name; @@ -1222,7 +1223,7 @@ begin if e_FindResource(DataDirs, path) = false then Exit; -// ×èòàåì âîçìîæíûå èìåíà áîòîâ èç ôàéëà: +// Читаем возможные имена ботов из файла: AssignFile(F, path); Reset(F); @@ -1240,10 +1241,10 @@ begin CloseFile(F); -// Ïåðåìåøèâàåì èõ: +// Перемешиваем их: g_Bot_MixNames(); -// ×èòàåì ôàéë ñ ïàðàìåòðàìè áîòîâ: +// Читаем файл с параметрами ботов: config := TConfig.CreateFile(path); BotList := nil; a := 0; @@ -1254,38 +1255,38 @@ begin with BotList[High(BotList)] do begin - // Èìÿ áîòà: + // Имя бота: name := config.ReadStr(IntToStr(a), 'name', ''); - // Ìîäåëü: + // Модель: model := config.ReadStr(IntToStr(a), 'model', ''); - // Êîìàíäà: + // Команда: if config.ReadStr(IntToStr(a), 'team', 'red') = 'red' then team := TEAM_RED else team := TEAM_BLUE; - // Öâåò ìîäåëè: + // Цвет модели: sa := parse(config.ReadStr(IntToStr(a), 'color', '')); color.R := StrToIntDef(sa[0], 0); color.G := StrToIntDef(sa[1], 0); color.B := StrToIntDef(sa[2], 0); - // Âåðîÿòíîñòü ñòðåëüáû ïîä óãëîì: + // Вероятность стрельбы под углом: diag_fire := config.ReadInt(IntToStr(a), 'diag_fire', 0); - // Âåðîÿòíîñòü îòâåòíîãî îãíÿ ïî íåâèäèìîìó ñîïåðíèêó: + // Вероятность ответного огня по невидимому сопернику: invis_fire := config.ReadInt(IntToStr(a), 'invis_fire', 0); - // Òî÷íîñòü ñòðåëüáû ïîä óãëîì: + // Точность стрельбы под углом: diag_precision := config.ReadInt(IntToStr(a), 'diag_precision', 0); - // Òî÷íîñòü ñòðåëüáû â ïîëåòå: + // Точность стрельбы в полете: fly_precision := config.ReadInt(IntToStr(a), 'fly_precision', 0); - // Òî÷íîñòü óêëîíåíèÿ îò ñíàðÿäîâ: + // Точность уклонения от снарядов: cover := config.ReadInt(IntToStr(a), 'cover', 0); - // Âåðîÿòíîñòü ïðûæêà ïðè ïðèáëèæåíèè ñîïåðíèêà: + // Вероятность прыжка при приближении соперника: close_jump := config.ReadInt(IntToStr(a), 'close_jump', 0); - // Ïðèîðèòåòû îðóæèÿ äëÿ äàëüíåãî áîÿ: + // Приоритеты оружия для дальнего боя: sa := parse(config.ReadStr(IntToStr(a), 'w_prior1', '')); if Length(sa) = 10 then for b := 0 to 9 do w_prior1[b] := EnsureRange(StrToInt(sa[b]), 0, 9); - // Ïðèîðèòåòû îðóæèÿ äëÿ áëèæíåãî áîÿ: + // Приоритеты оружия для ближнего боя: sa := parse(config.ReadStr(IntToStr(a), 'w_prior2', '')); if Length(sa) = 10 then for b := 0 to 9 do @@ -1364,57 +1365,6 @@ begin //e_WriteLog('***g_Player_UpdateAll: EXIT', MSG_WARNING); end; -procedure g_Player_DrawAll(); -var - i: Integer; -begin - if gPlayers = nil then Exit; - - for i := 0 to High(gPlayers) do - if gPlayers[i] <> nil then - if gPlayers[i] is TPlayer then gPlayers[i].Draw() - else TBot(gPlayers[i]).Draw(); -end; - -procedure g_Player_DrawDebug(p: TPlayer); -var - fW, fH: Byte; -begin - if p = nil then Exit; - if (@p.FObj) = nil then Exit; - - e_TextureFontGetSize(gStdFont, fW, fH); - - e_TextureFontPrint(0, 0 , 'Pos X: ' + IntToStr(p.FObj.X), gStdFont); - e_TextureFontPrint(0, fH , 'Pos Y: ' + IntToStr(p.FObj.Y), gStdFont); - e_TextureFontPrint(0, fH * 2, 'Vel X: ' + IntToStr(p.FObj.Vel.X), gStdFont); - e_TextureFontPrint(0, fH * 3, 'Vel Y: ' + IntToStr(p.FObj.Vel.Y), gStdFont); - e_TextureFontPrint(0, fH * 4, 'Acc X: ' + IntToStr(p.FObj.Accel.X), gStdFont); - e_TextureFontPrint(0, fH * 5, 'Acc Y: ' + IntToStr(p.FObj.Accel.Y), gStdFont); - e_TextureFontPrint(0, fH * 6, 'Old X: ' + IntToStr(p.FObj.oldX), gStdFont); - e_TextureFontPrint(0, fH * 7, 'Old Y: ' + IntToStr(p.FObj.oldY), gStdFont); -end; - -procedure g_Player_DrawHealth(); -var - i: Integer; - fW, fH: Byte; -begin - if gPlayers = nil then Exit; - e_TextureFontGetSize(gStdFont, fW, fH); - - for i := 0 to High(gPlayers) do - if gPlayers[i] <> nil then - begin - e_TextureFontPrint(gPlayers[i].FObj.X + gPlayers[i].FObj.Rect.X, - gPlayers[i].FObj.Y + gPlayers[i].FObj.Rect.Y + gPlayers[i].FObj.Rect.Height - fH * 2, - IntToStr(gPlayers[i].FHealth), gStdFont); - e_TextureFontPrint(gPlayers[i].FObj.X + gPlayers[i].FObj.Rect.X, - gPlayers[i].FObj.Y + gPlayers[i].FObj.Rect.Y + gPlayers[i].FObj.Rect.Height - fH, - IntToStr(gPlayers[i].FArmor), gStdFont); - end; -end; - function g_Player_Get(UID: Word): TPlayer; var a: Integer; @@ -1552,7 +1502,7 @@ begin if Player.alive then Exit; -// Ðàçðûâàåì ñâÿçü ñ ïðåæíèì òðóïîì: +// Разрываем связь с прежним трупом: i := Player.FCorpse; if (i >= 0) and (i < Length(gCorpses)) then begin @@ -1699,7 +1649,7 @@ var end; begin -// Êóñêè ìÿñà: +// Куски мяса: if gGibs <> nil then for i := 0 to High(gGibs) do if gGibs[i].alive then @@ -1718,7 +1668,7 @@ begin Continue; end; - // Îòëåòàåò îò óäàðà î ñòåíó/ïîòîëîê/ïîë: + // Отлетает от удара о стену/потолок/пол: if WordBool(mr and MOVE_HITWALL) then Obj.Vel.X := -(vel.X div 2); if WordBool(mr and (MOVE_HITCEIL or MOVE_HITLAND)) then @@ -1735,12 +1685,12 @@ begin RAngle := (360 - (Abs(RAngle) mod 360)) mod 360; end; - // Ñîïðîòèâëåíèå âîçäóõà äëÿ êóñêà òðóïà: + // Сопротивление воздуха для куска трупа: if gTime mod (GAME_TICK*3) = 0 then Obj.Vel.X := z_dec(Obj.Vel.X, 1); end; -// Òðóïû: +// Трупы: if gCorpses <> nil then for i := 0 to High(gCorpses) do if gCorpses[i] <> nil then @@ -1752,7 +1702,7 @@ begin else gCorpses[i].Update(); -// Ãèëüçû: +// Гильзы: if gShells <> nil then for i := 0 to High(gShells) do if gShells[i].alive then @@ -1771,7 +1721,7 @@ begin Continue; end; - // Îòëåòàåò îò óäàðà î ñòåíó/ïîòîëîê/ïîë: + // Отлетает от удара о стену/потолок/пол: if WordBool(mr and MOVE_HITWALL) then begin Obj.Vel.X := -(vel.X div 2); @@ -1847,61 +1797,6 @@ procedure TGib.positionChanged (); inline; begin end; procedure TShell.positionChanged (); inline; begin end; -procedure g_Player_DrawCorpses(); -var - i, fX, fY: Integer; - a: TDFPoint; -begin - if gGibs <> nil then - for i := 0 to High(gGibs) do - if gGibs[i].alive then - with gGibs[i] do - begin - if not g_Obj_Collide(sX, sY, sWidth, sHeight, @Obj) then - Continue; - - Obj.lerp(gLerpFactor, fX, fY); - - a.X := Obj.Rect.X+(Obj.Rect.Width div 2); - a.y := Obj.Rect.Y+(Obj.Rect.Height div 2); - - e_DrawAdv(ID, fX, fY, 0, True, False, RAngle, @a, TMirrorType.None); - - e_Colors := Color; - e_DrawAdv(MaskID, fX, fY, 0, True, False, RAngle, @a, TMirrorType.None); - e_Colors.R := 255; - e_Colors.G := 255; - e_Colors.B := 255; - end; - - if gCorpses <> nil then - for i := 0 to High(gCorpses) do - if gCorpses[i] <> nil then - gCorpses[i].Draw(); -end; - -procedure g_Player_DrawShells(); -var - i, fX, fY: Integer; - a: TDFPoint; -begin - if gShells <> nil then - for i := 0 to High(gShells) do - if gShells[i].alive then - with gShells[i] do - begin - if not g_Obj_Collide(sX, sY, sWidth, sHeight, @Obj) then - Continue; - - Obj.lerp(gLerpFactor, fX, fY); - - a.X := CX; - a.Y := CY; - - e_DrawAdv(SpriteID, fX, fY, 0, True, False, RAngle, @a, TMirrorType.None); - end; -end; - procedure g_Player_RemoveAllCorpses(); var i: Integer; @@ -1925,25 +1820,25 @@ 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].FModelName); - // Òèï ñìåðòè + // Тип смерти utils.writeBool(st, gCorpses[i].Mess); - // Ñîõðàíÿåì äàííûå òðóïà: + // Сохраняем данные трупа: gCorpses[i].SaveState(st); end; end; @@ -1960,22 +1855,22 @@ begin 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; @@ -2174,7 +2069,7 @@ begin g_Sound_PlayExAt('SOUND_ITEM_GETMED', FObj.X, FObj.Y) else g_Sound_PlayExAt('SOUND_ITEM_GETITEM', FObj.X, FObj.Y); - // Íàäî óáðàòü ñ êàðòû, åñëè ýòî íå êëþ÷, êîòîðûì íóæíî ïîäåëèòñÿ ñ äðóãèì èãðîêîì: + // Надо убрать с карты, если это не ключ, которым нужно поделится с другим игроком: if r and not ((ItemType in [ITEM_KEY_RED, ITEM_KEY_GREEN, ITEM_KEY_BLUE]) and (gGameSettings.GameType = GT_SINGLE) and (g_Player_GetCount() > 1)) then @@ -2271,7 +2166,7 @@ begin FLastHit := t; -// Íåóÿçâèìîñòü íå ñïàñàåò îò ëîâóøåê: +// Неуязвимость не спасает от ловушек: if ((t = HIT_TRAP) or (t = HIT_SELF)) and (not FGodMode) then begin if not g_Game_IsClient then @@ -2279,18 +2174,18 @@ begin FArmor := 0; if t = HIT_TRAP then begin - // Ëîâóøêà óáèâàåò ñðàçó: + // Ловушка убивает сразу: FHealth := -100; Kill(K_EXTRAHARDKILL, SpawnerUID, t); end; if t = HIT_SELF then begin - // Ñàìîóáèéñòâî: + // Самоубийство: FHealth := 0; Kill(K_SIMPLEKILL, SpawnerUID, t); end; end; - // Îáíóëèòü äåéñòâèÿ ïðèìî÷åê, ÷òîáû ôîí ïðîïàë + // Обнулить действия примочек, чтобы фон пропал FMegaRulez[MR_SUIT] := 0; FMegaRulez[MR_INVUL] := 0; FMegaRulez[MR_INVIS] := 0; @@ -2298,22 +2193,22 @@ begin FBerserk := 0; end; -// Íî îò îñòàëüíîãî ñïàñàåò: +// Но от остального спасает: if FMegaRulez[MR_INVUL] >= gTime then Exit; -// ×èò-êîä "ÃÎÐÅÖ": +// Чит-код "ГОРЕЦ": if FGodMode then Exit; -// Åñëè åñòü óðîí ñâîèì, èëè ðàíèë ñàì ñåáÿ, èëè òåáÿ ðàíèë ïðîòèâíèê: +// Если есть урон своим, или ранил сам себя, или тебя ранил противник: if LongBool(gGameSettings.Options and GAME_OPTION_TEAMDAMAGE) or (SpawnerUID = FUID) or (not SameTeam(FUID, SpawnerUID)) then begin FLastSpawnerUID := SpawnerUID; - // Êðîâü (ïóçûðüêè, åñëè â âîäå): + // Кровь (пузырьки, если в воде): if gBloodCount > 0 then begin c := Min(value, 200)*gBloodCount + Random(Min(value, 200) div 2); @@ -2340,11 +2235,11 @@ begin end; end; - // Áóôåð óðîíà: + // Буфер урона: if FAlive then Inc(FDamageBuffer, value); - // Âñïûøêà áîëè: + // Вспышка боли: if gFlash <> 0 then FPain := FPain + value; end; @@ -2404,640 +2299,6 @@ begin inherited; end; -procedure TPlayer.DrawIndicator(Color: TRGB); -var - indX, indY, fX, fY, fSlope: Integer; - indW, indH: Word; - indA: Single; - a: TDFPoint; - nW, nH: Byte; - ID: DWORD; - c: TRGB; -begin - if FAlive then - begin - FObj.lerp(gLerpFactor, fX, fY); - fSlope := nlerp(FSlopeOld, FObj.slopeUpLeft, gLerpFactor); - - case gPlayerIndicatorStyle of - 0: - begin - if g_Texture_Get('TEXTURE_PLAYER_INDICATOR', ID) then - begin - e_GetTextureSize(ID, @indW, @indH); - a.X := indW div 2; - a.Y := indH div 2; - - if (FObj.X + FObj.Rect.X) < 0 then - begin - indA := 90; - indX := fX + FObj.Rect.X + FObj.Rect.Width; - indY := fY + FObj.Rect.Y + (FObj.Rect.Height - indW) div 2; - end - - else if (FObj.X + FObj.Rect.X + FObj.Rect.Width) > Max(gMapInfo.Width, gPlayerScreenSize.X) then - begin - indA := 270; - indX := fX + FObj.Rect.X - indH; - indY := fY + FObj.Rect.Y + (FObj.Rect.Height - indW) div 2; - end - - else if (FObj.Y - indH) < 0 then - begin - indA := 180; - indX := fX + FObj.Rect.X + (FObj.Rect.Width - indW) div 2; - indY := fY + FObj.Rect.Y + FObj.Rect.Height; - end - - else - begin - indA := 0; - indX := fX + FObj.Rect.X + (FObj.Rect.Width - indW) div 2; - indY := fY - indH; - end; - - indY := indY + fSlope; - indX := EnsureRange(indX, 0, Max(gMapInfo.Width, gPlayerScreenSize.X) - indW); - indY := EnsureRange(indY, 0, Max(gMapInfo.Height, gPlayerScreenSize.Y) - indH); - - c := e_Colors; - e_Colors := Color; - e_DrawAdv(ID, indX, indY, 0, True, False, indA, @a); - e_Colors := c; - end; - end; - - 1: - begin - e_TextureFontGetSize(gStdFont, nW, nH); - indX := fX + FObj.Rect.X + (FObj.Rect.Width - Length(FName) * nW) div 2; - indY := fY - nH + fSlope; - e_TextureFontPrintEx(indX, indY, FName, gStdFont, Color.R, Color.G, Color.B, 1.0, True); - end; - end; - end; -end; - -procedure TPlayer.DrawBubble(); -var - bubX, bubY, fX, fY: Integer; - ID: LongWord; - Rb, Gb, Bb, - Rw, Gw, Bw: SmallInt; - Dot: Byte; - CObj: TObj; -begin - CObj := getCameraObj(); - CObj.lerp(gLerpFactor, fX, fY); - // NB: _F_Obj.Rect is used to keep the bubble higher; this is not a mistake - bubX := fX+FObj.Rect.X + IfThen(FDirection = TDirection.D_LEFT, -4, 18); - bubY := fY+FObj.Rect.Y - 18; - Rb := 64; - Gb := 64; - Bb := 64; - Rw := 240; - Gw := 240; - Bw := 240; - case gChatBubble of - 1: // simple textual non-bubble - begin - bubX := fX+FObj.Rect.X - 11; - bubY := fY+FObj.Rect.Y - 17; - e_TextureFontPrint(bubX, bubY, '[...]', gStdFont); - Exit; - end; - 2: // advanced pixel-perfect bubble - begin - if FTeam = TEAM_RED then - Rb := 255 - else - if FTeam = TEAM_BLUE then - Bb := 255; - end; - 3: // colored bubble - begin - Rb := FModel.Color.R; - Gb := FModel.Color.G; - Bb := FModel.Color.B; - Rw := Min(Rb * 2 + 64, 255); - Gw := Min(Gb * 2 + 64, 255); - Bw := Min(Bb * 2 + 64, 255); - if (Abs(Rw - Rb) < 32) - or (Abs(Gw - Gb) < 32) - or (Abs(Bw - Bb) < 32) then - begin - Rb := Max(Rw div 2 - 16, 0); - Gb := Max(Gw div 2 - 16, 0); - Bb := Max(Bw div 2 - 16, 0); - end; - end; - 4: // custom textured bubble - begin - if g_Texture_Get('TEXTURE_PLAYER_TALKBUBBLE', ID) then - if FDirection = TDirection.D_RIGHT then - e_Draw(ID, bubX - 6, bubY - 7, 0, True, False) - else - e_Draw(ID, bubX - 6, bubY - 7, 0, True, False, TMirrorType.Horizontal); - Exit; - end; - end; - - // Outer borders - e_DrawQuad(bubX + 1, bubY , bubX + 18, bubY + 13, Rb, Gb, Bb); - e_DrawQuad(bubX , bubY + 1, bubX + 19, bubY + 12, Rb, Gb, Bb); - // Inner box - e_DrawFillQuad(bubX + 1, bubY + 1, bubX + 18, bubY + 12, Rw, Gw, Bw, 0); - - // Tail - Dot := IfThen(FDirection = TDirection.D_LEFT, 14, 5); - e_DrawLine(1, bubX + Dot, bubY + 14, bubX + Dot, bubY + 16, Rb, Gb, Bb); - e_DrawLine(1, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 1, Dot + 1), bubY + 13, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 1, Dot + 1), bubY + 15, Rw, Gw, Bw); - e_DrawLine(1, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 2, Dot + 2), bubY + 13, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 2, Dot + 2), bubY + 14, Rw, Gw, Bw); - e_DrawLine(1, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 3, Dot + 3), bubY + 13, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 3, Dot + 3), bubY + 13, Rw, Gw, Bw); - e_DrawLine(1, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 3, Dot + 3), bubY + 14, bubX + IfThen(FDirection = TDirection.D_LEFT, Dot - 1, Dot + 1), bubY + 16, Rb, Gb, Bb); - - // Dots - Dot := 6; - e_DrawFillQuad(bubX + Dot, bubY + 8, bubX + Dot + 1, bubY + 9, Rb, Gb, Bb, 0); - e_DrawFillQuad(bubX + Dot + 3, bubY + 8, bubX + Dot + 4, bubY + 9, Rb, Gb, Bb, 0); - e_DrawFillQuad(bubX + Dot + 6, bubY + 8, bubX + Dot + 7, bubY + 9, Rb, Gb, Bb, 0); -end; - -procedure TPlayer.Draw(); -var - ID: DWORD; - w, h: Word; - dr: Boolean; - Mirror: TMirrorType; - fX, fY, fSlope: Integer; -begin - FObj.lerp(gLerpFactor, fX, fY); - fSlope := nlerp(FSlopeOld, FObj.slopeUpLeft, gLerpFactor); - - if FAlive then - begin - if Direction = TDirection.D_RIGHT then - Mirror := TMirrorType.None - else - Mirror := TMirrorType.Horizontal; - - if FPunchAnim <> nil then - begin - FPunchAnim.Draw(fX+IfThen(Direction = TDirection.D_LEFT, 15-FObj.Rect.X, FObj.Rect.X-15), - fY+fSlope+FObj.Rect.Y-11, Mirror); - if FPunchAnim.played then - begin - FPunchAnim.Free; - FPunchAnim := nil; - end; - end; - - if (FMegaRulez[MR_INVUL] > gTime) and ((gPlayerDrawn <> Self) or (FSpawnInvul >= gTime)) then - if g_Texture_Get('TEXTURE_PLAYER_INVULPENTA', ID) then - begin - e_GetTextureSize(ID, @w, @h); - if FDirection = TDirection.D_LEFT then - e_Draw(ID, fX+FObj.Rect.X+(FObj.Rect.Width div 2)-(w div 2)+4, - fY+FObj.Rect.Y+(FObj.Rect.Height div 2)-(h div 2)-7+fSlope, 0, True, False) - else - e_Draw(ID, fX+FObj.Rect.X+(FObj.Rect.Width div 2)-(w div 2)-2, - fY+FObj.Rect.Y+(FObj.Rect.Height div 2)-(h div 2)-7+fSlope, 0, True, False); - end; - - if FMegaRulez[MR_INVIS] > gTime then - begin - if (gPlayerDrawn <> nil) and ((Self = gPlayerDrawn) or - ((FTeam = gPlayerDrawn.Team) and (gGameSettings.GameMode <> GM_DM))) then - begin - if (FMegaRulez[MR_INVIS] - gTime) <= 2100 then - dr := not Odd((FMegaRulez[MR_INVIS] - gTime) div 300) - else - dr := True; - if dr then - FModel.Draw(fX, fY+fSlope, 200) - else - FModel.Draw(fX, fY+fSlope); - end - else - FModel.Draw(fX, fY+fSlope, 254); - end - else - FModel.Draw(fX, fY+fSlope); - end; - - if g_debug_Frames then - begin - e_DrawQuad(FObj.X+FObj.Rect.X, - FObj.Y+FObj.Rect.Y, - FObj.X+FObj.Rect.X+FObj.Rect.Width-1, - FObj.Y+FObj.Rect.Y+FObj.Rect.Height-1, - 0, 255, 0); - end; - - if (gChatBubble > 0) and (FKeys[KEY_CHAT].Pressed) and not FGhost then - if (FMegaRulez[MR_INVIS] <= gTime) or ((gPlayerDrawn <> nil) and ((Self = gPlayerDrawn) or - ((FTeam = gPlayerDrawn.Team) and (gGameSettings.GameMode <> GM_DM)))) then - DrawBubble(); - // e_DrawPoint(5, 335, 288, 255, 0, 0); // DL, UR, DL, UR - if gAimLine and alive and - ((Self = gPlayer1) or (Self = gPlayer2)) then - DrawAim(); -end; - - -procedure TPlayer.DrawAim(); - procedure drawCast (sz: Integer; ax0, ay0, ax1, ay1: Integer); - var - ex, ey: Integer; - begin - -{$IFDEF ENABLE_HOLMES} - if isValidViewPort and (self = gPlayer1) then - begin - g_Holmes_plrLaser(ax0, ay0, ax1, ay1); - end; -{$ENDIF} - - e_DrawLine(sz, ax0, ay0, ax1, ay1, 255, 0, 0, 96); - if (g_Map_traceToNearestWall(ax0, ay0, ax1, ay1, @ex, @ey) <> nil) then - begin - e_DrawLine(sz, ax0, ay0, ex, ey, 0, 255, 0, 96); - end - else - begin - e_DrawLine(sz, ax0, ay0, ex, ey, 0, 0, 255, 96); - end; - end; - -var - wx, wy, xx, yy: Integer; - angle: SmallInt; - sz, len: Word; -begin - wx := FObj.X + WEAPONPOINT[FDirection].X + IfThen(FDirection = TDirection.D_LEFT, 7, -7); - wy := FObj.Y + WEAPONPOINT[FDirection].Y; - angle := FAngle; - len := 1024; - sz := 2; - case FCurrWeap of - 0: begin // Punch - len := 12; - sz := 4; - end; - 1: begin // Chainsaw - len := 24; - sz := 6; - end; - 2: begin // Pistol - len := 1024; - sz := 2; - if angle = ANGLE_RIGHTUP then Dec(angle, 2); - if angle = ANGLE_RIGHTDOWN then Inc(angle, 4); - if angle = ANGLE_LEFTUP then Inc(angle, 2); - if angle = ANGLE_LEFTDOWN then Dec(angle, 4); - end; - 3: begin // Shotgun - len := 1024; - sz := 3; - if angle = ANGLE_RIGHTUP then Dec(angle, 2); - if angle = ANGLE_RIGHTDOWN then Inc(angle, 4); - if angle = ANGLE_LEFTUP then Inc(angle, 2); - if angle = ANGLE_LEFTDOWN then Dec(angle, 4); - end; - 4: begin // Double Shotgun - len := 1024; - sz := 4; - if angle = ANGLE_RIGHTUP then Dec(angle, 2); - if angle = ANGLE_RIGHTDOWN then Inc(angle, 4); - if angle = ANGLE_LEFTUP then Inc(angle, 2); - if angle = ANGLE_LEFTDOWN then Dec(angle, 4); - end; - 5: begin // Chaingun - len := 1024; - sz := 3; - if angle = ANGLE_RIGHTUP then Dec(angle, 2); - if angle = ANGLE_RIGHTDOWN then Inc(angle, 4); - if angle = ANGLE_LEFTUP then Inc(angle, 2); - if angle = ANGLE_LEFTDOWN then Dec(angle, 4); - end; - 6: begin // Rocket Launcher - len := 1024; - sz := 7; - if angle = ANGLE_RIGHTUP then Inc(angle, 2); - if angle = ANGLE_RIGHTDOWN then Inc(angle, 4); - if angle = ANGLE_LEFTUP then Dec(angle, 2); - if angle = ANGLE_LEFTDOWN then Dec(angle, 4); - end; - 7: begin // Plasmagun - len := 1024; - sz := 5; - if angle = ANGLE_RIGHTUP then Inc(angle); - if angle = ANGLE_RIGHTDOWN then Inc(angle, 3); - if angle = ANGLE_LEFTUP then Dec(angle); - if angle = ANGLE_LEFTDOWN then Dec(angle, 3); - end; - 8: begin // BFG - len := 1024; - sz := 12; - if angle = ANGLE_RIGHTUP then Inc(angle, 1); - if angle = ANGLE_RIGHTDOWN then Inc(angle, 2); - if angle = ANGLE_LEFTUP then Dec(angle, 1); - if angle = ANGLE_LEFTDOWN then Dec(angle, 2); - end; - 9: begin // Super Chaingun - len := 1024; - sz := 4; - if angle = ANGLE_RIGHTUP then Dec(angle, 2); - if angle = ANGLE_RIGHTDOWN then Inc(angle, 4); - if angle = ANGLE_LEFTUP then Inc(angle, 2); - if angle = ANGLE_LEFTDOWN then Dec(angle, 4); - end; - end; - xx := Trunc(Cos(-DegToRad(angle)) * len) + wx; - yy := Trunc(Sin(-DegToRad(angle)) * len) + wy; - {$IF DEFINED(D2F_DEBUG)} - drawCast(sz, wx, wy, xx, yy); - {$ELSE} - e_DrawLine(sz, wx, wy, xx, yy, 255, 0, 0, 96); - {$ENDIF} -end; - -procedure TPlayer.DrawGUI(); -var - ID: DWORD; - X, Y, SY, a, p, m: Integer; - tw, th: Word; - cw, ch: Byte; - s: string; - stat: TPlayerStatArray; -begin - X := gPlayerScreenSize.X; - SY := gPlayerScreenSize.Y; - Y := 0; - - if gShowScore and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then - begin - if gGameSettings.GameMode = GM_CTF then - a := 32 + 8 - else - a := 0; - if gGameSettings.GameMode = GM_CTF then - begin - s := 'TEXTURE_PLAYER_REDFLAG'; - if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then - s := 'TEXTURE_PLAYER_REDFLAG_S'; - if gFlags[FLAG_RED].State = FLAG_STATE_DROPPED then - s := 'TEXTURE_PLAYER_REDFLAG_D'; - if g_Texture_Get(s, ID) then - e_Draw(ID, X-16-32, 240-72-4, 0, True, False); - end; - - s := IntToStr(gTeamStat[TEAM_RED].Score); - e_CharFont_GetSize(gMenuFont, s, tw, th); - e_CharFont_PrintEx(gMenuFont, X-16-a-tw, 240-72-4, s, TEAMCOLOR[TEAM_RED]); - - if gGameSettings.GameMode = GM_CTF then - begin - s := 'TEXTURE_PLAYER_BLUEFLAG'; - if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then - s := 'TEXTURE_PLAYER_BLUEFLAG_S'; - if gFlags[FLAG_BLUE].State = FLAG_STATE_DROPPED then - s := 'TEXTURE_PLAYER_BLUEFLAG_D'; - if g_Texture_Get(s, ID) then - e_Draw(ID, X-16-32, 240-32-4, 0, True, False); - end; - - s := IntToStr(gTeamStat[TEAM_BLUE].Score); - e_CharFont_GetSize(gMenuFont, s, tw, th); - e_CharFont_PrintEx(gMenuFont, X-16-a-tw, 240-32-4, s, TEAMCOLOR[TEAM_BLUE]); - end; - - if g_Texture_Get('TEXTURE_PLAYER_HUDBG', ID) then - e_DrawFill(ID, X, 0, 1, (gPlayerScreenSize.Y div 256)+IfThen(gPlayerScreenSize.Y mod 256 > 0, 1, 0), - 0, False, False); - - if g_Texture_Get('TEXTURE_PLAYER_HUD', ID) then - e_Draw(ID, X+2, Y, 0, True, False); - - if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then - begin - if gShowStat then - begin - s := IntToStr(Frags); - e_CharFont_GetSize(gMenuFont, s, tw, th); - e_CharFont_PrintEx(gMenuFont, X-16-tw, Y, s, _RGB(255, 0, 0)); - - s := ''; - p := 1; - m := 0; - stat := g_Player_GetStats(); - if stat <> nil then - begin - p := 1; - - for a := 0 to High(stat) do - if stat[a].Name <> Name then - begin - if stat[a].Frags > m then m := stat[a].Frags; - if stat[a].Frags > Frags then p := p+1; - end; - end; - - s := IntToStr(p)+' / '+IntToStr(Length(stat))+' '; - if Frags >= m then s := s+'+' else s := s+'-'; - s := s+IntToStr(Abs(Frags-m)); - - e_CharFont_GetSize(gMenuSmallFont, s, tw, th); - e_CharFont_PrintEx(gMenuSmallFont, X-16-tw, Y+32, s, _RGB(255, 0, 0)); - end; - - if gLMSRespawn > LMS_RESPAWN_NONE then - begin - s := _lc[I_GAME_WARMUP]; - e_CharFont_GetSize(gMenuFont, s, tw, th); - s := s + ': ' + IntToStr((gLMSRespawnTime - gTime) div 1000); - e_CharFont_PrintEx(gMenuFont, X-64-tw, SY-32, s, _RGB(0, 255, 0)); - end - else if gShowLives and (gGameSettings.MaxLives > 0) then - begin - s := IntToStr(Lives); - e_CharFont_GetSize(gMenuFont, s, tw, th); - e_CharFont_PrintEx(gMenuFont, X-16-tw, SY-32, s, _RGB(0, 255, 0)); - end; - end; - - e_CharFont_GetSize(gMenuSmallFont, FName, tw, th); - e_CharFont_PrintEx(gMenuSmallFont, X+98-(tw div 2), Y+8, FName, _RGB(255, 0, 0)); - - if R_BERSERK in FRulez then - e_Draw(gItemsTexturesID[ITEM_MEDKIT_BLACK], X+37, Y+45, 0, True, False) - else - e_Draw(gItemsTexturesID[ITEM_MEDKIT_LARGE], X+37, Y+45, 0, True, False); - - if g_Texture_Get('TEXTURE_PLAYER_ARMORHUD', ID) then - e_Draw(ID, X+36, Y+77, 0, True, False); - - s := IntToStr(IfThen(FHealth > 0, FHealth, 0)); - e_CharFont_GetSize(gMenuFont, s, tw, th); - e_CharFont_PrintEx(gMenuFont, X+178-tw, Y+40, s, _RGB(255, 0, 0)); - - s := IntToStr(FArmor); - e_CharFont_GetSize(gMenuFont, s, tw, th); - e_CharFont_PrintEx(gMenuFont, X+178-tw, Y+68, s, _RGB(255, 0, 0)); - - s := IntToStr(GetAmmoByWeapon(FCurrWeap)); - - case FCurrWeap of - WEAPON_KASTET: - begin - s := '--'; - ID := gItemsTexturesID[ITEM_WEAPON_KASTET]; - end; - WEAPON_SAW: - begin - s := '--'; - ID := gItemsTexturesID[ITEM_WEAPON_SAW]; - end; - WEAPON_PISTOL: ID := gItemsTexturesID[ITEM_WEAPON_PISTOL]; - WEAPON_CHAINGUN: ID := gItemsTexturesID[ITEM_WEAPON_CHAINGUN]; - WEAPON_SHOTGUN1: ID := gItemsTexturesID[ITEM_WEAPON_SHOTGUN1]; - WEAPON_SHOTGUN2: ID := gItemsTexturesID[ITEM_WEAPON_SHOTGUN2]; - WEAPON_SUPERPULEMET: ID := gItemsTexturesID[ITEM_WEAPON_SUPERPULEMET]; - WEAPON_ROCKETLAUNCHER: ID := gItemsTexturesID[ITEM_WEAPON_ROCKETLAUNCHER]; - WEAPON_PLASMA: ID := gItemsTexturesID[ITEM_WEAPON_PLASMA]; - WEAPON_BFG: ID := gItemsTexturesID[ITEM_WEAPON_BFG]; - WEAPON_FLAMETHROWER: ID := gItemsTexturesID[ITEM_WEAPON_FLAMETHROWER]; - end; - - e_CharFont_GetSize(gMenuFont, s, tw, th); - e_CharFont_PrintEx(gMenuFont, X+178-tw, Y+158, s, _RGB(255, 0, 0)); - e_Draw(ID, X+20, Y+160, 0, True, False); - - if R_KEY_RED in FRulez then - e_Draw(gItemsTexturesID[ITEM_KEY_RED], X+78, Y+214, 0, True, False); - - if R_KEY_GREEN in FRulez then - e_Draw(gItemsTexturesID[ITEM_KEY_GREEN], X+95, Y+214, 0, True, False); - - if R_KEY_BLUE in FRulez then - e_Draw(gItemsTexturesID[ITEM_KEY_BLUE], X+112, Y+214, 0, True, False); - - if FJetFuel > 0 then - begin - if g_Texture_Get('TEXTURE_PLAYER_HUDAIR', ID) then - e_Draw(ID, X+2, Y+116, 0, True, False); - if g_Texture_Get('TEXTURE_PLAYER_HUDJET', ID) then - e_Draw(ID, X+2, Y+126, 0, True, False); - e_DrawLine(4, X+16, Y+122, X+16+Trunc(168*IfThen(FAir > 0, FAir, 0)/AIR_MAX), Y+122, 0, 0, 196); - e_DrawLine(4, X+16, Y+132, X+16+Trunc(168*FJetFuel/JET_MAX), Y+132, 208, 0, 0); - end - else - begin - if g_Texture_Get('TEXTURE_PLAYER_HUDAIR', ID) then - e_Draw(ID, X+2, Y+124, 0, True, False); - e_DrawLine(4, X+16, Y+130, X+16+Trunc(168*IfThen(FAir > 0, FAir, 0)/AIR_MAX), Y+130, 0, 0, 196); - end; - - if gShowPing and g_Game_IsClient then - begin - s := _lc[I_GAME_PING_HUD] + IntToStr(NetPeer.lastRoundTripTime) + _lc[I_NET_SLIST_PING_MS]; - e_TextureFontPrint(X + 4, Y + 242, s, gStdFont); - Y := Y + 16; - end; - - if FSpectator then - begin - e_TextureFontPrint(X + 4, Y + 242, _lc[I_PLAYER_SPECT], gStdFont); - e_TextureFontPrint(X + 4, Y + 258, _lc[I_PLAYER_SPECT2], gStdFont); - e_TextureFontPrint(X + 4, Y + 274, _lc[I_PLAYER_SPECT1], gStdFont); - if FNoRespawn then - begin - e_TextureFontGetSize(gStdFont, cw, ch); - s := _lc[I_PLAYER_SPECT4]; - e_TextureFontPrintEx(gScreenWidth div 2 - cw*(Length(s) div 2), - gScreenHeight-4-ch, s, gStdFont, 255, 255, 255, 1, True); - e_TextureFontPrint(X + 4, Y + 290, _lc[I_PLAYER_SPECT1S], gStdFont); - end; - - end; -end; - -procedure TPlayer.DrawRulez(); -var - dr: Boolean; -begin - // Ïðè âçÿòèè íåóÿçâèìîñòè ðèñóåòñÿ èíâåðñèîííûé áåëûé ôîí - if (FMegaRulez[MR_INVUL] >= gTime) and (FSpawnInvul < gTime) then - begin - if (FMegaRulez[MR_INVUL]-gTime) <= 2100 then - dr := not Odd((FMegaRulez[MR_INVUL]-gTime) div 300) - else - dr := True; - - if dr then - e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, - 191, 191, 191, 0, TBlending.Invert); - end; - - // Ïðè âçÿòèè çàùèòíîãî êîñòþìà ðèñóåòñÿ çåëåíîâàòûé ôîí - if FMegaRulez[MR_SUIT] >= gTime then - begin - if (FMegaRulez[MR_SUIT]-gTime) <= 2100 then - dr := not Odd((FMegaRulez[MR_SUIT]-gTime) div 300) - else - dr := True; - - if dr then - e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, - 0, 96, 0, 200, TBlending.None); - end; - - // Ïðè âçÿòèè áåðñåðêà ðèñóåòñÿ êðàñíîâàòûé ôîí - if (FBerserk >= 0) and (LongWord(FBerserk) >= gTime) and (gFlash = 2) then - begin - e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, - 255, 0, 0, 200, TBlending.None); - end; -end; - -procedure TPlayer.DrawPain(); -var - a, h: Integer; -begin - if FPain = 0 then Exit; - - a := FPain; - - if a < 15 then h := 0 - else if a < 35 then h := 1 - else if a < 55 then h := 2 - else if a < 75 then h := 3 - else if a < 95 then h := 4 - else h := 5; - - //if a > 255 then a := 255; - - e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, 255, 0, 0, 255-h*50); - //e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, 255-min(128, a), 255-a, 255-a, 0, B_FILTER); -end; - -procedure TPlayer.DrawPickup(); -var - a, h: Integer; -begin - if FPickup = 0 then Exit; - - a := FPickup; - - if a < 15 then h := 1 - else if a < 35 then h := 2 - else if a < 55 then h := 3 - else if a < 75 then h := 4 - else h := 5; - - e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, 150, 200, 150, 255-h*50); -end; - procedure TPlayer.DoPunch(); var id: DWORD; @@ -3066,8 +2327,8 @@ var locobj: TObj; begin if g_Game_IsClient then Exit; -// FBFGFireCounter - âðåìÿ ïåðåä âûñòðåëîì (äëÿ BFG) -// FReloading - âðåìÿ ïîñëå âûñòðåëà (äëÿ âñåãî) +// FBFGFireCounter - время перед выстрелом (для BFG) +// FReloading - время после выстрела (для всего) if FSpectator then begin @@ -3348,9 +2609,9 @@ begin if Timeout <= 0 then exit; if (FMegaRulez[MR_SUIT] > gTime) or (FMegaRulez[MR_INVUL] > gTime) then - exit; // Íå çàãîðàåìñÿ êîãäà åñòü çàùèòà + exit; // Не загораемся когда есть защита if g_Obj_CollidePanel(@FObj, 0, 0, PANEL_WATER or PANEL_ACID1 or PANEL_ACID2) then - exit; // Íå ïîäãîðàåì â âîäå íà âñÿêèé ñëó÷àé + exit; // Не подгораем в воде на всякий случай if FFireTime <= 0 then g_Sound_PlayExAt('SOUND_IGNITE', FObj.X, FObj.Y); FFireTime := Timeout; @@ -3363,7 +2624,7 @@ procedure TPlayer.Jump(); begin if gFly or FJetpack then begin - // Ïîëåò (÷èò-êîä èëè äæåòïàê): + // Полет (чит-код или джетпак): if FObj.Vel.Y > -VEL_FLY then FObj.Vel.Y := FObj.Vel.Y - 3; if FJetpack then @@ -3381,15 +2642,15 @@ begin Exit; end; -// Íå âêëþ÷àòü äæåòïàê â ðåæèìå ïðîõîæäåíèÿ ñêâîçü ñòåíû +// Не включать джетпак в режиме прохождения сквозь стены if FGhost then FCanJetpack := False; -// Ïðûãàåì èëè âñïëûâàåì: +// Прыгаем или всплываем: if (CollideLevel(0, 1) or g_Map_CollidePanel(FObj.X+PLAYER_RECT.X, FObj.Y+PLAYER_RECT.Y+36, PLAYER_RECT.Width, PLAYER_RECT.Height-33, PANEL_STEP, False) - ) and (FObj.Accel.Y = 0) then // Íå ïðûãàòü, åñëè åñòü âåðòèêàëüíîå óñêîðåíèå + ) and (FObj.Accel.Y = 0) then // Не прыгать, если есть вертикальное ускорение begin FObj.Vel.Y := -VEL_JUMP; FCanJetpack := False; @@ -3473,7 +2734,7 @@ begin if FLives = 0 then FNoRespawn := True; end; -// Íîìåð òèïà ñìåðòè: +// Номер типа смерти: a := 1; case KillType of K_SIMPLEKILL: a := 1; @@ -3482,13 +2743,13 @@ begin K_FALLKILL: a := 4; end; -// Çâóê ñìåðòè: +// Звук смерти: if not FModel.PlaySound(MODELSOUND_DIE, a, FObj.X, FObj.Y) then for i := 1 to 3 do if FModel.PlaySound(MODELSOUND_DIE, i, FObj.X, FObj.Y) then Break; -// Âðåìÿ ðåñïàóíà: +// Время респауна: if Srv then case KillType of K_SIMPLEKILL: @@ -3499,7 +2760,7 @@ begin FTime[T_RESPAWN] := gTime + TIME_RESPAWN3; end; -// Ïåðåêëþ÷àåì ñîñòîÿíèå: +// Переключаем состояние: case KillType of K_SIMPLEKILL: SetAction(A_DIE1); @@ -3507,12 +2768,12 @@ begin SetAction(A_DIE2); end; -// Ðåàêöèÿ ìîíñòðîâ íà ñìåðòü èãðîêà: +// Реакция монстров на смерть игрока: if (KillType <> K_FALLKILL) and (Srv) then g_Monsters_killedp(); if SpawnerUID = FUID then - begin // Ñàìîóáèëñÿ + begin // Самоубился if Srv then begin if gGameSettings.GameMode = GM_TDM then @@ -3527,7 +2788,7 @@ begin end else if g_GetUIDType(SpawnerUID) = UID_PLAYER then - begin // Óáèò äðóãèì èãðîêîì + begin // Убит другим игроком KP := g_Player_Get(SpawnerUID); if (KP <> nil) and Srv then begin @@ -3571,7 +2832,7 @@ begin end; end else if g_GetUIDType(SpawnerUID) = UID_MONSTER then - begin // Óáèò ìîíñòðîì + begin // Убит монстром mon := g_Monsters_ByUID(SpawnerUID); if mon = nil then s := '?' @@ -3593,7 +2854,7 @@ begin gShowKillMsg); end; end - else // Îñîáûå òèïû ñìåðòè + else // Особые типы смерти case t of HIT_DISCON: ; HIT_SELF: g_Console_Add(Format(_lc[I_PLAYER_KILL_SELF], [FName]), True); @@ -3606,7 +2867,7 @@ begin if Srv then begin -// Âûáðîñ îðóæèÿ: +// Выброс оружия: for a := WP_FIRST to WP_LAST do if FWeapon[a] then begin @@ -3627,15 +2888,15 @@ begin PushItem(i); end; -// Âûáðîñ ðþêçàêà: +// Выброс рюкзака: if R_ITEM_BACKPACK in FRulez then PushItem(ITEM_AMMO_BACKPACK); -// Âûáðîñ ðàêåòíîãî ðàíöà: +// Выброс ракетного ранца: if FJetFuel > 0 then PushItem(ITEM_JETPACK); -// Âûáðîñ êëþ÷åé: +// Выброс ключей: if (not (gGameSettings.GameMode in [GM_DM, GM_TDM, GM_CTF])) or (not LongBool(gGameSettings.Options and GAME_OPTION_DMKEYS)) then begin @@ -3649,7 +2910,7 @@ begin PushItem(ITEM_KEY_BLUE); end; -// Âûáðîñ ôëàãà: +// Выброс флага: DropFlag(KillType = K_FALLKILL); end; @@ -4044,7 +3305,7 @@ begin Result := False; if g_Game_IsClient then Exit; - // a = true - ìåñòî ñïàâíà ïðåäìåòà: + // a = true - место спавна предмета: a := LongBool(gGameSettings.Options and GAME_OPTION_WEAPONSTAY) and arespawn; remove := not a; case ItemType of @@ -4123,7 +3384,7 @@ begin ITEM_WEAPON_SHOTGUN1: if (FAmmo[A_SHELLS] < FMaxAmmo[A_SHELLS]) or not FWeapon[WEAPON_SHOTGUN1] then begin - // Íóæíî, ÷òîáû íå âçÿòü âñå ïóëè ñðàçó: + // Нужно, чтобы не взять все пули сразу: if a and FWeapon[WEAPON_SHOTGUN1] then Exit; hadWeapon := FWeapon[WEAPON_SHOTGUN1]; switchWeapon := WEAPON_SHOTGUN1; @@ -4476,7 +3737,7 @@ begin //FModel.PlaySound(MODELSOUND_PAIN, 1, FObj.X, FObj.Y); if FIamBot then begin - // Áðîñèòü ôëàã òîâàðèùó: + // Бросить флаг товарищу: if gGameSettings.GameMode = GM_CTF then DropFlag(); end; @@ -4550,9 +3811,9 @@ var c: Byte; begin Result := 255; - // Íà áóäóùåå: FSpawn - èãðîê óæå èãðàë è ïåðåðîæäàåòñÿ + // На будущее: FSpawn - игрок уже играл и перерождается - // Îäèíî÷íàÿ èãðà/êîîïåðàòèâ + // Одиночная игра/кооператив if gGameSettings.GameMode in [GM_COOP, GM_SINGLE] then begin if Self = gPlayer1 then @@ -4584,7 +3845,7 @@ begin end; end; - // Ìÿñîïîâàë + // Мясоповал if gGameSettings.GameMode = GM_DM then begin // try DM points first @@ -4592,7 +3853,7 @@ begin Exit(RESPAWNPOINT_DM); end; - // Êîìàíäíûå + // Командные if gGameSettings.GameMode in [GM_TDM, GM_CTF] then begin // try team points first @@ -4641,11 +3902,11 @@ begin // if server changes MaxLives we gotta be ready if gGameSettings.MaxLives = 0 then FNoRespawn := False; -// Åùå íåëüçÿ âîçðîäèòüñÿ: +// Еще нельзя возродиться: if FTime[T_RESPAWN] > gTime then Exit; -// Ïðîñðàë âñå æèçíè: +// Просрал все жизни: if FNoRespawn then begin if not FSpectator then Spectate(True); @@ -4654,23 +3915,23 @@ begin end; if (gGameSettings.GameType <> GT_SINGLE) and (gGameSettings.GameMode <> GM_COOP) then - begin // "Ñâîÿ èãðà" - // Áåðñåðê íå ñîõðàíÿåòñÿ ìåæäó óðîâíÿìè: + begin // "Своя игра" + // Берсерк не сохраняется между уровнями: FRulez := FRulez-[R_BERSERK]; end - else // "Îäèíî÷íàÿ èãðà"/"Êîîï" + else // "Одиночная игра"/"Кооп" begin - // Áåðñåðê è êëþ÷è íå ñîõðàíÿþòñÿ ìåæäó óðîâíÿìè: + // Берсерк и ключи не сохраняются между уровнями: FRulez := FRulez-[R_KEY_RED, R_KEY_GREEN, R_KEY_BLUE, R_BERSERK]; end; -// Ïîëó÷àåì òî÷êó ñïàóíà èãðîêà: +// Получаем точку спауна игрока: c := GetRespawnPoint(); ReleaseKeys(); SetFlag(FLAG_NONE); -// Âîñêðåøåíèå áåç îðóæèÿ: +// Воскрешение без оружия: if not FAlive then begin FHealth := Round(PLAYER_HP_SOFT * (FHandicap / 100)); @@ -4710,14 +3971,14 @@ begin FRulez := []; end; -// Ïîëó÷àåì êîîðäèíàòû òî÷êè âîçðîæäåíèÿ: +// Получаем координаты точки возрождения: if not g_Map_GetPoint(c, RespawnPoint) then begin g_FatalError(_lc[I_GAME_ERROR_GET_SPAWN]); Exit; end; -// Óñòàíîâêà êîîðäèíàò è ñáðîñ âñåõ ïàðàìåòðîâ: +// Установка координат и сброс всех параметров: FObj.X := RespawnPoint.X-PLAYER_RECT.X; FObj.Y := RespawnPoint.Y-PLAYER_RECT.Y; FObj.oldX := FObj.X; // don't interpolate after respawn @@ -4757,7 +4018,7 @@ begin FFirePainTime := 0; FFireAttacker := 0; -// Àíèìàöèÿ âîçðîæäåíèÿ: +// Анимация возрождения: if (not gLoadGameMode) and (not Silent) then if g_Frames_Get(ID, 'FRAMES_TELEPORT') then begin @@ -4851,7 +4112,7 @@ begin if MAX_RUNVEL > 8 then FlySmoke(); -// Áåæèì: +// Бежим: if Direction = TDirection.D_LEFT then begin if FObj.Vel.X > -MAX_RUNVEL then @@ -4861,7 +4122,7 @@ begin if FObj.Vel.X < MAX_RUNVEL then FObj.Vel.X := FObj.Vel.X + (MAX_RUNVEL shr 3); -// Âîçìîæíî, ïèíàåì êóñêè: +// Возможно, пинаем куски: if (FObj.Vel.X <> 0) and (gGibs <> nil) then begin b := Abs(FObj.Vel.X); @@ -4872,14 +4133,14 @@ begin g_Obj_Collide(FObj.X+FObj.Rect.X, FObj.Y+FObj.Rect.Y+FObj.Rect.Height-4, FObj.Rect.Width, 8, @gGibs[a].Obj) and (Random(3) = 0) then begin - // Ïèíàåì êóñêè + // Пинаем куски if FObj.Vel.X < 0 then begin - g_Obj_PushA(@gGibs[a].Obj, b, Random(61)+120) // íàëåâî + g_Obj_PushA(@gGibs[a].Obj, b, Random(61)+120) // налево end else begin - g_Obj_PushA(@gGibs[a].Obj, b, Random(61)); // íàïðàâî + g_Obj_PushA(@gGibs[a].Obj, b, Random(61)); // направо end; gGibs[a].positionChanged(); // this updates spatial accelerators end; @@ -5003,7 +4264,7 @@ begin end else if dir = 3 then - begin // îáðàòíîå + begin // обратное if FDirection = TDirection.D_RIGHT then begin SetDirection(TDirection.D_LEFT); @@ -5309,7 +4570,7 @@ begin PANEL_BLOCKMON, True); headwater := HeadInLiquid(0, 0); -// Ñîïðîòèâëåíèå âîçäóõà: +// Сопротивление воздуха: if (not FAlive) or not (FKeys[KEY_LEFT].Pressed or FKeys[KEY_RIGHT].Pressed) then if FObj.Vel.X <> 0 then FObj.Vel.X := z_dec(FObj.Vel.X, 1); @@ -5320,7 +4581,7 @@ begin if FAlive and (FObj.Y > Integer(gMapInfo.Height)+128) and AnyServer then begin - // Îáíóëèòü äåéñòâèÿ ïðèìî÷åê, ÷òîáû ôîí ïðîïàë + // Обнулить действия примочек, чтобы фон пропал FMegaRulez[MR_SUIT] := 0; FMegaRulez[MR_INVUL] := 0; FMegaRulez[MR_INVIS] := 0; @@ -5805,7 +5066,7 @@ begin if not g_Game_IsServer then Exit; -// Ïðèíåñ ÷óæîé ôëàã íà ñâîþ áàçó: +// Принес чужой флаг на свою базу: if (Flag = FTeam) and (gFlags[Flag].State = FLAG_STATE_NORMAL) and (FFlag <> FLAG_NONE) then @@ -5848,7 +5109,7 @@ begin Exit; end; -// Ïîäîáðàë ñâîé ôëàã - âåðíóë åãî íà áàçó: +// Подобрал свой флаг - вернул его на базу: if (Flag = FTeam) and (gFlags[Flag].State = FLAG_STATE_DROPPED) then begin @@ -5884,7 +5145,7 @@ begin Exit; end; -// Ïîäîáðàë ÷óæîé ôëàã: +// Подобрал чужой флаг: if (Flag <> FTeam) and (FTime[T_FLAGCAP] <= gTime) then begin SetFlag(Flag); @@ -6110,95 +5371,95 @@ var i: Integer; b: Byte; begin - // Ñèãíàòóðà èãðîêà + // Сигнатура игрока utils.writeSign(st, 'PLYR'); utils.writeInt(st, Byte(PLR_SAVE_VERSION)); // version - // Áîò èëè ÷åëîâåê + // Бот или человек utils.writeBool(st, FIamBot); - // UID èãðîêà + // UID игрока utils.writeInt(st, Word(FUID)); - // Èìÿ èãðîêà + // Имя игрока utils.writeStr(st, FName); - // Êîìàíäà + // Команда utils.writeInt(st, Byte(FTeam)); - // Æèâ ëè + // Жив ли utils.writeBool(st, FAlive); - // Èçðàñõîäîâàë ëè âñå æèçíè + // Израсходовал ли все жизни utils.writeBool(st, FNoRespawn); - // Íàïðàâëåíèå + // Направление if FDirection = TDirection.D_LEFT then b := 1 else b := 2; // D_RIGHT utils.writeInt(st, Byte(b)); - // Çäîðîâüå + // Здоровье utils.writeInt(st, LongInt(FHealth)); - // Êîýôôèöèåíò èíâàëèäíîñòè + // Коэффициент инвалидности utils.writeInt(st, LongInt(FHandicap)); - // Æèçíè + // Жизни utils.writeInt(st, Byte(FLives)); - // Áðîíÿ + // Броня utils.writeInt(st, LongInt(FArmor)); - // Çàïàñ âîçäóõà + // Запас воздуха utils.writeInt(st, LongInt(FAir)); - // Çàïàñ ãîðþ÷åãî + // Запас горючего utils.writeInt(st, LongInt(FJetFuel)); - // Áîëü + // Боль utils.writeInt(st, LongInt(FPain)); - // Óáèë + // Убил utils.writeInt(st, LongInt(FKills)); - // Óáèë ìîíñòðîâ + // Убил монстров utils.writeInt(st, LongInt(FMonsterKills)); - // Ôðàãîâ + // Фрагов utils.writeInt(st, LongInt(FFrags)); - // Ôðàãîâ ïîäðÿä + // Фрагов подряд utils.writeInt(st, Byte(FFragCombo)); - // Âðåìÿ ïîñëåäíåãî ôðàãà + // Время последнего фрага utils.writeInt(st, LongWord(FLastFrag)); - // Ñìåðòåé + // Смертей utils.writeInt(st, LongInt(FDeath)); - // Êàêîé ôëàã íåñåò + // Какой флаг несет utils.writeInt(st, Byte(FFlag)); - // Íàøåë ñåêðåòîâ + // Нашел секретов utils.writeInt(st, LongInt(FSecrets)); - // Òåêóùåå îðóæèå + // Текущее оружие utils.writeInt(st, Byte(FCurrWeap)); - // Æåëàåìîå îðóæèå + // Желаемое оружие utils.writeInt(st, Word(FNextWeap)); - // ...è ïàóçà + // ...и пауза utils.writeInt(st, Byte(FNextWeapDelay)); - // Âðåìÿ çàðÿäêè BFG + // Время зарядки BFG utils.writeInt(st, SmallInt(FBFGFireCounter)); - // Áóôåð óðîíà + // Буфер урона utils.writeInt(st, LongInt(FDamageBuffer)); - // Ïîñëåäíèé óäàðèâøèé + // Последний ударивший utils.writeInt(st, Word(FLastSpawnerUID)); - // Òèï ïîñëåäíåãî ïîëó÷åííîãî óðîíà + // Тип последнего полученного урона utils.writeInt(st, Byte(FLastHit)); - // Îáúåêò èãðîêà + // Объект игрока Obj_SaveState(st, @FObj); - // Òåêóùåå êîëè÷åñòâî ïàòðîíîâ + // Текущее количество патронов for i := A_BULLETS to A_HIGH do utils.writeInt(st, Word(FAmmo[i])); - // Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ + // Максимальное количество патронов for i := A_BULLETS to A_HIGH do utils.writeInt(st, Word(FMaxAmmo[i])); - // Íàëè÷èå îðóæèÿ + // Наличие оружия for i := WP_FIRST to WP_LAST do utils.writeBool(st, FWeapon[i]); - // Âðåìÿ ïåðåçàðÿäêè îðóæèÿ + // Время перезарядки оружия for i := WP_FIRST to WP_LAST do utils.writeInt(st, Word(FReloading[i])); - // Íàëè÷èå ðþêçàêà + // Наличие рюкзака utils.writeBool(st, (R_ITEM_BACKPACK in FRulez)); - // Íàëè÷èå êðàñíîãî êëþ÷à + // Наличие красного ключа utils.writeBool(st, (R_KEY_RED in FRulez)); - // Íàëè÷èå çåëåíîãî êëþ÷à + // Наличие зеленого ключа utils.writeBool(st, (R_KEY_GREEN in FRulez)); - // Íàëè÷èå ñèíåãî êëþ÷à + // Наличие синего ключа utils.writeBool(st, (R_KEY_BLUE in FRulez)); - // Íàëè÷èå áåðñåðêà + // Наличие берсерка utils.writeBool(st, (R_BERSERK in FRulez)); - // Âðåìÿ äåéñòâèÿ ñïåöèàëüíûõ ïðåäìåòîâ + // Время действия специальных предметов for i := MR_SUIT to MR_MAX do utils.writeInt(st, LongWord(FMegaRulez[i])); - // Âðåìÿ äî ïîâòîðíîãî ðåñïàóíà, ñìåíû îðóæèÿ, èñîëüçîâàíèÿ, çàõâàòà ôëàãà + // Время до повторного респауна, смены оружия, исользования, захвата флага for i := T_RESPAWN to T_FLAGCAP do utils.writeInt(st, LongWord(FTime[i])); - // Íàçâàíèå ìîäåëè + // Название модели utils.writeStr(st, FModel.Name); - // Öâåò ìîäåëè + // Цвет модели utils.writeInt(st, Byte(FColor.R)); utils.writeInt(st, Byte(FColor.G)); utils.writeInt(st, Byte(FColor.B)); @@ -6213,96 +5474,96 @@ var begin assert(st <> nil); - // Ñèãíàòóðà èãðîêà + // Сигнатура игрока if not utils.checkSign(st, 'PLYR') then raise XStreamError.Create('invalid player signature'); if (utils.readByte(st) <> PLR_SAVE_VERSION) then raise XStreamError.Create('invalid player version'); - // Áîò èëè ÷åëîâåê: + // Бот или человек: FIamBot := utils.readBool(st); - // UID èãðîêà + // UID игрока FUID := utils.readWord(st); - // Èìÿ èãðîêà + // Имя игрока str := utils.readStr(st); if (self <> gPlayer1) and (self <> gPlayer2) then FName := str; - // Êîìàíäà + // Команда FTeam := utils.readByte(st); - // Æèâ ëè + // Жив ли FAlive := utils.readBool(st); - // Èçðàñõîäîâàë ëè âñå æèçíè + // Израсходовал ли все жизни FNoRespawn := utils.readBool(st); - // Íàïðàâëåíèå + // Направление b := utils.readByte(st); if b = 1 then FDirection := TDirection.D_LEFT else FDirection := TDirection.D_RIGHT; // b = 2 - // Çäîðîâüå + // Здоровье FHealth := utils.readLongInt(st); - // Êîýôôèöèåíò èíâàëèäíîñòè + // Коэффициент инвалидности FHandicap := utils.readLongInt(st); - // Æèçíè + // Жизни FLives := utils.readByte(st); - // Áðîíÿ + // Броня FArmor := utils.readLongInt(st); - // Çàïàñ âîçäóõà + // Запас воздуха FAir := utils.readLongInt(st); - // Çàïàñ ãîðþ÷åãî + // Запас горючего FJetFuel := utils.readLongInt(st); - // Áîëü + // Боль FPain := utils.readLongInt(st); - // Óáèë + // Убил FKills := utils.readLongInt(st); - // Óáèë ìîíñòðîâ + // Убил монстров FMonsterKills := utils.readLongInt(st); - // Ôðàãîâ + // Фрагов FFrags := utils.readLongInt(st); - // Ôðàãîâ ïîäðÿä + // Фрагов подряд FFragCombo := utils.readByte(st); - // Âðåìÿ ïîñëåäíåãî ôðàãà + // Время последнего фрага FLastFrag := utils.readLongWord(st); - // Ñìåðòåé + // Смертей FDeath := utils.readLongInt(st); - // Êàêîé ôëàã íåñåò + // Какой флаг несет FFlag := utils.readByte(st); - // Íàøåë ñåêðåòîâ + // Нашел секретов FSecrets := utils.readLongInt(st); - // Òåêóùåå îðóæèå + // Текущее оружие FCurrWeap := utils.readByte(st); - // Æåëàåìîå îðóæèå + // Желаемое оружие FNextWeap := utils.readWord(st); - // ...è ïàóçà + // ...и пауза FNextWeapDelay := utils.readByte(st); - // Âðåìÿ çàðÿäêè BFG + // Время зарядки BFG FBFGFireCounter := utils.readSmallInt(st); - // Áóôåð óðîíà + // Буфер урона FDamageBuffer := utils.readLongInt(st); - // Ïîñëåäíèé óäàðèâøèé + // Последний ударивший FLastSpawnerUID := utils.readWord(st); - // Òèï ïîñëåäíåãî ïîëó÷åííîãî óðîíà + // Тип последнего полученного урона FLastHit := utils.readByte(st); - // Îáúåêò èãðîêà + // Объект игрока Obj_LoadState(@FObj, st); - // Òåêóùåå êîëè÷åñòâî ïàòðîíîâ + // Текущее количество патронов for i := A_BULLETS to A_HIGH do FAmmo[i] := utils.readWord(st); - // Ìàêñèìàëüíîå êîëè÷åñòâî ïàòðîíîâ + // Максимальное количество патронов for i := A_BULLETS to A_HIGH do FMaxAmmo[i] := utils.readWord(st); - // Íàëè÷èå îðóæèÿ + // Наличие оружия for i := WP_FIRST to WP_LAST do FWeapon[i] := utils.readBool(st); - // Âðåìÿ ïåðåçàðÿäêè îðóæèÿ + // Время перезарядки оружия for i := WP_FIRST to WP_LAST do FReloading[i] := utils.readWord(st); - // Íàëè÷èå ðþêçàêà + // Наличие рюкзака if utils.readBool(st) then Include(FRulez, R_ITEM_BACKPACK); - // Íàëè÷èå êðàñíîãî êëþ÷à + // Наличие красного ключа if utils.readBool(st) then Include(FRulez, R_KEY_RED); - // Íàëè÷èå çåëåíîãî êëþ÷à + // Наличие зеленого ключа if utils.readBool(st) then Include(FRulez, R_KEY_GREEN); - // Íàëè÷èå ñèíåãî êëþ÷à + // Наличие синего ключа if utils.readBool(st) then Include(FRulez, R_KEY_BLUE); - // Íàëè÷èå áåðñåðêà + // Наличие берсерка if utils.readBool(st) then Include(FRulez, R_BERSERK); - // Âðåìÿ äåéñòâèÿ ñïåöèàëüíûõ ïðåäìåòîâ + // Время действия специальных предметов for i := MR_SUIT to MR_MAX do FMegaRulez[i] := utils.readLongWord(st); - // Âðåìÿ äî ïîâòîðíîãî ðåñïàóíà, ñìåíû îðóæèÿ, èñîëüçîâàíèÿ, çàõâàòà ôëàãà + // Время до повторного респауна, смены оружия, исользования, захвата флага for i := T_RESPAWN to T_FLAGCAP do FTime[i] := utils.readLongWord(st); - // Íàçâàíèå ìîäåëè + // Название модели str := utils.readStr(st); - // Öâåò ìîäåëè + // Цвет модели FColor.R := utils.readByte(st); FColor.G := utils.readByte(st); FColor.B := utils.readByte(st); @@ -6316,7 +5577,7 @@ begin str := gPlayer2Settings.Model; FColor := gPlayer2Settings.Color; end; - // Îáíîâëÿåì ìîäåëü èãðîêà + // Обновляем модель игрока SetModel(str); if gGameSettings.GameMode in [GM_TDM, GM_CTF] then FModel.Color := TEAMCOLOR[FTeam] @@ -6643,12 +5904,12 @@ begin g_Player_CreateGibs(FObj.X+FObj.Rect.X+(FObj.Rect.Width div 2), FObj.Y+FObj.Rect.Y+(FObj.Rect.Height div 2), FModelName, FColor); - // Çâóê ìÿñà îò òðóïà: + // Звук мяса от трупа: pm := g_PlayerModel_Get(FModelName); pm.PlaySound(MODELSOUND_DIE, 5, FObj.X, FObj.Y); pm.Free; - // Çëîâåùèé ñìåõ: + // Зловещий смех: if (gBodyKillEvent <> -1) and gDelayedEvents[gBodyKillEvent].Pending then gDelayedEvents[gBodyKillEvent].Pending := False; @@ -6667,28 +5928,6 @@ begin end; end; -procedure TCorpse.Draw(); -var - fX, fY: Integer; -begin - if FState = CORPSE_STATE_REMOVEME then - Exit; - - FObj.lerp(gLerpFactor, fX, fY); - - if FAnimation <> nil then - FAnimation.Draw(fX, fY, TMirrorType.None); - - if FAnimationMask <> nil then - begin - e_Colors := FColor; - FAnimationMask.Draw(fX, fY, TMirrorType.None); - e_Colors.R := 255; - e_Colors.G := 255; - e_Colors.B := 255; - end; -end; - procedure TCorpse.Update(); var st: Word; @@ -6706,7 +5945,7 @@ begin Exit; end; -// Ñîïðîòèâëåíèå âîçäóõà äëÿ òðóïà: +// Сопротивление воздуха для трупа: FObj.Vel.X := z_dec(FObj.Vel.X, 1); st := g_Obj_Move(@FObj, True, True, True); @@ -6731,29 +5970,29 @@ var 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(FColor.R)); utils.writeInt(st, Byte(FColor.G)); utils.writeInt(st, Byte(FColor.B)); - // Îáúåêò òðóïà + // Объект трупа Obj_SaveState(st, @FObj); utils.writeInt(st, Word(FPlayerUID)); - // Åñòü ëè àíèìàöèÿ + // Есть ли анимация anim := (FAnimation <> nil); utils.writeBool(st, anim); - // Åñëè åñòü - ñîõðàíÿåì + // Если есть - сохраняем if anim then FAnimation.SaveState(st); - // Åñòü ëè ìàñêà àíèìàöèè + // Есть ли маска анимации anim := (FAnimationMask <> nil); utils.writeBool(st, anim); - // Åñëè åñòü - ñîõðàíÿåì + // Если есть - сохраняем if anim then FAnimationMask.SaveState(st); end; @@ -6764,31 +6003,31 @@ var 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); - // Öâåò + // Цвет FColor.R := utils.readByte(st); FColor.G := utils.readByte(st); FColor.B := utils.readByte(st); - // Îáúåêò òðóïà + // Объект трупа Obj_LoadState(@FObj, st); FPlayerUID := utils.readWord(st); - // Åñòü ëè àíèìàöèÿ + // Есть ли анимация anim := utils.readBool(st); - // Åñëè åñòü - çàãðóæàåì + // Если есть - загружаем if anim then begin Assert(FAnimation <> nil, 'TCorpse.LoadState: no FAnimation'); FAnimation.LoadState(st); end; - // Åñòü ëè ìàñêà àíèìàöèè + // Есть ли маска анимации anim := utils.readBool(st); - // Åñëè åñòü - çàãðóæàåì + // Если есть - загружаем if anim then begin Assert(FAnimationMask <> nil, 'TCorpse.LoadState: no FAnimationMask'); @@ -6826,14 +6065,6 @@ begin inherited Destroy(); end; -procedure TBot.Draw(); -begin - inherited Draw(); - - //if FTargetUID <> 0 then e_DrawLine(1, FObj.X, FObj.Y, g_Player_Get(FTargetUID).FObj.X, - // g_Player_Get(FTargetUID).FObj.Y, 255, 0, 0); -end; - procedure TBot.Respawn(Silent: Boolean; Force: Boolean = False); begin inherited Respawn(Silent, Force); @@ -6861,20 +6092,20 @@ type function Compare(a, b: TTarget): Integer; begin - if a.Line and not b.Line then // A íà ëèíèè îãíÿ + if a.Line and not b.Line then // A на линии огня Result := -1 else - if not a.Line and b.Line then // B íà ëèíèè îãíÿ + if not a.Line and b.Line then // B на линии огня Result := 1 - else // È A, è B íà ëèíèè èëè íå íà ëèíèè îãíÿ + else // И A, и B на линии или не на линии огня if (a.Line and b.Line) or ((not a.Line) and (not b.Line)) then begin - if a.Dist > b.Dist then // B áëèæå + if a.Dist > b.Dist then // B ближе Result := 1 - else // A áëèæå èëè ðàâíîóäàëåííî ñ B + else // A ближе или равноудаленно с B Result := -1; end - else // Ñòðàííî -> A + else // Странно -> A Result := -1; end; @@ -6900,10 +6131,10 @@ var x2 := mon.Obj.X+mon.Obj.Rect.X+(mon.Obj.Rect.Width div 2); y2 := mon.Obj.Y+mon.Obj.Rect.Y+(mon.Obj.Rect.Height div 2); - // Åñëè ìîíñòð íà ýêðàíå è íå ïðèêðûò ñòåíîé + // Если монстр на экране и не прикрыт стеной if g_TraceVector(x1, y1, x2, y2) then begin - // Äîáàâëÿåì ê ñïèñêó âîçìîæíûõ öåëåé + // Добавляем к списку возможных целей SetLength(targets, Length(targets)+1); with targets[High(targets)] do begin @@ -6927,11 +6158,11 @@ begin vsPlayer := LongBool(gGameSettings.Options and GAME_OPTION_BOTVSPLAYER); vsMonster := LongBool(gGameSettings.Options and GAME_OPTION_BOTVSMONSTER); -// Åñëè òåêóùåå îðóæèå íå òî, ÷òî íóæíî, òî ìåíÿåì: +// Если текущее оружие не то, что нужно, то меняем: if FCurrWeap <> FSelectedWeapon then NextWeapon(); -// Åñëè íóæíî ñòðåëÿòü è íóæíîå îðóæèå, òî íàæàòü "Ñòðåëÿòü": +// Если нужно стрелять и нужное оружие, то нажать "Стрелять": if (GetAIFlag('NEEDFIRE') <> '') and (FCurrWeap = FSelectedWeapon) then begin RemoveAIFlag('NEEDFIRE'); @@ -6943,7 +6174,7 @@ begin end; end; -// Êîîðäèíàòû ñòâîëà: +// Координаты ствола: x1 := FObj.X + WEAPONPOINT[FDirection].X; y1 := FObj.Y + WEAPONPOINT[FDirection].Y; @@ -6951,10 +6182,10 @@ begin ok := False; if Target.UID <> 0 then - begin // Öåëü åñòü - íàñòðàèâàåì + begin // Цель есть - настраиваем if (g_GetUIDType(Target.UID) = UID_PLAYER) and vsPlayer then - begin // Èãðîê + begin // Игрок tpla := g_Player_Get(Target.UID); if tpla <> nil then with tpla do @@ -6978,7 +6209,7 @@ begin else if (g_GetUIDType(Target.UID) = UID_MONSTER) and vsMonster then - begin // Ìîíñòð + begin // Монстр mon := g_Monsters_ByUID(Target.UID); if mon <> nil then begin @@ -6998,7 +6229,7 @@ begin end; if not ok then - begin // Öåëè íåò - îáíóëÿåì + begin // Цели нет - обнуляем Target.X := 0; Target.Y := 0; Target.cX := 0; @@ -7010,10 +6241,10 @@ begin targets := nil; -// Åñëè öåëü íå âèäèìà èëè íå íà ëèíèè îãíÿ, òî èùåì âñå âîçìîæíûå öåëè: +// Если цель не видима или не на линии огня, то ищем все возможные цели: if (not Target.Line) or (not Target.Visible) then begin - // Èãðîêè: + // Игроки: if vsPlayer then for a := 0 to High(gPlayers) do if (gPlayers[a] <> nil) and (gPlayers[a].alive) and @@ -7029,10 +6260,10 @@ begin x2 := gPlayers[a].FObj.X + PLAYER_RECT_CX; y2 := gPlayers[a].FObj.Y + PLAYER_RECT_CY; - // Åñëè èãðîê íà ýêðàíå è íå ïðèêðûò ñòåíîé: + // Если игрок на экране и не прикрыт стеной: if g_TraceVector(x1, y1, x2, y2) then begin - // Äîáàâëÿåì ê ñïèñêó âîçìîæíûõ öåëåé: + // Добавляем к списку возможных целей: SetLength(targets, Length(targets)+1); with targets[High(targets)] do begin @@ -7051,64 +6282,64 @@ begin end; end; - // Ìîíñòðû: + // Монстры: if vsMonster then g_Mons_ForEach(monsUpdate); end; -// Åñëè åñòü âîçìîæíûå öåëè: -// (Âûáèðàåì ëó÷øóþ, ìåíÿåì îðóæèå è áåæèì ê íåé/îò íåå) +// Если есть возможные цели: +// (Выбираем лучшую, меняем оружие и бежим к ней/от нее) if targets <> nil then begin - // Âûáèðàåì íàèëó÷øóþ öåëü: + // Выбираем наилучшую цель: BestTarget := targets[0]; if Length(targets) > 1 then for a := 1 to High(targets) do if Compare(BestTarget, targets[a]) = 1 then BestTarget := targets[a]; - // Åñëè ëó÷øàÿ öåëü "âèäíåå" òåêóùåé, òî òåêóùàÿ := ëó÷øàÿ: + // Если лучшая цель "виднее" текущей, то текущая := лучшая: if ((not Target.Visible) and BestTarget.Visible and (Target.UID <> BestTarget.UID)) or ((not Target.Line) and BestTarget.Line and BestTarget.Visible) then begin Target := BestTarget; if (Healthy() = 3) or ((Healthy() = 2)) then - begin // Åñëè çäîðîâû - äîãîíÿåì + begin // Если здоровы - догоняем if ((RunDirection() = TDirection.D_LEFT) and (Target.X > FObj.X)) then SetAIFlag('GORIGHT', '1'); if ((RunDirection() = TDirection.D_RIGHT) and (Target.X < FObj.X)) then SetAIFlag('GOLEFT', '1'); end else - begin // Åñëè ïîáèòû - óáåãàåì + begin // Если побиты - убегаем if ((RunDirection() = TDirection.D_LEFT) and (Target.X < FObj.X)) then SetAIFlag('GORIGHT', '1'); if ((RunDirection() = TDirection.D_RIGHT) and (Target.X > FObj.X)) then SetAIFlag('GOLEFT', '1'); end; - // Âûáèðàåì îðóæèå íà îñíîâå ðàññòîÿíèÿ è ïðèîðèòåòîâ: + // Выбираем оружие на основе расстояния и приоритетов: SelectWeapon(Abs(x1-Target.cX)); end; end; -// Åñëè åñòü öåëü: -// (Äîãîíÿåì/óáåãàåì, ñòðåëÿåì ïî íàïðàâëåíèþ ê öåëè) -// (Åñëè öåëü äàëåêî, òî õâàòèò ñëåäèòü çà íåé) +// Если есть цель: +// (Догоняем/убегаем, стреляем по направлению к цели) +// (Если цель далеко, то хватит следить за ней) if Target.UID <> 0 then begin if not TargetOnScreen(Target.X + Target.Rect.X, Target.Y + Target.Rect.Y) then - begin // Öåëü ñáåæàëà ñ "ýêðàíà" + begin // Цель сбежала с "экрана" if (Healthy() = 3) or ((Healthy() = 2)) then - begin // Åñëè çäîðîâû - äîãîíÿåì + begin // Если здоровы - догоняем if ((RunDirection() = TDirection.D_LEFT) and (Target.X > FObj.X)) then SetAIFlag('GORIGHT', '1'); if ((RunDirection() = TDirection.D_RIGHT) and (Target.X < FObj.X)) then SetAIFlag('GOLEFT', '1'); end else - begin // Åñëè ïîáèòû - çàáûâàåì î öåëè è óáåãàåì + begin // Если побиты - забываем о цели и убегаем Target.UID := 0; if ((RunDirection() = TDirection.D_LEFT) and (Target.X < FObj.X)) then SetAIFlag('GORIGHT', '1'); @@ -7117,11 +6348,11 @@ begin end; end else - begin // Öåëü ïîêà íà "ýêðàíå" - // Åñëè öåëü íå çàãîðîæåíà ñòåíîé, òî îòìå÷àåì, êîãäà åå âèäåëè: + begin // Цель пока на "экране" + // Если цель не загорожена стеной, то отмечаем, когда ее видели: if g_TraceVector(x1, y1, Target.cX, Target.cY) then FLastVisible := gTime; - // Åñëè ðàçíèöà âûñîò íå âåëèêà, òî äîãîíÿåì: + // Если разница высот не велика, то догоняем: if (Abs(FObj.Y-Target.Y) <= 128) then begin if ((RunDirection() = TDirection.D_LEFT) and (Target.X > FObj.X)) then @@ -7131,7 +6362,7 @@ begin end; end; - // Âûáèðàåì óãîë ââåðõ: + // Выбираем угол вверх: if FDirection = TDirection.D_LEFT then angle := ANGLE_LEFTUP else @@ -7140,18 +6371,18 @@ begin firew := Trunc(Cos(DegToRad(-angle))*gPlayerScreenSize.X*0.6); fireh := Trunc(Sin(DegToRad(-angle))*gPlayerScreenSize.X*0.6); - // Åñëè ïðè óãëå ââåðõ ìîæíî ïîïàñòü â ïðèáëèçèòåëüíîå ïîëîæåíèå öåëè: + // Если при угле вверх можно попасть в приблизительное положение цели: if g_CollideLine(x1, y1, x1+firew, y1+fireh, Target.X+Target.Rect.X+GetInterval(FDifficult.DiagPrecision, 128), //96 Target.Y+Target.Rect.Y+GetInterval(FDifficult.DiagPrecision, 128), Target.Rect.Width, Target.Rect.Height) and g_TraceVector(x1, y1, Target.cX, Target.cY) then - begin // òî íóæíî ñòðåëÿòü ââåðõ + begin // то нужно стрелять вверх SetAIFlag('NEEDFIRE', '1'); SetAIFlag('NEEDSEEUP', '1'); end; - // Âûáèðàåì óãîë âíèç: + // Выбираем угол вниз: if FDirection = TDirection.D_LEFT then angle := ANGLE_LEFTDOWN else @@ -7160,34 +6391,34 @@ begin firew := Trunc(Cos(DegToRad(-angle))*gPlayerScreenSize.X*0.6); fireh := Trunc(Sin(DegToRad(-angle))*gPlayerScreenSize.X*0.6); - // Åñëè ïðè óãëå âíèç ìîæíî ïîïàñòü â ïðèáëèçèòåëüíîå ïîëîæåíèå öåëè: + // Если при угле вниз можно попасть в приблизительное положение цели: if g_CollideLine(x1, y1, x1+firew, y1+fireh, Target.X+Target.Rect.X+GetInterval(FDifficult.DiagPrecision, 128), Target.Y+Target.Rect.Y+GetInterval(FDifficult.DiagPrecision, 128), Target.Rect.Width, Target.Rect.Height) and g_TraceVector(x1, y1, Target.cX, Target.cY) then - begin // òî íóæíî ñòðåëÿòü âíèç + begin // то нужно стрелять вниз SetAIFlag('NEEDFIRE', '1'); SetAIFlag('NEEDSEEDOWN', '1'); end; - // Åñëè öåëü âèäíî è îíà íà òàêîé æå âûñîòå: + // Если цель видно и она на такой же высоте: if Target.Visible and (y1+4 < Target.Y+Target.Rect.Y+Target.Rect.Height) and (y1-4 > Target.Y+Target.Rect.Y) then begin - // Åñëè èäåì â ñòîðîíó öåëè, òî íàäî ñòðåëÿòü: + // Если идем в сторону цели, то надо стрелять: if ((FDirection = TDirection.D_LEFT) and (Target.X < FObj.X)) or ((FDirection = TDirection.D_RIGHT) and (Target.X > FObj.X)) then - begin // òî íóæíî ñòðåëÿòü âïåðåä + begin // то нужно стрелять вперед SetAIFlag('NEEDFIRE', '1'); SetAIFlag('NEEDSEEDOWN', ''); SetAIFlag('NEEDSEEUP', ''); end; - // Åñëè öåëü â ïðåäåëàõ "ýêðàíà" è ñëîæíîñòü ïîçâîëÿåò ïðûæêè ñáëèæåíèÿ: + // Если цель в пределах "экрана" и сложность позволяет прыжки сближения: if Abs(FObj.X-Target.X) < Trunc(gPlayerScreenSize.X*0.75) then if GetRnd(FDifficult.CloseJump) then - begin // òî åñëè ïîâåçåò - ïðûãàåì (îñîáåííî, åñëè áëèçêî) + begin // то если повезет - прыгаем (особенно, если близко) if Abs(FObj.X-Target.X) < 128 then a := 4 else @@ -7197,75 +6428,75 @@ begin end; end; - // Åñëè öåëü âñå åùå åñòü: + // Если цель все еще есть: if Target.UID <> 0 then - if gTime-FLastVisible > 2000 then // Åñëè âèäåëè äàâíî - Target.UID := 0 // òî çàáûòü öåëü - else // Åñëè âèäåëè íåäàâíî - begin // íî öåëü óáèëè + if gTime-FLastVisible > 2000 then // Если видели давно + Target.UID := 0 // то забыть цель + else // Если видели недавно + begin // но цель убили if Target.IsPlayer then - begin // Öåëü - èãðîê + begin // Цель - игрок pla := g_Player_Get(Target.UID); if (pla = nil) or (not pla.alive) or pla.NoTarget or (pla.FMegaRulez[MR_INVIS] >= gTime) then - Target.UID := 0; // òî çàáûòü öåëü + Target.UID := 0; // то забыть цель end else - begin // Öåëü - ìîíñòð + begin // Цель - монстр mon := g_Monsters_ByUID(Target.UID); if (mon = nil) or (not mon.alive) then - Target.UID := 0; // òî çàáûòü öåëü + Target.UID := 0; // то забыть цель end; end; end; // if Target.UID <> 0 FTargetUID := Target.UID; -// Åñëè âîçìîæíûõ öåëåé íåò: -// (Àòàêà ÷åãî-íèáóäü ñëåâà èëè ñïðàâà) +// Если возможных целей нет: +// (Атака чего-нибудь слева или справа) if targets = nil then if GetAIFlag('ATTACKLEFT') <> '' then - begin // Åñëè íóæíî àòàêîâàòü íàëåâî + begin // Если нужно атаковать налево RemoveAIFlag('ATTACKLEFT'); SetAIFlag('NEEDJUMP', '1'); if RunDirection() = TDirection.D_RIGHT then - begin // Èäåì íå â òó ñòîðîíó + begin // Идем не в ту сторону if (Healthy() > 1) and GetRnd(FDifficult.InvisFire) then - begin // Åñëè çäîðîâû, òî, âîçìîæíî, ñòðåëÿåì áåæèì âëåâî è ñòðåëÿåì + begin // Если здоровы, то, возможно, стреляем бежим влево и стреляем SetAIFlag('NEEDFIRE', '1'); SetAIFlag('GOLEFT', '1'); end; end else - begin // Èäåì â íóæíóþ ñòîðîíó - if GetRnd(FDifficult.InvisFire) then // Âîçìîæíî, ñòðåëÿåì âñëåïóþ + begin // Идем в нужную сторону + if GetRnd(FDifficult.InvisFire) then // Возможно, стреляем вслепую SetAIFlag('NEEDFIRE', '1'); - if Healthy() <= 1 then // Ïîáèòû - óáåãàåì + if Healthy() <= 1 then // Побиты - убегаем SetAIFlag('GORIGHT', '1'); end; end else if GetAIFlag('ATTACKRIGHT') <> '' then - begin // Åñëè íóæíî àòàêîâàòü íàïðàâî + begin // Если нужно атаковать направо RemoveAIFlag('ATTACKRIGHT'); SetAIFlag('NEEDJUMP', '1'); if RunDirection() = TDirection.D_LEFT then - begin // Èäåì íå â òó ñòîðîíó + begin // Идем не в ту сторону if (Healthy() > 1) and GetRnd(FDifficult.InvisFire) then - begin // Åñëè çäîðîâû, òî, âîçìîæíî, áåæèì âïðàâî è ñòðåëÿåì + begin // Если здоровы, то, возможно, бежим вправо и стреляем SetAIFlag('NEEDFIRE', '1'); SetAIFlag('GORIGHT', '1'); end; end else begin - if GetRnd(FDifficult.InvisFire) then // Âîçìîæíî, ñòðåëÿåì âñëåïóþ + if GetRnd(FDifficult.InvisFire) then // Возможно, стреляем вслепую SetAIFlag('NEEDFIRE', '1'); - if Healthy() <= 1 then // Ïîáèòû - óáåãàåì + if Healthy() <= 1 then // Побиты - убегаем SetAIFlag('GOLEFT', '1'); end; end; @@ -7273,15 +6504,15 @@ begin //HACK! (does it belongs there?) RealizeCurrentWeapon(); -// Åñëè åñòü âîçìîæíûå öåëè: -// (Ñòðåëÿåì ïî íàïðàâëåíèþ ê öåëÿì) +// Если есть возможные цели: +// (Стреляем по направлению к целям) if (targets <> nil) and (GetAIFlag('NEEDFIRE') <> '') then for a := 0 to High(targets) do begin - // Åñëè ìîæåì ñòðåëÿòü ïî äèàãîíàëè: + // Если можем стрелять по диагонали: if GetRnd(FDifficult.DiagFire) then begin - // Èùåì öåëü ñâåðõó è ñòðåëÿåì, åñëè åñòü: + // Ищем цель сверху и стреляем, если есть: if FDirection = TDirection.D_LEFT then angle := ANGLE_LEFTUP else @@ -7300,7 +6531,7 @@ begin SetAIFlag('NEEDSEEUP', '1'); end; - // Èùåì öåëü ñíèçó è ñòðåëÿåì, åñëè åñòü: + // Ищем цель снизу и стреляем, если есть: if FDirection = TDirection.D_LEFT then angle := ANGLE_LEFTDOWN else @@ -7320,7 +6551,7 @@ begin end; end; - // Åñëè öåëü "ïåðåä íîñîì", òî ñòðåëÿåì: + // Если цель "перед носом", то стреляем: if targets[a].Line and targets[a].Visible and (((FDirection = TDirection.D_LEFT) and (targets[a].X < FObj.X)) or ((FDirection = TDirection.D_RIGHT) and (targets[a].X > FObj.X))) then @@ -7330,20 +6561,20 @@ begin end; end; -// Åñëè ëåòèò ïóëÿ, òî, âîçìîæíî, ïîäïðûãèâàåì: +// Если летит пуля, то, возможно, подпрыгиваем: if g_Weapon_Danger(FUID, FObj.X+PLAYER_RECT.X, FObj.Y+PLAYER_RECT.Y, PLAYER_RECT.Width, PLAYER_RECT.Height, 40+GetInterval(FDifficult.Cover, 40)) then SetAIFlag('NEEDJUMP', '1'); -// Åñëè êîí÷èëèñü ïàòîðíû, òî íóæíî ñìåíèòü îðóæèå: +// Если кончились паторны, то нужно сменить оружие: ammo := GetAmmoByWeapon(FCurrWeap); if ((FCurrWeap = WEAPON_SHOTGUN2) and (ammo < 2)) or ((FCurrWeap = WEAPON_BFG) and (ammo < 40)) or (ammo = 0) then SetAIFlag('SELECTWEAPON', '1'); -// Åñëè íóæíî ñìåíèòü îðóæèå, òî âûáèðàåì íóæíîå: +// Если нужно сменить оружие, то выбираем нужное: if GetAIFlag('SELECTWEAPON') = '1' then begin SelectWeapon(-1); @@ -7364,7 +6595,7 @@ begin begin EnableAI := True; - // Ïðîâåðÿåì, îòêëþ÷¸í ëè AI áîòîâ + // Проверяем, отключён ли AI ботов if (g_debug_BotAIOff = 1) and (Team = TEAM_RED) then EnableAI := False; if (g_debug_BotAIOff = 2) and (Team = TEAM_BLUE) then @@ -7525,7 +6756,7 @@ procedure TBot.UpdateMove; var x, sx: Integer; begin - { TODO 5 : Ëåñòíèöû } + { TODO 5 : Лестницы } sx := IfThen(RunDirection() = TDirection.D_LEFT, -1, 1); for x := 1 to PLAYER_RECT.Width do if (not StayOnStep(x*sx, 0)) and @@ -7543,7 +6774,7 @@ procedure TBot.UpdateMove; var x, sx, xx: Integer; begin - { TODO 5 : Ëåñòíèöû } + { TODO 5 : Лестницы } sx := IfThen(RunDirection() = TDirection.D_LEFT, -1, 1); for x := 1 to PLAYER_RECT.Width do if (not StayOnStep(x*sx, 0)) and @@ -7716,11 +6947,11 @@ procedure TBot.UpdateMove; end; begin -// Âîçìîæíî, íàæèìàåì êíîïêó: +// Возможно, нажимаем кнопку: if Rnd(16) and IsSafeTrigger() then PressKey(KEY_OPEN); -// Åñëè ïîä ëèôòîì èëè ñòóïåíüêàìè, òî, âîçìîæíî, ïðûãàåì: +// Если под лифтом или ступеньками, то, возможно, прыгаем: if OnLadder() or ((BelowLadder() or BelowLiftUp()) and Rnd(8)) then begin ReleaseKey(KEY_LEFT); @@ -7728,7 +6959,7 @@ begin Jump(); end; -// Èäåì âëåâî, åñëè íàäî áûëî: +// Идем влево, если надо было: if GetAIFlag('GOLEFT') <> '' then begin RemoveAIFlag('GOLEFT'); @@ -7736,7 +6967,7 @@ begin GoLeft(360); end; -// Èäåì âïðàâî, åñëè íàäî áûëî: +// Идем вправо, если надо было: if GetAIFlag('GORIGHT') <> '' then begin RemoveAIFlag('GORIGHT'); @@ -7744,21 +6975,21 @@ begin GoRight(360); end; -// Åñëè âûëåòåëè çà êàðòó, òî ïðîáóåì âåðíóòüñÿ: +// Если вылетели за карту, то пробуем вернуться: if FObj.X < -32 then GoRight(360) else if FObj.X+32 > gMapInfo.Width then GoLeft(360); -// Ïðûãàåì, åñëè íàäî áûëî: +// Прыгаем, если надо было: if GetAIFlag('NEEDJUMP') <> '' then begin Jump(0); RemoveAIFlag('NEEDJUMP'); end; -// Ñìîòðèì ââåðõ, åñëè íàäî áûëî: +// Смотрим вверх, если надо было: if GetAIFlag('NEEDSEEUP') <> '' then begin ReleaseKey(KEY_UP); @@ -7767,7 +6998,7 @@ begin RemoveAIFlag('NEEDSEEUP'); end; -// Ñìîòðèì âíèç, åñëè íàäî áûëî: +// Смотрим вниз, если надо было: if GetAIFlag('NEEDSEEDOWN') <> '' then begin ReleaseKey(KEY_UP); @@ -7776,7 +7007,7 @@ begin RemoveAIFlag('NEEDSEEDOWN'); end; -// Åñëè íóæíî áûëî â äûðó è ìû íå íà çåìëå, òî ïîêîðíî ëåòèì: +// Если нужно было в дыру и мы не на земле, то покорно летим: if GetAIFlag('GOINHOLE') <> '' then if not OnGround() then begin @@ -7786,12 +7017,12 @@ begin SetAIFlag('FALLINHOLE', '1'); end; -// Åñëè ïàäàëè è äîñòèãëè çåìëè, òî õâàòèò ïàäàòü: +// Если падали и достигли земли, то хватит падать: if GetAIFlag('FALLINHOLE') <> '' then if OnGround() then RemoveAIFlag('FALLINHOLE'); -// Åñëè ëåòåëè ïðÿìî è ñåé÷àñ íå íà ëåñòíèöå èëè íà âåðøèíå ëèôòà, òî îòõîäèì â ñòîðîíó: +// Если летели прямо и сейчас не на лестнице или на вершине лифта, то отходим в сторону: if not (KeyPressed(KEY_LEFT) or KeyPressed(KEY_RIGHT)) then if GetAIFlag('FALLINHOLE') = '' then if (not OnLadder()) or (FObj.Vel.Y >= 0) or (OnTopLift()) then @@ -7800,40 +7031,40 @@ begin else GoRight(360); -// Åñëè íà çåìëå è ìîæíî ïîäïðûãíóòü, òî, âîçìîæíî, ïðûãàåì: +// Если на земле и можно подпрыгнуть, то, возможно, прыгаем: if OnGround() and CanJumpUp(IfThen(RunDirection() = TDirection.D_LEFT, -1, 1)*32) and Rnd(8) then Jump(); -// Åñëè íà çåìëå è âîçëå äûðû (ãëóáèíà > 2 ðîñòîâ èãðîêà): +// Если на земле и возле дыры (глубина > 2 ростов игрока): if OnGround() and NearHole() then - if NearDeepHole() then // Åñëè ýòî áåçäíà + if NearDeepHole() then // Если это бездна case Random(6) of - 0..3: Turn(); // Áåæèì îáðàòíî - 4: Jump(); // Ïðûãàåì - 5: begin // Ïðûãàåì îáðàòíî + 0..3: Turn(); // Бежим обратно + 4: Jump(); // Прыгаем + 5: begin // Прыгаем обратно Turn(); Jump(); end; end - else // Ýòî íå áåçäíà è ìû åùå íå ëåòèì òóäà + else // Это не бездна и мы еще не летим туда if GetAIFlag('GOINHOLE') = '' then case Random(6) of - 0: Turn(); // Íå íóæíî òóäà - 1: Jump(); // Âäðóã ïîâåçåò - ïðûãàåì - else // Åñëè ÿìà ñ ãðàíèöåé, òî ïðè ñëó÷àå ìîæíî òóäà ïðûãíóòü + 0: Turn(); // Не нужно туда + 1: Jump(); // Вдруг повезет - прыгаем + else // Если яма с границей, то при случае можно туда прыгнуть if BorderHole() then SetAIFlag('GOINHOLE', '1'); end; -// Åñëè íà çåìëå, íî íåêóäà èäòè: +// Если на земле, но некуда идти: if (not CanRun()) and OnGround() then begin - // Åñëè ìû íà ëåñòíèöå èëè ìîæíî ïåðåïðûãíóòü, òî ïðûãàåì: + // Если мы на лестнице или можно перепрыгнуть, то прыгаем: if CanJumpOver() or OnLadder() then Jump() - else // èíà÷å ïîïûòàåìñÿ â äðóãóþ ñòîðîíó + else // иначе попытаемся в другую сторону if Random(2) = 0 then begin if IsSafeTrigger() then @@ -7842,11 +7073,11 @@ begin Turn(); end; -// Îñòàëîñü ìàëî âîçäóõà: +// Осталось мало воздуха: if FAir < 36 * 2 then Jump(20); -// Âûáèðàåìñÿ èç êèñëîòû, åñëè íåò êîñòþìà, îáîæãëèñü, èëè ìàëî çäîðîâüÿ: +// Выбираемся из кислоты, если нет костюма, обожглись, или мало здоровья: if (FMegaRulez[MR_SUIT] < gTime) and ((FLastHit = HIT_ACID) or (Healthy() <= 1)) then if BodyInAcid(0, 0) then Jump(); @@ -7887,7 +7118,7 @@ begin if Dist = -1 then Dist := BOT_LONGDIST; if Dist > BOT_LONGDIST then - begin // Äàëüíèé áîé + begin // Дальний бой for a := 0 to 9 do if FWeapon[FDifficult.WeaponPrior[a]] and HaveAmmo(FDifficult.WeaponPrior[a]) then begin @@ -7896,7 +7127,7 @@ begin end; end else //if Dist > BOT_UNSAFEDIST then - begin // Áëèæíèé áîé + begin // Ближний бой for a := 0 to 9 do if FWeapon[FDifficult.CloseWeaponPrior[a]] and HaveAmmo(FDifficult.CloseWeaponPrior[a]) then begin @@ -7955,7 +7186,7 @@ begin ok := False; if (g_GetUIDType(FLastSpawnerUID) = UID_PLAYER) and LongBool(gGameSettings.Options and GAME_OPTION_BOTVSPLAYER) then - begin // Èãðîê + begin // Игрок pla := g_Player_Get(FLastSpawnerUID); ok := not TargetOnScreen(pla.FObj.X + PLAYER_RECT.X, pla.FObj.Y + PLAYER_RECT.Y); @@ -7963,7 +7194,7 @@ begin else if (g_GetUIDType(FLastSpawnerUID) = UID_MONSTER) and LongBool(gGameSettings.Options and GAME_OPTION_BOTVSMONSTER) then - begin // Ìîíñòð + begin // Монстр mon := g_Monsters_ByUID(FLastSpawnerUID); ok := not TargetOnScreen(mon.Obj.X + mon.Obj.Rect.X, mon.Obj.Y + mon.Obj.Rect.Y); @@ -8031,22 +7262,22 @@ var begin inherited SaveState(st); utils.writeSign(st, 'BOT0'); - // Âûáðàííîå îðóæèå + // Выбранное оружие utils.writeInt(st, Byte(FSelectedWeapon)); - // UID öåëè + // UID цели utils.writeInt(st, Word(FTargetUID)); - // Âðåìÿ ïîòåðè öåëè + // Время потери цели utils.writeInt(st, LongWord(FLastVisible)); - // Êîëè÷åñòâî ôëàãîâ ÈÈ + // Количество флагов ИИ dw := Length(FAIFlags); utils.writeInt(st, LongInt(dw)); - // Ôëàãè ÈÈ + // Флаги ИИ for i := 0 to dw-1 do begin utils.writeStr(st, FAIFlags[i].Name, 20); utils.writeStr(st, FAIFlags[i].Value, 20); end; - // Íàñòðîéêè ñëîæíîñòè + // Настройки сложности FDifficult.save(st); end; @@ -8058,23 +7289,23 @@ var begin inherited LoadState(st); if not utils.checkSign(st, 'BOT0') then raise XStreamError.Create('invalid bot signature'); - // Âûáðàííîå îðóæèå + // Выбранное оружие FSelectedWeapon := utils.readByte(st); - // UID öåëè + // UID цели FTargetUID := utils.readWord(st); - // Âðåìÿ ïîòåðè öåëè + // Время потери цели FLastVisible := utils.readLongWord(st); - // Êîëè÷åñòâî ôëàãîâ ÈÈ + // Количество флагов ИИ dw := utils.readLongInt(st); if (dw < 0) or (dw > 16384) then raise XStreamError.Create('invalid number of bot AI flags'); SetLength(FAIFlags, dw); - // Ôëàãè ÈÈ + // Флаги ИИ for i := 0 to dw-1 do begin FAIFlags[i].Name := utils.readStr(st, 20); FAIFlags[i].Value := utils.readStr(st, 20); end; - // Íàñòðîéêè ñëîæíîñòè + // Настройки сложности FDifficult.load(st); end; diff --git a/src/game/opengl/r_game.pas b/src/game/opengl/r_game.pas index d752fdc..caa60c4 100644 --- a/src/game/opengl/r_game.pas +++ b/src/game/opengl/r_game.pas @@ -32,7 +32,7 @@ implementation g_textures, e_input, e_sound, g_language, g_console, g_menu, g_triggers, g_player, g_options, g_monsters, g_map, g_panel, g_window, g_items, g_weapons, g_gfx, g_phys, g_net, g_gui, g_netmaster, - g_game, r_console, r_gfx, r_items, r_map, r_panel, r_monsters, r_weapons, r_netmaster + g_game, r_console, r_gfx, r_items, r_map, r_panel, r_monsters, r_weapons, r_netmaster, r_player ; var @@ -1240,9 +1240,9 @@ begin drawPanelType('*step', PANEL_STEP, g_rlayer_step); drawOther('items', @r_Items_Draw); drawOther('weapons', @r_Weapon_Draw); - drawOther('shells', @g_Player_DrawShells); - drawOther('drawall', @g_Player_DrawAll); - drawOther('corpses', @g_Player_DrawCorpses); + drawOther('shells', @r_Player_DrawShells); + drawOther('drawall', @r_Player_DrawAll); + drawOther('corpses', @r_Player_DrawCorpses); drawPanelType('*wall', PANEL_WALL, g_rlayer_wall); drawOther('monsters', @r_Monsters_Draw); drawOther('itemdrop', @r_Items_DrawDrop); @@ -1266,7 +1266,7 @@ begin if g_debug_HealthBar then begin r_Monsters_DrawHealth(); - g_Player_DrawHealth(); + r_Player_DrawHealth(); end; if (profileFrameDraw <> nil) then profileFrameDraw.mainEnd(); // map rendering @@ -1436,16 +1436,18 @@ begin if (gGameSettings.GameMode <> GM_SINGLE) and (gPlayerIndicator > 0) then case gPlayerIndicator of 1: - p.DrawIndicator(_RGB(255, 255, 255)); + r_Player_DrawIndicator(p, _RGB(255, 255, 255)); 2: for i := 0 to High(gPlayers) do if gPlayers[i] <> nil then - if gPlayers[i] = p then p.DrawIndicator(_RGB(255, 255, 255)) + if gPlayers[i] = p then + r_Player_DrawIndicator(p, _RGB(255, 255, 255)) else if (gPlayers[i].Team = p.Team) and (gPlayers[i].Team <> TEAM_NONE) then if gPlayerIndicatorStyle = 1 then - gPlayers[i].DrawIndicator(_RGB(192, 192, 192)) - else gPlayers[i].DrawIndicator(gPlayers[i].GetColor); + r_Player_DrawIndicator(gPlayers[i], _RGB(192, 192, 192)) + else + r_Player_DrawIndicator(gPlayers[i], gPlayers[i].GetColor); end; { @@ -1468,13 +1470,13 @@ begin glPopMatrix(); - p.DrawPain(); - p.DrawPickup(); - p.DrawRulez(); + r_Player_DrawPain(p); + r_Player_DrawPickup(p); + r_Player_DrawRulez(p); if gShowMap then DrawMinimap(p, _TRect(0, 0, 128, 128)); if g_Debug_Player then - g_Player_DrawDebug(p); - p.DrawGUI(); + r_Player_DrawDebug(p); + r_Player_DrawGUI(p); end; procedure drawProfilers (); diff --git a/src/game/opengl/r_player.pas b/src/game/opengl/r_player.pas new file mode 100644 index 0000000..c054d06 --- /dev/null +++ b/src/game/opengl/r_player.pas @@ -0,0 +1,806 @@ +(* 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 r_player; + +interface + + uses g_player, e_graphics; // TPlayer, TRGB + + procedure r_Player_DrawAll; + procedure r_Player_DrawDebug (p: TPlayer); + procedure r_Player_DrawHealth; + + procedure r_Player_DrawCorpses; + procedure r_Player_DrawShells; + + procedure r_Player_Draw (p: TPlayer); + procedure r_Player_DrawIndicator (p: TPlayer; Color: TRGB); + procedure r_Player_DrawBubble (p: TPlayer); + procedure r_Player_DrawAim (p: TPlayer); + procedure r_Player_DrawGUI (pl: TPlayer); + procedure r_Player_DrawRulez (p: TPlayer); + procedure r_Player_DrawPain (p: TPlayer); + procedure r_Player_DrawPickup (p: TPlayer); + + procedure r_Player_DrawCorpse (p: TCorpse); + +implementation + + uses + SysUtils, Classes, Math, + MAPDEF, utils, + g_basic, g_game, g_phys, g_map, g_textures, g_menu, g_language, g_weapons, g_items, g_net, g_options + ; + + procedure r_Player_DrawAll; + var i: Integer; + begin + if gPlayers <> nil then + for i := 0 to High(gPlayers) do + if gPlayers[i] <> nil then + r_Player_Draw(gPlayers[i]) + end; + +procedure r_Player_DrawDebug (p: TPlayer); +var + fW, fH: Byte; +begin + if p = nil then Exit; + if (@p.Obj) = nil then Exit; + + e_TextureFontGetSize(gStdFont, fW, fH); + + e_TextureFontPrint(0, 0 , 'Pos X: ' + IntToStr(p.Obj.X), gStdFont); + e_TextureFontPrint(0, fH , 'Pos Y: ' + IntToStr(p.Obj.Y), gStdFont); + e_TextureFontPrint(0, fH * 2, 'Vel X: ' + IntToStr(p.Obj.Vel.X), gStdFont); + e_TextureFontPrint(0, fH * 3, 'Vel Y: ' + IntToStr(p.Obj.Vel.Y), gStdFont); + e_TextureFontPrint(0, fH * 4, 'Acc X: ' + IntToStr(p.Obj.Accel.X), gStdFont); + e_TextureFontPrint(0, fH * 5, 'Acc Y: ' + IntToStr(p.Obj.Accel.Y), gStdFont); + e_TextureFontPrint(0, fH * 6, 'Old X: ' + IntToStr(p.Obj.oldX), gStdFont); + e_TextureFontPrint(0, fH * 7, 'Old Y: ' + IntToStr(p.Obj.oldY), gStdFont); +end; + +procedure r_Player_DrawHealth; +var + i: Integer; + fW, fH: Byte; +begin + if gPlayers = nil then Exit; + e_TextureFontGetSize(gStdFont, fW, fH); + + for i := 0 to High(gPlayers) do + if gPlayers[i] <> nil then + begin + e_TextureFontPrint(gPlayers[i].Obj.X + gPlayers[i].Obj.Rect.X, + gPlayers[i].Obj.Y + gPlayers[i].Obj.Rect.Y + gPlayers[i].Obj.Rect.Height - fH * 2, + IntToStr(gPlayers[i].Health), gStdFont); + e_TextureFontPrint(gPlayers[i].Obj.X + gPlayers[i].Obj.Rect.X, + gPlayers[i].Obj.Y + gPlayers[i].Obj.Rect.Y + gPlayers[i].Obj.Rect.Height - fH, + IntToStr(gPlayers[i].Armor), gStdFont); + end; +end; + +procedure r_Player_DrawCorpses; +var + i, fX, fY: Integer; + a: TDFPoint; +begin + if gGibs <> nil then + for i := 0 to High(gGibs) do + if gGibs[i].alive then + with gGibs[i] do + begin + if not g_Obj_Collide(sX, sY, sWidth, sHeight, @Obj) then + Continue; + + Obj.lerp(gLerpFactor, fX, fY); + + a.X := Obj.Rect.X+(Obj.Rect.Width div 2); + a.y := Obj.Rect.Y+(Obj.Rect.Height div 2); + + e_DrawAdv(ID, fX, fY, 0, True, False, RAngle, @a, TMirrorType.None); + + e_Colors := Color; + e_DrawAdv(MaskID, fX, fY, 0, True, False, RAngle, @a, TMirrorType.None); + e_Colors.R := 255; + e_Colors.G := 255; + e_Colors.B := 255; + end; + + if gCorpses <> nil then + for i := 0 to High(gCorpses) do + if gCorpses[i] <> nil then + r_Player_DrawCorpse(gCorpses[i]) +end; + +procedure r_Player_DrawShells; +var + i, fX, fY: Integer; + a: TDFPoint; +begin + if gShells <> nil then + for i := 0 to High(gShells) do + if gShells[i].alive then + with gShells[i] do + begin + if not g_Obj_Collide(sX, sY, sWidth, sHeight, @Obj) then + Continue; + + Obj.lerp(gLerpFactor, fX, fY); + + a.X := CX; + a.Y := CY; + + e_DrawAdv(SpriteID, fX, fY, 0, True, False, RAngle, @a, TMirrorType.None); + end; +end; + +procedure r_Player_DrawIndicator (p: TPlayer; Color: TRGB); +var + indX, indY, fX, fY, fSlope: Integer; + indW, indH: Word; + indA: Single; + a: TDFPoint; + nW, nH: Byte; + ID: DWORD; + c: TRGB; +begin + if p.Alive then + begin + p.Obj.lerp(gLerpFactor, fX, fY); + fSlope := nlerp(p.SlopeOld, p.Obj.slopeUpLeft, gLerpFactor); + + case gPlayerIndicatorStyle of + 0: + begin + if g_Texture_Get('TEXTURE_PLAYER_INDICATOR', ID) then + begin + e_GetTextureSize(ID, @indW, @indH); + a.X := indW div 2; + a.Y := indH div 2; + + if (p.Obj.X + p.Obj.Rect.X) < 0 then + begin + indA := 90; + indX := fX + p.Obj.Rect.X + p.Obj.Rect.Width; + indY := fY + p.Obj.Rect.Y + (p.Obj.Rect.Height - indW) div 2; + end + + else if (p.Obj.X + p.Obj.Rect.X + p.Obj.Rect.Width) > Max(gMapInfo.Width, gPlayerScreenSize.X) then + begin + indA := 270; + indX := fX + p.Obj.Rect.X - indH; + indY := fY + p.Obj.Rect.Y + (p.Obj.Rect.Height - indW) div 2; + end + + else if (p.Obj.Y - indH) < 0 then + begin + indA := 180; + indX := fX + p.Obj.Rect.X + (p.Obj.Rect.Width - indW) div 2; + indY := fY + p.Obj.Rect.Y + p.Obj.Rect.Height; + end + + else + begin + indA := 0; + indX := fX + p.Obj.Rect.X + (p.Obj.Rect.Width - indW) div 2; + indY := fY - indH; + end; + + indY := indY + fSlope; + indX := EnsureRange(indX, 0, Max(gMapInfo.Width, gPlayerScreenSize.X) - indW); + indY := EnsureRange(indY, 0, Max(gMapInfo.Height, gPlayerScreenSize.Y) - indH); + + c := e_Colors; + e_Colors := Color; + e_DrawAdv(ID, indX, indY, 0, True, False, indA, @a); + e_Colors := c; + end; + end; + + 1: + begin + e_TextureFontGetSize(gStdFont, nW, nH); + indX := fX + p.Obj.Rect.X + (p.Obj.Rect.Width - Length(p.Name) * nW) div 2; + indY := fY - nH + fSlope; + e_TextureFontPrintEx(indX, indY, p.Name, gStdFont, Color.R, Color.G, Color.B, 1.0, True); + end; + end; + end +end; + +procedure r_Player_DrawBubble (p: TPlayer); +var + bubX, bubY, fX, fY: Integer; + ID: LongWord; + Rb, Gb, Bb, + Rw, Gw, Bw: SmallInt; + Dot: Byte; + CObj: TObj; +begin + CObj := p.getCameraObj(); + 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); + bubY := fY + p.Obj.Rect.Y - 18; + Rb := 64; + Gb := 64; + Bb := 64; + Rw := 240; + Gw := 240; + Bw := 240; + case gChatBubble of + 1: // simple textual non-bubble + begin + bubX := fX + p.Obj.Rect.X - 11; + bubY := fY + p.Obj.Rect.Y - 17; + e_TextureFontPrint(bubX, bubY, '[...]', gStdFont); + Exit; + end; + 2: // advanced pixel-perfect bubble + begin + if p.Team = TEAM_RED then + Rb := 255 + else + if p.Team = TEAM_BLUE then + Bb := 255; + end; + 3: // colored bubble + begin + Rb := p.Model.Color.R; + Gb := p.Model.Color.G; + Bb := p.Model.Color.B; + Rw := Min(Rb * 2 + 64, 255); + Gw := Min(Gb * 2 + 64, 255); + Bw := Min(Bb * 2 + 64, 255); + if (Abs(Rw - Rb) < 32) + or (Abs(Gw - Gb) < 32) + or (Abs(Bw - Bb) < 32) then + begin + Rb := Max(Rw div 2 - 16, 0); + Gb := Max(Gw div 2 - 16, 0); + Bb := Max(Bw div 2 - 16, 0); + end; + end; + 4: // custom textured bubble + begin + if g_Texture_Get('TEXTURE_PLAYER_TALKBUBBLE', ID) then + if p.Direction = TDirection.D_RIGHT then + e_Draw(ID, bubX - 6, bubY - 7, 0, True, False) + else + e_Draw(ID, bubX - 6, bubY - 7, 0, True, False, TMirrorType.Horizontal); + Exit; + end; + end; + + // Outer borders + e_DrawQuad(bubX + 1, bubY , bubX + 18, bubY + 13, Rb, Gb, Bb); + e_DrawQuad(bubX , bubY + 1, bubX + 19, bubY + 12, Rb, Gb, Bb); + // Inner box + e_DrawFillQuad(bubX + 1, bubY + 1, bubX + 18, bubY + 12, Rw, Gw, Bw, 0); + + // Tail + Dot := IfThen(p.Direction = TDirection.D_LEFT, 14, 5); + e_DrawLine(1, bubX + Dot, bubY + 14, bubX + Dot, bubY + 16, Rb, Gb, Bb); + e_DrawLine(1, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 1, Dot + 1), bubY + 13, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 1, Dot + 1), bubY + 15, Rw, Gw, Bw); + e_DrawLine(1, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 2, Dot + 2), bubY + 13, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 2, Dot + 2), bubY + 14, Rw, Gw, Bw); + e_DrawLine(1, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 3, Dot + 3), bubY + 13, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 3, Dot + 3), bubY + 13, Rw, Gw, Bw); + e_DrawLine(1, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 3, Dot + 3), bubY + 14, bubX + IfThen(p.Direction = TDirection.D_LEFT, Dot - 1, Dot + 1), bubY + 16, Rb, Gb, Bb); + + // Dots + Dot := 6; + e_DrawFillQuad(bubX + Dot, bubY + 8, bubX + Dot + 1, bubY + 9, Rb, Gb, Bb, 0); + e_DrawFillQuad(bubX + Dot + 3, bubY + 8, bubX + Dot + 4, bubY + 9, Rb, Gb, Bb, 0); + e_DrawFillQuad(bubX + Dot + 6, bubY + 8, bubX + Dot + 7, bubY + 9, Rb, Gb, Bb, 0); +end; + +procedure r_Player_Draw (p: TPlayer); +var + ID: DWORD; + w, h: Word; + dr: Boolean; + Mirror: TMirrorType; + fX, fY, fSlope: Integer; +begin + p.Obj.lerp(gLerpFactor, fX, fY); + fSlope := nlerp(p.SlopeOld, p.Obj.slopeUpLeft, gLerpFactor); + + if p.Alive then + begin + if p.Direction = TDirection.D_RIGHT then + Mirror := TMirrorType.None + else + Mirror := TMirrorType.Horizontal; + + if p.PunchAnim <> nil then + begin + p.PunchAnim.Draw(fX + IfThen(p.Direction = TDirection.D_LEFT, 15 - p.Obj.Rect.X, p.Obj.Rect.X - 15), fY + fSlope + p.Obj.Rect.Y - 11, Mirror); + if p.PunchAnim.played then + begin + p.PunchAnim.Free; + p.PunchAnim := nil; + end; + end; + + if (p.FMegaRulez[MR_INVUL] > gTime) and ((gPlayerDrawn <> p) or (p.SpawnInvul >= gTime)) then + if g_Texture_Get('TEXTURE_PLAYER_INVULPENTA', ID) then + begin + e_GetTextureSize(ID, @w, @h); + if p.Direction = TDirection.D_LEFT then + e_Draw(ID, fX + p.Obj.Rect.X + (p.Obj.Rect.Width div 2) - (w div 2) + 4, + fY + p.Obj.Rect.Y + (p.Obj.Rect.Height div 2) - (h div 2) - 7 + fSlope, 0, True, False) + else + e_Draw(ID, fX + p.Obj.Rect.X + (p.Obj.Rect.Width div 2) - (w div 2) - 2, + fY + p.Obj.Rect.Y + (p.Obj.Rect.Height div 2) - (h div 2) - 7 + fSlope, 0, True, False) + end; + + if p.FMegaRulez[MR_INVIS] > gTime then + begin + if (gPlayerDrawn <> nil) and ((p = gPlayerDrawn) or + ((p.Team = gPlayerDrawn.Team) and (gGameSettings.GameMode <> GM_DM))) then + begin + if (p.FMegaRulez[MR_INVIS] - gTime) <= 2100 then + dr := not Odd((p.FMegaRulez[MR_INVIS] - gTime) div 300) + else + dr := True; + if dr then + p.Model.Draw(fX, fY + fSlope, 200) + else + p.Model.Draw(fX, fY + fSlope); + end + else + p.Model.Draw(fX, fY + fSlope, 254); + end + else + p.Model.Draw(fX, fY + fSlope); + end; + + if g_debug_Frames then + begin + e_DrawQuad(p.Obj.X + p.Obj.Rect.X, + p.Obj.Y + p.Obj.Rect.Y, + p.Obj.X + p.Obj.Rect.X + p.Obj.Rect.Width - 1, + p.Obj.Y + p.Obj.Rect.Y + p.Obj.Rect.Height - 1, + 0, 255, 0); + end; + + if (gChatBubble > 0) and (p.FKeys[KEY_CHAT].Pressed) and not p.Ghost then + if (p.FMegaRulez[MR_INVIS] <= gTime) or ((gPlayerDrawn <> nil) and ((p = gPlayerDrawn) or + ((p.Team = gPlayerDrawn.Team) and (gGameSettings.GameMode <> GM_DM)))) then + r_Player_DrawBubble(p); + + // e_DrawPoint(5, 335, 288, 255, 0, 0); // DL, UR, DL, UR + if gAimLine and p.Alive and ((p = gPlayer1) or (p = gPlayer2)) then + r_Player_DrawAim(p); +end; + +procedure r_Player_DrawAim (p: TPlayer); + + procedure drawCast (sz: Integer; ax0, ay0, ax1, ay1: Integer); + var + ex, ey: Integer; + begin + +{$IFDEF ENABLE_HOLMES} + if isValidViewPort and (self = gPlayer1) then + begin + g_Holmes_plrLaser(ax0, ay0, ax1, ay1); + end; +{$ENDIF} + + e_DrawLine(sz, ax0, ay0, ax1, ay1, 255, 0, 0, 96); + if (g_Map_traceToNearestWall(ax0, ay0, ax1, ay1, @ex, @ey) <> nil) then + begin + e_DrawLine(sz, ax0, ay0, ex, ey, 0, 255, 0, 96); + end + else + begin + e_DrawLine(sz, ax0, ay0, ex, ey, 0, 0, 255, 96); + end; + end; + +var + wx, wy, xx, yy: Integer; + angle: SmallInt; + sz, len: Word; +begin + wx := p.Obj.X + WEAPONPOINT[p.Direction].X + IfThen(p.Direction = TDirection.D_LEFT, 7, -7); + wy := p.Obj.Y + WEAPONPOINT[p.Direction].Y; + angle := p.Angle_; + len := 1024; + sz := 2; + case p.CurrWeap of + 0: begin // Punch + len := 12; + sz := 4; + end; + 1: begin // Chainsaw + len := 24; + sz := 6; + end; + 2: begin // Pistol + len := 1024; + sz := 2; + if angle = ANGLE_RIGHTUP then Dec(angle, 2); + if angle = ANGLE_RIGHTDOWN then Inc(angle, 4); + if angle = ANGLE_LEFTUP then Inc(angle, 2); + if angle = ANGLE_LEFTDOWN then Dec(angle, 4); + end; + 3: begin // Shotgun + len := 1024; + sz := 3; + if angle = ANGLE_RIGHTUP then Dec(angle, 2); + if angle = ANGLE_RIGHTDOWN then Inc(angle, 4); + if angle = ANGLE_LEFTUP then Inc(angle, 2); + if angle = ANGLE_LEFTDOWN then Dec(angle, 4); + end; + 4: begin // Double Shotgun + len := 1024; + sz := 4; + if angle = ANGLE_RIGHTUP then Dec(angle, 2); + if angle = ANGLE_RIGHTDOWN then Inc(angle, 4); + if angle = ANGLE_LEFTUP then Inc(angle, 2); + if angle = ANGLE_LEFTDOWN then Dec(angle, 4); + end; + 5: begin // Chaingun + len := 1024; + sz := 3; + if angle = ANGLE_RIGHTUP then Dec(angle, 2); + if angle = ANGLE_RIGHTDOWN then Inc(angle, 4); + if angle = ANGLE_LEFTUP then Inc(angle, 2); + if angle = ANGLE_LEFTDOWN then Dec(angle, 4); + end; + 6: begin // Rocket Launcher + len := 1024; + sz := 7; + if angle = ANGLE_RIGHTUP then Inc(angle, 2); + if angle = ANGLE_RIGHTDOWN then Inc(angle, 4); + if angle = ANGLE_LEFTUP then Dec(angle, 2); + if angle = ANGLE_LEFTDOWN then Dec(angle, 4); + end; + 7: begin // Plasmagun + len := 1024; + sz := 5; + if angle = ANGLE_RIGHTUP then Inc(angle); + if angle = ANGLE_RIGHTDOWN then Inc(angle, 3); + if angle = ANGLE_LEFTUP then Dec(angle); + if angle = ANGLE_LEFTDOWN then Dec(angle, 3); + end; + 8: begin // BFG + len := 1024; + sz := 12; + if angle = ANGLE_RIGHTUP then Inc(angle, 1); + if angle = ANGLE_RIGHTDOWN then Inc(angle, 2); + if angle = ANGLE_LEFTUP then Dec(angle, 1); + if angle = ANGLE_LEFTDOWN then Dec(angle, 2); + end; + 9: begin // Super Chaingun + len := 1024; + sz := 4; + if angle = ANGLE_RIGHTUP then Dec(angle, 2); + if angle = ANGLE_RIGHTDOWN then Inc(angle, 4); + if angle = ANGLE_LEFTUP then Inc(angle, 2); + if angle = ANGLE_LEFTDOWN then Dec(angle, 4); + end; + end; + xx := Trunc(Cos(-DegToRad(angle)) * len) + wx; + yy := Trunc(Sin(-DegToRad(angle)) * len) + wy; + {$IF DEFINED(D2F_DEBUG)} + drawCast(sz, wx, wy, xx, yy); + {$ELSE} + e_DrawLine(sz, wx, wy, xx, yy, 255, 0, 0, 96); + {$ENDIF} +end; + +procedure r_Player_DrawGUI (pl: TPlayer); +var + ID: DWORD; + X, Y, SY, a, p, m: Integer; + tw, th: Word; + cw, ch: Byte; + s: string; + stat: TPlayerStatArray; +begin + X := gPlayerScreenSize.X; + SY := gPlayerScreenSize.Y; + Y := 0; + + if gShowScore and (gGameSettings.GameMode in [GM_TDM, GM_CTF]) then + begin + if gGameSettings.GameMode = GM_CTF then + a := 32 + 8 + else + a := 0; + if gGameSettings.GameMode = GM_CTF then + begin + s := 'TEXTURE_PLAYER_REDFLAG'; + if gFlags[FLAG_RED].State = FLAG_STATE_CAPTURED then + s := 'TEXTURE_PLAYER_REDFLAG_S'; + if gFlags[FLAG_RED].State = FLAG_STATE_DROPPED then + s := 'TEXTURE_PLAYER_REDFLAG_D'; + if g_Texture_Get(s, ID) then + e_Draw(ID, X-16-32, 240-72-4, 0, True, False); + end; + + s := IntToStr(gTeamStat[TEAM_RED].Score); + e_CharFont_GetSize(gMenuFont, s, tw, th); + e_CharFont_PrintEx(gMenuFont, X-16-a-tw, 240-72-4, s, TEAMCOLOR[TEAM_RED]); + + if gGameSettings.GameMode = GM_CTF then + begin + s := 'TEXTURE_PLAYER_BLUEFLAG'; + if gFlags[FLAG_BLUE].State = FLAG_STATE_CAPTURED then + s := 'TEXTURE_PLAYER_BLUEFLAG_S'; + if gFlags[FLAG_BLUE].State = FLAG_STATE_DROPPED then + s := 'TEXTURE_PLAYER_BLUEFLAG_D'; + if g_Texture_Get(s, ID) then + e_Draw(ID, X-16-32, 240-32-4, 0, True, False); + end; + + s := IntToStr(gTeamStat[TEAM_BLUE].Score); + e_CharFont_GetSize(gMenuFont, s, tw, th); + e_CharFont_PrintEx(gMenuFont, X-16-a-tw, 240-32-4, s, TEAMCOLOR[TEAM_BLUE]); + end; + + if g_Texture_Get('TEXTURE_PLAYER_HUDBG', ID) then + e_DrawFill(ID, X, 0, 1, (gPlayerScreenSize.Y div 256)+IfThen(gPlayerScreenSize.Y mod 256 > 0, 1, 0), + 0, False, False); + + if g_Texture_Get('TEXTURE_PLAYER_HUD', ID) then + e_Draw(ID, X+2, Y, 0, True, False); + + if gGameSettings.GameType in [GT_CUSTOM, GT_SERVER, GT_CLIENT] then + begin + if gShowStat then + begin + s := IntToStr(pl.Frags); + e_CharFont_GetSize(gMenuFont, s, tw, th); + e_CharFont_PrintEx(gMenuFont, X-16-tw, Y, s, _RGB(255, 0, 0)); + + s := ''; + p := 1; + m := 0; + stat := g_Player_GetStats(); + if stat <> nil then + begin + p := 1; + + for a := 0 to High(stat) do + if stat[a].Name <> pl.Name then + begin + if stat[a].Frags > m then m := stat[a].Frags; + if stat[a].Frags > pl.Frags then p := p+1; + end; + end; + + s := IntToStr(p)+' / '+IntToStr(Length(stat))+' '; + if pl.Frags >= m then s := s+'+' else s := s+'-'; + s := s+IntToStr(Abs(pl.Frags-m)); + + e_CharFont_GetSize(gMenuSmallFont, s, tw, th); + e_CharFont_PrintEx(gMenuSmallFont, X-16-tw, Y+32, s, _RGB(255, 0, 0)); + end; + + if gLMSRespawn > LMS_RESPAWN_NONE then + begin + s := _lc[I_GAME_WARMUP]; + e_CharFont_GetSize(gMenuFont, s, tw, th); + s := s + ': ' + IntToStr((gLMSRespawnTime - gTime) div 1000); + e_CharFont_PrintEx(gMenuFont, X-64-tw, SY-32, s, _RGB(0, 255, 0)); + end + else if gShowLives and (gGameSettings.MaxLives > 0) then + begin + s := IntToStr(pl.Lives); + e_CharFont_GetSize(gMenuFont, s, tw, th); + e_CharFont_PrintEx(gMenuFont, X-16-tw, SY-32, s, _RGB(0, 255, 0)); + end; + end; + + e_CharFont_GetSize(gMenuSmallFont, pl.Name, tw, th); + e_CharFont_PrintEx(gMenuSmallFont, X+98-(tw div 2), Y+8, pl.Name, _RGB(255, 0, 0)); + + if R_BERSERK in pl.FRulez then + e_Draw(gItemsTexturesID[ITEM_MEDKIT_BLACK], X+37, Y+45, 0, True, False) + else + e_Draw(gItemsTexturesID[ITEM_MEDKIT_LARGE], X+37, Y+45, 0, True, False); + + if g_Texture_Get('TEXTURE_PLAYER_ARMORHUD', ID) then + e_Draw(ID, X+36, Y+77, 0, True, False); + + s := IntToStr(IfThen(pl.Health > 0, pl.Health, 0)); + e_CharFont_GetSize(gMenuFont, s, tw, th); + e_CharFont_PrintEx(gMenuFont, X+178-tw, Y+40, s, _RGB(255, 0, 0)); + + s := IntToStr(pl.Armor); + e_CharFont_GetSize(gMenuFont, s, tw, th); + e_CharFont_PrintEx(gMenuFont, X+178-tw, Y+68, s, _RGB(255, 0, 0)); + + s := IntToStr(pl.GetAmmoByWeapon(pl.CurrWeap)); + + case pl.CurrWeap of + WEAPON_KASTET: + begin + s := '--'; + ID := gItemsTexturesID[ITEM_WEAPON_KASTET]; + end; + WEAPON_SAW: + begin + s := '--'; + ID := gItemsTexturesID[ITEM_WEAPON_SAW]; + end; + WEAPON_PISTOL: ID := gItemsTexturesID[ITEM_WEAPON_PISTOL]; + WEAPON_CHAINGUN: ID := gItemsTexturesID[ITEM_WEAPON_CHAINGUN]; + WEAPON_SHOTGUN1: ID := gItemsTexturesID[ITEM_WEAPON_SHOTGUN1]; + WEAPON_SHOTGUN2: ID := gItemsTexturesID[ITEM_WEAPON_SHOTGUN2]; + WEAPON_SUPERPULEMET: ID := gItemsTexturesID[ITEM_WEAPON_SUPERPULEMET]; + WEAPON_ROCKETLAUNCHER: ID := gItemsTexturesID[ITEM_WEAPON_ROCKETLAUNCHER]; + WEAPON_PLASMA: ID := gItemsTexturesID[ITEM_WEAPON_PLASMA]; + WEAPON_BFG: ID := gItemsTexturesID[ITEM_WEAPON_BFG]; + WEAPON_FLAMETHROWER: ID := gItemsTexturesID[ITEM_WEAPON_FLAMETHROWER]; + end; + + e_CharFont_GetSize(gMenuFont, s, tw, th); + e_CharFont_PrintEx(gMenuFont, X+178-tw, Y+158, s, _RGB(255, 0, 0)); + e_Draw(ID, X+20, Y+160, 0, True, False); + + if R_KEY_RED in pl.FRulez then + e_Draw(gItemsTexturesID[ITEM_KEY_RED], X+78, Y+214, 0, True, False); + + if R_KEY_GREEN in pl.FRulez then + e_Draw(gItemsTexturesID[ITEM_KEY_GREEN], X+95, Y+214, 0, True, False); + + if R_KEY_BLUE in pl.FRulez then + e_Draw(gItemsTexturesID[ITEM_KEY_BLUE], X+112, Y+214, 0, True, False); + + if pl.JetFuel > 0 then + begin + if g_Texture_Get('TEXTURE_PLAYER_HUDAIR', ID) then + e_Draw(ID, X+2, Y+116, 0, True, False); + if g_Texture_Get('TEXTURE_PLAYER_HUDJET', ID) then + e_Draw(ID, X+2, Y+126, 0, True, False); + e_DrawLine(4, X+16, Y+122, X+16+Trunc(168*IfThen(pl.Air > 0, pl.Air, 0)/AIR_MAX), Y+122, 0, 0, 196); + e_DrawLine(4, X+16, Y+132, X+16+Trunc(168*pl.JetFuel/JET_MAX), Y+132, 208, 0, 0); + end + else + begin + if g_Texture_Get('TEXTURE_PLAYER_HUDAIR', ID) then + e_Draw(ID, X+2, Y+124, 0, True, False); + e_DrawLine(4, X+16, Y+130, X+16+Trunc(168*IfThen(pl.Air > 0, pl.Air, 0)/AIR_MAX), Y+130, 0, 0, 196); + end; + + if gShowPing and g_Game_IsClient then + begin + s := _lc[I_GAME_PING_HUD] + IntToStr(NetPeer.lastRoundTripTime) + _lc[I_NET_SLIST_PING_MS]; + e_TextureFontPrint(X + 4, Y + 242, s, gStdFont); + Y := Y + 16; + end; + + if pl.Spectator then + begin + e_TextureFontPrint(X + 4, Y + 242, _lc[I_PLAYER_SPECT], gStdFont); + e_TextureFontPrint(X + 4, Y + 258, _lc[I_PLAYER_SPECT2], gStdFont); + e_TextureFontPrint(X + 4, Y + 274, _lc[I_PLAYER_SPECT1], gStdFont); + if pl.NoRespawn then + begin + e_TextureFontGetSize(gStdFont, cw, ch); + s := _lc[I_PLAYER_SPECT4]; + e_TextureFontPrintEx(gScreenWidth div 2 - cw*(Length(s) div 2), + gScreenHeight-4-ch, s, gStdFont, 255, 255, 255, 1, True); + e_TextureFontPrint(X + 4, Y + 290, _lc[I_PLAYER_SPECT1S], gStdFont); + end; + + end; +end; + +procedure r_Player_DrawRulez (p: TPlayer); +var + dr: Boolean; +begin + // При взятии неуязвимости рисуется инверсионный белый фон + if (p.FMegaRulez[MR_INVUL] >= gTime) and (p.SpawnInvul < gTime) then + begin + if (p.FMegaRulez[MR_INVUL]-gTime) <= 2100 then + dr := not Odd((p.FMegaRulez[MR_INVUL]-gTime) div 300) + else + dr := True; + + if dr then + e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, + 191, 191, 191, 0, TBlending.Invert); + end; + + // При взятии защитного костюма рисуется зеленоватый фон + if p.FMegaRulez[MR_SUIT] >= gTime then + begin + if (p.FMegaRulez[MR_SUIT]-gTime) <= 2100 then + dr := not Odd((p.FMegaRulez[MR_SUIT]-gTime) div 300) + else + dr := True; + + if dr then + e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, + 0, 96, 0, 200, TBlending.None); + end; + + // При взятии берсерка рисуется красноватый фон + if (p.Berserk >= 0) and (LongWord(p.Berserk) >= gTime) and (gFlash = 2) then + begin + e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, + 255, 0, 0, 200, TBlending.None); + end; +end; + +procedure r_Player_DrawPain (p: TPlayer); +var + a, h: Integer; +begin + if p.Pain = 0 then Exit; + + a := p.Pain; + + if a < 15 then h := 0 + else if a < 35 then h := 1 + else if a < 55 then h := 2 + else if a < 75 then h := 3 + else if a < 95 then h := 4 + else h := 5; + + //if a > 255 then a := 255; + + e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, 255, 0, 0, 255-h*50); + //e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, 255-min(128, a), 255-a, 255-a, 0, B_FILTER); +end; + +procedure r_Player_DrawPickup (p: TPlayer); +var + a, h: Integer; +begin + if p.Pickup = 0 then Exit; + + a := p.Pickup; + + if a < 15 then h := 1 + else if a < 35 then h := 2 + else if a < 55 then h := 3 + else if a < 75 then h := 4 + else h := 5; + + e_DrawFillQuad(0, 0, gPlayerScreenSize.X-1, gPlayerScreenSize.Y-1, 150, 200, 150, 255-h*50); +end; + +procedure r_Player_DrawCorpse (p: TCorpse); +var + fX, fY: Integer; +begin + if p.State = CORPSE_STATE_REMOVEME then + Exit; + + p.Obj.lerp(gLerpFactor, fX, fY); + + if p.Animation <> nil then + p.Animation.Draw(fX, fY, TMirrorType.None); + + if p.AnimationMask <> nil then + begin + e_Colors := p.Color; + p.AnimationMask.Draw(fX, fY, TMirrorType.None); + e_Colors.R := 255; + e_Colors.G := 255; + e_Colors.B := 255; + end; +end; + +end.