[Developing]HeadshotRecover Mut - 爆头回血插件 - Killing Floor 2

目前正在翻看官方SRC

收获如下

[class'Weapon']

simulated function CalcWeaponFire(vector StartTrace, vector EndTrace, optional out array<ImpactInfo> ImpactList, optional vector Extent)  : ImpactInfo

/**
 * CalcWeaponFire: Simulate an instant hit shot.
 * This doesn't deal any damage nor trigger any effect. It just simulates a shot and returns
 * the hit information, to be post-processed later.
 *
 * ImpactList returns a list of ImpactInfo containing all listed impacts during the simulation.
 * CalcWeaponFire however returns one impact (return variable) being the first geometry impact
 * straight, with no direction change. If you were to do refraction, reflection, bullet penetration
 * or something like that, this would return exactly when the crosshair sees:
 * The first 'real geometry' impact, skipping invisible triggers and volumes.
 *
 * @param	StartTrace	world location to start trace from
 * @param	EndTrace	world location to end trace at
 * @param	Extent		extent of trace performed
 * @output	ImpactList	list of all impacts that occured during simulation
 * @return	first 'real geometry' impact that occured.
 *
 * @note if an impact didn't occur, and impact is still returned, with its HitLocation being the EndTrace value.
 */
simulated function ImpactInfo CalcWeaponFire(vector StartTrace, vector EndTrace, optional out array<ImpactInfo> ImpactList, optional vector Extent)
{
	local vector			HitLocation, HitNormal, Dir;
	local Actor				HitActor;
	local TraceHitInfo		HitInfo;
	local ImpactInfo		CurrentImpact;
	local PortalTeleporter	Portal;
	local float				HitDist;
	local bool				bOldBlockActors, bOldCollideActors;

	// Perform trace to retrieve hit info
	HitActor = GetTraceOwner().Trace(HitLocation, HitNormal, EndTrace, StartTrace, TRUE, Extent, HitInfo, TRACEFLAG_Bullet);

	// If we didn't hit anything, then set the HitLocation as being the EndTrace location
	if( HitActor == None )
	{
		HitLocation	= EndTrace;
	}

	// Convert Trace Information to ImpactInfo type.
	CurrentImpact.HitActor		= HitActor;
	CurrentImpact.HitLocation	= HitLocation;
	CurrentImpact.HitNormal		= HitNormal;
	CurrentImpact.RayDir		= Normal(EndTrace-StartTrace);
	CurrentImpact.StartTrace	= StartTrace;
	CurrentImpact.HitInfo		= HitInfo;

	// Add this hit to the ImpactList
	ImpactList[ImpactList.Length] = CurrentImpact;

	// check to see if we've hit a trigger.
	// In this case, we want to add this actor to the list so we can give it damage, and then continue tracing through.
	if( HitActor != None )
	{
		if (PassThroughDamage(HitActor))
		{
			// disable collision temporarily for the actor we can pass-through
			HitActor.bProjTarget = false;
			bOldCollideActors = HitActor.bCollideActors;
			bOldBlockActors = HitActor.bBlockActors;
			if (HitActor.IsA('Pawn'))
			{
				// For pawns, we need to disable bCollideActors as well
				HitActor.SetCollision(false, false);

				// recurse another trace
				CalcWeaponFire(HitLocation, EndTrace, ImpactList, Extent);
			}
			else
			{
				if( bOldBlockActors )
				{
					HitActor.SetCollision(bOldCollideActors, false);
				}
				// recurse another trace and override CurrentImpact
				CurrentImpact = CalcWeaponFire(HitLocation, EndTrace, ImpactList, Extent);
			}

			// and reenable collision for the trigger
			HitActor.bProjTarget = true;
			HitActor.SetCollision(bOldCollideActors, bOldBlockActors);
		}
		else
		{
			// if we hit a PortalTeleporter, recurse through
			Portal = PortalTeleporter(HitActor);
			if( Portal != None && Portal.SisterPortal != None )
			{
				Dir = EndTrace - StartTrace;
				HitDist = VSize(HitLocation - StartTrace);
				// calculate new start and end points on the other side of the portal
				StartTrace = Portal.TransformHitLocation(HitLocation);
				EndTrace = StartTrace + Portal.TransformVectorDir(Normal(Dir) * (VSize(Dir) - HitDist));
				//@note: intentionally ignoring return value so our hit of the portal is used for effects
				//@todo: need to figure out how to replicate that there should be effects on the other side as well
				CalcWeaponFire(StartTrace, EndTrace, ImpactList, Extent);
			}
		}
	}

	return CurrentImpact;
}

 

