[цикл статей] Практическое использование мутатора VipItemMut

Тема в разделе "Кодинг", создана пользователем 2/5, 21 июл 2016.

  1. 2/5

    2/5 Соучастник

  2. 2/5

    2/5 Соучастник

    Броня для Vip игрока

    Для начала так поправим ServerPerks, чтобы у вип игрока
    1. при появлении было 150 брони (пока решил не делать этот пункт, подробности ниже)
    2. в магазине закупка брони была до 150 (при этом 150 всё равно будет 100%, просто 100 брони будет считаться 66%)
    3. при подъёме брони с земли она пополнялась бы до 150
    4. правильно отображалась полосочка брони в HUD

    Рассмотрим подробнее эти исправления

    0. Создадим класс утилит - Utilities.
    Там будем складировать полезные static функции, которые вызываются из разных кусков кода.
    Пока добавим одну только функцию HasItem, которая проверяет есть ли у игрока P в инвентаре что-то под именем ItemName.
    Код:
    class Utilities extends Actor
        abstract;
    
    //Проверка есть ли у игрока P в инвентаре что-то под именем ItemName
    static function bool HasItem(Pawn P, Name ItemName)
    {
        local Inventory Inv;
        for(Inv=P.Inventory;Inv!=None;Inv=Inv.Inventory)
        {
            if(Inv.Name==ItemName)
                return true;
        }
        return false;
    }
    

    1. Появление с бронёй
    Решил опустить этот пункт. Во-первых, этот бонус выбивается из общей концепции, где мы правим ёмкость брони - задание конкретного значения это дело настроек каждого перка.
    Во-вторых, у меня сходу не получилось придумать изящное решение, а громоздить некрасивый код я не хочу)

    2. Покупка брони
    Отображение в магазине:
    Правим класс SRKFBuyMenuInvList, функцию UpdateMyBuyables

    Добавляем переменную ShieldItemAmmoMax и в зависимости от того вип или нет присваиваем этой переменной значения 150 или 100.
    И в дальнейшем в стандартном коде этой функции заменяем 100 на ShieldItemAmmoMax в кусочке относящимся к броне
    Код:
    function UpdateMyBuyables()
    {
        ...
        local float ShieldItemAmmoMax;
        ...
        ...
        if    (
                PlayerOwner()!=none
                &&    class'Utilities'.static.HasItem(PlayerOwner().Pawn,'VipItem')
            )
        {
            ShieldItemAmmoMax=150.0;
        }
        else
            ShieldItemAmmoMax=100.0;
    
        MyBuyable.ItemName         = class'BuyableVest'.default.ItemName;
        MyBuyable.ItemDescription    = class'BuyableVest'.default.ItemDescription;
        MyBuyable.ItemCategorie        = "";
        MyBuyable.ItemImage        = class'BuyableVest'.default.ItemImage;
        MyBuyable.ItemAmmoCurrent    = PlayerOwner().Pawn.ShieldStrength;
        MyBuyable.ItemAmmoMax        = ShieldItemAmmoMax;
        MyBuyable.ItemCost        = int(class'BuyableVest'.default.ItemCost * KFV.static.GetCostScaling(PRI, class'Vest'));
        MyBuyable.ItemAmmoCost        = MyBuyable.ItemCost / ShieldItemAmmoMax;
        MyBuyable.ItemFillAmmoCost    = int((ShieldItemAmmoMax - MyBuyable.ItemAmmoCurrent) * MyBuyable.ItemAmmoCost);
        MyBuyable.bIsVest            = true;
        MyBuyable.bMelee            = false;
        MyBuyable.bSaleList        = false;
        MyBuyable.bSellable        = false;
        MyBuyable.ItemPerkIndex        = class'BuyableVest'.default.CorrespondingPerkIndex;
        ...
    }
    
    Фактическая реализация покупки:
    Правим класс SRHumanPawn, функцию ServerBuyKevlar

    Как и в прошлом случае создаём переменную, которая будет хранить 100 или 150. Пусть она называется cShield.
    И в стандартном коде заменяем 100 на cShield.
    Код:
    function ServerBuyKevlar()
    {
        local float Cost;
        local int UnitsAffordable;
    
        local float cShield;
    
        if(class'Utilities'.static.HasItem(self,'VipItem'))
            cShield=150.0;
        else
            cShield=100.0;
        Cost = class'Vest'.default.ItemCost * ((cShield - ShieldStrength) / cShield);
        if ( KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill != none)
        {
            Cost *= KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.GetCostScaling(KFPlayerReplicationInfo(PlayerReplicationInfo), class'Vest');
        }
        if ( !CanBuyNow() || ShieldStrength==cShield )
        {
            SetTraderUpdate();
            Return;
        }
        if ( PlayerReplicationInfo.Score >= Cost )
        {
            PlayerReplicationInfo.Score -= Cost;
            ShieldStrength = cShield;
        }
        else if ( ShieldStrength > 0 )
        {
            Cost = class'Vest'.default.ItemCost;
            if ( KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill != none)
            {
                Cost *= KFPlayerReplicationInfo(PlayerReplicationInfo).ClientVeteranSkill.static.GetCostScaling(KFPlayerReplicationInfo(PlayerReplicationInfo), class'Vest');
            }
            Cost /= cShield;
            UnitsAffordable = int(PlayerReplicationInfo.Score / Cost);
            PlayerReplicationInfo.Score -= int(Cost * UnitsAffordable);
            ShieldStrength += UnitsAffordable;
        }
        SetTraderUpdate();
    }
    

    3. Подбор брони с земли
    Прежде всего хотелось бы заменить, что в броне (KFMod.Vest) уже прописано сколько брони добавлять игроку - 100
    И если мы хотим это изменить, то надо добавить в какой-то мутатор такую строку в ту же функцию PostBeginPlay, например
    Код:
    class'KFMod.Vest'.default.ShieldAmount=150;
    
    Если хотите можете добавить эту строчку в VipItemMut, хотя как по мне так это неправильно. Должен быть порядок на сервере и должен быть какой-то отдельный мутатор для того, чтобы заменять стандартные значения у классов.

    Теперь правим класс SRHumanPawn, функцию AddShieldStrength
    Идея всё та же. Создаём переменную и заполняем её значением 100 или 150 в зависимости от того есть ли у игрока VipItem.
    После этого меняем стандартную функцию, заменяя 100 на cShield.
    Код:
    function bool AddShieldStrength(int ShieldAmount)
    {
        local float cShield;
    
        if(class'Utilities'.static.HasItem(self,'VipItem'))
            cShield=150.0;
        else
            cShield=100.0;
        if(ShieldStrength >= cShield)
            return false;
    
        ShieldStrength+=ShieldAmount;
        if(ShieldStrength > cShield)
            ShieldStrength = cShield;
        return true ;
    }
    

    4. Отображение брони в HUD. Точнее заполненность полоски брони игрока с точки зрения других игроков.
    Правим класс SRHUDKillingFloor, функцию DrawPlayerInfo
    Идея всё та же. Переменная cShield со значениями 100 или 150 и замена стандартного кода
    Код:
    function DrawPlayerInfo(Canvas C, Pawn P, float ScreenLocX, float ScreenLocY)
    {
        ...
        local float cShield;
        ...
        if(P!=none && class'Utilities'.static.HasItem(P,'VipItem'))
            cShield=150.0;
        else
            cShield=100.0;
        if ( P.ShieldStrength > 0 )
            DrawKFBar(C, ScreenLocX - OffsetX, (ScreenLocY - YL) - 1.5 * BarHeight, FClamp(P.ShieldStrength / cShield, 0, 1), BeaconAlpha, true);
        ...
    }
    

    5. Сварка брони.
    Тут уже надо глядеть код сварочного аппарата. Если там тупо добавляется бронь - сами глядите, есть ли там ограничение до 100 и где его заменить на 150. Если же используется функция AddShieldStrength, то достаточно реализовать пункт 3

    Изменённые файлы в SP 7.50
    Ссылка 1 или Ссылка 2

    Поправленный и скомпилированный ServerPerks.u для ленивых (не забудьте мутатор VipItemMut добавить на сервер)) )
    Ссылка 1 или Ссылка 2

    P.S.
    p.s. Как вы заметили раз за разом мы пишем число 150 в коде. Было бы неплохо не писать конкретное число. Можно это количество брони хранить в VipItem в какой-нибудь переменной, а в мутаторе расширить структуру и прописывать там количество брони для каждого игрока.
    Есть много способов расширить возможности VipItem с формального информирования до каких-то практических плюшек.
    Это вы сами пытайтесь)
    Возможно будут статьи на эту тему.

    p.p.s Ещё неплохой способ хранить информацию о том является ли игрок випом или нет - переменная в LinkedReplicationInfo.
    Экземпляр LinkedReplicationInfo цепляется к обычному PlayerReplicationInfo, но это так же тема отдельной статьи.

    p.p.p.s Ну и совсем адский способ - цепляемся к редко используемым переменным Pawn, Contoller или PlayerReplicationInfo и их детей.
     
    Последнее редактирование модератором: 21 июл 2016
    Essence, HATAXA и STaJIKeR нравится это.
  3. 2/5

    2/5 Соучастник

    Не так давно пришло в голову - нет никакой необходимости увеличивать количество брони
    Можно просто улучшить её качество)
    Броня в 150 единиц = обычной броне, которая в 1.5 раза сильнее стандартных настроек
    Это резко уменьшает количество кода и мороки с бронёй

    Нам достаточно в каждый SRVet* класс (или в класс SRVeterancyTypes) добавить изменённую функцию GetBodyArmorDamageModifier
    Что-нибудь такое:
    Код:
    static function float GetBodyArmorDamageModifier(KFPlayerReplicationInfo KFPRI)
    {
        if(class'Utilities'.static.HasItem(KFPRI.Owner.Pawn,'VipItem'))
            return 0.66;
        return 1.00;
    }
    
    И мы получим по сути ровно тот же эффект)
     
    idpro2, STaJIKeR и Essence нравится это.
  4. Essence

    Essence Солдат

    Нужно писать так:
    Код:
        if(class'Utilities'.static.HasItem(PlayerController(KFPRI.Owner).Pawn,'VipItem'))
    Иначе выдаст ошибку и ничего не скомпилируется.