I have a Brute problem.
For EXU, I've rewritten all of the fire offsets for pawns, using a
Location + (vect(x,y,z) * DrawScale>>Rotation) system instead of the collision-cylinder-based fire offset seen in ScriptedPawn.FireProjectile(). I do this so I can have more accurate fire offsets based on the pawn's animations, more flexible projectile accuracy controls, custom muzzle flashes, etc. This also prevents weird offsets if the collision cylinder is bigger than normal. It looks pretty good ingame and is working fine for most pawns, but Brutes have a problem: they can shoot through walls and doors in some cases.
Location 1: This is the current, new fire offset for spawning the left shot in StillFire mode: vect( 134, -29, 30 )
Location 2: This is the original, collision-based fire offset (calculated to the actual location), used for all instances of SpawnLeftShot: vect( 62.4, -36.4, 20.8 )
Location 3: This is the CanFireAtEnemy offset used for tracing. It was also collision-based, but I am now using an actual offset calculated from the original formula: vect( 26, -42, 20.8 )
THE PROBLEM:As you can see, the fire offset is pretty far ahead of the collision cylinder by default, but it was also somewhat ahead even in Unreal based on the original offset. However, if a Brute is partially obscured by a thin wall or a door, it can fire "through" the door since the fire offset will be on one side while the collision cylinder will be on the other. Is there a way to rectify this?
I've considered doing a trace or a fasttrace in CanFireAtEnemey from Location 3 to Location 1 to verify that the pawn should be able to shoot, but don't know if this would be overly expensive in terms of performance. I really would like to keep the fire offsets where they visually should be, as the muzzle flashes look good spawned there. But if nothing else works, I can always make the muzzle flashes spawn at Location 1 and have the projectiles spawn at Location 2.
Here's the original Brute fire functions, for reference:
[spoiler]
Code: Select all
//=======================================================
function SpawnLeftShot()
{
FireProjectile( vect(1.2,0.7,0.4), 750);
}
function SpawnRightShot()
{
FireProjectile( vect(1.2,-0.7,0.4), 750);
}
function GutShotTarget()
{
FireProjectile( vect(1.2,-0.55,0.0), 800);
}
//=======================================================
[/spoiler]
And here's the original ScriptedPawn.FireProjectile function for reference:
[spoiler]
Code: Select all
//=======================================================
final function FireProjectile(vector StartOffset, float Accuracy)
{
local vector X,Y,Z, projStart;
MakeNoise(1.0);
GetAxes(Rotation,X,Y,Z);
projStart = Location + StartOffset.X * CollisionRadius * X + StartOffset.Y * CollisionRadius * Y + StartOffset.Z * CollisionRadius * Z;
spawn(RangedProjectile ,self,'',projStart,AdjustAim(ProjectileSpeed, projStart, Accuracy, bLeadTarget, bWarnTarget));
}
//=======================================================
[/spoiler]
Also: the CanFireAtEnemy function for the Brute. Commented is the original collision-based calculation, and the current code uses the actual position times drawscale (note that I removed some local vars that weren't needed with the actual offsets in use instead of getting the offset from collision):
[spoiler]
Code: Select all
//=======================================================
function bool CanFireAtEnemy()
{
local vector HitLocation, HitNormal, projStart, EnemyDir, EnemyUp;
local actor HitActor1, HitActor2;
local float EnemyDist;
EnemyDir = Enemy.Location - Location;
EnemyDist = VSize(EnemyDir);
EnemyUp = Enemy.CollisionHeight * vect(0,0,0.9);
if ( EnemyDist > 300 )
{
EnemyDir = 300 * EnemyDir/EnemyDist;
EnemyUp = 300 * EnemyUp/EnemyDist;
}
projStart = Location + ( vect(26, 41.6, 20.8) * DrawScale>>Rotation ); // Right Shot
HitActor1 = Trace(HitLocation, HitNormal, projStart + EnemyDir + EnemyUp, projStart, true);
if ( (HitActor1!=Enemy) && (Pawn(HitActor1)!=None) && (AttitudeTo(Pawn(HitActor1)) > ATTITUDE_Ignore) )
return false;
projStart = Location + ( vect(26, -41.6, 20.8) * DrawScale>>Rotation ); // Left Shot
HitActor2 = Trace(HitLocation, HitNormal, projStart + EnemyDir + EnemyUp, projStart, true);
if ( (HitActor2 == None) || (HitActor2 == Enemy) || ((Pawn(HitActor2) != None) && (AttitudeTo(Pawn(HitActor2)) <= ATTITUDE_Ignore)) )
return true;
HitActor2 = Trace(HitLocation, HitNormal, projStart + EnemyDir, projStart , true);
return ( (HitActor2 == None) || (HitActor2 == Enemy) || ((Pawn(HitActor2) != None) && (AttitudeTo(Pawn(HitActor2)) <= ATTITUDE_Ignore)) );
/*
GetAxes(Rotation,X,Y,Z);
projStart = Location + 0.5 * CollisionRadius * X + 0.8 * CollisionRadius * Y + 0.4 * CollisionRadius * Z;
HitActor1 = Trace(HitLocation, HitNormal, projStart + EnemyDir + EnemyUp, projStart, true);
if ( (HitActor1 != Enemy) && (Pawn(HitActor1)!=None) && (AttitudeTo(Pawn(HitActor1)) > ATTITUDE_Ignore) )
return false;
projStart = Location + 0.5 * CollisionRadius * X - 0.8 * CollisionRadius * Y + 0.4 * CollisionRadius * Z;
HitActor2 = Trace(HitLocation, HitNormal, projStart + EnemyDir + EnemyUp, projStart, true);
if ( (HitActor2 == None) || (HitActor2 == Enemy) || ((Pawn(HitActor2) != None) && (AttitudeTo(Pawn(HitActor2)) <= ATTITUDE_Ignore)) )
return true;
HitActor2 = Trace(HitLocation, HitNormal, projStart + EnemyDir, projStart , true);
return ( (HitActor2 == None) || (HitActor2 == Enemy) || ((Pawn(HitActor2) != None) && (AttitudeTo(Pawn(HitActor2)) <= ATTITUDE_Ignore)) );
*/
}
//=======================================================
[/spoiler]