simulated function InstantFire() 其中调用了 ProcessInstantHit()

原理:

对 玩家武器位置 以 GetAdjustedAim() 为方向 发射Trace(类似U3d Raycast)进行模拟武器伤害输出计算 得到 RealImpact

对RealImpact调用ProcessInstantHit,参数 <当前武器,RealImpact>

/**
 * Performs an 'Instant Hit' shot.
 * Also, sets up replication for remote clients,
 * and processes all the impacts to deal proper damage and play effects.
 *
 * Network: Local Player and Server
 */

simulated function InstantFire()
{
	local vector			StartTrace, EndTrace;
	local Array<ImpactInfo>	ImpactList;
	local int				Idx;
	local ImpactInfo		RealImpact;

	// define range to use for CalcWeaponFire()
	StartTrace = Instigator.GetWeaponStartTraceLocation();
	EndTrace = StartTrace + vector(GetAdjustedAim(StartTrace)) * GetTraceRange();

	// Perform shot
	RealImpact = CalcWeaponFire(StartTrace, EndTrace, ImpactList);

	if (Role == ROLE_Authority)
	{
/*		FlushPersistentDebugLines();
		DrawDebugSphere( StartTrace, 10, 10, 0, 255, 0 );
		DrawDebugSphere( EndTrace, 10, 10, 255, 0, 0 );
		DrawDebugSphere( RealImpact.HitLocation, 10, 10, 0, 0, 255 );
		`log( self@GetFuncName()@Instigator@RealImpact.HitLocation@RealImpact.HitActor );*/

		// Set flash location to trigger client side effects.
		// if HitActor == None, then HitLocation represents the end of the trace (maxrange)
		// Remote clients perform another trace to retrieve the remaining Hit Information (HitActor, HitNormal, HitInfo...)
		// Here, The final impact is replicated. More complex bullet physics (bounce, penetration...)
		// would probably have to run a full simulation on remote clients.
		SetFlashLocation(RealImpact.HitLocation);
	}

	// Process all Instant Hits on local player and server (gives damage, spawns any effects).
	for (Idx = 0; Idx < ImpactList.Length; Idx++)
	{
		ProcessInstantHit(CurrentFireMode, ImpactList[Idx]);
	}
}


simulated function ProcessInstantHit(byte FiringMode, ImpactInfo Impact, optional int NumHits)

作为子级被InstantFire()调用

作为父级被class'KFWeapon'::simulated function ProcessInstantHitEx(byte FiringMode, ImpactInfo Impact, optional int NumHits, optional out float out_PenetrationVal, optional int ImpactNum)调用,其中所有延续KFWeapon的类如KFWeap_MedicBase和KFWeap_Assaultrifle_Medic可以重写以实现自定义

原理:

对ImpactInfo::HitActor执行即刻的TakeDamage

/**
 * Processes a successful 'Instant Hit' trace and eventually spawns any effects.
 * Network: LocalPlayer and Server
 * @param FiringMode: index of firing mode being used
 * @param Impact: hit information
 * @param NumHits (opt): number of hits to apply using this impact
 * 			this is useful for handling multiple nearby impacts of multihit weapons (e.g. shotguns)
 *			without having to execute the entire damage code path for each one
 *			an omitted or <= 0 value indicates a single hit
 */
simulated function ProcessInstantHit(byte FiringMode, ImpactInfo Impact, optional int NumHits)
{
	local int TotalDamage;
	local KActorFromStatic NewKActor;
	local StaticMeshComponent HitStaticMesh;

	if (Impact.HitActor != None)
	{
		// default damage model is just hits * base damage
		NumHits = Max(NumHits, 1);
		TotalDamage = InstantHitDamage[CurrentFireMode] * NumHits;

		if ( Impact.HitActor.bWorldGeometry )
		{
			HitStaticMesh = StaticMeshComponent(Impact.HitInfo.HitComponent);
			if ( (HitStaticMesh != None) && HitStaticMesh.CanBecomeDynamic() )
			{
				NewKActor = class'KActorFromStatic'.Static.MakeDynamic(HitStaticMesh);
				if ( NewKActor != None )
				{
					Impact.HitActor = NewKActor;
				}
			}
		}
		Impact.HitActor.TakeDamage( TotalDamage, Instigator.Controller,
						Impact.HitLocation, InstantHitMomentum[FiringMode] * Impact.RayDir,
						InstantHitDamageTypes[FiringMode], Impact.HitInfo, self );
	}
}

