rott/kf_sources/ScrnBalanceSrv/Classes/ScrnAchHandler.uc
2026-07-14 20:27:09 +07:00

1084 lines
50 KiB
Ucode

class ScrnAchHandler extends ScrnAchHandlerBase;
var bool bOnePlayerPerPerk, bAllSamePerk;
var transient class<ScrnVeterancyTypes> SamePerk;
var transient bool bBossXbowOnly, bBossM99Only, bBossMeleeOnly, bBoss9mmOnly, bBossPrey;
var transient float BossSpawnTime, BossLastDamageTime;
var float InstantKillTime; // instant kill counts, if time between shot is less than this
var bool bPerfectWave, bPerfectGame;
var byte PlayersKilledByJason;
var float LastTeslaChainTime;
var int TeslaChainedPlayers;
var ScrnPlayerInfo TeslaChainedP1, TeslaChainedP2;
var int iDoT_Damage;
var bool bNoHeadshots, bAllCanDoHeadshots;
function PlayerDamaged(int Damage, ScrnPlayerInfo VictimSPI, KFMonster InstigatedBy, class<DamageType> DamType)
{
bPerfectWave = false;
if ( InstigatedBy != none && InstigatedBy.IsA('TeslaHusk') && GetItemName(string(DamType)) == "DamTypeTesla" ) {
if ( LastTeslaChainTime == Level.TimeSeconds) {
TeslaChainedPlayers++;
switch ( TeslaChainedPlayers ) {
case 2:
TeslaChainedP2 = VictimSPI; // remember player, but need at least 1 more to get ach
break;
case 3:
// give ach to first 3 chained players
TeslaChainedP1.ProgressAchievement('TeslaChain', 1);
TeslaChainedP2.ProgressAchievement('TeslaChain', 1);
VictimSPI.ProgressAchievement('TeslaChain', 1);
break;
default:
VictimSPI.ProgressAchievement('TeslaChain', 1); // previously chained players already have this ach
}
}
else {
LastTeslaChainTime = Level.TimeSeconds;
TeslaChainedP1 = VictimSPI;
TeslaChainedP2 = none;
TeslaChainedPlayers = 1;
}
}
}
function PlayerDied(ScrnPlayerInfo DeadPlayerInfo, Controller Killer, class<DamageType> DamType)
{
bPerfectGame = false;
if ( DeadPlayerInfo.Deaths >= GameRules.Mut.KF.FinalWave * 0.8 )
Ach2All('Kenny', 1);
if ( Killer != none && Killer.Pawn != none && Killer.Pawn.IsA('ZombieJason') )
PlayersKilledByJason++;
if ( DeadPlayerInfo.PlayerOwner.PlayerReplicationInfo.Score >= 1000
&& DeadPlayerInfo.PlayerOwner.PlayerReplicationInfo.Score > AverageScore() * 3 ) {
Ach2Alive('LuxuryFuneral', 1, DeadPlayerInfo);
DeadPlayerInfo.PlayerOwner.ReceiveLocalizedMessage(class'ScrnBalanceSrv.ScrnFakedAchMsg', 2);
}
}
function int AverageScore()
{
local ScrnPlayerInfo SPI;
local int TotalScore, PlayerCount;
for ( SPI=GameRules.PlayerInfo; SPI!=none; SPI=SPI.NextPlayerInfo ) {
if ( SPI.PlayerOwner != none && SPI.PlayerOwner.PlayerReplicationInfo != none ) {
PlayerCount++;
TotalScore += SPI.PlayerOwner.PlayerReplicationInfo.Score;
}
}
if ( PlayerCount == 0 )
return 0; // tbs, shouldn't happen
return TotalScore / PlayerCount;
}
function bool HasGoodWeapon(Pawn Pawn)
{
local Inventory Inv;
local KFWeapon Weap;
for ( Inv = Pawn.Inventory; Inv != none; Inv = Inv.Inventory ) {
Weap = KFWeapon(Inv);
if ( Weap != none ) {
if ( Weap.bIsTier3Weapon || Crossbow(Weap) != none || Weap.SellValue >= 450 )
return true;
}
}
return false;
}
function WaveStarted(byte WaveNum)
{
local int i;
local ScrnPlayerInfo SPI;
local class<ScrnVeterancyTypes> Perk;
local array< class<ScrnVeterancyTypes> > UsedPerks;
local int TotalPlayers;
PlayersKilledByJason = 0;
for ( SPI=GameRules.PlayerInfo; SPI!=none; SPI=SPI.NextPlayerInfo ) {
TotalPlayers++;
if ( SPI.PlayerOwner.PlayerReplicationInfo.Score >= 10000 )
SPI.ProgressAchievement('Money10k', 1);
if ( bOnePlayerPerPerk || bAllSamePerk ) {
if ( KFPlayerReplicationInfo(SPI.PlayerOwner.PlayerReplicationInfo) != none )
Perk = class<ScrnVeterancyTypes>( KFPlayerReplicationInfo(SPI.PlayerOwner.PlayerReplicationInfo).ClientVeteranSkill );
if ( Perk == none ) {
bOnePlayerPerPerk = false; // no perk = no balance
bAllSamePerk = false;
}
else {
if ( bAllSamePerk ) {
if ( SamePerk == none )
SamePerk = Perk;
else if ( SamePerk.default.SamePerkAch != Perk.default.SamePerkAch )
bAllSamePerk = false;
}
for ( i = 0; i < UsedPerks.length; ++i ) {
if ( UsedPerks[i] == Perk ) {
bOnePlayerPerPerk = false;
break;
}
}
if ( bOnePlayerPerPerk )
UsedPerks[i] = Perk;
}
}
}
if ( WaveNum == 0 ) {
// game started
bPerfectGame = true;
}
else {
bPerfectGame = bPerfectGame && TotalPlayers >= 2; // each wave must have 2+ players for perfect game
bPerfectWave = WaveNum > 0; // can't score perfect wave in wave 1
}
}
function WaveEnded(byte WaveNum)
{
local ScrnPlayerInfo SPI, OnlyHealerSPI, LastAliveSPI, TopKillsSPI;
local bool bCheckOnlyHealed;
local int TotalPlayers, AlivePlayers;
local int i;
local bool bAsh;
local int ChainsawKills, BoomstickKills, M4Kills, MinKills, MaxKills, SecondPlaceKills;
bCheckOnlyHealed = true;
MinKills = 0x7FFFFFFF; // max int
for ( SPI=GameRules.PlayerInfo; SPI!=none; SPI=SPI.NextPlayerInfo ) {
if ( SPI.PlayerOwner == none )
continue; // player disconnected
TotalPlayers++;
if ( !SPI.bDied ) {
LastAliveSPI = SPI;
AlivePlayers++;
if ( ScrnPlayerController(SPI.PlayerOwner) != none ) {
if ( ScrnPlayerController(SPI.PlayerOwner).bCowboyForWave )
SPI.ProgressAchievement('TrueCowboy', 1);
}
}
if ( bCheckOnlyHealed && SPI.HealedPointsInWave > 0 ) {
if ( OnlyHealerSPI != none || SPI.HealedPointsInWave < 200 ) {
OnlyHealerSPI = none;
bCheckOnlyHealed = false;
}
else
OnlyHealerSPI = SPI;
}
if ( SPI.DecapsPerWave >= 30 && SPI.GetAccuracyWave() >= 0.75 )
SPI.ProgressAchievement('Accuracy', 1);
if ( WaveNum == 9 && SPI.DecapsPerWave >= 30 && SPI.GetAccuracyWave() >= 0.10 )
SPI.ProgressAchievement('KFG2', 1);
if ( SPI.KillsPerWave < MinKills )
MinKills = SPI.KillsPerWave;
if ( SPI.KillsPerWave > MaxKills ) {
TopKillsSPI = SPI;
SecondPlaceKills = MaxKills;
MaxKills = SPI.KillsPerWave;
}
else if ( SPI.KillsPerWave > SecondPlaceKills )
SecondPlaceKills = SPI.KillsPerWave;
bAsh = true;
ChainsawKills = 0;
BoomstickKills = 0;
M4Kills = 0;
for ( i=0; i<SPI.WeapInfos.length; ++i ) {
if ( bAsh ) {
if ( ClassIsChildOf(SPI.WeapInfos[i].DamType, Class'DamTypeDBShotgun') )
BoomstickKills += SPI.WeapInfos[i].KillsPerWave;
else if ( ClassIsChildOf(SPI.WeapInfos[i].DamType, Class'DamTypeChainsaw') )
ChainsawKills += SPI.WeapInfos[i].KillsPerWave;
else if ( SPI.WeapInfos[i].KillsPerWave > 0 )
bAsh = false;
}
if ( ClassIsChildOf(SPI.WeapInfos[i].DamType, Class'ScrnDamTypeM4AssaultRifle')
|| ClassIsChildOf(SPI.WeapInfos[i].DamType, Class'DamTypeM203Grenade')
|| ClassIsChildOf(SPI.WeapInfos[i].DamType, Class'ScrnDamTypeM4203M') )
{
M4Kills += SPI.WeapInfos[i].KillsPerWave;
}
}
if ( bAsh && ChainsawKills >= 10 && BoomstickKills >= 10 && ChainsawKills + BoomstickKills >= 40 )
SPI.ProgressAchievement('Ash', 1);
else if ( M4Kills >= 40 )
SPI.ProgressAchievement('M4203Kill50Zeds', 1);
}
if ( OnlyHealerSPI != none && TotalPlayers >= 4 )
OnlyHealerSPI.ProgressAchievement('OnlyHealer', 1);
if ( AlivePlayers == 1 && TotalPlayers >= 4) {
LastAliveSPI.ProgressAchievement('ThinIcePirouette', 1);
KFSteamStatsAndAchievements(SPI.PlayerOwner.SteamStatsAndAchievements).AddOnlySurvivorOfWave();
}
if ( TotalPlayers >= 3 && MinKills > max(1, MaxKills * 0.9) )
Ach2All('NoI', 1);
if ( TotalPlayers >= 5 && MaxKills > SecondPlaceKills * 2.5 )
TopKillsSPI.ProgressAchievement('KillWhore', 1);
if ( bPerfectWave )
Ach2Alive('PerfectWave', 1);
if ( PlayersKilledByJason >= 3 )
Ach2Alive('Friday13', 1);
}
function GameWon(string MapName)
{
local ScrnPlayerInfo SPI;
local ScrnPlayerController ScrnPC;
local TeamInfo WinnerTeam;
local int PlayerCount;
WinnerTeam = TeamInfo(Level.GRI.Winner);
PlayerCount = GameRules.PlayerCountInWave();
if ( bOnePlayerPerPerk && PlayerCount >= 6 )
Ach2All('PerfectBalance', 1);
if ( bNoHeadshots && PlayerCount >= 2 )
Ach2All('TW_NoHeadshots', 1, none, WinnerTeam);
if ( bAllCanDoHeadshots && PlayerCount >= 2 )
Ach2All('TW_SkullCrackers', 1, none, WinnerTeam);
if ( !GameRules.Mut.bTSCGame ) {
// boss-related achievements
if (GameRules.bSuperPat)
Ach2All('KillSuperPat', 1);
if ( bBossPrey && Level.TimeSeconds - BossSpawnTime <= 120 )
Ach2All('PatPrey', 1);
if (bBossXbowOnly)
Ach2All('MerryMen', 1);
else if (bBossM99Only)
Ach2All('MerryMen50cal', 1);
else if (bBossMeleeOnly)
Ach2All('PatMelee', 1);
else if (bBoss9mmOnly)
Ach2All('Pat9mm', 1);
if ( bAllSamePerk && SamePerk != none && PlayerCount >= 3 )
Ach2All( SamePerk.default.SamePerkAch, 1, none, WinnerTeam );
}
for ( SPI=GameRules.PlayerInfo; SPI!=none; SPI=SPI.NextPlayerInfo ) {
ScrnPC = ScrnPlayerController(SPI.PlayerOwner);
if ( ScrnPC != none ) {
if ( SPI.Deaths == 0 && SPI.StartWave <= 1 && !ScrnPC.bHadArmor )
SPI.ProgressAchievement('BallsOfSteel', 1);
}
if ( SPI.StartWave == 0 && !GameRules.Mut.bTSCGame ) {
if ( bPerfectGame )
SPI.ProgressAchievement('PerfectGame', 1);
if ( GameRules.Mut.KF.ShopList.Length >= 3 && Level.Game.GameReplicationInfo.ElapsedTime <= 2700 ) {
SPI.ProgressAchievement('SpeedrunBronze', 1);
if ( Level.Game.GameReplicationInfo.ElapsedTime <= 2400) {
SPI.ProgressAchievement('SpeedrunSilver', 1);
if ( Level.Game.GameReplicationInfo.ElapsedTime <= 1980) {
SPI.ProgressAchievement('SpeedrunGold', 1);
}
}
}
}
}
}
function BossSpawned(KFMonster EndGameBoss)
{
if ( ZombieBoss(EndGameBoss) != none ) {
bBossXbowOnly = true;
bBossM99Only = true;
bBossMeleeOnly = true;
bBossPrey = true;
}
bBoss9mmOnly = true; // avaliable for other bosses as well, e.g. Doom3 bosses
BossSpawnTime = Level.TimeSeconds;
}
function int RowHeadhots(ScrnPlayerInfo SPI, int Count)
{
if ( Count >= 5 && SPI.LastDamage > 40 )
SPI.ProgressAchievement('Impressive', 1); // 5 and subsequent headshots in a row (5,6,7...)
return 5;
}
function int WRowHeadhots(ScrnPlayerInfo SPI, KFWeapon Weapon, class<KFWeaponDamageType> DamType, int Count)
{
if ( ClassIsChildOf(DamType, class'DamTypeM14EBR') ) {
if ( Count < 25)
return 25; // need 25 headshots for the achievement
// if achiemvent is earned - no need to track stat anymore, so return IGNORE_STAT
SPI.ProgressAchievement('DotOfDoom', 1);
}
else if ( ClassIsChildOf(DamType, class'DamTypeSPSniper') ) {
// Achievement has progress > 1, so need always need to return required value.
// Function will be called on 15, 16, 17 etc. headshots in a row and there is no way to
// reset it. But achievement can be earned only every 15 headshots (15, 30, 45 etc).
if ( Count >= 10 && Count % 10 == 0) {
if ( !SPI.ProgressAchievement('SteampunkSniper', 1) )
return IGNORE_STAT; // if achievement already earned, then ProgressAchievement() returns false, meaning no need to track it anymore
}
return 10; // need 10 headshots for the achievement
}
return IGNORE_STAT;
}
function int WKillsPerShot(ScrnPlayerInfo SPI, KFWeapon Weapon, class<KFWeaponDamageType> DamType, int Count, float DeltaTime)
{
if ( DamType.default.bIsExplosive ) {
if ( !ClassIsChildOf(DamType, class'KFMod.DamTypePipeBomb') ) {
if ( Count >= 10 && Count % 10 == 0) {
if ( !SPI.ProgressAchievement('RocketBlow', 1) )
return IGNORE_STAT; // if achievement already earned, then ProgressAchievement() returns false, meaning no need to track it anymore
}
return 10;
}
}
else if ( ClassIsChildOf(DamType, class'KFMod.DamTypeSPThompson')
|| ClassIsChildOf(DamType, class'KFMod.DamTypeThompsonDrum') )
{
if ( Count >= 5 ) { // count 5,6,7 etc.
if ( !SPI.ProgressAchievement('OldGangster', 1) )
return IGNORE_STAT; // if achievement already earned, then ProgressAchievement() returns false, meaning no need to track it anymore
}
return 5; // need 5 kills for the achievement
}
else if ( ClassIsChildOf(DamType, class'ScrnBalanceSrv.ScrnDamTypeHuskGun_Alt') ) {
if ( Count < 20)
return 20;
SPI.ProgressAchievement('NapalmStrike', 1);
}
else if ( ClassIsChildOf(DamType, class'ScrnBalanceSrv.ScrnDamTypeDualies') ) {
if ( Count < 8 )
return 8;
SPI.ProgressAchievement('MadCowboy', 1);
}
return IGNORE_STAT;
}
function int WDecapsPerShot(ScrnPlayerInfo SPI, KFWeapon Weapon, class<KFWeaponDamageType> DamType, int Count, float DeltaTime)
{
if ( ClassIsChildOf(DamType, class'ScrnBalanceSrv.ScrnDamTypeDualies') ) {
if ( Count < 8 )
return 8;
SPI.ProgressAchievement('MadCowboy', 1);
}
return IGNORE_STAT;
}
function int WKillsPerMagazine(ScrnPlayerInfo SPI, KFWeapon Weapon, class<KFWeaponDamageType> DamType, int Count)
{
if ( ClassIsChildOf(DamType, class'ScrnBalanceSrv.ScrnDamTypeM4203M') ) {
if ( Count < 15)
return 15; // need 15 kills for the achievement
// if achievement is earned - no need to track stat anymore, so return IGNORE_STAT
SPI.ProgressAchievement('MedicOfDoom', 1);
}
else if ( ClassIsChildOf(DamType, class'KFMod.DamTypeDual44Magnum') ) {
// DamTypeDual44Magnum is a subclass of DamTypeMagnum44Pistol, so it must be checked first
if ( Count < 18)
return 18;
SPI.ProgressAchievement('Magnum12Kills', 1);
}
else if ( ClassIsChildOf(DamType, class'KFMod.DamTypeMagnum44Pistol') ) {
if ( Count < 12)
return 12;
SPI.ProgressAchievement('Magnum12Kills', 1);
}
else if ( ClassIsChildOf(DamType, class'KFMod.DamTypeDualMK23Pistol') ) {
// DamTypeDualMK23Pistol is a subclass of DamTypeMK23Pistol, so it must be checked first
if ( Count < 24)
return 24;
SPI.ProgressAchievement('MK23_12Kills', 1);
}
else if ( ClassIsChildOf(DamType, class'KFMod.DamTypeMK23Pistol') ) {
if ( Count < 12)
return 12;
SPI.ProgressAchievement('MK23_12Kills', 1);
}
return IGNORE_STAT;
}
function int WDecapsPerMagazine(ScrnPlayerInfo SPI, KFWeapon Weapon, class<KFWeaponDamageType> DamType, int Count)
{
if ( ClassIsChildOf(DamType, class'KFMod.DamTypeDual44Magnum') ) {
// DamTypeDual44Magnum is a subclass of DamTypeMagnum44Pistol, so it must be checked first
if ( Count < 18)
return 18;
SPI.ProgressAchievement('Magnum12Kills', 1);
}
else if ( ClassIsChildOf(DamType, class'KFMod.DamTypeMagnum44Pistol') ) {
if ( Count < 12)
return 12;
SPI.ProgressAchievement('Magnum12Kills', 1);
}
else if ( ClassIsChildOf(DamType, class'KFMod.DamTypeDualMK23Pistol') ) {
// DamTypeDualMK23Pistol is a subclass of DamTypeMK23Pistol, so it must be checked first
if ( Count < 24)
return 24;
SPI.ProgressAchievement('MK23_12Kills', 1);
}
else if ( ClassIsChildOf(DamType, class'KFMod.DamTypeMK23Pistol') ) {
if ( Count < 12)
return 12;
SPI.ProgressAchievement('MK23_12Kills', 1);
}
return IGNORE_STAT;
}
function MonsterDamaged(int Damage, KFMonster Victim, ScrnPlayerInfo InstigatorInfo,
class<KFWeaponDamageType> DamType, bool bIsHeadshot, bool bWasDecapitated)
{
local int index, TotalDamage, df;
local KFMonsterController MC;
if ( Victim.Health <= 0 || Victim.IsInState('ZombieDying') )
return; // don't track damages done to dead bodies
MC = KFMonsterController(Victim.Controller);
if ( MC == none )
return; // wtf?
bNoHeadshots = bNoHeadshots && !bIsHeadshot;
bAllCanDoHeadshots = bAllCanDoHeadshots && DamType.default.bCheckForHeadShots;
index = GameRules.GetMonsterIndex(Victim);
if ( Damage >= iDoT_Damage && ClassIsChildOf(DamType, class'DamTypeFlareRevolver') ) {
InstigatorInfo.ProgressAchievement('iDoT', 1);
}
else if ( DamType.default.bIsMeleeDamage ) {
if ( Victim.bBackstabbed )
InstigatorInfo.ProgressAchievement('MeleeHitBehind', 1);
}
else if ( bIsHeadshot && ClassIsChildOf(DamType, class'KFMod.DamTypeDeagle') )
InstigatorInfo.SetCustomFloat(self, 'LastDeagleHSTime', Level.TimeSeconds);
if ( ZombieCrawler(Victim) != none ) {
if ( !Victim.bDamagedAPlayer && DamType.default.bIsMeleeDamage && !ClassIsChildOf(DamType, class'DamTypeCrossbuzzsaw') ) {
if ( Damage >= Victim.Health )
InstigatorInfo.ProgressAchievement('MeleeKillCrawlers', 1);
if ( Victim.Physics == PHYS_Falling ) {
if ( Damage >= Victim.Health && ClassIsChildOf(DamType, class'KFMod.DamTypeMachete') )
InstigatorInfo.ProgressAchievement('MacheteKillMidairCrawler', 1);
InstigatorInfo.ProgressAchievement('MeleeKillMidairCrawlers', 1);
}
}
}
else if ( Victim.IsA('ZombieShiver') ) {
// TeamWork: TeamWork: Grilled Shiver Brains
// KillAss1 must be set Shiver on fire
// KillAss2 must decapitate Shiver with commando weapon
if ( !Victim.bDecapitated && DamType.default.bDealBurningDamage ) {
if ( GameRules.MonsterInfos[index].DamType1 == none ) {
GameRules.MonsterInfos[index].KillAss1 = InstigatorInfo;
GameRules.MonsterInfos[index].DamType1 = DamType;
GameRules.MonsterInfos[index].DamTime1 = Level.TimeSeconds;
}
}
else if ( Victim.bDecapitated && !bWasDecapitated ) {
if ( GameRules.MonsterInfos[index].KillAss1 != none && IsAssaultRifleDamage(InstigatorInfo, DamType) ) {
GameRules.MonsterInfos[index].KillAss2 = InstigatorInfo;
GameRules.MonsterInfos[index].DamType2 = DamType;
GameRules.MonsterInfos[index].DamTime2 = Level.TimeSeconds;
GameRules.MonsterInfos[index].DamageFlags2 = GameRules.DF_DECAP;
}
else
GameRules.MonsterInfos[index].TW_Ach_Failed = true;
}
}
else if ( ZombieStalker(Victim) != none ) {
if ( Victim.IsA('ZombieGhost') ) {
if ( bIsHeadshot && Victim.bDecapitated && !bWasDecapitated ) {
if ( VSizeSquared(Victim.Location - InstigatorInfo.PlayerOwner.Pawn.Location) > 1000000 )
InstigatorInfo.ProgressAchievement('GhostSmell', 1);
}
}
}
else if ( ZombieBloat(Victim) != none ) {
if ( Victim.bDecapitated && !bWasDecapitated
&& DamType.default.bIsMeleeDamage && !ClassIsChildOf(DamType, class'DamTypeCrossbuzzsaw')
&& !Victim.bDamagedAPlayer && KFPawn(InstigatorInfo.PlayerOwner.Pawn).BileCount <= 0 )
{
InstigatorInfo.ProgressAchievement('MeleeDecapBloats', 1);
}
}
else if ( ZombieSiren(Victim) != none ) {
// TeamWork: No Big Guns on Skinny Bitches
// Any damage made with pistols or assault rifles counts as assistance.
// Other damages break the achievement (if deal significant damage).
if ( !GameRules.MonsterInfos[index].TW_Ach_Failed ) {
if ( IsPistolDamage(InstigatorInfo, DamType) || IsAssaultRifleDamage(InstigatorInfo, DamType) ) {
if ( GameRules.MonsterInfos[index].DamType1 == none ) {
GameRules.MonsterInfos[index].KillAss1 = InstigatorInfo;
GameRules.MonsterInfos[index].DamType1 = DamType;
GameRules.MonsterInfos[index].DamTime1 = Level.TimeSeconds;
}
else if ( GameRules.MonsterInfos[index].KillAss1 != InstigatorInfo && GameRules.MonsterInfos[index].DamType2 == none ) {
GameRules.MonsterInfos[index].KillAss2 = InstigatorInfo;
GameRules.MonsterInfos[index].DamType2 = DamType;
GameRules.MonsterInfos[index].DamTime2 = Level.TimeSeconds;
}
}
else if ( Damage > 50 || GetTotalDamageMadeC(MC, InstigatorInfo.PlayerOwner) > 100 )
GameRules.MonsterInfos[index].TW_Ach_Failed = true;
}
}
else if ( ZombieHusk(Victim) != none ) {
if ( Victim.bDecapitated && !bWasDecapitated && Damage < Victim.Health && Victim.IsA('TeslaHusk') ) {
GameRules.MonsterInfos[index].KillAss1 = InstigatorInfo;
GameRules.MonsterInfos[index].DamType1 = DamType;
GameRules.MonsterInfos[index].DamTime1 = Level.TimeSeconds;
GameRules.MonsterInfos[index].DamageFlags1 = DF_DECAP;
}
else if ( !Victim.bDecapitated && Damage < Victim.Health
&& (Damage > Victim.default.Health/1.5
|| (Damage > 200 && !Victim.IsA('TeslaHusk')
&& ( ClassIsChildOf(DamType, class'DamTypeCrossbow')
|| ClassIsChildOf(DamType, class'DamTypeWinchester')
|| ClassIsChildOf(DamType, class'DamTypeM14EBR')))) )
{
// track only stun damage
if ( GameRules.MonsterInfos[index].DamType1 == none ) {
GameRules.MonsterInfos[index].KillAss1 = InstigatorInfo;
GameRules.MonsterInfos[index].DamType1 = DamType;
GameRules.MonsterInfos[index].DamTime1 = Level.TimeSeconds;
GameRules.MonsterInfos[index].DamageFlags1 = DF_STUNNED;
}
}
}
else if ( ZombieScrake(Victim) != none || Victim.IsA('ZombieJason') ) {
if ( !Victim.bDecapitated && Damage < Victim.Health && !GameRules.MonsterInfos[index].TW_Ach_Failed ) {
if ( ClassIsChildOf(GameRules.MonsterInfos[index].DamType1, class'KFMod.DamTypeLAW') ) {
if ( (ClassIsChildOf(DamType, class'ScrnDamTypeHeavyBase')
|| DamType == class'KFMod.DamTypeDBShotgun'
|| DamType == class'KFMod.DamTypeBenelli')
&& !ClassIsChildOf(GameRules.MonsterInfos[index].DamType2, class'ScrnDamTypeHeavyBase')
&& GameRules.MonsterInfos[index].DamType2 != class'KFMod.DamTypeDBShotgun'
&& GameRules.MonsterInfos[index].DamType2 != class'KFMod.DamTypeBenelli' ) {
GameRules.MonsterInfos[index].KillAss2 = InstigatorInfo;
GameRules.MonsterInfos[index].DamType2 = DamType;
GameRules.MonsterInfos[index].DamTime2 = Level.TimeSeconds;
GameRules.MonsterInfos[index].DamageFlags2 = 0;
}
}
else if ( Damage >= Victim.default.Health/1.5 ) {
if ( DamType.default.bIsMeleeDamage && ClassIsChildOf(DamType, class'KFMod.DamTypeMachete') )
InstigatorInfo.ProgressAchievement('MacheteStunSC', 1);
// STUN DAMAGE
if ( GameRules.MonsterInfos[index].DamType1 == none ) {
GameRules.MonsterInfos[index].KillAss1 = InstigatorInfo;
GameRules.MonsterInfos[index].DamType1 = DamType;
GameRules.MonsterInfos[index].DamTime1 = Level.TimeSeconds;
GameRules.MonsterInfos[index].DamageFlags1 = DF_STUNNED;
if ( bIsHeadshot )
GameRules.MonsterInfos[index].DamageFlags1 = GameRules.MonsterInfos[index].DamageFlags1 | DF_HEADSHOT;
}
else if ( GameRules.MonsterInfos[index].DamType1.default.bSniperWeapon ) {
// Instant Kill can be achieved by 3 snipers, if they all had shot in the same time
if ( DamType.default.bSniperWeapon && bIsHeadshot && Level.TimeSeconds < GameRules.MonsterInfos[index].FirstHitTime + InstantKillTime ) {
GameRules.MonsterInfos[index].KillAss2 = InstigatorInfo;
GameRules.MonsterInfos[index].DamType2 = DamType;
GameRules.MonsterInfos[index].DamageFlags2 = DF_STUNNED | DF_HEADSHOT;
}
else
GameRules.MonsterInfos[index].TW_Ach_Failed = true;
}
}
}
}
else if ( ZombieFleshpound(Victim) != none ) {
if ( Victim.bDecapitated && !bWasDecapitated && ClassIsChildOf(DamType, class'DamTypeAxe') )
InstigatorInfo.ProgressAchievement('OldSchoolKiting', 1);
// both TW FP achievements requires first rage shot to be from heavy sniper rifgle: Xbow, M99, HR or 2xSVD
// Pipe achievement also requires it to be a rage shot.
// M14 ach allows to shoot already raged FP.
if ( !Victim.bDecapitated && Damage < Victim.Health && !GameRules.MonsterInfos[index].TW_Ach_Failed ) {
if ( bIsHeadshot && Damage >= 600 ) {
df = DF_HEADSHOT;
// FP can't be stunned, but DF_STUNNED flag is used to differ heavy headshots from other damages
if ( Damage >= 800 )
df = df | DF_STUNNED;
if ( GameRules.MonsterInfos[index].DamType1 == none ) {
GameRules.MonsterInfos[index].KillAss1 = InstigatorInfo;
GameRules.MonsterInfos[index].DamType1 = DamType;
GameRules.MonsterInfos[index].DamTime1 = Level.TimeSeconds;
if ( !ZombieFleshpound(Victim).bChargingPlayer && !ZombieFleshpound(Victim).bFrustrated )
df = df | DF_RAGED;
GameRules.MonsterInfos[index].DamageFlags1 = df;
}
else if ( GameRules.MonsterInfos[index].KillAss1 == InstigatorInfo ) {
GameRules.MonsterInfos[index].DamageFlags1 = GameRules.MonsterInfos[index].DamageFlags1 | DF_STUNNED; // in case of 2xSVD
}
else if ( GameRules.MonsterInfos[index].KillAss2 == InstigatorInfo ) {
GameRules.MonsterInfos[index].DamageFlags2 = GameRules.MonsterInfos[index].DamageFlags2 | DF_STUNNED; // in case of 2xSVD
}
else if ( (GameRules.MonsterInfos[index].DamageFlags2 & DF_STUNNED) == 0 ) {
// heavy headshots always override other assistances (which don't have DF_STUNNED flag)
GameRules.MonsterInfos[index].KillAss2 = InstigatorInfo;
GameRules.MonsterInfos[index].DamType2 = DamType;
GameRules.MonsterInfos[index].DamTime2 = Level.TimeSeconds;
GameRules.MonsterInfos[index].DamageFlags2 = df;
}
}
else if ( GameRules.MonsterInfos[index].DamType1 == none ) {
// raging FP with other weapons fails TW achievements
if ( ZombieFleshpound(Victim).bChargingPlayer || ZombieFleshpound(Victim).bFrustrated
|| Damage + ZombieFleshpound(Victim).TwoSecondDamageTotal > ZombieFleshpound(Victim).RageDamageThreshold )
GameRules.MonsterInfos[index].TW_Ach_Failed = true;
}
else if ( DamType.Default.bSniperWeapon ) {
if ( bIsHeadshot && GameRules.MonsterInfos[index].DamType2 == none && Damage >= 200 ) {
GameRules.MonsterInfos[index].KillAss2 = InstigatorInfo;
GameRules.MonsterInfos[index].DamType2 = DamType;
GameRules.MonsterInfos[index].DamTime2 = Level.TimeSeconds;
GameRules.MonsterInfos[index].DamageFlags2 = DF_HEADSHOT;
}
}
else {
TotalDamage = GetTotalDamageMadeC(MC, InstigatorInfo.PlayerOwner);
if ( TotalDamage > Victim.HealthMax * 0.5 )
GameRules.MonsterInfos[index].TW_Ach_Failed = true; // why need teamwork, if this player just spams at FP?
else if ( GameRules.MonsterInfos[index].DamType2 == none && TotalDamage > Victim.HealthMax * 0.1 ) {
GameRules.MonsterInfos[index].KillAss2 = InstigatorInfo;
GameRules.MonsterInfos[index].DamType2 = DamType;
GameRules.MonsterInfos[index].DamTime2 = Level.TimeSeconds;
GameRules.MonsterInfos[index].DamageFlags2 = 0;
}
}
}
}
else if ( GameRules.BossClass == Victim.class ) {
bBossM99Only = bBossM99Only && ClassIsChildOf(DamType, class'DamTypeM99SniperRifle');
bBossXbowOnly = bBossXbowOnly && ClassIsChildOf(DamType, class'DamTypeCrossbow');
bBossMeleeOnly = bBossMeleeOnly && DamType.default.bIsMeleeDamage && !ClassIsChildOf(DamType, class'DamTypeCrossbuzzsaw');
bBoss9mmOnly = bBoss9mmOnly && ClassIsChildOf(DamType, class'DamTypeDualies');
BossLastDamageTime = Level.TimeSeconds;
}
//
if ( bBossPrey && GameRules.BossClass != Victim.class ) {
// to get "Hunting The Prey" achievements players must follow Pat all the time without
// focusing on other zeds. However, players are allowed to damage/kill other zeds too, if
// players are attacking the Pat at that moment. If Pat ran away, but players are killing
// other zeds, then ach is failed
if ( Level.TimeSeconds > BossLastDamageTime + 5.0 )
bBossPrey = false;
}
}
function MonsterKilled(KFMonster Victim, ScrnPlayerInfo KillerInfo, class<KFWeaponDamageType> DamType)
{
local int index;
local ScrnHumanPawn KillerInfoPawn;
local KFPlayerReplicationInfo KFPRI;
local int PipeBombsAround;
local int w_idx; // current KillerInfo.WeaponInfos index
local KFMonsterController MC;
local KFWeapon Weapon;
local ScrnPlayerInfo AssSPI; // Ass for Assistant ;)
KillerInfoPawn = ScrnHumanPawn(KillerInfo.PlayerOwner.Pawn);
KFPRI = KFPlayerReplicationInfo(KillerInfo.PlayerOwner.PlayerReplicationInfo);
Weapon = KillerInfo.FindWeaponByDamType(DamType);
w_idx = KillerInfo.FindWeaponInfoByDamType(DamType);
index = GameRules.GetMonsterIndex(Victim);
MC = KFMonsterController(Victim.Controller);
if ( MC == none )
return; // wtf?
KillerInfo.ProgressAchievement('Kill1000Zeds', 1); //kill counter, incs all 3 achievements
if ( Level.TimeSeconds < KillerInfo.LastKillTime + 5.0 ) {
if ( !DamType.default.bIsMeleeDamage && !DamType.default.bIsExplosive && !DamType.default.bDealBurningDamage
&& KillerInfo.IncCustomValue(self, 'KillsBetween5s', 1) == 30 )
KillerInfo.ProgressAchievement('OutOfTheGum', 1);
}
else
KillerInfo.SetCustomValue(self, 'KillsBetween5s', 0);
if ( GameRules.MonsterInfos[index].PlayerKillCounter > 0 && Level.TimeSeconds < GameRules.MonsterInfos[index].PlayerKillTime + 5 )
KillerInfo.ProgressAchievement('FastVengeance', 1);
// big zeds
if ( Victim.default.Health >= 1000 ) {
if ( ClassIsChildOf(DamType, class'KFMod.DamTypeLAW') )
KillerInfo.ProgressAchievement('BringingLAW', 1);
else if ( ClassIsChildOf(DamType, class'KFMod.DamTypePipeBomb') ) {
// check if somebody is blocking it
if ( Victim.LastDamageAmount >= 1300 && GameRules.MonsterInfos[index].PlayerKillCounter == 0
&& GameRules.MonsterInfos[index].DamageCounter < 30 * Victim.DifficultyDamageModifer()
&& GiveAchToBlockingPlayers(Victim, MC, 'TW_PipeBlock') > 0 )
KillerInfo.ProgressAchievement('TW_PipeBlock', 1);
}
if ( !Victim.bDamagedAPlayer && GameRules.MonsterInfos[index].Headshots == 0 )
KillerInfo.ProgressAchievement('NoHeadshots', 1);
}
if ( DamType.default.bIsMeleeDamage ) {
if ( Victim.bCrispified )
KillerInfo.ProgressAchievement('CarveRoast', 1);
if ( ClassIsChildOf(DamType, class'KFMod.DamTypeScythe')
|| ClassIsChildOf(DamType, class'ScrnBalanceSrv.ScrnDamTypeScythe') )
KillerInfo.ProgressAchievement('GrimReaper', 1);
}
else if ( ClassIsChildOf(DamType, class'KFMod.DamTypeRocketImpact') ) {
KillerInfo.ProgressAchievement('PrematureDetonation', 1);
}
if ( DamType.name == 'DamTypeEMP' ) {
KillerInfo.ProgressAchievement('TeslaBomber', 1);
}
if ( KillerInfoPawn != none) {
// don't give Combat Medic, if healed person is dead, healed himself, or was healed by another person later
if ( KillerInfoPawn.IsMedic() && KillerInfoPawn.LastHealed != none && KillerInfoPawn.LastHealed !=KillerInfoPawn
&& KillerInfoPawn.LastHealed.Health > 0
&& KillerInfoPawn.LastHealed.LastHealedBy == KillerInfoPawn ) {
// medic has 5 seconds to kill a target
if ( (KillerInfoPawn.LastHealed.CombatMedicTarget == Victim || KillerInfoPawn.LastHealed.LastDamagedBy == Victim)
&& level.TimeSeconds - KillerInfoPawn.LastHealed.LastHealTime < 5.0 )
KillerInfo.ProgressAchievement('CombatMedic', 1);
}
}
if ( w_idx != -1 ) {
if ( KillerInfo.WeapInfos[w_idx].PrevOwner != none && KillerInfo.WeapInfos[w_idx].bPrevOwnerDead
&& KillerInfo.WeapInfos[w_idx].PickupWave == GameRules.Mut.KF.WaveNum )
{
AssSPI = GameRules.GetPlayerInfo(KillerInfo.WeapInfos[w_idx].PrevOwner);
if ( AssSPI != none ) {
KillerInfo.ProgressAchievement('Cookies', 1);
if ( AssSPI.LastKilledBy == MC )
KillerInfo.ProgressAchievement('EyeForAnEye', 1);
}
}
}
if ( ZombieClot(Victim) == none )
KillerInfo.SetCustomValue(self, 'RowClotKills', 0, false);
if ( ZombieClot(Victim) != none ) {
if ( KillerInfo.IncCustomValue(self, 'RowClotKills', 1, false) == 15 ) {
KillerInfo.ProgressAchievement('ClotHater', 1);
KillerInfo.SetCustomValue(self, 'RowClotKills', 0, false);
}
if ( Level.TimeSeconds < KillerInfo.GetCustomFloat(self, 'LastClotKillTime') + 4.0 ) {
if ( KillerInfo.IncCustomValue(self, 'ClotKills4s', 1, true) == 25 )
KillerInfo.ProgressAchievement('Plus25Clots', 1);
}
else
KillerInfo.SetCustomValue(self, 'ClotKills4s', 0, true);
KillerInfo.SetCustomFloat(self, 'LastClotKillTime', Level.TimeSeconds, true);
}
else if ( ZombieCrawler(Victim) != none ) {
if ( Victim.LastDamageAmount < 80 && (DamType == class'DamTypeFlamethrower' || DamType == class'DamTypeBurned') )
KillerInfo.ProgressAchievement('HFC', 1);
else if ( IsPistolDamage(KillerInfo, DamType) ) {
if ( GameRules.MonsterInfos[index].Headshots + GameRules.MonsterInfos[index].Bodyshots == 1 )
KillerInfo.ProgressAchievement('Gunslingophobia', 1);
}
else if ( GameRules.MonsterInfos[index].bHeadshot ) {
// Overkills
if ( ClassIsChildOf(DamType, class'KFMod.DamTypeM99HeadShot') )
KillerInfo.ProgressAchievement('Overkill', 1);
else if ( Victim.LastDamageAmount > 6950 && ClassIsChildOf(DamType, class'KFMod.DamTypeHuskGunProjectileImpact') )
KillerInfo.ProgressAchievement('Overkill1', 1);
else if ( ClassIsChildOf(DamType, class'KFMod.DamTypeLawRocketImpact') )
KillerInfo.ProgressAchievement('Overkill2', 1);
}
}
else if ( Victim.IsA('ZombieShiver') ) {
// Firebug (KillAss1) ignites Shiver and makes him unable to teleport
// Then Commando (KillAss2) - decapitates
// KillerInfo doesn't get teamwork achievement (the one who killed decapitated body)
if ( Victim.bDecapitated && !GameRules.MonsterInfos[index].TW_Ach_Failed ) {
if ( GameRules.MonsterInfos[index].KillAss2 != none )
GameRules.RewardTeamwork(none, index, 'TW_Shiver'); //KillerInfo took down already decapitated body, so no TW ach for him
else if ( DamType.default.bCheckForHeadShots && IsAssaultRifleDamage(KillerInfo, DamType) )
GameRules.RewardTeamwork(KillerInfo, index, 'TW_Shiver'); // Decapitation shot killed the Victim
}
}
else if ( ZombieStalker(Victim) != none ) {
if ( !Victim.bDamagedAPlayer && KFPRI != none && KFPRI.ClientVeteranSkill.static.ShowStalkers(KFPRI)
&& VSizeSquared(Victim.Location - KillerInfo.PlayerOwner.Pawn.Location)
< 360000 * KFPRI.ClientVeteranSkill.static.GetStalkerViewDistanceMulti(KFPRI) )
KillerInfo.ProgressAchievement('Ghostbuster', 1);
}
else if ( ZombieBloat(Victim) != none ) {
PipeBombsAround = InPipeBombRange(Victim, 600);
if ( PipeBombsAround > 0 )
KillerInfo.ProgressAchievement('SavingResources', 1);
}
else if ( ZombieSiren(Victim) != none ) {
PipeBombsAround = InPipeBombRange(Victim, 250);
if ( PipeBombsAround > 0 )
KillerInfo.ProgressAchievement('SavingResources', 1);
if ( !GameRules.MonsterInfos[index].TW_Ach_Failed )
GameRules.RewardTeamwork(KillerInfo, index, 'TW_Siren');
}
else if ( ZombieHusk(Victim) != none ) {
if ( ClassIsChildOf(DamType, class'DamTypeHuskGun') || ClassIsChildOf(DamType, class'DamTypeHuskGunProjectileImpact') )
KillerInfo.ProgressAchievement('KillHuskHuskGun', 1);
else if ( ClassIsChildOf(DamType, class'DamTypeCrossbowHeadShot') ) {
if ( Level.TimeSeconds - KillerInfo.GetCustomFloat(self, 'LastDeagleHSTime') < 2.0 )
KillerInfo.ProgressAchievement('KFG1', 1);
}
if ( !Victim.bDamagedAPlayer ) {
// give Fast Shot achievement only if player killed Husk alone
if ( GameRules.MonsterInfos[index].KillAss1 == KillerInfo )
KillerInfo.ProgressAchievement('FastShot', 1);
else if ( DamType.default.bIsPowerWeapon && GameRules.MonsterInfos[index].KillAss1 != none
&& (GameRules.MonsterInfos[index].DamageFlags1 & DF_STUNNED) > 0 )
{
// finishing with shotgun after the stun
GameRules.RewardTeamwork(KillerInfo, index, 'TW_Husk_Stun');
}
}
else if ( KillerInfoPawn.LastDamagedBy == Victim && (DamType.default.bIsPowerWeapon || DamType.default.bIsMeleeDamage)
&& Victim.IsA('TeslaHusk') )
{
KillerInfo.ProgressAchievement('NikolaTesla', 1);
}
}
else if ( ZombieScrake(Victim) != none || Victim.IsA('ZombieJason') ) {
if ( Victim.IsA('ZombieJason') && ClassIsChildOf(DamType, class'DamTypeMachete') )
KillerInfo.ProgressAchievement('ComeatMe', 1);
//scrake unnader
if ( !Victim.bDamagedAPlayer && GameRules.MonsterInfos[index].DamType2 == class'KFMod.DamTypeFrag'
&& GameRules.MonsterInfos[index].DamageFlags2 == (DF_RAGED | DF_STUPID) )
KillerInfo.ProgressAchievement('ScrakeUnnader', 1);
if ( DamType.default.bSniperWeapon || ClassIsChildOf(DamType, class'KFMod.DamTypeCrossbuzzsawHeadShot') ) {
KillerInfo.ProgressAchievement('Snipe250SC', 1);
// Teamwork: InstantKill
if ( GameRules.MonsterInfos[index].KillAss1 != KillerInfo
&& GameRules.MonsterInfos[index].DamType1 != none && GameRules.MonsterInfos[index].DamType1.default.bSniperWeapon
&& (GameRules.MonsterInfos[index].DamageFlags1 & DF_STUNNED) > 0
&& Level.TimeSeconds - GameRules.MonsterInfos[index].FirstHitTime < InstantKillTime ) {
GameRules.RewardTeamwork(KillerInfo, index, 'TW_SC_Instant');
}
}
else if ( DamType.default.bIsPowerWeapon ) {
if ( (ClassIsChildOf(DamType, class'ScrnDamTypeHeavyBase')
|| DamType == class'KFMod.DamTypeDBShotgun'
|| DamType == class'KFMod.DamTypeBenelli')
&& GameRules.MonsterInfos[index].DamType1 != none && GameRules.MonsterInfos[index].DamType1.default.bIsExplosive
&& ClassIsChildOf(GameRules.MonsterInfos[index].DamType1, class'KFMod.DamTypeLAW')
&& (GameRules.MonsterInfos[index].DamageFlags1 & DF_STUNNED) > 0 )
{
// TeamWork: LAW Stun + HSg finish
GameRules.RewardTeamwork(KillerInfo, index, 'TW_SC_LAWHSG');
}
}
else if ( DamType.default.bIsMeleeDamage ) {
if ( GameRules.MonsterInfos[index].BodyShots == 0 && !ClassIsChildOf(DamType, class'KFMod.DamTypeCrossbuzzsaw') )
KillerInfo.ProgressAchievement('MeleeGod', 1);
if ( ClassIsChildOf(DamType, class'KFMod.DamTypeChainsaw') )
KillerInfo.ProgressAchievement('BitterIrony', 1);
if ( !Victim.bDamagedAPlayer && GameRules.MonsterInfos[index].bWasBackstabbed )
CheckAchBackstabAttract(Victim, KillerInfo);
}
else if ( ClassIsChildOf(DamType, class'KFMod.DamTypeHuskGunProjectileImpact')
|| ClassIsChildOf(DamType, class'KFMod.DamTypeHuskGun') )
KillerInfo.ProgressAchievement('HuskGunSC', 1);
else if ( !Victim.bDamagedAPlayer && (ClassIsChildOf(DamType, class'ScrnDamTypeDualDeagle')
|| ClassIsChildOf(DamType, class'ScrnDamTypeDual44Magnum')) )
KillerInfo.ProgressAchievement('GunslingerSC', 1);
}
else if ( ZombieFleshpound(Victim) != none ) {
if ( DamType.default.bIsExplosive )
KillerInfo.ProgressAchievement('Kill100FPExplosives', 1);
else if ( DamType.default.bIsPowerWeapon) {
if ( DamType == class'KFMod.DamTypeDBShotgun' || DamType == class'KFMod.DamTypeBenelli' )
KillerInfo.ProgressAchievement('GetOffMyLawn', 1);
}
else if ( DamType.default.bIsMeleeDamage) {
// if Victim is already decapitated, then OldSchoolKiting is granted on decapitation
if ( !Victim.bDecapitated && ClassIsChildOf(DamType, class'DamTypeAxe') )
KillerInfo.ProgressAchievement('OldSchoolKiting', 1);
}
if ( (MC.KillAssistants.Length == 0 ||
(MC.KillAssistants.Length == 1 && MC.KillAssistants[0].PC == KillerInfo.PlayerOwner))
&& GameRules.AlivePlayerCount() >= 6 )
{
KillerInfo.ProgressAchievement('Unassisted', 1);
}
else {
if ( GameRules.MonsterInfos[index].DamType1 != none ) {
if ( DamType == class'KFMod.DamTypePipeBomb'
&& (GameRules.MonsterInfos[index].DamageFlags1 & DF_RAGED) != 0 )
GameRules.RewardTeamwork(KillerInfo, index, 'TW_FP_Pipe');
else if ( DamType.default.bSniperWeapon ) {
// ensure that second assistant used sniper headshots too
if ( (GameRules.MonsterInfos[index].DamageFlags2 & DF_HEADSHOT) == 0 )
GameRules.MonsterInfos[index].KillAss2 = none;
GameRules.RewardTeamwork(KillerInfo, index, 'TW_FP_Snipe');
}
}
}
}
else if ( Victim.IsA('FemaleFP') ) {
if ( DamType.default.bIsExplosive )
KillerInfo.ProgressAchievement('Kill100FPExplosives', 1);
else if ( DamType.default.bIsPowerWeapon) {
if ( DamType == class'KFMod.DamTypeDBShotgun' || DamType == class'KFMod.DamTypeBenelli' )
KillerInfo.ProgressAchievement('GetOffMyLawn', 1);
}
}
else if ( Victim.IsA('ZombieBrute') ) {
if ( DamType.default.bIsExplosive )
KillerInfo.ProgressAchievement('BruteExplosive', 1);
if ( !Victim.bDamagedAPlayer ) {
if ( ClassIsChildOf(DamType, class'KFMod.DamTypeM14EBR') )
KillerInfo.ProgressAchievement('BruteM14', 1);
else if ( ClassIsChildOf(DamType, class'KFMod.DamTypeM99HeadShot') || ClassIsChildOf(DamType, class'KFMod.DamTypeCrossbowHeadShot') )
KillerInfo.ProgressAchievement('BruteXbow', 1);
else if ( ClassIsChildOf(DamType, class'KFMod.DamTypeSCARMK17AssaultRifle') )
KillerInfo.ProgressAchievement('BruteSCAR', 1);
}
}
}
/**
* Gives achievement for all players who are blocking the Monster.
* Player is blocking the Monster if:
* a) he is 1 meter (50uu) or closer to the Monster's collision cylinder
* b) he is Monster's current or previous enemy OR he is extremely close to the Monster and can see it
* c) he is in line of sight of the Monster
*
* @param Monster enemy, who is supposed to be blocked
* @param MC Monster's controller
* @param AchID achievement ID to progress
* @return number of players, who gained (or supposed to gain) achievement progress
*/
function int GiveAchToBlockingPlayers(KFMonster Monster, KFMonsterController MC, name AchID)
{
local KFHumanPawn Pawn;
local ScrnPlayerInfo SPI;
local int c;
if ( Monster == none || MC == none )
return 0;
foreach Monster.VisibleCollidingActors( class'KFHumanPawn', Pawn, Monster.CollisionRadius + 50, Monster.Location ) {
if ( ((Pawn == MC.Enemy || Pawn == MC.VisibleEnemy || Pawn == MC.OldEnemy)
|| (vsize(Pawn.Location - Monster.Location) < Monster.CollisionRadius + Pawn.CollisionRadius + 10
&& Pawn.Controller.CanSee(Monster)))
&& mc.CanSee(Pawn) )
{
c++;
SPI = GameRules.GetPlayerInfo(PlayerController(Pawn.Controller));
if ( SPI != none )
SPI.ProgressAchievement(AchID, 1);
}
}
return c;
}
function private CheckAchBackstabAttract(KFMonster Monster, ScrnPlayerInfo KillerSPI)
{
local KFPawn KFP;
local ScrnPlayerInfo SPI;
local int i;
local KFMonsterController MC;
local bool bGiveAch;
local float Pawn2MonsterDistanceSquared, Killer2PawnDistanceSquared;
MC = KFMonsterController(Monster.Controller);
for ( i = 0; i < MC.KillAssistants.Length; ++i ) {
if ( MC.KillAssistants[i].PC != none && MC.KillAssistants[i].PC != KillerSPI.PlayerOwner ) {
KFP = KFPawn(MC.KillAssistants[i].PC.Pawn);
// attractive pawn and killed must be on opposite sides of monsters, so distance between
// monster and player must be smaller than distance between 2 players
Pawn2MonsterDistanceSquared = VSizeSquared(Monster.Location - KFP.Location);
Killer2PawnDistanceSquared = VSizeSquared(KillerSPI.PlayerOwner.Pawn.Location - KFP.Location);
// attractive person and monster must be within 5m range and see each other
// only God knows where Scrake is looking during stun animation, so don't use CanSee() here
if ( KFP != none && Pawn2MonsterDistanceSquared < 62500 && Pawn2MonsterDistanceSquared < Killer2PawnDistanceSquared )
// && KFP.Controller.CanSee(Monster) && (MC.LineOfSightTo(KFP) || KillerSPI.PlayerOwner.LineOfSightTo(KF)) )
{
bGiveAch = true;
SPI = GameRules.GetPlayerInfo(PlayerController(KFP.Controller));
if ( SPI != none )
SPI.ProgressAchievement('TW_BackstabSC', 1);
}
}
}
if ( bGiveAch )
KillerSPI.ProgressAchievement('TW_BackstabSC', 1);
}
function PickedCash(int CashAmount, ScrnPlayerInfo ReceiverInfo, ScrnPlayerInfo DonatorInfo, bool bDroppedCash)
{
if ( bDroppedCash && DonatorInfo != none && DonatorInfo.PlayerOwner != none ) {
if ( CashAmount == 1 && DonatorInfo.PlayerOwner.PlayerReplicationInfo.Score >=
max(500, 10 * (ReceiverInfo.PlayerOwner.PlayerReplicationInfo.Score + ReceiverInfo.CashDonated)) )
DonatorInfo.ProgressAchievement('SpareChange', 1);
if ( DonatorInfo.CashDonated - DonatorInfo.CashReceived - DonatorInfo.CashFound >= 2000 )
DonatorInfo.ProgressAchievement('MilkingCow', 1);
}
}
function PickedWeapon(ScrnPlayerInfo SPI, KFWeaponPickup WeaponPickup)
{
if ( MachetePickup(WeaponPickup ) != none ) {
if ( SPI.IncCustomValue(none, 'MacheteWalker', 1) == 422 )
SPI.ProgressAchievement('MacheteWalker', 1);
}
}
defaultproperties
{
bOnePlayerPerPerk=True
bAllSamePerk=True
InstantKillTime=0.700000
iDoT_Damage=300
bNoHeadshots=True
bAllCanDoHeadshots=True
}