НЕ ЗАХОДИТЬ! СЛИШКОМ МНОГО ТУПЫХ ВОПРОСОВ!

Тема в разделе "Мутаторы", создана пользователем CFH, 24 фев 2018.

  1. CFH

    CFH Новенький

    Доброго времени суток, камрады. Есть парочка вопросов по коду. Пилю свой скорбоард для кф. Для начала наследуемся от KFScoreBoard или KFScoreBoardNew, добавляем свою механику обсчета, отрисовки и т.д. Предположим, нужно отслеживать и отрисовывать число убитых фп, ск тем или иным игроком с дальнейшей отрисовкой в скорбоарде. Переопределяем ф-ю UpdateScoreBoard, но тут возникает вопрос: по умолчанию доступ к PlayerReplicationInfo всей тимы в этой ф-ии доступен через объект класса GameReplicationInfo, но, если юзать контроллеры игрока, т.е. заполнять массив тимы типа KFPlayerController, и в дальнейшем через него получать доступ к PlayerReplicationInfo, т.е. KFPlayerControllerObject.PlayerReplicationInfo, то каст в ф-ии UpdateScoreBoard
    Код:
    KFPRI = KFPlayerReplicationInfo(KFPlayerControllerObject.PlayerReplicationInfo);
    не робит, по крайней мере ссылка на KFPRI=none, , в то же время как дефолтный код
    Код:
    KFPRI = KFPlayerReplicationInfo(TeamPRIArray[i]);
    робит на ура - первая неясность. Далее, если пытаться обсчитывать механику киллов фп, ск в классе скорбоарда, в том же таймере, например, то не происходит изменения счетчика киллов фп или же ск - вторая неясность, НО, если же обсчитывать это дело в мутаторе (наследнике класса Mutator), то все норм, но опять же, нет отрисовки в самом скорбоарде: через тот же итератор foreach allactors не получается найти объект моего класса скорбоарда - третья неясность. Есть предположение, что дело в репликации, но опять же не до конца понятно, что и куда реплицировать (да, я быдлонубокодер). Вот весь код сей вакханалии:
    Код:
    class BHScoreboard extends KFScoreBoardNew;
    
    var localized string FPText, SCText;
    var BHScoreboardMut BHSM;
    //var int FPKills[32], SCKills[32];
    
    /*replication
    {
        // Things the server should send to the client.
        reliable if ( bNetDirty && (Role == Role_Authority) )
            FPKills, SCKills;
    }*/
    
    function SetMutator(BHScoreboardMut M)
    {
        BHSM = M;
    }
    
    simulated event UpdateScoreBoard(Canvas Canvas)
    {
        local PlayerReplicationInfo PRI, OwnerPRI;
        local int i,j, FontReduction, NetXPos, PlayerCount, HeaderOffsetY, HeadFoot, MessageFoot, PlayerBoxSizeY, BoxSpaceY, NameXPos, BoxTextOffsetY, OwnerOffset, HealthXPos, BoxXPos,KillsXPos, TitleYPos, BoxWidth, VetXPos, TempVetXPos, VetYPos, FPXPos, SCXPos;
        local float XL,YL, MaxScaling;
        local float deathsXL, KillsXL, netXL,HealthXL, MaxNamePos, KillWidthX, HealthWidthX, TimeXL, ScoreXPos, ScoreXL, FPXL, FPWidthX, SCXL, SCWidthX;
        local bool bNameFontReduction;
        local Material VeterancyBox, StarMaterial;
        local int TempLevel;
        local KFPlayerReplicationInfo KFPRI;
        local float AssistsXPos,AssistsWidthX;
        local float CashX;
        local string CashString,HealthString;
        local float OutX;
        local array<PlayerReplicationInfo> TeamPRIArray;
    //************************************************************************************************
    /*    local Controller C;
        local array<KFMonsterController> FPS, SCS;
        local array<KFPlayerController> PCS;*/
    //************************************************************************************************
        OwnerPRI = KFPlayerController(Owner).PlayerReplicationInfo;
        OwnerOffset = -1;
    
        for (i = 0; i < GRI.PRIArray.Length; i++)
        {
            PRI = GRI.PRIArray[i];
    
            if ( !PRI.bOnlySpectator )
            {
                if ( PRI == OwnerPRI )
                    OwnerOffset = i;
    
                PlayerCount++;
                TeamPRIArray[ TeamPRIArray.Length ] = PRI;
            }
        }
    
        PlayerCount = Min(PlayerCount, MAXPLAYERS);
    //************************************************************************************************
    /*    PCS.Length = 0;
        FPS.Length = 0;
        SCS.Length = 0;
    
        for ( C = Level.ControllerList; C != none; C = C.NextController )
        {
            if ( KFPlayerController(C) != none )
                PCS[ PCS.Length ] = KFPlayerController(C);
    
            if ( KFMonsterController(C) != none )
            {
                if ( KFMonsterController(C).KFM.IsA('ZombieFleshpound') )
                    FPS[ FPS.Length ] = KFMonsterController(C);
    
                if ( KFMonsterController(C).KFM.IsA('ZombieScrake') )
                    SCS[ SCS.Length ] = KFMonsterController(C);
            }
        }*/
    //************************************************************************************************
        Canvas.Font = class'ROHud'.static.GetSmallMenuFont(Canvas);
        Canvas.StrLen("Test", XL, YL);
        BoxSpaceY = 0.25 * YL;
        PlayerBoxSizeY = 1.2 * YL;
        HeadFoot = 7 * YL;
        MessageFoot = 1.5 * HeadFoot;
    
        if ( PlayerCount > (Canvas.ClipY - 1.5 * HeadFoot) / (PlayerBoxSizeY + BoxSpaceY) )
        {
            BoxSpaceY = 0.125 * YL;
            PlayerBoxSizeY = 1.25 * YL;
    
            if ( PlayerCount > (Canvas.ClipY - 1.5 * HeadFoot) / (PlayerBoxSizeY + BoxSpaceY) )
            {
                if ( PlayerCount > (Canvas.ClipY - 1.5 * HeadFoot) / (PlayerBoxSizeY + BoxSpaceY) )
                {
                    PlayerBoxSizeY = 1.125 * YL;
                }
            }
        }
    
        if (Canvas.ClipX < 512)
            PlayerCount = Min(PlayerCount, 1+(Canvas.ClipY - HeadFoot) / (PlayerBoxSizeY + BoxSpaceY) );
        else
            PlayerCount = Min(PlayerCount, (Canvas.ClipY - HeadFoot) / (PlayerBoxSizeY + BoxSpaceY) );
    
        if (FontReduction > 2)
            MaxScaling = 3;
        else
            MaxScaling = 2.125;
    
        PlayerBoxSizeY = FClamp((1.25 + (Canvas.ClipY - 0.67 * MessageFoot)) / PlayerCount - BoxSpaceY, PlayerBoxSizeY, MaxScaling * YL);
    
        bDisplayMessages = (PlayerCount <= (Canvas.ClipY - MessageFoot) / (PlayerBoxSizeY + BoxSpaceY));
    
        HeaderOffsetY = 10 * YL;
        BoxWidth = 0.7 * Canvas.ClipX;
        BoxXPos = 0.5 * (Canvas.ClipX - BoxWidth);
        BoxWidth = Canvas.ClipX - 2 * BoxXPos;
        VetXPos = BoxXPos + 0.00005 * BoxWidth;
        NameXPos = BoxXPos + 0.075 * BoxWidth;
    //************************************************************************************************
        FPXPos = BoxXPos + 0.30 * BoxWidth;
        SCXPos = BoxXPos + 0.40 * BoxWidth;
    //************************************************************************************************
        KillsXPos = BoxXPos + 0.50 * BoxWidth;
        AssistsXPos = BoxXPos + 0.60 * BoxWidth;
        HealthXpos = BoxXPos + 0.70 * BoxWidth;
        ScoreXPos = BoxXPos + 0.80 * BoxWidth;
        NetXPos = BoxXPos + 0.95 * BoxWidth;
    
        // Draw background boxes
        Canvas.Style = ERenderStyle.STY_Alpha;
        Canvas.DrawColor = HUDClass.default.WhiteColor;
        Canvas.DrawColor.A = 128;
    
        for (i = 0; i < PlayerCount; i++)
        {
            Canvas.SetPos(BoxXPos, HeaderOffsetY + (PlayerBoxSizeY + BoxSpaceY) * i);
            Canvas.DrawTileStretched(BoxMaterial, BoxWidth, PlayerBoxSizeY);
        }
    
        // Draw title
        Canvas.Style = ERenderStyle.STY_Normal;
        DrawTitle(Canvas, HeaderOffsetY, (PlayerCount + 1) * (PlayerBoxSizeY + BoxSpaceY), PlayerBoxSizeY);
    
        // Draw headers
        TitleYPos = HeaderOffsetY - 1.1 * YL;
        Canvas.StrLen(HealthText, HealthXL, YL);
        Canvas.StrLen(DeathsText, DeathsXL, YL);
        Canvas.StrLen(KillsText, KillsXL, YL);
        Canvas.StrLen(PointsText, ScoreXL, YL);
        Canvas.StrLen(AssistsHeaderText, TimeXL, YL);
        Canvas.StrLen(FPText, FPXL, YL);
        Canvas.StrLen(SCText, SCXL, YL);
    
        Canvas.DrawColor = HUDClass.default.WhiteColor;
        Canvas.SetPos(NameXPos, TitleYPos);
        Canvas.DrawText(PlayerText,true);
    
        Canvas.SetPos(KillsXPos - 0.5 * KillsXL, TitleYPos);
        Canvas.DrawText(KillsText,true);
    
        Canvas.SetPos(ScoreXPos - 0.5 * ScoreXL, TitleYPos);
        Canvas.DrawText(PointsText,true);
    
        Canvas.SetPos(AssistsXPos - 0.5 * TimeXL, TitleYPos);
        Canvas.DrawText(AssistsHeaderText,true);
    
        Canvas.SetPos(HealthXPos - 0.5 * HealthXL, TitleYPos);
        Canvas.DrawText(HealthText,true);
    //************************************************************************************************
        Canvas.SetPos(FPXPos - 0.5 * FPXL, TitleYPos);
        Canvas.DrawText(FPText,true);
    
        Canvas.SetPos(SCXPos - 0.5 * SCXL, TitleYPos);
        Canvas.DrawText(SCText,true);
    //************************************************************************************************
        // Draw player names
        MaxNamePos = 0.9 * (KillsXPos - NameXPos);
        for (i = 0; i < PlayerCount; i++)
        {
            Canvas.StrLen(TeamPRIArray[i].PlayerName, XL, YL);
    
            if ( XL > MaxNamePos )
            {
                bNameFontReduction = true;
                break;
            }
        }
    
        if ( bNameFontReduction )
            Canvas.Font = GetSmallerFontFor(Canvas, FontReduction - 1);
    
        Canvas.Style = ERenderStyle.STY_Alpha;
        Canvas.DrawColor = HUDClass.default.WhiteColor;
        Canvas.SetPos(0.5 * Canvas.ClipX, HeaderOffsetY + 4);
        BoxTextOffsetY = HeaderOffsetY + 0.5 * (PlayerBoxSizeY - YL);
    
        Canvas.DrawColor = HUDClass.default.WhiteColor;
        MaxNamePos = Canvas.ClipX;
        Canvas.ClipX = KillsXPos - 4.f;
    
        for (i = 0; i < PlayerCount; i++)
        {
            Canvas.SetPos(NameXPos, (PlayerBoxSizeY + BoxSpaceY)*i + BoxTextOffsetY);
    
            if( i == OwnerOffset )
            {
                Canvas.DrawColor.G = 0;
                Canvas.DrawColor.B = 0;
            }
            else
            {
                Canvas.DrawColor.G = 255;
                Canvas.DrawColor.B = 255;
            }
    
            Canvas.DrawTextClipped(TeamPRIArray[i].PlayerName);
        }
    
        Canvas.ClipX = MaxNamePos;
        Canvas.DrawColor = HUDClass.default.WhiteColor;
    
        if (bNameFontReduction)
            Canvas.Font = GetSmallerFontFor(Canvas, FontReduction);
    
        Canvas.Style = ERenderStyle.STY_Normal;
        MaxScaling = FMax(PlayerBoxSizeY, 30.f);
    
        // Draw each player's information
        for (i = 0; i < PlayerCount; i++)
        {
    //************************************************************************************************
            KFPRI = KFPlayerReplicationInfo(TeamPRIArray[i]);
            //PRI = FindPlayerControllerPRI(PCS[i], TeamPRIArray);
            //KFPRI = KFPlayerReplicationInfo(PCS[i].PlayerReplicationInfo);
    
            //DebugMessage("KFPRI "@string(KFPRI));
            //DebugMessage("PCS"@string(i+1)@" "@string(PCS[i]));
    
            //FPKills[i] = KillingCounter(PCS[i], FPS);
            //SCKills[i] = KillingCounter(PCS[i], SCS);
    //************************************************************************************************
            Canvas.DrawColor = HUDClass.default.WhiteColor;
    
            // Display perks.
            if ( KFPRI!=None && KFPRI.ClientVeteranSkill != none )
            {
                if(KFPRI.ClientVeteranSkillLevel == 6)
                {
                    VeterancyBox = KFPRI.ClientVeteranSkill.default.OnHUDGoldIcon;
                    StarMaterial = class'HUDKillingFloor'.default.VetStarGoldMaterial;
                    TempLevel = KFPRI.ClientVeteranSkillLevel - 5;
                }
                else
                {
                    VeterancyBox = KFPRI.ClientVeteranSkill.default.OnHUDIcon;
                    StarMaterial = class'HUDKillingFloor'.default.VetStarMaterial;
                    TempLevel = KFPRI.ClientVeteranSkillLevel;
                }
    
                if ( VeterancyBox != None )
                {
                    TempVetXPos = VetXPos;
                    VetYPos = (PlayerBoxSizeY + BoxSpaceY) * i + BoxTextOffsetY - PlayerBoxSizeY * 0.22;
                    Canvas.SetPos(TempVetXPos, VetYPos);
                    Canvas.DrawTile(VeterancyBox, PlayerBoxSizeY, PlayerBoxSizeY, 0, 0, VeterancyBox.MaterialUSize(), VeterancyBox.MaterialVSize());
    
                    if(StarMaterial != none)
                    {
                        TempVetXPos += PlayerBoxSizeY - ((PlayerBoxSizeY/5) * 0.75);
                        VetYPos += PlayerBoxSizeY - ((PlayerBoxSizeY/5) * 1.5);
    
                        for ( j = 0; j < TempLevel; j++ )
                        {
                            Canvas.SetPos(TempVetXPos, VetYPos);
                            Canvas.DrawTile(StarMaterial, (PlayerBoxSizeY/5) * 0.7, (PlayerBoxSizeY/5) * 0.7, 0, 0, StarMaterial.MaterialUSize(), StarMaterial.MaterialVSize());
    
                            VetYPos -= (PlayerBoxSizeY/5) * 0.7;
                        }
                    }
                }
            }
    
    //************************************************************************************************
            Canvas.StrLen(BHSM.FPKills[i], FPWidthX, YL);
            Canvas.SetPos(FPXPos - 0.5 * FPWidthX, (PlayerBoxSizeY + BoxSpaceY) * i + BoxTextOffsetY);
            Canvas.DrawText(BHSM.FPKills[i], true);
    
            Canvas.StrLen(BHSM.SCKills[i], SCWidthX, YL);
            Canvas.SetPos(SCXPos - 0.5 * SCWidthX, (PlayerBoxSizeY + BoxSpaceY) * i + BoxTextOffsetY);
            Canvas.DrawText(BHSM.SCKills[i], true);
    //************************************************************************************************
    
            // draw kills
            if( bDisplayWithKills )
            {
                Canvas.StrLen(KFPRI.Kills, KillWidthX, YL);
                Canvas.SetPos(KillsXPos - 0.5 * KillWidthX, (PlayerBoxSizeY + BoxSpaceY) * i + BoxTextOffsetY);
                Canvas.DrawText(KFPRI.Kills, true);
    
              // Draw Kill Assists
    
                Canvas.StrLen(KFPRI.KillAssists, AssistsWidthX, YL);
                Canvas.SetPos(AssistsXPos - 0.5 * AssistsWidthX, (PlayerBoxSizeY + BoxSpaceY) * i + BoxTextOffsetY);
                Canvas.DrawText(KFPRI.KillAssists, true);
            }
            // draw cash
            CashString = "£"@string(int(TeamPRIArray[i].Score)) ;
    
            if(TeamPRIArray[i].Score >= 1000)
            {
                CashString = "£"@string(TeamPRIArray[i].Score/1000.f)$"K" ;
            }
    
            Canvas.StrLen(CashString,CashX,YL);
            Canvas.SetPos(ScoreXPos - CashX/2 , (PlayerBoxSizeY + BoxSpaceY)*i + BoxTextOffsetY);
            Canvas.DrawColor = Canvas.MakeColor(255,255,125,255);
            Canvas.DrawText(CashString);
            Canvas.DrawColor = HUDClass.default.WhiteColor;
    
            // Draw health status
    
            HealthString = KFPRI.PlayerHealth$" HP" ;
            Canvas.StrLen(HealthString,HealthWidthX,YL);
            Canvas.SetPos(HealthXpos - HealthWidthX/2, (PlayerBoxSizeY + BoxSpaceY) * i + BoxTextOffsetY);
    
            if ( TeamPRIArray[i].bOutOfLives )
            {
                Canvas.StrLen(OutText,OutX,YL);
                Canvas.DrawColor = HUDClass.default.RedColor;
                Canvas.SetPos(HealthXpos - OutX/2, (PlayerBoxSizeY + BoxSpaceY) * i + BoxTextOffsetY);
                Canvas.DrawText(OutText);
            }
            else
            {
                if( KFPRI.PlayerHealth>=80 )
                {
                    Canvas.DrawColor = HUDClass.default.GreenColor;
                }
                else if( KFPRI.PlayerHealth>=50 )
                {
                    Canvas.DrawColor = HUDClass.default.GoldColor;
                }
                else
                {
                    Canvas.DrawColor = HUDClass.default.RedColor;
                }
    
                Canvas.DrawText(HealthString);
            }
        }
    
        if (Level.NetMode == NM_Standalone)
            return;
    
        Canvas.StrLen(NetText, NetXL, YL);
        Canvas.DrawColor = HUDClass.default.WhiteColor;
        Canvas.SetPos(NetXPos - 0.5 * NetXL, TitleYPos);
        Canvas.DrawText(NetText,true);
    
        for (i=0; i<GRI.PRIArray.Length; i++)
            PRIArray[i] = GRI.PRIArray[i];
    
        DrawNetInfo(Canvas, FontReduction, HeaderOffsetY, PlayerBoxSizeY, BoxSpaceY, BoxTextOffsetY, OwnerOffset, PlayerCount, NetXPos);
        DrawMatchID(Canvas, FontReduction);
    }
    
    defaultproperties
    {
        FPText="FP"
        SCText="SC"
    }
    
    Код:
    class BHScoreboardMut extends Mutator;
    
    var BHScoreboard B;
    var Controller C;
    var array<KFMonsterController> FPS, SCS;
    var array<KFPlayerController> PCS;
    var int FPKills[32], SCKills[32];
    var int i;
    
    replication
    {
        // Things the server should send to the client.
        reliable if ( bNetDirty && (Role == Role_Authority) )
            FPKills, SCKills;
    }
    
    simulated function PostBeginPlay()
    {
        Level.Game.ScoreBoardType = "BHScoreboard.BHScoreboard";
    
        /*foreach AllActors(class'BHScoreboard', B)
            break;
    
        B.SetMutator(self);*/
    
        SetTimer(1.0, true);
    }
    
    function DebugMessage(string Message)
    {
        //local Controller C;
    
        for ( C = Level.ControllerList; C != none; C = C.NextController )
        {
            if ( PlayerController(C) != none )
                PlayerController(C).ClientMessage(Message);
        }
    }
    
    function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
    {
        //local BHScoreboard Board;
    
        B = BHScoreboard(Other);
    
        if ( B != none )
        {
            B.SetMutator(self);
        }
    
        return true;
    }
    
    function Timer()
    {
        PCS.Length = 0;
        FPS.Length = 0;
        SCS.Length = 0;
    
        for ( C = Level.ControllerList; C != none; C = C.NextController )
        {
            if ( KFPlayerController(C) != none )
                PCS[ PCS.Length ] = KFPlayerController(C);
    
            if ( KFMonsterController(C) != none )
            {
                if ( KFMonsterController(C).KFM.IsA('ZombieFleshpound') )
                    FPS[ FPS.Length ] = KFMonsterController(C);
    
                if ( KFMonsterController(C).KFM.IsA('ZombieScrake') )
                    SCS[ SCS.Length ] = KFMonsterController(C);
            }
        }
    
        for (i = 0; i < PCS.Length; i++)
        {
            FPKills[i] += KillingCounter(PCS[i], FPS);
            SCKills[i] += KillingCounter(PCS[i], SCS);
            DebugMessage(string(SCKills[i]));
        }
    
        DebugMessage(string(B.Name));
        DebugMessage(Level.Game.ScoreBoardType);
    }
    
    function int KillingCounter(KFPlayerController Killer, array<KFMonsterController> Victim)
    {
        local int i1, Counter;
    
        Counter = 0;
    
        for ( i1 = 0; i1 < Victim.Length; i1++)
        {
            if ( KFPlayerController(Victim[i1].KFM.LastHitBy) == Killer && Victim[i1].KFM.Health <= 0 )
                Counter++;
        }
    
        return Counter;
    }
    
    /*function PlayerReplicationInfo FindPlayerControllerPRI(KFPlayerController KFPC, array<PlayerReplicationInfo> PRI)
    {
        local int i;
    
        if ( KFPC == none )
            return none;
        else
        {
            for ( i = 0; i < PRI.Length; i++)
            {
                if ( KFPC.PlayerReplicationInfo.PlayerID == PRI[i].PlayerID )
                    return PRI[i];
                else
                    continue;
            }
        }
    }*/
    
    defaultproperties
    {
        bAlwaysRelevant=true
        RemoteRole=ROLE_SimulatedProxy
        //bNetNotify=True
        bAddToServerPackages=true
        GroupName="BHScoreboard"
        FriendlyName="Bloody Hell Scoreboard"
        Description="FP and SC stats were added."
    }
    
    Буду признателен за любой совет и помощь.
     
    Последнее редактирование модератором: 25 фев 2018
    Flame и Arckon. нравится это.
  2. Essence

    Essence Солдат

    Кол-во убитых мобов лучше отлавливать в GameRules в функции ScoreKill
    Дальше либо передавать это кол-во в кастомный PlayerReplicationInfo, либо хранить в мутаторе
    И уже тогда тянуть инфу в кастомный ScoreBoard
    Кстати, ещё можно глянуть эту тему
     
    Flame нравится это.
  3. CFH

    CFH Новенький

    Если можно, то подробнее, почему конкретно мой вариант не робит, в чем косяк? 0_0 хотелось бы понять :)
     
  4. STaJIKeR

    STaJIKeR Капо

    Ты не против, если я подкину ему кусочек кода, с которым ты мне помог очень давно?

    Прошу кидай код по спойлер.
     
  5. Flame

    Flame -Заслуженый кодер форума-

    Ну основная беда в том, что ты на клиенте работаешь с тем кодом, который надо звать на сервере. Так себе идея тащить мутатор (который должен выполняться на сервере) в ScoreBoard, которая выполняется только на клиенте.

    Нужен некоторый объект, который принадлежит игроку и существует как на сервере (сбор информации), так и на клиенте (отображение в табличке)
    Хорошее решение - использование либо SRPlayerReplicationInfo или (если не хочется его захламлять) отдельного ReplicationInfo класса. Кроме того вполне возможно цеплять объекты к контроллеру. То есть цеплять на контроллер новый объект содержащий эти переменные. Но пока не будем усложнять - чуть ниже напишу реализацию с PlayerReplicationInfo + GameRules

    И вообще есть смысл написать краткую статью по модификации таблички раз такое дело. Но это чуть позже

    Мутатор:
    Просто добавим GameRules в котором будем отслеживать убийства, урон и т.д.
    Кроме того в CheckReplacement добавим новую PlayerReplicationInfo унаследованную от KFPlayerReplicationInfo
    Ну и пропишем ссылку на новую табличку
    Код:
    class ScoreBoardMut extends Mutator;
    var ScoreBoardGameRules sbGameRules;
    function PostBeginPlay()
    {
        Level.Game.ScoreBoardType = "ScoreBoardMut.NewScoreBoard";
        sbGameRules=Spawn(Class'ScoreBoardGameRules');
    }
    function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
    {
        if(Controller(Other)!=None)
            Controller(Other).PlayerReplicationInfoClass = Class'SBPlayerReplicationInfo';
        return true;
    }
    defaultproperties
    {
        bAlwaysRelevant=true
        RemoteRole=ROLE_SimulatedProxy
        bAddToServerPackages=true
        GroupName="KF-NewScoreboard"
        FriendlyName="NewScoreboard"
        Description="NewScoreboard"
    }
    

    Файл репликации:
    Просто создаём переменную и реплицируем её на клиент
    Код:
    class SBPlayerReplicationInfo extends KFPlayerReplicationInfo;
    var int FPKilled;
    replication
    {
        reliable if (bNetDirty && Role == Role_Authority)
            FPKilled;
    }
    

    GameRules:
    Здесь основные функции это PreventDeath (можно так же использовать ScoreKill) и NetDamage
    Первая отслеживает убийства, вторая урон
    Код:
    class ScoreBoardGameRules extends GameRules;
    function PostBeginPlay()
    {
        if( Level.Game.GameRulesModifiers==None )
            Level.Game.GameRulesModifiers = Self;
        else Level.Game.GameRulesModifiers.AddGameRules(Self);
    }
    function AddGameRules(GameRules GR)
    {
        if ( GR!=Self )
            Super.AddGameRules(GR);
    }
    //Тут информацию по убийствам собираем
    function bool PreventDeath(Pawn Killed, Controller Killer, class<DamageType> DamageType, vector HitLocation)
    {
        if(DamageType==None || Killer==None || Killed==None)
        {
            if ( NextGameRules != None)
                return NextGameRules.PreventDeath(Killed,Killer,DamageType,HitLocation);
            return false;
        }
        if    (
                Killed.IsA('ZombieFleshpound')
                &&    Killer!=none
                &&    SBPlayerReplicationInfo(Killer.PlayerReplicationInfo)!=none
            )
        {
            SBPlayerReplicationInfo(Killer.PlayerReplicationInfo).FPKilled++;
        }
        if(NextGameRules != None)
            return NextGameRules.PreventDeath(Killed,Killer, damageType,HitLocation);
        return false;
    }
    //Тут информацию по урону собираем
    function int NetDamage(int OriginalDamage, int Damage, Pawn Injured, Pawn InstigatedBy, vector HitLocation, out vector Momentum, class<DamageType> DamageType)
    {
        if ( NextGameRules != None)
            return NextGameRules.NetDamage(OriginalDamage, Damage, injured, instigatedBy, HitLocation, Momentum, DamageType);
        return Damage;
    }
    

    Ну и сама табличка:
    Тут в общем просто выводится SBPlayerReplicationInfo(PlayerReplicationInfo).FPKilled переменная
    Переменную FPsText я сделал глобальной просто аналогично коду от TWI, но в принципе в этом нет особого смысла и можно делать её локальной наряду с другими новыми переменными. Модифицированный код я выделил следующим образом:
    //Flame
    Код
    //
    Код:
    class NewScoreBoard extends KFScoreBoardNew;
    var localized string FPsText;
    simulated event UpdateScoreBoard(Canvas Canvas)
    {
        local PlayerReplicationInfo PRI, OwnerPRI;
        local int i,j, FontReduction, NetXPos, PlayerCount, HeaderOffsetY, HeadFoot, MessageFoot, PlayerBoxSizeY, BoxSpaceY, NameXPos, BoxTextOffsetY, OwnerOffset, HealthXPos, BoxXPos,KillsXPos, TitleYPos, BoxWidth, VetXPos, TempVetXPos, VetYPos;
        local float XL,YL, MaxScaling;
        local float deathsXL, KillsXL, netXL,HealthXL, MaxNamePos, KillWidthX, HealthWidthX, TimeXL, TimeWidthX, TimeXPos, ScoreXPos, ScoreXL;
        local bool bNameFontReduction;
        local Material VeterancyBox, StarMaterial;
        local int TempLevel, TempY;
        local string PlayerTime;
        local KFPlayerReplicationInfo KFPRI;
        local float AssistsXPos,AssistsWidthX;
        local float CashX;
        local string CashString,HealthString;
        local float OutX;
        local array<PlayerReplicationInfo> TeamPRIArray;
        //Flame
        local int FPsXPos;
        local float FPsXL,FPsWidthX;
        //
        OwnerPRI = KFPlayerController(Owner).PlayerReplicationInfo;
        OwnerOffset = -1;
        for (i = 0; i < GRI.PRIArray.Length; i++)
        {
            PRI = GRI.PRIArray[i];
            if ( !PRI.bOnlySpectator )
            {
                if ( PRI == OwnerPRI )
                    OwnerOffset = i;
                PlayerCount++;
                TeamPRIArray[ TeamPRIArray.Length ] = PRI;
            }
        }
        PlayerCount = Min(PlayerCount, MAXPLAYERS);
        Canvas.Font = class'ROHud'.static.GetSmallMenuFont(Canvas);
        Canvas.StrLen("Test", XL, YL);
        BoxSpaceY = 0.25 * YL;
        PlayerBoxSizeY = 1.2 * YL;
        HeadFoot = 7 * YL;
        MessageFoot = 1.5 * HeadFoot;
        if ( PlayerCount > (Canvas.ClipY - 1.5 * HeadFoot) / (PlayerBoxSizeY + BoxSpaceY) )
        {
            BoxSpaceY = 0.125 * YL;
            PlayerBoxSizeY = 1.25 * YL;
            if ( PlayerCount > (Canvas.ClipY - 1.5 * HeadFoot) / (PlayerBoxSizeY + BoxSpaceY) )
            {
                if ( PlayerCount > (Canvas.ClipY - 1.5 * HeadFoot) / (PlayerBoxSizeY + BoxSpaceY) )
                {
                    PlayerBoxSizeY = 1.125 * YL;
                }
            }
        }
        if (Canvas.ClipX < 512)
            PlayerCount = Min(PlayerCount, 1+(Canvas.ClipY - HeadFoot) / (PlayerBoxSizeY + BoxSpaceY) );
        else
            PlayerCount = Min(PlayerCount, (Canvas.ClipY - HeadFoot) / (PlayerBoxSizeY + BoxSpaceY) );
        if (FontReduction > 2)
            MaxScaling = 3;
        else
            MaxScaling = 2.125;
        PlayerBoxSizeY = FClamp((1.25 + (Canvas.ClipY - 0.67 * MessageFoot)) / PlayerCount - BoxSpaceY, PlayerBoxSizeY, MaxScaling * YL);
        bDisplayMessages = (PlayerCount <= (Canvas.ClipY - MessageFoot) / (PlayerBoxSizeY + BoxSpaceY));
        HeaderOffsetY = 10 * YL;
        BoxWidth = 0.7 * Canvas.ClipX;
        BoxXPos = 0.5 * (Canvas.ClipX - BoxWidth);
        BoxWidth = Canvas.ClipX - 2 * BoxXPos;
        VetXPos = BoxXPos + 0.00005 * BoxWidth;
        NameXPos = BoxXPos + 0.075 * BoxWidth;
        //Flame
        KillsXPos = BoxXPos + 0.30 * BoxWidth; //0.50 * BoxWidth;
        FPsXPos = BoxXPos + 0.40 * BoxWidth;
        //
        AssistsXPos = BoxXPos + 0.60 * BoxWidth;
        HealthXpos = BoxXPos + 0.70 * BoxWidth;
        ScoreXPos = BoxXPos + 0.80 * BoxWidth;
        NetXPos = BoxXPos + 0.95 * BoxWidth;
        // Draw background boxes
        Canvas.Style = ERenderStyle.STY_Alpha;
        Canvas.DrawColor = HUDClass.default.WhiteColor;
        Canvas.DrawColor.A = 128;
        for (i = 0; i < PlayerCount; i++)
        {
            Canvas.SetPos(BoxXPos, HeaderOffsetY + (PlayerBoxSizeY + BoxSpaceY) * i);
            Canvas.DrawTileStretched(BoxMaterial, BoxWidth, PlayerBoxSizeY);
        }
        // Draw title
        Canvas.Style = ERenderStyle.STY_Normal;
        DrawTitle(Canvas, HeaderOffsetY, (PlayerCount + 1) * (PlayerBoxSizeY + BoxSpaceY), PlayerBoxSizeY);
        // Draw headers
        TitleYPos = HeaderOffsetY - 1.1 * YL;
        Canvas.StrLen(HealthText, HealthXL, YL);
        Canvas.StrLen(DeathsText, DeathsXL, YL);
        Canvas.StrLen(KillsText, KillsXL, YL);
        //Flame
        Canvas.StrLen(FPsText, FPsXL, YL);
        //
        Canvas.StrLen(PointsText, ScoreXL, YL);
        Canvas.StrLen(AssistsHeaderText, TimeXL, YL);
        Canvas.DrawColor = HUDClass.default.WhiteColor;
        Canvas.SetPos(NameXPos, TitleYPos);
        Canvas.DrawText(PlayerText,true);
        Canvas.SetPos(KillsXPos - 0.5 * KillsXL, TitleYPos);
        Canvas.DrawText(KillsText,true);
        //Flame
        Canvas.SetPos(FPsXPos - 0.5 * FPsXL, TitleYPos);
        Canvas.DrawText(FPsText,true);
        //
        Canvas.SetPos(ScoreXPos - 0.5 * ScoreXL, TitleYPos);
        Canvas.DrawText(PointsText,true);
        Canvas.SetPos(AssistsXPos - 0.5 * TimeXL, TitleYPos);
        Canvas.DrawText(AssistsHeaderText,true);
        Canvas.SetPos(HealthXPos - 0.5 * HealthXL, TitleYPos);
        Canvas.DrawText(HealthText,true);
        // Draw player names
        MaxNamePos = 0.9 * (KillsXPos - NameXPos);
        for (i = 0; i < PlayerCount; i++)
        {
            Canvas.StrLen(TeamPRIArray[i].PlayerName, XL, YL);
            if ( XL > MaxNamePos )
            {
                bNameFontReduction = true;
                break;
            }
        }
        if ( bNameFontReduction )
            Canvas.Font = GetSmallerFontFor(Canvas, FontReduction - 1);
        Canvas.Style = ERenderStyle.STY_Alpha;
        Canvas.DrawColor = HUDClass.default.WhiteColor;
        Canvas.SetPos(0.5 * Canvas.ClipX, HeaderOffsetY + 4);
        BoxTextOffsetY = HeaderOffsetY + 0.5 * (PlayerBoxSizeY - YL);
        Canvas.DrawColor = HUDClass.default.WhiteColor;
        MaxNamePos = Canvas.ClipX;
        Canvas.ClipX = KillsXPos - 4.f;
        for (i = 0; i < PlayerCount; i++)
        {
            Canvas.SetPos(NameXPos, (PlayerBoxSizeY + BoxSpaceY)*i + BoxTextOffsetY);
            if( i == OwnerOffset )
            {
                Canvas.DrawColor.G = 0;
                Canvas.DrawColor.B = 0;
            }
            else
            {
                Canvas.DrawColor.G = 255;
                Canvas.DrawColor.B = 255;
            }
            Canvas.DrawTextClipped(TeamPRIArray[i].PlayerName);
        }
        Canvas.ClipX = MaxNamePos;
        Canvas.DrawColor = HUDClass.default.WhiteColor;
        if (bNameFontReduction)
            Canvas.Font = GetSmallerFontFor(Canvas, FontReduction);
        Canvas.Style = ERenderStyle.STY_Normal;
        MaxScaling = FMax(PlayerBoxSizeY, 30.f);
        // Draw each player's information
        for (i = 0; i < PlayerCount; i++)
        {
            KFPRI = KFPlayerReplicationInfo(TeamPRIArray[i]) ;
            Canvas.DrawColor = HUDClass.default.WhiteColor;
            // Display perks.
            if ( KFPRI!=None && KFPRI.ClientVeteranSkill != none )
            {
                if(KFPRI.ClientVeteranSkillLevel == 6)
                {
                    VeterancyBox = KFPRI.ClientVeteranSkill.default.OnHUDGoldIcon;
                    StarMaterial = class'HUDKillingFloor'.default.VetStarGoldMaterial;
                    TempLevel = KFPRI.ClientVeteranSkillLevel - 5;
                }
                else
                {
                    VeterancyBox = KFPRI.ClientVeteranSkill.default.OnHUDIcon;
                    StarMaterial = class'HUDKillingFloor'.default.VetStarMaterial;
                    TempLevel = KFPRI.ClientVeteranSkillLevel;
                }
                if ( VeterancyBox != None )
                {
                    TempVetXPos = VetXPos;
                    VetYPos = (PlayerBoxSizeY + BoxSpaceY) * i + BoxTextOffsetY - PlayerBoxSizeY * 0.22;
                    Canvas.SetPos(TempVetXPos, VetYPos);
                    Canvas.DrawTile(VeterancyBox, PlayerBoxSizeY, PlayerBoxSizeY, 0, 0, VeterancyBox.MaterialUSize(), VeterancyBox.MaterialVSize());
                    if(StarMaterial != none)
                    {
                        TempVetXPos += PlayerBoxSizeY - ((PlayerBoxSizeY/5) * 0.75);
                        VetYPos += PlayerBoxSizeY - ((PlayerBoxSizeY/5) * 1.5);
                        for ( j = 0; j < TempLevel; j++ )
                        {
                            Canvas.SetPos(TempVetXPos, VetYPos);
                            Canvas.DrawTile(StarMaterial, (PlayerBoxSizeY/5) * 0.7, (PlayerBoxSizeY/5) * 0.7, 0, 0, StarMaterial.MaterialUSize(), StarMaterial.MaterialVSize());
                            VetYPos -= (PlayerBoxSizeY/5) * 0.7;
                        }
                    }
                }
            }
            // draw kills
            if( bDisplayWithKills )
            {
                Canvas.StrLen(KFPRI.Kills, KillWidthX, YL);
                Canvas.SetPos(KillsXPos - 0.5 * KillWidthX, (PlayerBoxSizeY + BoxSpaceY) * i + BoxTextOffsetY);
                Canvas.DrawText(KFPRI.Kills, true);
                //Flame
                Canvas.StrLen(SBPlayerReplicationInfo(KFPRI).FPKilled, FPsWidthX, YL);
                Canvas.SetPos(FPsXPos - 0.5 * FPsWidthX, (PlayerBoxSizeY + BoxSpaceY) * i + BoxTextOffsetY);
                Canvas.DrawText(SBPlayerReplicationInfo(KFPRI).FPKilled, true);
                //
    
                // Draw Kill Assists
                Canvas.StrLen(KFPRI.KillAssists, AssistsWidthX, YL);
                Canvas.SetPos(AssistsXPos - 0.5 * AssistsWidthX, (PlayerBoxSizeY + BoxSpaceY) * i + BoxTextOffsetY);
                Canvas.DrawText(KFPRI.KillAssists, true);
            }
            // draw cash
            CashString = "Ј"@string(int(TeamPRIArray[i].Score)) ;
            if(TeamPRIArray[i].Score >= 1000)
            {
                CashString = "Ј"@string(TeamPRIArray[i].Score/1000.f)$"K" ;
            }
            Canvas.StrLen(CashString,CashX,YL);
            Canvas.SetPos(ScoreXPos - CashX/2 , (PlayerBoxSizeY + BoxSpaceY)*i + BoxTextOffsetY);
            Canvas.DrawColor = Canvas.MakeColor(255,255,125,255);
            Canvas.DrawText(CashString);
            Canvas.DrawColor = HUDClass.default.WhiteColor;
            // Draw health status
            HealthString = KFPRI.PlayerHealth$" HP" ;
            Canvas.StrLen(HealthString,HealthWidthX,YL);
            Canvas.SetPos(HealthXpos - HealthWidthX/2, (PlayerBoxSizeY + BoxSpaceY) * i + BoxTextOffsetY);
            if ( TeamPRIArray[i].bOutOfLives )
            {
                Canvas.StrLen(OutText,OutX,YL);
                Canvas.DrawColor = HUDClass.default.RedColor;
                Canvas.SetPos(HealthXpos - OutX/2, (PlayerBoxSizeY + BoxSpaceY) * i + BoxTextOffsetY);
                Canvas.DrawText(OutText);
            }
            else
            {
                if( KFPRI.PlayerHealth>=80 )
                {
                    Canvas.DrawColor = HUDClass.default.GreenColor;
                }
                else if( KFPRI.PlayerHealth>=50 )
                {
                    Canvas.DrawColor = HUDClass.default.GoldColor;
                }
                else
                {
                    Canvas.DrawColor = HUDClass.default.RedColor;
                }
                Canvas.DrawText(HealthString);
            }
        }
        if (Level.NetMode == NM_Standalone)
            return;
        Canvas.StrLen(NetText, NetXL, YL);
        Canvas.DrawColor = HUDClass.default.WhiteColor;
        Canvas.SetPos(NetXPos - 0.5 * NetXL, TitleYPos);
        Canvas.DrawText(NetText,true);
        for (i=0; i<GRI.PRIArray.Length; i++)
            PRIArray[i] = GRI.PRIArray[i];
        DrawNetInfo(Canvas, FontReduction, HeaderOffsetY, PlayerBoxSizeY, BoxSpaceY, BoxTextOffsetY, OwnerOffset, PlayerCount, NetXPos);
        DrawMatchID(Canvas, FontReduction);
    }
    defaultproperties
    {
        FPsText="FPs"
    }
    

    Ссылка
    ScoreBoardMut.ScoreBoardMut
    [​IMG]

    Замечание:
    Не забываем, что GameRules это цепочка последовательно выполняющихся правил, поэтому если есть правило, которое модифицирует урон - убедитесь, что оно выполняется раньше этого правила ведущего статистику. Ибо иначе статистика в табличке будет неверная

    Замечание 2:
    Тема создана не в том подфоруме. Перенесу её попозже в кодинг

    Замечание 3:
    Мутатор серый + закачивается на клиент, следовательно кача на классических серверах не будет

    Замечание 4:
    Тем кто использует ServerPerks нет нужды создавать новый файл репликации - используйте SRPlayerReplicationInfo
    Табличку опять же стоит использовать стандартную из SP - её и модифицировать
    То есть всё что нужно тем, кто использует SP - дополнительные переменные в SRPlayerReplicationInfo и их репликация + GameRules, где будет отслеживание убийств и урона
     
    Последнее редактирование: 25 фев 2018
    Arckon. нравится это.
  6. Flame

    Flame -Заслуженый кодер форума-

    А. Вижу ты хотел подробностей почему твой мутатор не работает

    1.
    CheckReplacement выполняется на сервере, SetMutator нигде не определён, но эт ладно. Он тоже будет выполняться на сервере. И я честно говоря не представляю куда ты будешь привязываться в SetMutator, ибо не существует таблички на сервере.
    Но так как у тебя мутатор существует и для клиента, то в принципе можешь найти этот мутатор тем же ForEach циклом по мутаторам. То есть внутри кода таблички цикл по всем мутаторам и если это мутатор BHScoreboardMut, то сохраняем его. Ну или просто цикл по классам BHScoreboardMut.

    2. Очень сомнительный способ фиксации убийств. Таймер то у тебя раз в секунду работает.
    А здоровье<0 и как следствие уничтожение тела происходит достаточно быстро.
    Но это то ладно - можно и поменьше период сделать у таймера. Но всёж лучше использовать GameRules и не изобретать велосипед)

    3. PCS - массив составленный циклом по Level.ControllerList из KFPlayerController.
    И ты выводишь i значение этого массива в табличке:
    Код:
    Canvas.DrawText(BHSM.FPKills[i], true);
    
    Но почему игрок с контроллером (I) это игрок с PRIArray(I)? (использую круглые скобки, чтобы редактор форума не менял шрифт на курсив)) )
    Ты проверял? Они совпадают? Я вот совсем не уверен

    Заполнение PRIArray идёт в GameReplicationInfo.PostNetBeginPlay
    Код:
    ForEach DynamicActors(class'PlayerReplicationInfo',PRI)
        AddPRI(PRI);
    

    В общем основная проблема - передача ссылки на мутатор
    Добавь поиск
    Код:
    ForEach DynamicActors(class'BHScoreboardMut',bhs)
    
    в код таблички и можешь потестировать свой вариант. Есть вероятность что он худо бедно заработает. Хотя может будет показывать чужой кач

    Но лучше используй GameRules и сторонний объект для репликации
     
    Последнее редактирование: 25 фев 2018
    Arckon. нравится это.
  7. CFH

    CFH Новенький

    Нет, тут не вариант юзать марковский SP. Я прекрасно понимаю тему с согласованием PRI и контроллеров, в коде отдельная для этого ф-я прописана (может я забыл ее скопировать, не суть).
    Я искал скорбоард, но в муте через данный итератор, ссылка none по дефолту, почему? Из-за того, что нет репликации скорбоарда на серев? Да, мб говнокод будет в этом случае, но все же интересно узнать. А убийства да, они не робят, это пока что черновой вариант) И еще вопрос в таком случае: зачем спавнить класс ScoreBoardGameRules, т.е sbGameRules=Spawn(Class'ScoreBoardGameRules')?
     
  8. Flame

    Flame -Заслуженый кодер форума-

    Эт, а причём тут ServerPerks?
    Я дал решение без использования SP - проверил на чистом серваке

    Фиг знает как ты искал - ща тож поищу
    Репликации таблички на сервер нет, конечно, но при чём тут это
    У тебя мутатор выполняется на каждом из клиентов, ибо у тебя прописано
    Код:
        bAlwaysRelevant=true
        RemoteRole=ROLE_SimulatedProxy
    
    Значит на клиенте этот мутатор вполне можно найти

    В смысле зачем спавнить ScoreBoardGameRules?)
    Чтобы создать объект класса GameRules и отследить там убийства и урон
    Можно было не создавать переменную sbGameRules и тем более не делать её глобальной, это я по привычке
    Но Spawn(Class'ScoreBoardGameRules') то нужен

    Upd. А. Невнимательно прочёл. Зачем ты ScoreBoard то искал?
    Ищи в ScoreBoard мутатор, а не наоборот.
    Ну если очень хочешь искать в мутаторе, то убедись, что функция с префиском simulated
    Лучше всего в Tick'е, наверное

    Как-нибудь так:
    Код:
    var bool bInitiated;
    ...
    simulated function Tick(float dt)
    {
        local BHScoreboard bhs;
        if(Role<ROLE_AUTHORITY && !bInitiated)
        {
            foreach DynamicActors(class'BHScoreboard',bhs)
            {
                bhs.mut=self;
                bInitiated=true;
            }
        }
        Super.Tick(dt);
    }
    
    ну или можно ещё покрасивее
    Код:
    simulated function Tick(float dt)
    {
        local BHScoreboard bhs;
        if(Role<ROLE_AUTHORITY)
        {
            foreach DynamicActors(class'BHScoreboard',bhs)
            {
                bhs.mut=self;
                Disable('Tick');
                break;
            }
        }
        Super.Tick(dt);
    }
    
     
    Последнее редактирование: 25 фев 2018