SprintMut

Тема в разделе "Общего назначения", создана пользователем Essence, 12 ноя 2019.

  1. Essence

    Essence Moderator Команда форума

    Данная тема является продолжением этого сообщения.

    Пофиксен баг с бесконечным бегом с помощью прыжков.
    Спринт активируется при удерживании клавиши "Shift".
    Обновил полоску усталости (взял код от гранаты из [ScrN] Total Game Balance).
    Оптимизирован код (Спасибо Флейму).
    Переписал реализацию изменения скорости игрока.

    Теперь подробнее о механике:
    Прыжок расходует выносливость. Если он был совершен во время бега, то выносливости расходуется больше.
    Игрок не может прицеливаться во время бега.
    Скорость бега постепенно снижается (за счёт расхода выносливости).
    Если игрок стоит на месте, то выносливость восстанавливается быстрее.

    Код с комментариями:
    Код:
    class SprintMut extends Mutator;
    
    // Для предотвращения бесконечного бега с помощью прыжков во время спринта
    simulated function PostBeginPlay()
    {
        Class'KFHumanPawn'.Default.bCountJumps=True;
    }
    
    // Выдаем айтем, который будет отвечать за управление спринтом
    function ModifyPlayer(Pawn Other)
    {
        local SprintControl SC;
        Super.ModifyPlayer(Other);
        SC=Spawn(class'SprintControl',Other);
        SC.GiveTo(Other);
    }
    
    // Интеракшн для отображения полоски усталости
    simulated function Tick(float DeltaTime)
    {
        local PlayerController PC;
        PC=Level.GetLocalPlayerController();
         if(PC!=None)
        {
            PC.Player.InteractionMaster.AddInteraction("SprintMut.SprintInteraction", PC.Player);
            Disable('Tick');
        }
        if(Role==Role_Authority) Disable('Tick');
    }
    
    defaultproperties
    {
        GroupName="KF-SprintMut"
        FriendlyName="Ballistic Sprint"
        Description="This mutator gives players the ability to sprint for a short period of time. Sprint key can be set in the controls configuration menu. Cut from Ballistic Weapons pack"
        bAlwaysRelevant=True
        RemoteRole=ROLE_SimulatedProxy
        bNetNotify=True
        bAddToServerPackages=True
    }
    
    Код:
    class SprintControl extends Inventory;
    
    var float Stamina; // Уровень выносливости игрока. Игроки не могут бежать, когда он на исходе
    var float MaxStamina; // Максимальный уровень выносливости
    var float StaminaDrainRate; // Количество выносливости, которое теряется каждый тик во время бега
    var float StaminaChargeRate; // Количество выносливости, которое восстанавливается каждый тик, когда игрок остановился или перешёл на шаг
    var bool bSprinting; // Флаг, который свидетельствует о том, что игрок бежит
    var float SpeedFactor; // Модификатор скорости
    var float SprintRechargeDelay; // Через какое время после остановки бега или прыжка выносливость начнёт восстанавливаться
    
    replication
    {
        // Добавляем репликацию функции с сервера на клиент
        reliable if(Role == ROLE_Authority)
            ClientTryingToAim, ClientReceiveStamina;
        // Добавляем репликацию функции с клиента на сервер
        reliable if(Role < ROLE_Authority)
            ServerStartSprint, ServerStopSprint;
        // Добавляем репликацию переменной с сервера на клиент
        reliable if(Role == ROLE_Authority)
            SpeedFactor;
    }
    
    // Меняем скорость игрока здесь
    simulated function float GetMovementModifierFor(Pawn InPawn)
    {
        Return SpeedFactor;
    }
    
    // Отслеживаем прыжки игрока
    function OwnerEvent(name EventName)
    {
        Super.OwnerEvent(EventName);
        if(EventName == 'Jumped')
        {
            // Уменьшаем высоту прыжка в зависимости от кол-ва выносливости
            // Instigator.JumpZ=Instigator.Default.JumpZ*(FMax((Stamina/MaxStamina), 0.5));
            ConsumeStaminaForJump();
        }
    }
    
    // Прыжок тоже расходует выносливость
    function ConsumeStaminaForJump()
    {
        // Если прыжок был соверщен во время бега, выносливости расходуется больше
        if(bSprinting) Stamina = FMax(0, Stamina - StaminaDrainRate * 1.5);
        else Stamina = FMax(0, Stamina - StaminaDrainRate * 0.5);
        SprintRechargeDelay = Level.TimeSeconds + 1.5;
    }
    
    // Кнопка зажата, начинаем бежать
    function ServerStartSprint()
    {
        if(Stamina <= 0 || bSprinting)
            return;
        bSprinting=true;
    }
    
    function Tick(float DT)
    {
        // Расходуем выносливость, когда бежим
        if    (
                bSprinting
                &&    Instigator.Physics != PHYS_Falling
                &&    VSize(Instigator.Acceleration) > 100
                &&    VSize(Instigator.Velocity) > 50
            )
        {
            // Блокируем возможность прицеливания во время бега
            ClientTryingToAim();
            //
            Stamina -= StaminaDrainRate * DT;
            if(Stamina <= 0)
                ServerStopSprint();
            if(Stamina > 0)
            {
                // Старая реализация изменения скорости бега
                // KFHumanPawn(Instigator).InventorySpeedModifier=300;
                // Instigator.AccelRate=100000.0;
                //
                // Увеличиваем скорость бега в два раза
                // Постепенно она снижается, игрок выдыхается
                SpeedFactor=Default.SpeedFactor+((Stamina/MaxStamina));
                KFHumanPawn(Instigator).ModifyVelocity(DT, Instigator.Velocity);
            }
        }
        // Восстанавливаем выносливость
        else
        {
            if(Stamina < MaxStamina && Level.TimeSeconds > SprintRechargeDelay)
            {
                // Если мы стоим на месте, то выносливость восстанавливается быстрее
                if(VSize(Instigator.Velocity)>0)
                    Stamina += StaminaChargeRate * DT;
                else
                    Stamina += StaminaChargeRate * 2.0 * DT;
            }
        }
        Stamina = FClamp(Stamina, 0, MaxStamina);
        ClientReceiveStamina(Stamina);
    }
    
    // Запрещаем игроку прицеливаться во время бега
    simulated function ClientTryingToAim()
    {
        if(KFWeapon(Instigator.Weapon).bAimingRifle)
            KFWeapon(Instigator.Weapon).bForceLeaveIronsights=True;
    }
    
    // Передаем клиенту значения выносливости
    simulated function ClientReceiveStamina(int Stamina)
    {
        local int i;
        local array<Interaction> InteractionList;
        local SprintInteraction sprintI;
        if(Level.NetMode != NM_DedicatedServer)
        {
            InteractionList = Level.GetLocalPlayerController().Player.LocalInteractions;
            for(i=0; i < InteractionList.Length; i++)
            {
                if(InteractionList[i].IsA('SprintInteraction'))
                {
                    sprintI = SprintInteraction(InteractionList[i]);
                    sprintI.SetStamina(Stamina,MaxStamina);
                    break;
                }
            }
        }
    }
    
    // Кнопка отпущена, заканчиваем бег
    function ServerStopSprint()
    {
        if(!bSprinting)
            return;
        bSprinting=false;
        if(Instigator != None)
        {
            // Старая реализация изменения скорости бега
            // KFHumanPawn(Instigator).InventorySpeedModifier = ((Instigator.default.GroundSpeed * KFHumanPawn(Instigator).BaseMeleeIncrease) - KFWeapon(Instigator.Weapon).Weight * 2);
            // Instigator.AccelRate=Instigator.default.AccelRate;
            // Instigator.AirControl=Instigator.default.AirControl;
            // Instigator.AirSpeed=Instigator.default.AirSpeed;
            //
            SpeedFactor=1.0;
            KFHumanPawn(Instigator).ModifyVelocity(Level.TimeSeconds, Instigator.Velocity);
        }
        SprintRechargeDelay = Level.TimeSeconds + 1.5;
    }
    
    defaultproperties
    {
        SpeedFactor=1.0
        Stamina=100.0
        MaxStamina=100.0
        StaminaDrainRate=10.0
        StaminaChargeRate=15.0
        bReplicateInstigator=True
    }
    
    Код:
    class SprintInteraction extends Interaction;
    
    var bool bSprintButtonPressed, bSprintButtonReleased;
    var float ExhaustValue, ExhaustMax, ExhaustAlpha;
    var KFPlayerController PC;
    var string SprintButton;
    
    // Стандартная функция
    event Initialized()
    {
        ExhaustValue=0;
        ExhaustMax=0;
        ExhaustAlpha=0;
    }
    
    // Получаем PlayerController игрока и проверяем бинды
    function Initialize()
    {
        PC=KFPlayerController(ViewportOwner.Actor);
        if(PC==None) Master.RemoveInteraction(Self);
        CheckKeys();
    }
    
    // Стандартная функция
    event NotifyLevelChange()
    {
        Master.RemoveInteraction(Self);
    }
    
    // Если шифт занят какой-то командой, то отвязываем её
    // И уведомляем об этом игрока
    function CheckKeys()
    {
        if(GetCurrentBind(SprintButton))
        {
            PC.ConsoleCommand("Set Input"@SprintButton);
            PC.ClientMessage(SprintButton@"key bound to sprint");
        }
    }
    
    function bool GetCurrentBind(string BindKeyName)
    {
        local string BindKeyValue;
        if(BindKeyName!="")
        {
            BindKeyValue=PC.ConsoleCommand("KEYBINDING" @ BindKeyName);
            Return BindKeyValue!="";
        }
        Return False;
    }
    
    // Отслеживаем удерживание шифта
    function bool KeyEvent(EInputKey Key, EInputAction Action, float Delta)
    {
        local SprintControl SCI;
        local string KeyName;
        KeyName=GetFriendlyName(Key);
        if(Caps(KeyName)!=Caps(SprintButton)) Return False;
        if(KeyName~=SprintButton && Action==IST_Press)
        {
            bSprintButtonPressed=True;
            bSprintButtonReleased=False;
        }
        if(KeyName~=SprintButton && Action==IST_Release)
        {
            bSprintButtonReleased=True;
            bSprintButtonPressed=False;
        }
        if(bSprintButtonPressed && !bSprintButtonReleased)
        {
            if(PC!=None && PC.Pawn!=None)
            {
                SCI=SprintControl(PC.Pawn.FindInventoryType(Class'SprintControl'));
                if(SCI!=None) SCI.ServerStartSprint();
            }
        }
        if(!bSprintButtonPressed)
        {
            if(PC!=None && PC.Pawn!=None)
            {
                SCI=SprintControl(PC.Pawn.FindInventoryType(Class'SprintControl'));
                if(SCI!=None) SCI.ServerStopSprint();
            }
        }
        Return False;
    }
    
    // Получаем значения выносливость из айтема
    simulated function SetStamina(int Value, int Max)
    {
        ExhaustValue=Value;
        ExhaustMax=Max;
    }
    
    // Рисуем полоску усталости. Код Пуша от Cook Nade, который я чутка отредактировал
    simulated function PostRender(Canvas C)
    {
        local float MyBarPercentage;
        local float MyBarLength, MyBarHeight, PosX, PosY;
        local float ClrR, ClrG;
        if(ExhaustValue<ExhaustMax)
        { 
            MyBarPercentage=FClamp(ExhaustValue/ExhaustMax, 0, 1);
            if(MyBarPercentage>0.0) ExhaustAlpha=255.0-(255.0*MyBarPercentage);
            else ExhaustAlpha=255;
    
            MyBarLength=C.ClipX*0.2;
            MyBarHeight=12;
            PosX=(C.ClipX-MyBarLength)/2;
            PosY=C.ClipY*0.8;
    
            // Чтобы для полосочки работала альфа
            if(PC!=None) C.Style=PC.myHUD.ERenderStyle.STY_Alpha;
    
            C.SetPos(PosX, PosY);
            C.SetDrawColor(192, 192, 192, ExhaustAlpha);
            C.DrawTileStretched(Class'HUDKillingFloor'.Default.WhiteMaterial, MyBarLength, MyBarHeight);
    
            C.SetPos(PosX+1.0, PosY+1.0);
            if(MyBarPercentage>0.5) ClrG=255;
            if(MyBarPercentage<0.75) ClrR=255;
            C.SetDrawColor(ClrR, ClrG, 0, ExhaustAlpha);
            C.DrawTileStretched(Class'HUDKillingFloor'.Default.WhiteMaterial, (MyBarLength-2.0)*MyBarPercentage, MyBarHeight-2.0);
        }
    }
    
    defaultproperties
    {
        SprintButton="Shift"
        bVisible=True
    }
    

    Путь к мутатору:
    Код:
    SprintMut.SprintMut

    Скачать:
    Ссылка
     
    Последнее редактирование: 4 дек 2019
    RaideN-, Mashina, Arckon. и 5 другим нравится это.
  2. AssassinTim

    AssassinTim Новенький

    Щас по тестим
     
  3. RaideN-

    RaideN- Игровая Администрация

    Потестил... Можно без каких либо проблем делать бесконечный бег с помощью прыжков.

    Вот видео:
     
  4. Essence

    Essence Moderator Команда форума

    Играйся с значениями.
    За прыжок должно расходоваться больше выносливости чем сможет восстановиться во время прыжка.

     
  5. RaideN-

    RaideN- Игровая Администрация

    Дык я даже значения не трогал. Все по дефолту.
    Просто скачал, закинул исходники и всё
     
  6. Essence

    Essence Moderator Команда форума

    Проверь HumanPawn, функцию DoJump, может быть она у тебя переписана.
    Я в мутаторе принудительно выставляю bCountJumps=True чтобы отслеживать прыжки.
    Если эта переменная False или в DoJump нет вызова OwnerEvent в инвентаре, то прыжок не будет расходовать выносливость.
    Что у тебя и наблюдается.
     
    Arckon. и RaideN- нравится это.
  7. RaideN-

    RaideN- Игровая Администрация

    Спасибо за подсказку! В мутаторе изменил KFHumanPawn на SRHumanPawn и все заработало!