--

 

大体思路:

进行Trace确定ImpactInfo,原理是运用CalcWeaponFire(),方法类似于InstantFire(),然后判断HitZoneIndex == HZI_Head是否成立以检测是否爆头

 

目前在BGWeap_Assaultrifle_Medic.uc中 爆头提示 的实现

class BGWeap_Assaultrifle_Medic extends KFWeap_MedicBase;

simulated function ProcessInstantHitEx( byte FiringMode, ImpactInfo Impact, optional int NumHits, optional out float out_PenetrationVal, optional int ImpactNum )
{
	local int HitZoneIndex;
	local KFPawn ImpactTarget;
	local KFPawn_Human HealTarget;
	local KFPawn_Monster KFPM_Victim;
	local KFPlayerController doer;
	local KFPlayerController KFPC;
	
	ImpactTarget=KFPawn(Impact.HitActor);
	HealTarget=KFPawn_Human(ImpactTarget);
	KFPM_Victim=KFPawn_Monster(ImpactTarget);
	doer=KFPlayerController(Instigator.Controller);
	
	if(KFPM_Victim!=None && !KFPM_Victim.bIsHeadless)
	{
		HitZoneIndex=KFPM_Victim.HitZones.Find('ZoneName', Impact.HitInfo.BoneName);
		if(HitZoneIndex == HZI_Head && KFPM != none && KFPM.IsAliveAndWell())
		{
			if(doer.GetPerk()==class'KFPerk_FieldMedic')
			{
				doer.ServerTeamSay("[HMT401_BUFF]Healing AOE Triggered");
				ForEach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
				{
					if(KFPC.Pawn!=None)
					{
						KFPC.Pawn.Health=Min(KFPC.Pawn.Health+1, KFPC.Pawn.HealthMax);
						KFPC.TeamMessage(KFPC.PlayerReplicationInfo,"[HMT401_BUFF]Getting healed +2 by headshots from "$doer.PlayerReplicationInfo.PlayerName, 'Event');
					}
				}
			}
		}
	}
	
	super.ProcessInstantHitEx(FiringMode, Impact, NumHits, out_PenetrationVal, ImpactNum);
}

