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

393 lines
16 KiB
Ucode

class ScrnAchHandlerBase extends Info
abstract;
var protected ScrnGameRules GameRules;
const IGNORE_STAT = 0x7FFFFFFF;
// Damage flags
// copy-pasted from ScrnGameRules for easy access
const DF_STUNNED = 0x01; // stun shot
const DF_RAGED = 0x02; // rage shot
const DF_STUPID = 0x04; // damage shouldn't be done to this zed
const DF_DECAP = 0x08; // decapitaion shot
const DF_HEADSHOT = 0x10; // 100% sure that this damage was made by headshot
function PostBeginPlay()
{
FindGameRules();
}
function FindGameRules()
{
local GameRules G;
if ( GameRules != none )
return;
for ( G=Level.Game.GameRulesModifiers; G!=None; G=G.NextGameRules ) {
if ( ScrnGameRules(G) != none ) {
GameRules = ScrnGameRules(G);
GameRules.RegisterAchHandler(self);
return;
}
}
log(GetItemName(String(self)) $ ": unable to find ScrnGameRules. Trying again in 5 seconds.", 'ScrnBalance');
SetTimer(5.0, false);
}
function Timer()
{
FindGameRules();
}
// -----------------------------------------------------------------------------------
// STATIC FUNCTIONS
// -----------------------------------------------------------------------------------
/*
Helper function for quickly determining whether a pawn is rotated at a point in space.
original authors : Alex Quick, Ron Prestenback.
* @param Pawn pawn, which rotation needs to be checked
* @param TargetLocation the location to check
* @param MinDotResult a value between 0 and 1 representing the minimum dot result value that should be
* considered acceptable for testing whether the location is within our field of
* view. Default value (0) is corresponds to a maximum angle of around 180 degrees
*
* @return TRUE if the location is within the angle specified from the pawn's rotation.
*/
static function bool IsRotatedAtLocation(Pawn Pawn, vector TargetLocation, optional float MinDotResult)
{
local vector DirectionNormal;
local float ViewAngleCosine;
// get the normalized distance between the two locations
DirectionNormal = Normal(TargetLocation - Pawn.Location);
// get the dot result of the pawn's rotation and the target location
ViewAngleCosine = DirectionNormal dot vector(Pawn.Rotation);
// determine if the angle between pawn's rotation and the target location is within range.
// log("IS LOOKING AT LOCATION - "@ViewAngleCosine@" Required : "@MinDotResult);
return ViewAngleCosine >= MinDotResult;
}
// returns number of not triggered pipeboms, which detonation range covers Pawn's location
// and are in lone of sight of a given pawn
static function int InPipeBombRange(Pawn Pawn, float PipeDetonationRange)
{
local PipeBombProjectile pipe;
local int count;
local Controller C;
C = Pawn.Controller;
if ( C == none )
return 0;
foreach Pawn.VisibleCollidingActors( class 'PipeBombProjectile', pipe, PipeDetonationRange, Pawn.Location ) {
if ( !pipe.bTriggered && !pipe.bHasExploded && !pipe.bDisintegrated && !pipe.bEnemyDetected
&& IsRotatedAtLocation(Pawn, pipe.Location, 0.5) )
count++;
}
return count;
}
//returns how much damage Instigator made to Victim
static function int GetTotalDamageMade(KFMonster Victim, Pawn Instigator)
{
if ( Victim == none || Instigator == none )
return 0;
return GetTotalDamageMadeC(KFMonsterController(Victim.Controller), Instigator.Controller);
}
//returns how much damage PC made to MC
static function int GetTotalDamageMadeC(KFMonsterController MC, Controller PC)
{
local int i;
if ( MC == none || PC == none )
return 0;
for ( i = 0; i < MC.KillAssistants.Length; ++i ) {
if ( PC == MC.KillAssistants[i].PC )
return MC.KillAssistants[i].Damage;
}
return 0;
}
static function bool IsPistolDamage(ScrnPlayerInfo InstigatorSPI, class<KFWeaponDamageType> DamageType)
{
local KFPlayerReplicationInfo KFPRI;
if ( DamageType == none || InstigatorSPI == none || InstigatorSPI.PlayerOwner == none )
return false;
KFPRI = KFPlayerReplicationInfo(InstigatorSPI.PlayerOwner.PlayerReplicationInfo);
if ( KFPRI == none )
return false;
// Gunslinger gets damage bonus on all pistols (even level 0).
// So if output damage > input damage, DamageType is pistol damage
return class'ScrnVetGunslinger'.static.AddDamage(KFPRI, none, none, 100, DamageType) > 100;
}
static function bool IsAssaultRifleDamage(ScrnPlayerInfo InstigatorSPI, class<KFWeaponDamageType> DamageType)
{
local KFPlayerReplicationInfo KFPRI;
if ( DamageType == none || InstigatorSPI == none || InstigatorSPI.PlayerOwner == none )
return false;
KFPRI = KFPlayerReplicationInfo(InstigatorSPI.PlayerOwner.PlayerReplicationInfo);
if ( KFPRI == none )
return false;
return class'ScrnVetCommando'.static.AddDamage(KFPRI, none, none, 100, DamageType) > 100;
}
// returns true, if player is using medic perk
static function bool IsMedic(ScrnPlayerInfo SPI)
{
local KFPlayerReplicationInfo KFPRI;
if ( SPI == none || SPI.PlayerOwner == none )
return false;
KFPRI = KFPlayerReplicationInfo(SPI.PlayerOwner.PlayerReplicationInfo);
if ( KFPRI != none && KFPRI.ClientVeteranSkill != none )
return KFPRI.ClientVeteranSkill.static.GetHealPotency(KFPRI) > 1.01;
return false;
}
// -----------------------------------------------------------------------------------
// give achievement to all players, except ExcludeSPI
final function Ach2All(name AchID, int Inc, optional ScrnPlayerInfo ExcludeSPI, optional TeamInfo Team)
{
if ( GameRules != none )
GameRules.ProgressAchievementForAllPlayers(AchID, Inc, false, ExcludeSPI, Team);
}
// give achievement to alive players, except ExcludeSPI
final function Ach2Alive(name AchID, int Inc, optional ScrnPlayerInfo ExcludeSPI, optional TeamInfo Team)
{
if ( GameRules != none )
GameRules.ProgressAchievementForAllPlayers(AchID, Inc, true, ExcludeSPI, Team);
}
/**
* Function is called every time after the start of new wave
* @param WaveNum - wave number, where 0 is first wave
*/
function WaveStarted(byte WaveNum) { }
/**
* Function is called every time after the end of new wave
* ScrnPlayerInfos aren't reset yet and can be used freely here
* @param WaveNum - wave number, where 0 is first wave
*/
function WaveEnded(byte WaveNum) { }
// Function is called only when squad is survived.
// Map name without .rom, e.g. "KF-Farm". MapName is already checked for GameRules.MapAliases before
// calling GameWon()
function GameWon(string MapName) { }
// override this to catch all NetDamage() calls passed to GameRules.
// NetDamage is executed after MonsterDamaged() or PlayerDamaged()
function NetDamage( int Damage, pawn injured, pawn instigatedBy, vector HitLocation, out vector Momentum,
class<DamageType> DamType ) {}
// override this to catch all ScoreKill() calls passed to GameRules.
// score killed is executed after MonsterDied() or PlayerDied()
function ScoreKill(Controller Killer, Controller Killed) {}
// player-to-monster damages
// called from ScrnPlayerInfo.MadeDamage()
// At this moment InstigatorInfo.LastDmgTime still stores previos damage time (not the given one).
// So time between subsequent damages can be calculated by Level.TimeSeconds - InstigatorInfo.LastKillTime
function MonsterDamaged(int Damage, KFMonster Victim, ScrnPlayerInfo InstigatorInfo,
class<KFWeaponDamageType> DamType, bool bIsHeadshot, bool bWasDecapitated) {}
// monster got killed by a player
// called from ScrnPlayerInfo.KilledMonster()
// At this moment KillerInfo.LastKillTime still stores previos kill time (not the given one).
// So time between subsequent kills can be calculated by Level.TimeSeconds - KillerInfo.LastKillTime
function MonsterKilled(KFMonster Victim, ScrnPlayerInfo KillerInfo, class<KFWeaponDamageType> DamType) {}
// player took a hit from monster
// called from ScrnPlayerInfo.TookDamage()
function PlayerDamaged(int Damage, ScrnPlayerInfo VictimSPI, KFMonster InstigatedBy, class<DamageType> DamType) {}
// player got killed
// called from ScrnPlayerInfo.Died()
// DeadPlayerInfo already has bDead = true and increased Deaths
function PlayerDied(ScrnPlayerInfo DeadPlayerInfo, Controller Killer, class<DamageType> DamType) {}
/**
* Triggers when end game boss spawned on the map.
* This function is called before KFMonster.PostBeginPlay(), where difficulty multipliers are applied,
* so it can't be used for checking actual monster parameters such as health or damage!
* @param EndGameBoss The monster to spawned as end game boss (usually Patriarch)
*/
function BossSpawned(KFMonster EndGameBoss) {}
/**
* Triggers when monster of a new class first time spawns in the game, i.e. it triggers on first Clot, first Gorefast etc.
* This function is called before KFMonster.PostBeginPlay(), where difficulty multipliers are applied,
* so it can't be used for checking actual monster parameters such as health or damage!
*
* @param Monster The momnster who spawned first time in the game
*/
function MonsterIntroduced(KFMonster Monster) {}
// Functions below are triggered, if stat value reaches a given number.
// AchHandler must return the minimal number, which it wants to be triggered.
// If AchHandler isn't interested in a given stat, it must return IGNORE_STAT.
// Keep in mind that return value is used as recomendation, not as a rule. It means that even
// if function had returned 5, there are no guaranties that it won't be triggered with a lower value
// e.g. 3. So input value must be checked always.
/**
* Triggers when player scored multiple headshots in a row.
* @param SPI Intigator's ScrnPlayerInfo record
* @param Count Headhot count in a row
* @return Minimal headshot count to be reached before the next call of this function
*/
function int RowHeadhots(ScrnPlayerInfo SPI, int Count)
{
return IGNORE_STAT;
}
/**
* Triggers when player scored multiple headshots in a row with one weapon.
* @param SPI Intigator's ScrnPlayerInfo record
* @param Weapon Weapon that PROBABLY was used to produce this damage type
* @param DamType Damage type that was catched by GameRules
* @param Count Headhot count in a row
* @return Minimal headshot count to be reached before the next call of this function
*/
function int WRowHeadhots(ScrnPlayerInfo SPI, KFWeapon Weapon, class<KFWeaponDamageType> DamType, int Count)
{
return IGNORE_STAT;
}
/**
* Triggers when player scored multiple headshots per single fire
* (due to bullet penetration or F/A mode without releasing a trigger).
* @param SPI Intigator's ScrnPlayerInfo record
* @param Weapon Weapon that PROBABLY was used to produce this damage type
* @param DamType Damage type that was catched by GameRules
* @param Count Headhot count scored without releasing a fire trigger
* @return Minimal headshot count to be reached before the next call of this function
*/
function int WInstantHeadhots(ScrnPlayerInfo SPI, KFWeapon Weapon, class<KFWeaponDamageType> DamType, int Count)
{
return IGNORE_STAT;
}
// Same as WInstantHeadhots, but triggers on headshots without reloading
function int WHeadshotsPerMagazine(ScrnPlayerInfo SPI, KFWeapon Weapon, class<KFWeaponDamageType> DamType, int Count)
{
return IGNORE_STAT;
}
/**
* Triggers when players scored multiple kills with one shot or without releasing a trigger
* (depends from a weapon). Sometimes shot isn't registred and multiple shots are registred as one.
* So DeltaTime needs to be checked too.
*
* @param SPI Intigator's ScrnPlayerInfo record
* @param Weapon Weapon that PROBABLY was used to produce this damage type
* @param DamType Damage type that was catched by GameRules
* @param Count Kill count in one shot
* @param DeltaTime time between last and previous kill
* @return Minimal headshot count to be reached before the next call of this function
*/
function int WKillsPerShot(ScrnPlayerInfo SPI, KFWeapon Weapon, class<KFWeaponDamageType> DamType, int Count, float DeltaTime)
{
return IGNORE_STAT;
}
// Same as WKillsPerShot, but triggers on kills without reloading
function int WKillsPerMagazine(ScrnPlayerInfo SPI, KFWeapon Weapon, class<KFWeaponDamageType> DamType, int Count)
{
return IGNORE_STAT;
}
// number of decapitaions per shot
function int WDecapsPerShot(ScrnPlayerInfo SPI, KFWeapon Weapon, class<KFWeaponDamageType> DamType, int Count, float DeltaTime)
{
return IGNORE_STAT;
}
// Same as WDecapsPerShot, but triggers on kills without reloading
function int WDecapsPerMagazine(ScrnPlayerInfo SPI, KFWeapon Weapon, class<KFWeaponDamageType> DamType, int Count)
{
return IGNORE_STAT;
}
// amount of damage per shot
function int WDamagePerShot(ScrnPlayerInfo SPI, KFWeapon Weapon, class<KFWeaponDamageType> DamType, int Damage, float DeltaTime)
{
return IGNORE_STAT;
}
// Same as WDamagePerShot, but triggers on kills without reloading
function int WDamagePerMagazine(ScrnPlayerInfo SPI, KFWeapon Weapon, class<KFWeaponDamageType> DamType, int Damage)
{
return IGNORE_STAT;
}
/**
* Triggers each time when one player healed another one. Doesn't trigger if player already had 100% hp.
* @param HealAmount the amount of health Patient received
* @param Patient player, who received healing
* @param InstigatorSPI Instigator's player info. Player, who made healing.
* @param MedicGun Instigator's weapon, which was used for healing
*
*/
function HealingMade(int HealAmount, ScrnHumanPawn Patient, ScrnPlayerInfo InstigatorSPI, KFWeapon MedicGun) {}
/**
* Triggers when player picks up the cash. ReceiverInfo.CashReceived and CashFound and DonatorInfo.CashDonated,
* as well as their PerWave variables, already include CashAmount.
* Function isn't triggered, when player picks up own cash.
*
* @param CashAmount amount of cash picked up
* @param ReceiverInfo player, who received cash (always != none)
* @param DonatorInfo player, who dropped the cash (can be none)
* @param bDroppedCash if true, cash was dropped by a player. False means it was spawned on the map.
* There can be situations when bDroppedCash=true and DonatorInfo=none, so
* DonatorInfo must be always checked for none.
*/
function PickedCash(int CashAmount, ScrnPlayerInfo ReceiverInfo, ScrnPlayerInfo DonatorInfo, bool bDroppedCash) {}
/**
* Triggers when player picks up a weapon. Weapon is not added to player's inventory yet.
* Function is triggered on picking up own weapons too.
* @param SPI Player's info, who picked up a weapon
* @param WeaponPickup The weapon picked up by the player
*/
function PickedWeapon(ScrnPlayerInfo SPI, KFWeaponPickup WeaponPickup) {}
/**
* Triggers when player picks up an item from the ground, excluding cash and weapons,
* which have own trigger functions. Item is not added to player's inventory yet.
* Function is triggered on picking up own items too.
* @param SPI Player's info, who picked up an item
* @param Item The item picked up by the player.
*/
function PickedItem(ScrnPlayerInfo SPI, Pickup Item) {}
/**
* Triggers every time players reloads weapon.
* Per-magazine weapon stats are not nulled at this moment yet (e.g. KillsPerMagazine)
* @param SPI Player's info, who picked up an item
* @param W Reloaded Weapon
*/
function WeaponReloaded(ScrnPlayerInfo SPI, KFWeapon W) {}
defaultproperties
{
}