Хард Патриарх для версии 1046 и выше

Тема в разделе "Игровое редактирование", создана пользователем Александра, 11 янв 2013.

  1. Александра

    Александра Новенький

    Для начала всем приветы! Хотелось бы узнать у форумчан имеется ли мутатор Hardpat для версии игры 1046 и выше? Так как после этого патча на стим версии и на пиратке он отказывается работать, а без него игра не мила. Стандартный патриарх прям деревянный. Заглянула в тему по этому мутатору там DLL(дальше не помню) тоже интересовался но ответа так и нет никакого вот и создала тему. Спасибо за внимание. Буду благодарна в виде + всем за совет(ы) как исправить хотя бы это дело.

    П.С. на всех форумах глухо, неужели только 2 человека используют этот мутатор или только у нас проблемы? На чистой игре тоже не работает.
     
  2. Ghost721

    Ghost721 Соучастник

    ну есть темка как увеличить сложность....помоему гдето был мутатор "хардер специменс" так там можно выставить любому мобу его мощь! жду в скайпе ghost7215! поиграем на новом диком сервере! нужен народ ребят)
     
  3. Александра

    Александра Новенький

    Нет нет, мне нужен именно ХардПат. "хардер специменс" это вообще не тот мутатор так как ХардПат изменяет само поведение Босса. Вот. Спасибо за приглашение я обязательно зайду только скайп установлю :) Аа сервер стим, дай адрес хоть?
     
  4. xorex

    xorex Соучастник

    Да, действительно HardPat не работает на патче 1046
     
  5. marius5704

    marius5704 Соучастник

    Поставьте мутатор ScrnBalance. С v5 (релиз был как раз с выходом 1046) в файле-конфигураторе появилась строчка
     
  6. Gwyn

    Gwyn Чудо Йогурт

    *soft bread*
     
  7. Flame

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

    Ну вообще говоря этот косяк пошёл с версии 1044
    Проблема в том, что в функции KFMod.KFGameType.AddBoss есть такой кусок кода
    Код:
    if(NextSpawnSquad[0].default.EventClasses.Length > eventNum)
    {
    	NextSpawnSquad[0] = Class<KFMonster>(DynamicLoadObject(NextSpawnSquad[0].default.EventClasses[eventNum],Class'Class'));
    }
    
    Сделано для автоматической замены босса на ивентовый вариант.
    А HardPat напрямую наследует ZombieBoss в котором с версии 1044 появились след. строчки в defaultproperties
    Код:
    	EventClasses(0)="KFChar.ZombieBoss"
    	EventClasses(1)="KFChar.ZombieBoss"
    	EventClasses(2)="KFChar.ZombieBoss_HALLOWEEN"
    	EventClasses(3)="KFChar.ZombieBoss_XMAS"
    
    И вот когда дело доходит до вызова босса - проверяется есть ли элемент массива для текущего ивента.
    И босс заменяется на нужный (а для нас совершенно ненужный) вариант.

    Поэтому, либо правим SRGameType, либо правим HardPat, либо создаём внешний мутатор правящий размер массива у HardPat (или вообще у всех монстров), либо наследуем не от KFChar.ZombieBoss

    Разумнее всего поправить HardPat:
    Добавим строчку
    class'KFHardPat.HardPat'.default.EventClasses.Length=0;
    например, в KFHardPat.KFHardPat.PreBeginPlay
    (Если добавляем босса с помощью именно этого мутатора. Но те, кто добавляют его другим способом - должны быть в состоянии дописать эту строчку там где надо в их случае. Хоть бы и в самом HardPat, в каком-нибудь PostBeginPlay или в ServerPerksMut.ServerPerksMut, если прописывают босса туда)
    Код:
    Class KFHardPat extends Mutator;
    
    function PreBeginPlay()
    {
    	AddToPackageMap();
    	class'HardPat'.default.EventClasses.Length=0;
    	KFGameType(Level.Game).EndGameBossClass = string(Class'HardPat');
    }
    function Timer()
    {
    	Destroy();
    }
    
    defaultproperties
    {
    	 GroupName="KF-BossMut"
    	 FriendlyName="Hard Patriarch mode"
    	 Description="Make the Patriarch boss harder than ever."
    }
    
    Поправленный мутатор KFHardPat:
    Ссылка

    Ну или вот мутатор, если лень править KFHardPat
    Код:
    class ClearEventArrayMut extends Mutator;
    
    function PostBeginPlay()
    {
    	class'KFHardPat.HardPat'.default.EventClasses.Length=0;
    	Super.PostBeginPlay();
    }
    
    defaultproperties
    {
    	bAddToServerPackages=True
    	GroupName="ClearEventArrayMut"
    	FriendlyName="ClearEventArrayMut"
    	Description="ClearEventArrayMut"
    }
    
    Добавлять в виде: ClearEventArrayMut.ClearEventArrayMut
    Ссылка
     
    xorex, Папка01, marius5704 и 3 другим нравится это.
  8. Александра

    Александра Новенький

    Ничего не получилось с этими вариантами. Помог способ, в котором пришлось создать геймтип.
     
  9. limikus

    limikus Соучастник

    чет вообще не работает!
     
  10. Flame

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

    у меня оба моих способа работают )
    впрочем ладно
    позже ещё разок проверю
    вы как добавляете этого патриарха в игру? просто добавлением мутатора?
    логи сервера чтоль кидайте )
     
    Папка01 нравится это.
  11. limikus

    limikus Соучастник

    да просто добавляю мутатор.как и все остальные

    не знаю как сполер тут делать.так что вот сылка на логи!

    http://www.fayloobmennik.net/2514430
     
  12. Flame

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

    Ну я ещё разок проверил - отлично мутаторы работают. Без них - классический босс. С ними HardPat

    Ладно
    Вот ещё вариант KFHardPat
    По-сути самый "правильный" вариант:
    Хочет KFGameType заменить патрика? Да и пусть меняет - а мы укажем на что менять.
    Напишем в KFHardPat.HardPat.defaultproperties
    Код:
    defaultproperties
    {
        EventClasses(0)="KFHardPat.HardPat"
        EventClasses(1)="KFHardPat.HardPat"
        EventClasses(2)="KFHardPat.HardPat"
        EventClasses(3)="KFHardPat.HardPat"
    ...
    
    Добавлять как и обычный вариант: KFHardPat.KFHardPat
    Ссылка 1 или Ссылка 2

    Кроме того я добавил вывод сообщения (в лог файл сервера) "HardPat arrives" при появлении патриарха.
    Эт сделано для простоты проверки - заменился патриарх или нет.
     
    xorex, Папка01, key и ещё 1-му нравится это.
  13. denfil777

    denfil777 Солдат

    Вот решил выложить свою версию, делал для знакомого. В этой версии не наследуется класс стандартного патриарха, в нее уже вшит патрик с версии 1043, в плане кода он независим от стандартного патрика.
    Код для батника: R13Patriarch.KFHardPat
    Ссылка
     
    MARYhuANNA, Папка01 и Flame нравится это.
  14. armenas

    armenas Новенький

    Кому не работает этот мут, проверте, не запушчен ли у вас INeedBodies. Этот HardPat мутатор не работает с мутатором INeedBodies, так как в нем есть прописано прямо на ZombieBoss. INeedBodies надо или отключить или в нем указать в место ZombieBoss прямо на HardPat.
     
  15. Interneyron

    Interneyron Соучастник

    мутатор не работает на 1046.Перепробовал все варианты
     
  16. Flame

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

    Отлично всё работает на 1046
    По крайней мере эта версия
    В принципе возможен конфликт с другими мутаторами, если в этих мутаторах, например, есть строчка, типа,
    KFGameType(Level.Game).EndGameBossClass = "ПутьКОчередномуБоссу"
    Частенько такое любят прописывать в ServerPerksMut.ServerPerksMut
    Тогда может зваться этот Очередной (или, если его нет, стандартный), а не KFPatHard
    Но если поставить мутатор KFHardPat.KFHardPat последним в очереди мутаторов, то не должно быть такого
    Ну ещё можно в KillingFloor.ini заменить в секции [KFMod.KFGameType]
    EndGameBossClass=KFChar.ZombieBoss на EndGameBossClass=KFHardPat.KFHardPat
    Но вряд ли это поможет

    Ну в общем нужны логи сервера с запуска и до момента появления патриарха
     
    ЛОХМАТЫЙ нравится это.
  17. max1988

    max1988 Новенький

    Не работает :facepalm:
     
  18. Flame

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

    А. Походу разобрались в чём беда у тех, у кого не работает
    Работает у тех, кто включил песочницу
    Вот версия, которая должна работать у всех (для текущей 1058 версии, ибо фиг знает, что там потом ещё TWI выдумают)
    Ссылка

    К предыдущей версии добавлена 1 строчка в коде мутатора

    Код:
    class'KFMod.KFMonstersCollection'.default.EndGameBossClass = string(Class'HardPat');
     
    HATAXA нравится это.
  19. Slava90

    Slava90 Соучастник

    Киньте плиз ссылку на скачку Хард Патриарха на МонстерКонфиг пожалуста! Версия игры v 1064. :eek:hmy:
     
  20. w.a.l

    w.a.l Консильери

    Вот ХардПат второй версии который работает у меня:

    HardPat_STANDARD.uc
    Class HardPat_STANDARD extends KFChar.ZombieBoss_STANDARD
    config(HardPat_STANDARD);

    struct FCombatState
    {
    var() config bool bMovCG,bRunCG,bPauseCG,bMisIgnoreRange,bAltRoute;
    var() config int NumMis[2],NumCG[2];
    var() config float MisRepTime;
    };
    var() config FCombatState PatStates[4];
    var transient float GiveUpTime;
    var byte MissilesLeft;
    var bool bValidBoss,bMovingChaingunAttack;

    replication
    {
    reliable if( ROLE==ROLE_AUTHORITY )
    bMovingChaingunAttack;
    }

    function bool MakeGrandEntry()
    {
    bValidBoss = true;
    return Super.MakeGrandEntry();
    }
    function Died(Controller Killer, class<DamageType> damageType, vector HitLocation)
    {
    if( bValidBoss )
    Super.Died(Killer,damageType,HitLocation);
    else Super(KFMonster).Died(Killer,damageType,HitLocation);
    }

    simulated function bool HitCanInterruptAction()
    {
    return (!bWaitForAnim && !bShotAnim);
    }

    function DoorAttack(Actor A)
    {
    if ( !bShotAnim && A!=None )
    {
    Controller.Target = A;
    bShotAnim = true;
    Acceleration = vect(0,0,0);
    HandleWaitForAnim('MeleeImpale');
    SetAnimAction('MeleeImpale');
    }
    }

    function RangedAttack(Actor A)
    {
    local float D;
    local bool bOnlyE;
    local bool bDesireChainGun;

    // Randomly make him want to chaingun more
    if( Controller.LineOfSightTo(A) && FRand() < 0.15 && LastChainGunTime<Level.TimeSeconds )
    {
    bDesireChainGun = true;
    }

    if ( bShotAnim )
    {
    if( !IsAnimating(ExpectingChannel) )
    bShotAnim = false;
    return;
    }
    D = VSize(A.Location-Location);
    bOnlyE = (Pawn(A)!=None && OnlyEnemyAround(Pawn(A)));
    if ( IsCloseEnuf(A) )
    {
    bShotAnim = true;
    if( Health>1500 && Pawn(A)!=None && FRand() < 0.5 )
    {
    SetAnimAction('MeleeImpale');
    }
    else
    {
    SetAnimAction('MeleeClaw');
    //PlaySound(sound'Claw2s', SLOT_None); KFTODO: Replace this
    }
    }
    else if( Level.TimeSeconds>LastSneakedTime )
    {
    if( Rand(3)==0 )
    {
    // Wait another 20-40 to try this again
    LastSneakedTime = Level.TimeSeconds+20.f+FRand()*20;
    Return;
    }
    SetAnimAction('transition');
    GoToState('SneakAround');
    }
    else if( bChargingPlayer && (bOnlyE || D<200) )
    Return;
    else if( !bDesireChainGun && !bChargingPlayer && (D<300 || (D<700 && bOnlyE)) &&
    (Level.TimeSeconds - LastChargeTime > (5.0 + 5.0 * FRand())) ) // Don't charge again for a few seconds
    {
    SetAnimAction('transition');
    GoToState('Charging');
    }
    else if( LastMissileTime<Level.TimeSeconds && (PatStates[SyringeCount].bMisIgnoreRange || D>500) )
    {
    if( !Controller.LineOfSightTo(A) || FRand() > 0.75 )
    {
    LastMissileTime = Level.TimeSeconds+FRand() * 5;
    Return;
    }

    LastMissileTime = Level.TimeSeconds + 10 + FRand() * 15;

    bShotAnim = true;
    Acceleration = vect(0,0,0);
    SetAnimAction('PreFireMissile');

    HandleWaitForAnim('PreFireMissile');

    GoToState('FireMissile');
    }
    else if ( !bWaitForAnim && !bShotAnim && LastChainGunTime<Level.TimeSeconds )
    {
    if ( !Controller.LineOfSightTo(A) || FRand()> 0.85 )
    {
    LastChainGunTime = Level.TimeSeconds+FRand()*4;
    Return;
    }

    LastChainGunTime = Level.TimeSeconds + 5 + FRand() * 10;

    bShotAnim = true;
    Acceleration = vect(0,0,0);
    SetAnimAction('PreFireMG');

    HandleWaitForAnim('PreFireMG');
    MGFireCounter = PatStates[SyringeCount].NumCG[0] + Rand(PatStates[SyringeCount].NumCG[1]);

    GoToState('FireChaingun');
    }
    }

    simulated function bool AnimNeedsWait(name TestAnim)
    {
    if( TestAnim == 'FireMG' )
    return !bMovingChaingunAttack;
    return Super.AnimNeedsWait(TestAnim);
    }
    simulated function int DoAnimAction( name AnimName )
    {
    if( AnimName=='FireMG' && bMovingChaingunAttack )
    {
    AnimBlendParams(1, 1.0, 0.0,, FireRootBone, True);
    PlayAnim('FireMG',, 0.f, 1);
    return 1;
    }
    else if( AnimName=='FireEndMG' )
    {
    //SetBoneDirection(FireRootBone,rot(0,0,0),,0,0);
    AnimBlendParams(1, 0);
    }
    return Super.DoAnimAction( AnimName );
    }
    simulated function AnimEnd(int Channel)
    {
    local name Sequence;
    local float Frame, Rate;

    if( Level.NetMode==NM_Client && bMinigunning )
    {
    GetAnimParams( Channel, Sequence, Frame, Rate );

    if( Sequence != 'PreFireMG' && Sequence != 'FireMG' )
    {
    //SetBoneDirection(FireRootBone,rot(0,0,0),,0,0);
    Super(KFMonster).AnimEnd(Channel);
    return;
    }

    if( bMovingChaingunAttack )
    DoAnimAction('FireMG');
    else
    {
    PlayAnim('FireMG');
    bWaitForAnim = true;
    bShotAnim = true;
    IdleTime = Level.TimeSeconds;
    }
    }
    else
    {
    //SetBoneDirection(FireRootBone,rot(0,0,0),,0,0);
    Super(KFMonster).AnimEnd(Channel);
    }
    }

    // Fix: Don't spawn needle before last stage.
    simulated function NotifySyringeA()
    {
    if( Level.NetMode!=NM_Client )
    {
    if( SyringeCount<3 )
    SyringeCount++;
    if( Level.NetMode!=NM_DedicatedServer )
    PostNetReceive();
    }
    if( Level.NetMode!=NM_DedicatedServer )
    DropNeedle();
    }
    simulated function NotifySyringeC()
    {
    if( Level.NetMode!=NM_DedicatedServer )
    {
    CurrentNeedle = Spawn(Class'BossHPNeedle');
    CurrentNeedle.Velocity = vect(-45,300,-90) >> Rotation;
    DropNeedle();
    }
    }

    simulated function ZombieCrispUp() // Don't become crispy.
    {
    bAshen = true;
    bCrispified = true;
    SetBurningBehavior();
    }

    function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
    {
    if( ZombieBoss(InstigatedBy)==None ) // ignore damage from other patriarch and own rockets
    Super.TakeDamage(Damage,InstigatedBy,Hitlocation,Momentum,damageType,HitIndex);
    }

    state KnockDown
    {
    Ignores RangedAttack,TakeDamage;
    }
    state FireChaingun
    {
    function BeginState()
    {
    Super.BeginState();
    bMovingChaingunAttack = PatStates[SyringeCount].bMovCG;
    bChargingPlayer = (PatStates[SyringeCount].bRunCG && Rand(2)==0);
    bCanStrafe = true;
    }
    function EndState()
    {
    bChargingPlayer = false;
    Super.EndState();
    bMovingChaingunAttack = false;
    bCanStrafe = false;
    }
    function Tick( float Delta )
    {
    Super(KFMonster).Tick(Delta);
    if( bChargingPlayer )
    GroundSpeed = OriginalGroundSpeed * 2.3;
    else GroundSpeed = OriginalGroundSpeed * 1.15;
    }
    function AnimEnd( int Channel )
    {
    if( MGFireCounter <= 0 )
    {
    bShotAnim = true;
    Acceleration = vect(0,0,0);
    SetAnimAction('FireEndMG');
    HandleWaitForAnim('FireEndMG');
    GoToState('');
    }
    else if( bMovingChaingunAttack )
    {
    if( bFireAtWill && Channel!=1 )
    return;
    if( Controller.Target!=None )
    Controller.Focus = Controller.Target;
    bShotAnim = false;
    bFireAtWill = True;
    SetAnimAction('FireMG');
    }
    else
    {
    if ( Controller.Enemy != none )
    {
    if ( Controller.LineOfSightTo(Controller.Enemy) && FastTrace(GetBoneCoords('tip').Origin,Controller.Enemy.Location))
    {
    MGLostSightTimeout = 0.0;
    Controller.Focus = Controller.Enemy;
    Controller.FocalPoint = Controller.Enemy.Location;
    }
    else
    {
    MGLostSightTimeout = Level.TimeSeconds + (0.25 + FRand() * 0.35);
    Controller.Focus = None;
    }
    Controller.Target = Controller.Enemy;
    }
    else
    {
    MGLostSightTimeout = Level.TimeSeconds + (0.25 + FRand() * 0.35);
    Controller.Focus = None;
    }

    if( !bFireAtWill )
    {
    MGFireDuration = Level.TimeSeconds + (0.75 + FRand() * 0.5);
    }
    else if ( FRand() < 0.03 && Controller.Enemy != none && PlayerController(Controller.Enemy.Controller) != none )
    {
    // Randomly send out a message about Patriarch shooting chain gun(3% chance)
    PlayerController(Controller.Enemy.Controller).Speech('AUTO', 9, "");
    }

    bFireAtWill = True;
    bShotAnim = true;
    Acceleration = vect(0,0,0);

    SetAnimAction('FireMG');
    bWaitForAnim = true;
    }
    }
    function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
    {
    local float EnemyDistSq, DamagerDistSq;

    global.TakeDamage(Damage,instigatedBy,hitlocation,vect(0,0,0),damageType);
    if( bMovingChaingunAttack || Health<=0 )
    return;

    // if someone close up is shooting us, just charge them
    if( InstigatedBy != none )
    {
    DamagerDistSq = VSizeSquared(Location - InstigatedBy.Location);

    if( (ChargeDamage > 200 && DamagerDistSq < (500 * 500)) || DamagerDistSq < (100 * 100) )
    {
    SetAnimAction('transition');
    GoToState('Charging');
    return;
    }
    }

    if( Controller.Enemy != none && InstigatedBy != none && InstigatedBy != Controller.Enemy )
    {
    EnemyDistSq = VSizeSquared(Location - Controller.Enemy.Location);
    DamagerDistSq = VSizeSquared(Location - InstigatedBy.Location);
    }

    if( InstigatedBy != none && (DamagerDistSq < EnemyDistSq || Controller.Enemy == none) )
    {
    MonsterController(Controller).ChangeEnemy(InstigatedBy,Controller.CanSee(InstigatedBy));
    Controller.Target = InstigatedBy;
    Controller.Focus = InstigatedBy;

    if( DamagerDistSq < (500 * 500) )
    {
    SetAnimAction('transition');
    GoToState('Charging');
    }
    }
    }

    Begin:
    While( True )
    {
    if( !bMovingChaingunAttack )
    Acceleration = vect(0,0,0);

    if( MGLostSightTimeout > 0 && Level.TimeSeconds > MGLostSightTimeout )
    {
    Acceleration = vect(0,0,0);
    bShotAnim = true;
    Acceleration = vect(0,0,0);
    SetAnimAction('FireEndMG');
    HandleWaitForAnim('FireEndMG');
    GoToState('');
    }

    if( MGFireCounter <= 0 )
    {
    bShotAnim = true;
    Acceleration = vect(0,0,0);
    SetAnimAction('FireEndMG');
    HandleWaitForAnim('FireEndMG');
    GoToState('');
    }

    // Give some randomness to the patriarch's firing (constantly fire after first stage passed)
    if( Level.TimeSeconds > MGFireDuration && PatStates[SyringeCount].bPauseCG )
    {
    if( AmbientSound != MiniGunSpinSound )
    {
    SoundVolume=185;
    SoundRadius=200;
    AmbientSound = MiniGunSpinSound;
    }
    Sleep(0.5 + FRand() * 0.75);
    MGFireDuration = Level.TimeSeconds + (0.75 + FRand() * 0.5);
    }
    else
    {
    if( bFireAtWill )
    FireMGShot();
    Sleep(0.05);
    }
    }
    }

    state Healing
    {
    Ignores TakeDamage;
    }

    state FireMissile
    {
    function RangedAttack(Actor A)
    {
    if( MissilesLeft>1 )
    {
    Controller.Target = A;
    Controller.Focus = A;
    }
    }
    function BeginState()
    {
    MissilesLeft = PatStates[SyringeCount].NumMis[0];
    if( PatStates[SyringeCount].NumMis[1]>0 )
    MissilesLeft += Rand(PatStates[SyringeCount].NumMis[1]+1);
    MissilesLeft = Max(MissilesLeft,1);
    Acceleration = vect(0,0,0);
    }

    function AnimEnd( int Channel )
    {
    local vector Start;
    local Rotator R;

    Start = GetBoneCoords('tip').Origin;
    if( Controller.Target==None )
    Controller.Target = Controller.Enemy;

    if ( !SavedFireProperties.bInitialized )
    {
    SavedFireProperties.AmmoClass = MyAmmo.Class;
    SavedFireProperties.ProjectileClass = Class'BossLAWProjX';
    SavedFireProperties.WarnTargetPct = 0.15;
    SavedFireProperties.MaxRange = 10000;
    SavedFireProperties.bTossed = False;
    SavedFireProperties.bLeadTarget = True;
    SavedFireProperties.bInitialized = true;
    }
    SavedFireProperties.bInstantHit = (SyringeCount<1);
    SavedFireProperties.bTrySplash = (SyringeCount>=2);

    R = AdjustAim(SavedFireProperties,Start,100);
    PlaySound(RocketFireSound,SLOT_Interact,2.0,,TransientSoundRadius,,false);
    Spawn(Class'BossLAWProjX',,,Start,R);

    bShotAnim = true;
    Acceleration = vect(0,0,0);
    SetAnimAction('FireEndMissile');
    HandleWaitForAnim('FireEndMissile');

    // Randomly send out a message about Patriarch shooting a rocket(5% chance)
    if ( FRand() < 0.05 && Controller.Enemy != none && PlayerController(Controller.Enemy.Controller) != none )
    {
    PlayerController(Controller.Enemy.Controller).Speech('AUTO', 10, "");
    }

    if( --MissilesLeft==0 )
    GoToState('');
    else GoToState(,'SecondMissile');
    }
    Begin:
    while ( true )
    {
    Acceleration = vect(0,0,0);
    Sleep(0.1);
    }
    SecondMissile:
    Acceleration = vect(0,0,0);
    Sleep(PatStates[SyringeCount].MisRepTime);
    AnimEnd(0);
    }

    State Escaping // Added god-mode.
    {
    Ignores TakeDamage,RangedAttack;

    function BeginState()
    {
    GiveUpTime = Level.TimeSeconds+20.f+FRand()*20.f;
    Super.BeginState();
    bBlockActors = false; // Run through players.
    bIgnoreEncroachers = true; // Allow run past cade if needed.
    }
    function EndState()
    {
    Super.EndState();
    bIgnoreEncroachers = false;
    bHidden = false;
    if( Health>0 )
    bBlockActors = true;
    }
    function Tick( float Delta )
    {
    if( Level.TimeSeconds>GiveUpTime )
    {
    BeginHealing();
    return;
    }
    if( !bChargingPlayer )
    {
    bChargingPlayer = true;
    if( Level.NetMode!=NM_DedicatedServer )
    PostNetReceive();
    }
    GroundSpeed = OriginalGroundSpeed * 2.5;
    Global.Tick(Delta);
    }
    simulated function UnCloakBoss()
    {
    bHidden = false;
    Super.UnCloakBoss();
    }
    Begin:
    While( true )
    {
    Sleep(0.5);
    if( !bCloaked && !bShotAnim )
    CloakBoss();
    else if( bCloaked && SyringeCount>=2 )
    bHidden = true;

    if( !Controller.IsInState('SyrRetreat') && !Controller.IsInState('WaitForAnim'))
    Controller.GoToState('SyrRetreat');
    }
    }

    State SneakAround
    {
    function BeginState()
    {
    super.BeginState();
    SneakStartTime = Level.TimeSeconds+10.f+FRand()*15.f;
    }
    function EndState()
    {
    super.EndState();
    bHidden = false;
    LastSneakedTime = Level.TimeSeconds+20.f+FRand()*30.f;
    if( Controller!=None && Controller.IsInState('PatFindWay') )
    Controller.GoToState('ZombieHunt');
    }
    function TakeDamage( int Damage, Pawn InstigatedBy, Vector Hitlocation, Vector Momentum, class<DamageType> damageType, optional int HitIndex)
    {
    global.TakeDamage(Damage,instigatedBy,hitlocation,vect(0,0,0),damageType);
    if( Health<=0 )
    return;

    // if someone close up is shooting us, just charge them
    if( InstigatedBy!=none && VSizeSquared(Location - InstigatedBy.Location)<62500 )
    GoToState('Charging');
    }
    simulated function UnCloakBoss()
    {
    bHidden = false;
    Super.UnCloakBoss();
    }

    Begin:
    CloakBoss();
    if( PatStates[SyringeCount].bAltRoute && Rand(5)<=3 )
    HardPatController(Controller).FindPathAround();
    While( true )
    {
    Sleep(0.5);

    if( !bCloaked && !bShotAnim )
    CloakBoss();
    else if( bCloaked && SyringeCount>=2 )
    bHidden = true;
    if( !Controller.IsInState('PatFindWay') )
    {
    if( Level.TimeSeconds>SneakStartTime )
    GoToState('');
    if( !Controller.IsInState('WaitForAnim') && !Controller.IsInState('ZombieHunt') )
    Controller.GoToState('ZombieHunt');
    }
    else SneakStartTime = Level.TimeSeconds+30.f;
    }
    }

    defaultproperties
    {
    PatStates(0)=(bPauseCG=True,NumMis[0]=1,NumCG[0]=35,NumCG[1]=60,MisRepTime=1.000000)
    PatStates(1)=(NumMis[0]=2,NumMis[1]=1,NumCG[0]=70,NumCG[1]=60,MisRepTime=0.700000)
    PatStates(2)=(bMovCG=True,bMisIgnoreRange=True,bAltRoute=True,NumMis[0]=3,NumMis[1]=2,NumCG[0]=105,NumCG[1]=60,MisRepTime=0.500000)
    PatStates(3)=(bMovCG=True,bRunCG=True,bMisIgnoreRange=True,bAltRoute=True,NumMis[0]=4,NumMis[1]=4,NumCG[0]=140,NumCG[1]=60,MisRepTime=0.400000)
    bCanDistanceAttackDoors=False
    ControllerClass=Class'HardPatController'
    LODBias=4.000000
    }
    HardPatController.uc
    //-----------------------------------------------------------
    //
    //-----------------------------------------------------------
    class HardPatController extends BossZombieController;

    var NavigationPoint MidGoals[2];
    var byte ReachOffset;
    var Actor OldPathsCheck[3];

    final function Debugf( string S )
    {
    Level.GetLocalPlayerController().ClientMessage(S);
    Log(S);
    }
    final function FindPathAround()
    {
    local Actor Res;
    local NavigationPoint N;
    local NavigationPoint OldPts[12];
    local byte i;
    local bool bResult;

    if( Enemy==None || VSizeSquared(Enemy.Location-Pawn.Location)<360000 )
    return; // No can do this.

    // Attempt to find an alternative path to enemy.
    /* This works by:
    - finding shortest path to enemy
    - block middle path point
    - if the path is still about same to enemy, try block the new path and repeat up to 6 times.
    */
    for( i=0; i<ArrayCount(OldPts); ++i )
    {
    Res = FindPathToward(Enemy);
    if( Res==None )
    break;
    if( i>0 && CompareOldPaths() )
    {
    bResult = true;
    break;
    }
    N = GetMidPoint();
    if( N==None )
    break;
    N.bBlocked = true;
    OldPts = N;
    if( i==0 )
    SetOldPaths();
    }

    // Unblock temp blocked paths.
    for( i=0; i<ArrayCount(OldPts); ++i )
    if( OldPts!=None )
    OldPts.bBlocked = false;
    if( !bResult )
    return;

    // Fetch results and switch state.
    GetMidGoals();
    if( ReachOffset<2 )
    GoToState('PatFindWay');
    }
    final function NavigationPoint GetMidPoint()
    {
    local byte n;

    for( n=0; n<ArrayCount(RouteCache); ++n )
    if( RouteCache[n]==None )
    break;
    if( n==0 )
    return None;
    return NavigationPoint(RouteCache[(n-1)*0.5]);
    }
    final function bool CompareOldPaths()
    {
    local byte n,i;

    for( i=0; i<6; ++i )
    {
    if( RouteCache==None )
    break;
    for( n=0; n<ArrayCount(OldPathsCheck); ++n )
    if( RouteCache==OldPathsCheck[n] )
    return false;
    }
    return true;
    }
    final function SetOldPaths()
    {
    local byte n;

    for( n=0; n<ArrayCount(OldPathsCheck); ++n )
    OldPathsCheck[n] = RouteCache[n+1];
    if( RouteCache[1]==None )
    OldPathsCheck[0] = RouteCache[0];
    }
    final function GetMidGoals()
    {
    local byte n;

    for( n=0; n<ArrayCount(RouteCache); ++n )
    if( RouteCache[n]==None )
    break;
    if( n==0 )
    {
    ReachOffset = 2;
    return;
    }
    --n;
    MidGoals[0] = NavigationPoint(RouteCache[n*0.5]);
    MidGoals[1] = NavigationPoint(RouteCache[n]);
    if( MidGoals[0]==MidGoals[1] )
    ReachOffset = 1;
    else ReachOffset = 0;
    }

    state PatFindWay
    {
    Ignores Timer,SeePlayer,HearNoise,DamageAttitudeTo,EnemyChanged,Startle,Tick;

    final function PickDestination()
    {
    if( ReachOffset>=2 )
    {
    GotoState('ZombieHunt');
    return;
    }
    if( ActorReachable(MidGoals[ReachOffset]) )
    {
    MoveTarget = MidGoals[ReachOffset];
    ++ReachOffset;
    }
    else
    {
    MoveTarget = FindPathToward(MidGoals[ReachOffset]);
    if( MoveTarget==None )
    ++ReachOffset;
    }
    }
    function BreakUpDoor( KFDoorMover Other, bool bTryDistanceAttack )
    {
    Global.BreakUpDoor(Other,bTryDistanceAttack);
    Pawn.GoToState('');
    }
    Begin:
    PickDestination();
    if( MoveTarget==None )
    Sleep(0.5f);
    else MoveToward(MoveTarget,MoveTarget,,False);
    GoTo'Begin';
    }

    defaultproperties
    {
    }


    INI:
    [HardPat_STANDARD.HardPat_STANDARD]
    PatStates[0]=(bMovCG=True,bRunCG=False,bPauseCG=True,bMisIgnoreRange=False,bAltRoute=True,NumMis[0]=4,NumMis[1]=0,NumCG[0]=100,NumCG[1]=100,MisRepTime=0.700000)
    PatStates[1]=(bMovCG=True,bRunCG=True,bPauseCG=False,bMisIgnoreRange=True,bAltRoute=True,NumMis[0]=5,NumMis[1]=1,NumCG[0]=100,NumCG[1]=100,MisRepTime=0.500000)
    PatStates[2]=(bMovCG=True,bRunCG=True,bPauseCG=False,bMisIgnoreRange=True,bAltRoute=True,NumMis[0]=6,NumMis[1]=2,NumCG[0]=130,NumCG[1]=100,MisRepTime=0.300000)
    PatStates[3]=(bMovCG=True,bRunCG=True,bPauseCG=False,bMisIgnoreRange=True,bAltRoute=True,NumMis[0]=7,NumMis[1]=4,NumCG[0]=140,NumCG[1]=100,MisRepTime=0.100000)
    ;PatStates[0]=количество его шприцов (по данным что мне раньше сообщали - выше чем 3 шприца,анимация что патриарх лечится не должно отображаться в игре (сам пока не пробовал ставить выше чем 3 шприца))
    ;bMovCG=команда отвечает будет ли он двигатся или нет при стрельбе из пулемета
    ;bRunCG=будет ли он бежать при стрельбе
    ;bPauseCG=команда отвечает за паузу во время перестрелки (как всем известно он делает короткую паузу во время перестрелки из пулемета)
    ;bAltRoute=альтернативное нападение на игроков (он может в любое время стать невидимым и обойти игрока со стороны и напасть на любого себе понравившегося)
    ;NumMis[0]=количество ракет которое он выпустит
    ;NumCG[0]=количество патронов которое выпустит из пулемета
    ;MisRepTime=время в секундах (если стоит 1.000000 - значит будет выпускать ракету за ракетой каждую секунду,в зависимости сколько вы поставили ракет) Ниже чем 1.000000 - идут доли секунды


    HardPat_STANDARD можно заменить на:
    HardPat_CIRCUS
    HardPat_HALLOWEEN
    HardPat_HALLOWEEN_OLD (моё личное)
    HardPat_XMas