defaultproperties
{
	// Healing charge
    HealAmount=20 //Changed from 15 to 20
	HealFullRechargeSeconds=10

	// Inventory
	InventorySize=7
	GroupPriority=100
	WeaponSelectTexture=Texture2D'ui_weaponselect_tex.UI_WeaponSelect_MedicAssault'
	SecondaryAmmoTexture=Texture2D'UI_SecondaryAmmo_TEX.MedicDarts'

	// Shooting Animations
	FireSightedAnims[0]=Shoot_Iron
	FireSightedAnims[1]=Shoot_Iron2
	FireSightedAnims[2]=Shoot_Iron3

    // FOV
    MeshFOV=75
	MeshIronSightFOV=52
    PlayerIronSightFOV=70

	// Depth of field
	DOF_FG_FocalRadius=85
	DOF_FG_MaxNearBlurSize=2.5

	Begin Object Name=FirstPersonMesh
		SkeletalMesh=SkeletalMesh'WEP_1P_Medic_Assault_MESH.Wep_1stP_Medic_Assault_Rig'
		AnimSets(0)=AnimSet'WEP_1P_Medic_Assault_ANIM.Wep_1stP_Medic_Assault_Anim'
	End Object

	Begin Object Name=StaticPickupComponent
		StaticMesh=StaticMesh'WEP_3P_Pickups_MESH.Wep_Medic_Assault_Pickup'
	End Object

	AttachmentArchetype=KFWeaponAttachment'WEP_Medic_Assault_ARCH.Wep_Medic_Assault_3P'

   	// Zooming/Position
	PlayerViewOffset=(X=15.0,Y=6.5,Z=-3)
	IronSightPosition=(X=12,Y=0,Z=0)

	// Ammo
	MagazineCapacity[0]=30
	SpareAmmoCapacity[0]=360
	InitialSpareMags[0]=3
	bCanBeReloaded=true
	bReloadFromMagazine=true

	// Recoil
	maxRecoilPitch=200
	minRecoilPitch=150
	maxRecoilYaw=175
	minRecoilYaw=-125
	RecoilRate=0.085
	RecoilMaxYawLimit=500
	RecoilMinYawLimit=65035
	RecoilMaxPitchLimit=900
	RecoilMinPitchLimit=65035
	RecoilISMaxYawLimit=75
	RecoilISMinYawLimit=65460
	RecoilISMaxPitchLimit=375
	RecoilISMinPitchLimit=65460
	IronSightMeshFOVCompensationScale=1.5

	// DEFAULT_FIREMODE
	FireModeIconPaths(DEFAULT_FIREMODE)=Texture2D'ui_firemodes_tex.UI_FireModeSelect_BulletAuto'
	FiringStatesArray(DEFAULT_FIREMODE)=WeaponFiring
	WeaponFireTypes(DEFAULT_FIREMODE)=EWFT_InstantHit
	WeaponProjectiles(DEFAULT_FIREMODE)=class'KFProj_Bullet_AssaultRifle'
	InstantHitDamageTypes(DEFAULT_FIREMODE)=class'KFDT_Ballistic_Assault_Medic'
	FireInterval(DEFAULT_FIREMODE)=+0.1 // 650 to 600
	Spread(DEFAULT_FIREMODE)=0.0085
	InstantHitDamage(DEFAULT_FIREMODE)=40
	FireOffset=(X=30,Y=4.5,Z=-5)

	// ALTFIRE_FIREMODE
	AmmoCost(ALTFIRE_FIREMODE)=25 //decrease 16.7%

	// BASH_FIREMODE
	InstantHitDamage(BASH_FIREMODE)=27
	InstantHitDamageTypes(BASH_FIREMODE)=class'KFDT_Bludgeon_Assault_Medic'

	// Fire Effects
	MuzzleFlashTemplate=KFMuzzleFlash'WEP_Medic_Assault_ARCH.Wep_Medic_Assault_MuzzleFlash'
	WeaponFireSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_SA_MedicAssault.Play_SA_MedicAssault_Fire_3P_Loop', FirstPersonCue=AkEvent'WW_WEP_SA_MedicAssault.Play_SA_MedicAssault_Fire_1P_Loop')
	WeaponFireSnd(ALTFIRE_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_SA_MedicAssault.Play_SA_MedicAssault_Fire_3P_Single', FirstPersonCue=AkEvent'WW_WEP_SA_MedicAssault.Play_SA_MedicAssault_Fire_1P_Single')
	WeaponFireLoopEndSnd(DEFAULT_FIREMODE)=(DefaultCue=AkEvent'WW_WEP_SA_MedicAssault.Play_SA_MedicAssault_Fire_3P_EndLoop', FirstPersonCue=AkEvent'WW_WEP_SA_MedicAssault.Play_SA_MedicAssault_Fire_1P_EndLoop')

	WeaponDryFireSnd(DEFAULT_FIREMODE)=AkEvent'WW_WEP_SA_MedicAssault.Play_SA_MedicAssault_Handling_DryFire'
	WeaponDryFireSnd(ALTFIRE_FIREMODE)=AkEvent'WW_WEP_SA_MedicDart.Play_WEP_SA_Medic_Dart_DryFire'

	// Advanced (High RPM) Fire Effects
	bLoopingFireAnim(DEFAULT_FIREMODE)=true
	bLoopingFireSnd(DEFAULT_FIREMODE)=true
	SingleFireSoundIndex=ALTFIRE_FIREMODE

	// Attachments
	bHasIronSights=true
	bHasFlashlight=true

   	AssociatedPerkClasses(0)=class'KFPerk_FieldMedic'
   	AssociatedPerkClasses(1)=class'KFPerk_Commando'
}

目前版本

//=============================================================================
// Healing Extend Mutator : Head Shot Recover
// This is the second mutator containing in HealingExtend Mut
// 
// This mutator provides you the possibility to recover Armour or Health 
//        while you just did a single head shot
//        
//        Attention:    You have to do a decap to get the effect !
//                    The mutator is supposed to install on a server which is
//                        having less than 16 players due to the structure and
//                        the process, I'm working on a better way to do it
//
// Code And Concept By ArHShRn
// http://steamcommunity.com/id/ArHShRn/
//=============================================================================
//struct native PostWaveReplicationInfo
//{
//    var Vector     VectData1; //used for compressing data //X:HeadShots Y:Dosh Earned Z:Damage Dealt
//    var Vector     VectData2;    //used for compressing data //Damage Taken, Heals Received, Heals Given
//
//    var byte    LargeZedKills;
//    //Dialog
//    var bool     bDiedDuringWave;
//    var bool    bBestTeammate;
//    var bool    bKilledMostZeds;
//    var bool    bEarnedMostDosh;
//    var bool    bAllSurvivedLastWave;
//    var bool    bSomeSurvivedLastWave;
//    var bool    bOneSurvivedLastWave;
//    var bool    bKilledFleshpoundLastWave;
//    var bool    bKilledScrakeLastWave;
//    /** Work-around so we don't have to wait for GRI.OpenTrader() to determine dialog */
//    var bool    bOpeningTrader;
//
//    var class< KFPawn_Monster > ClassKilledByLastWave;
//
//    var byte    RepCount;
//};
//=============================================================================

class HeadshotRecover extends KFMutator
    config(HealingExtend);

/* Every player in the game should have a Healing Extend structure
    to restore the info he has
*/
    struct HEPlayer
{
    var Pawn                    pShotTarget;            //    A shot target pawn he owns, Use to avoidi checking ShotTarget frequently
    var Pawn                    LastTarget;                //    His last zed target
    var KFPlayerController        KFPC;                    //    His KFPlayerController class
    var KFPawn_Monster            KFPM_Victim;            //    Zed victim who damaged by him
    var KFPawn_Human            KFPH;                    //    His KFPawn_Human
    
    var int                        Index;                    //  Shows his Index
    var int                        HeadshotsInLogTime;        //  How many head shots are done by him in dLogTime
    var int                        TotalHsThisWave;        //  How many head shots are done by him in this wave
    var int                        TotalHsThisZedTime;        //  How many head shots are done bt him in zed time
};
    
    //System
var config int                dLogTime;                // Set how much time to log the headshot been done and health been healed
var config bool                bEnableProcessFreqcy;    // Set if it's enabled to limit healing frequency
var config float            fHealingFreq;            // Set how much time (seconds) to process each healing of health or armour
var config bool                bEnableHeadshotCount;    // Set if it's enabled to see how many head shots are done by him every dLogTime
//var config bool            bEnableHeadshotSort;    // Set if it's enabled to sort and detect him who shoots most headshoots in dLogTime
//var config bool            bEnableFirstScoreBonus;    // Set if it's enabled to add bonus health to him who shoots most headshoots in dLogTime
var config bool                bAllowOverClocking;        // Set if it's enabled to get beyond the max health or armor
var config bool                bInitedConfig;            // If you want to restore the default setting plz set this to False
var config bool                bRecoverAmmo;            // Set if it;s enabled to recover ammo if he does a decap
var config bool                bGetDosh;                // Set if it's enabled to get bonus dosh if he does a decap
var    bool                    bClearZedTime;            // To check if it's ZedTime clear or not
var bool                    bHLFlag;                // If it's true, then process healing function
var bool                    bLogTHTW_Flag;            //A flag to check if it's time to log TotalHsThisWave

    //GamePlay
var array<HEPlayer>            Players;
var HEPlayer                EmptyInstance;
var int                        PlayerNumber;            // How many players are in the game
var bool                    bIsWaveEnded;
     
    //Settings
var config int                HealthHealingAmount;    // How much health to heal when he does a headshot
var config int                ArmourHealingAmount;    // How much armour to heal when he does a headshot
var config int                AmmoRecoverAmout;        // How much ammo to recover when he does a headshot
var config int                BonusDosh;                // How much dosh to give when he does a headshot
var config int                HealingMode;            // 0 for both, 1 for health only, 2 for armour only
var config int                OverclockLimitHealth;    // The maximum health he can get in Overclocking mode
var config int                OverclockLimitArmour;    // The maximum armour he can get in Overclocking mode
//var config int            dFirstScoreBonus;        // Set bonus ammount

function InitMutator(string Options, out string ErrorMessage)
{
    if(!bInitedConfig)
    {
        InitBasicMutatorValues();
        SaveConfig();
    }
    super.InitMutator( Options, ErrorMessage );
}

function PostBeginPlay()
{    
    /*Init basic values which are not in config*/
    PlayerNumber=0;
    bIsWaveEnded=False;
    bHLFlag=False;
    bLogTHTW_Flag=True;
    //Add empty Instance into Players Array
    //In order to make array's Index equal to player's Index
    InitPlayersArry();


    //Healing Limit Freq
    if(bEnableProcessFreqcy)
        SetTimer(fHealingFreq, True, 'SetHLimitFlag');
    
    //Headshot counter
    if(bEnableHeadshotCount)
        SetTimer(dLogTime, True, 'LogHeadshots');
        
    //Logger
    //SetTimer(60, True, 'LogMutStat');
    //SetTimer(bHEZedArryClearDura, True, 'ClearDeadZeds');
    
    super.PostBeginPlay();
}

function ModifyPlayer(Pawn Other)
{    
    //1.Re-initialize Players Array, Check if he exists in the game
    ReInitPlayersArry(Other);
    
    //2.Add this player in to Players array if he's new in this game
    AddHimIntoPlayers(Other);
    
    super.ModifyPlayer(Other);
}

//Initialize basic config default values used in the mutator
//Author recommended values, plz do not edit
function InitBasicMutatorValues()
{
    //System
    dLogTime=30;
    bEnableProcessFreqcy=True; 
    fHealingFreq=0.25; 
//    bEnableHeadshotMsg;
    bEnableHeadshotCount=False;
//    bEnableHeadshotSort;
//    bEnableFirstScoreBonus;
    bAllowOverClocking=False;
    bClearZedTime=True;
    bInitedConfig=True;
    bRecoverAmmo=True;
    bGetDosh=True;

    //Settings
    HealthHealingAmount=3; 
    ArmourHealingAmount=5;
    AmmoRecoverAmout=1; //Means he will not cost ammo if he did a decap
    BonusDosh=50; 
    HealingMode=0; 
    OverclockLimitHealth=175; 
    OverclockLimitArmour=200; 
//    dFirstScoreBonus; 
}

//Set Flag to limit healing frequency
function SetHLimitFlag()
{
    bHLFlag=True;
}

//Return true if player is already in game
//Use to detect if player is died last wave
function bool isAlreadyInGame(Pawn P, optional out int Index)
{
    local int                            i;
    local KFPlayerController            KFPC;
    KFPC = KFPlayerController(P.controller);
    
    for(i=0;i<Players.Length;++i)
    {
        if(Players[i].KFPC==KFPC)
        {
            Index=i;
            return true;
        }
    }
    return false;
}

//Log headshots each player in dLogTime
function LogHeadshots()
{
    local int i;
    for(i=1; i<=PlayerNumber; ++i)
    {
        Players[i].KFPC.ServerSay("["$Players[i].HeadshotsInLogTime$"] Headshots");
        Players[i].HeadshotsInLogTime=0;
        `Log(
            "[ArHShRn Mutators]HeadshotRecover: HeadshotsInLogTime["
            $i
            $"] Reset"
            );
    }
}

//Mutator Stat Logger
function LogMutStat()
{
    `Log("[ArHShRn Mutators]HeadshotRecover: dLogTime="$dLogTime);
    `Log("[ArHShRn Mutators]HeadshotRecover: bEnableHeadshotCount="$bEnableHeadshotCount);
    `Log("[ArHShRn Mutators]HeadshotRecover: HealingMode="$HealingMode);
    `Log("[ArHShRn Mutators]HeadshotRecover: HealthHealingAmount="$HealthHealingAmount);
    `Log("[ArHShRn Mutators]HeadshotRecover: ArmourHealingAmount="$ArmourHealingAmount);
    //`Log("[ArHShRn Mutators]HeadshotRecover: bEnableFirstScoreBonus="$bEnableFirstScoreBonus);
    //`Log("[ArHShRn Mutators]HeadshotRecover: dFirstScoreBonus="$dFirstScoreBonus);
}

//Re-initialize Players Array
//Check if there's player left the game
function ReInitPlayersArry(Pawn P=None)
{
    //local int                        i;
    local int                        InGamePlayerIndex;
    local KFPawn_Human                PlayerKFPH;
    //local KFPawn_Human                KFPH;
    //local array<KFPawn_Human>        ArrayKFPH;
    //local KFPlayerController        KFPC;
    local KFPlayerController        PlayerKFPC;
    PlayerKFPH=KFPawn_Human(P);
    PlayerKFPC=KFPlayerController(P.Controller);
    
    ////1.Add all KFPH in the world to ArrayKFPH
    //ForEach WorldInfo.AllPawns(class'KFPawn_Human', KFPH)
        //ArrayKFPH.AddItem(KFPH);
        
    //2.Update Player number each player restarted
    //PlayerNumber=ArrayKFPH.Length;
    
    //When the function is called without Pawn, just do 1.2. stuffs
    if(P!=None)
    {
        //3.If player died last wave, update Player Info (Like KFPH)
        if(isAlreadyInGame(P, InGamePlayerIndex)) //If his KFPC is already in game (like he died last wave)
        {
            //Update his new KFPH into the array, to avoid failing
            Players[InGamePlayerIndex].KFPH=PlayerKFPH;
            `Log("["$InGamePlayerIndex$"]"$" Respawned and Pawn updated");
            PlayerKFPC.ServerSay
                (
                "No."
                $InGamePlayerIndex
                $" Player Respawned "
                );
        }
        
        ////4.Check if there's a player left game
        //for(i=1;i<Players.Length;++i)
        //{
            ////If there is, remove it
            //if(ArrayKFPH.Find(,Players[i].KFPH)==-1)
            //{
                //Players.RemoveItem(Players[i]);
                //`Log("[ArHShRn Mutators]HeadshotRecover: Removed A Left Player");
            //}
        //}
    
        ////5.Re-assign players' index
        //ForEach WorldInfo.AllControllers(class'KFPlayerController', KFPC)
        //{
            ////ForEach HEPlayer in Players Array
            //for(i=1;i<Players.Length;++i)
            //{
                ////if find him in Players Array
                //if(KFPC==Players[i].KFPC)
                //{
                    //Players[i].Index=i;
                    //`Log("[ArHShRn Mutators]HeadshotRecover:"$Players[i].KFPC.PlayerReplicationInfo.PlayerName$"Re-Assigned Index With ["$i$"]");
                    //break;
                //}
            //}
        //}
    }
}

//To add a new player into Players Array
//if player is died last wave, update his info to the array
function AddHimIntoPlayers(Pawn P)
{
    local HEPlayer                instance;
    local KFPlayerController    PlayerKFPC;
    local KFPawn_Human            PlayerKFPH;
    local int                    PlayerIndex;
    local int                    InGamePlayerIndex;
    //local int                    LastTotalKill;

    PlayerKFPC=KFPlayerController(P.Controller);
    PlayerKFPH=KFPawn_Human(P);
    if(PlayerKFPC==None || PlayerKFPH==None) //if he's not Human, return
        return;
    
    if(isAlreadyInGame(P, InGamePlayerIndex)) //If his KFPC is already in game (like he died last wave)
        return;
    
    //If he's a new player to game
    ++PlayerNumber; // player number +1
    PlayerIndex=PlayerNumber; // Nth player's index is N because there's an empty instance in array[0];
    
    instance.KFPC=PlayerKFPC;
    instance.KFPH=PlayerKFPH;
    Players.AddItem(instance);

    Players[PlayerIndex].Index=PlayerIndex;
    Players[PlayerIndex].KFPC.ServerSay
        (
            "The No."$PlayerIndex
            $" Player"$Players[PlayerIndex].KFPC.PlayerReplicationInfo.PlayerName
            $" Joins Game!"
        );
}

//Initialize Players Array, add a Null instance into it
function InitPlayersArry()
{
    EmptyInstance.KFPC=None;
    EmptyInstance.LastTarget=None;
    EmptyInstance.KFPM_Victim=None;
    EmptyInstance.pShotTarget=None;
    EmptyInstance.KFPH=None;
    EmptyInstance.Index=0;
    EmptyInstance.HeadshotsInLogTime=0;
    EmptyInstance.TotalHsThisWave=0;
    //EmptyInstance.TotalHsThisRound=0;
    EmptyInstance.TotalHsThisZedTime=0;
    Players.AddItem(EmptyInstance);
}

//Return true if this Pawn is his LastTarget
simulated function bool isSameTarget(int PlayerIndex, Pawn P)
{
    return P==Players[PlayerIndex].LastTarget;
}

//Log Total Headshots This Wave
function TotalHSTW()
{
    local int i;
    for(i=1; i<=Players.Length; ++i)
    {
        Players[i].KFPC.ServerSay
        (
            "["
            $Players[i].TotalHsThisWave
            $"]Headshots Done"
        );
        Players[i].TotalHsThisWave=0;
    }
}

Event Tick(float DeltaTime)
{
    local int                    i;
    local KFWeapon                KFWeap;
    //local KFPerk                KFP;
    
    //If wave is ended
    if(!MyKFGI.IsWaveActive())
    {
        if(bLogTHTW_Flag)
        {
            SetTimer(1, false, 'TotalHSTW');
            ReInitPlayersArry();
        }
        bLogTHTW_Flag=False;
        return;
    }
    bLogTHTW_Flag=True;
    
    //ForEach Player in Players Array
    for(i=1; i<=PlayerNumber; ++i)
    {
        //Set his pShotTarget to his ShotTarget
        Players[i].pShotTarget=Players[i].KFPC.ShotTarget;
        
        //If he's not shooting a target, continue to check next player
        if(Players[i].pShotTarget==None)
            continue;
        
        //If his ShotTarget is not the LastTarget
        if(!isSameTarget(i, Players[i].pShotTarget))
            //Set his LastTarget to ShotTarget
            Players[i].LastTarget=Players[i].pShotTarget; 
            
        //KFPawn_Monster victim he owns is his monster shooting target
        Players[i].KFPM_Victim=KFPawn_Monster(Players[i].pShotTarget);
        
        //If he's not shooting a monster (like shooting a KFHealing_Dart to teammates)
        //Continue to check next player
        if(Players[i].KFPM_Victim==None)
            continue;
        
        //If his KFPM_Victim's head health <=0, which means its head is been shot and dismembered
        if(Players[i].KFPM_Victim.HitZones[HZI_HEAD].GoreHealth<=0 && bHLFlag)
        {
            
            //    A simulated function can't exec so I put it here for a short time
            /* Main Function */
            //0 for both
            if(HealingMode==0)
            {    
                if(bAllowOverClocking)
                {
                    Players[i].KFPC.Pawn.Health=Min(Players[i].KFPC.Pawn.Health+HealthHealingAmount, OverclockLimitHealth);
                    Players[i].KFPH.Armor=Min(Players[i].KFPH.Armor+ArmourHealingAmount, OverclockLimitArmour);
                }
                else
                {
                    Players[i].KFPC.Pawn.Health=Min
                    (
                    Players[i].KFPC.Pawn.Health+HealthHealingAmount, 
                    Players[i].KFPC.Pawn.HealthMax
                    );
                
                    //need to know max armor ammount
                    Players[i].KFPH.Armor=Min(Players[i].KFPH.Armor+ArmourHealingAmount, 175);
                }
            }
            //1 for health only
            if(HealingMode==1)
            {
                if(bAllowOverClocking)
                {
                    Players[i].KFPC.Pawn.Health=Min(Players[i].KFPC.Pawn.Health+HealthHealingAmount, OverclockLimitHealth);
                }
                else
                {
                    Players[i].KFPC.Pawn.Health=Min
                    (
                    Players[i].KFPC.Pawn.Health+HealthHealingAmount, 
                    Players[i].KFPC.Pawn.HealthMax
                    );
                }
            }
            //2 for armor only
            if(HealingMode==2)
            {
                if(bAllowOverClocking)
                {
                    Players[i].KFPH.Armor=Min(Players[i].KFPH.Armor+ArmourHealingAmount, OverclockLimitArmour);
                }
                else
                {
                    //need to know max armor ammount
                    Players[i].KFPH.Armor=Min(Players[i].KFPH.Armor+ArmourHealingAmount, 175);
                }
            }
            
            //Recover ammo if takes a decap
            if(bRecoverAmmo)
            {
                KFWeap=KFWeapon(Players[i].KFPC.Pawn.Weapon);
                if(KFWeap!=None)
                {
                    KFWeap.AmmoCount[KFWeap.GetAmmoType(KFWeap.CurrentFireMode)]=Min
                    (
                        KFWeap.AmmoCount[KFWeap.GetAmmoType(KFWeap.CurrentFireMode)]+AmmoRecoverAmout,
                        KFWeap.MagazineCapacity[KFWeap.GetAmmoType(KFWeap.CurrentFireMode)]
                    );
                }
            }
            //if(bGetDosh)
            //{
                //
            //}
            
            //Add one shot in his HeadshotsInLogTime and TotalHsThisWave
            ++Players[i].HeadshotsInLogTime;
            ++Players[i].TotalHsThisWave;
            
            /* Record how many headshots are done by him in Zed Time */
            bClearZedTime=True;
            if(`IsInZedTime(self))
            {
                bClearZedTime=False;
                ++Players[i].TotalHsThisZedTime;
                Players[i].KFPC.ServerSay(Players[i].TotalHsThisZedTime$" Combo in ZedTime!");
            }
            if(bClearZedTime)
            {
                Players[i].TotalHsThisZedTime=0;
            }
            
            
            /* Clear flags */
            Players[i].pShotTarget=None;
            Players[i].KFPC.ShotTarget=None;    //Last Zed is killed, avoiding continiously healing
            bHLFlag=False;    //Disable healing process
        }
    }
    super.Tick(DeltaTime);
}

defaultproperties
{
}

 

参考Instant Healing Mut

http://www.cnblogs.com/ArHShRn/p/7140193.html

posted @ 2017-07-09 01:41  ArHShRn  阅读(600)  评论(0编辑  收藏  举报