首页 > 代码库 > [Developing]HeadshotRecover Mut - 爆头回血插件 - Killing Floor 2

[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‘
}

  

参考Instant Healing Mut

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

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