void ASWeaponInstant::FireWeapon()
{
    const FVector AimDir = GetAdjustedAim();
    const FVector CameraPos = GetCameraDamageStartLocation(AimDir);
    const FVector EndPos = CameraPos + (AimDir * WeaponRange);

    /* Check for impact by tracing from the camera position */
    FHitResult Impact = WeaponTrace(CameraPos, EndPos);

    const FVector MuzzleOrigin = GetMuzzleLocation();

    FVector AdjustedAimDir = AimDir;
    if (Impact.bBlockingHit)
    {
        /* Adjust the shoot direction to hit at the crosshair. */
        AdjustedAimDir = (Impact.ImpactPoint - MuzzleOrigin).GetSafeNormal();

        /* Re-trace with the new aim direction coming out of the weapon muzzle */
        Impact = WeaponTrace(MuzzleOrigin, MuzzleOrigin + (AdjustedAimDir * WeaponRange));
    }
    else
    {
        /* Use the maximum distance as the adjust direction */
        Impact.ImpactPoint = FVector_NetQuantize(EndPos);
    }

    ProcessInstantHit(Impact, MuzzleOrigin, AdjustedAimDir);
}
void ASWeaponInstant::FireWeapon()
{	
	const FVector AimDir = GetAdjustedAim();
	const FVector StartPos = GetCameraDamageStartLocation(AimDir);
	const FVector EndPos = StartPos + (AimDir * WeaponRange);

	const FHitResult Impact = WeaponTrace(StartPos, EndPos);
	ProcessInstantHit(Impact, StartPos, AimDir);
}
void AAmethystWeapon_Projectile::FireWeapon()
{
    FVector ShootDir = GetAdjustedAim();
    FVector Origin = GetMuzzleLocation();
    
    // trace from camera to check what's under crosshair
    const float ProjectileAdjustRange = 10000.0f;
    const FVector StartTrace = GetCameraDamageStartLocation(ShootDir);
    const FVector EndTrace = StartTrace + ShootDir * ProjectileAdjustRange;
    FHitResult Impact = WeaponTrace(StartTrace, EndTrace);
    
    // and adjust directions to hit that actor
    if (Impact.bBlockingHit)
    {
        const FVector AdjustedDir = (Impact.ImpactPoint - Origin).SafeNormal();
        bool bWeaponPenetration = false;
        
        const float DirectionDot = FVector::DotProduct(AdjustedDir, ShootDir);
        if (DirectionDot < 0.0f)
        {
            // shooting backwards = weapon is penetrating
            bWeaponPenetration = true;
        }
        else if (DirectionDot < 0.5f)
        {
            // check for weapon penetration if angle difference is big enough
            // raycast along weapon mesh to check if there's blocking hit
            
            FVector MuzzleStartTrace = Origin - GetMuzzleDirection() * 150.0f;
            FVector MuzzleEndTrace = Origin;
            FHitResult MuzzleImpact = WeaponTrace(MuzzleStartTrace, MuzzleEndTrace);
            
            if (MuzzleImpact.bBlockingHit)
            {
                bWeaponPenetration = true;
            }
        }
        
        if (bWeaponPenetration)
        {
            // spawn at crosshair position
            Origin = Impact.ImpactPoint - ShootDir * 10.0f;
        }
        else
        {
            // adjust direction to hit
            ShootDir = AdjustedDir;
        }
    }
    
    ServerFireProjectile(Origin, ShootDir);
}
void AShooterWeapon_Instant::FireWeapon()
{
    const int32 RandomSeed = FMath::Rand();
    FRandomStream WeaponRandomStream(RandomSeed);
    const float CurrentSpread = GetCurrentSpread();
    const float ConeHalfAngle = FMath::DegreesToRadians(CurrentSpread * 0.5f);

    const FVector AimDir = GetAdjustedAim();
    const FVector StartTrace = GetCameraDamageStartLocation(AimDir);
    const FVector ShootDir = WeaponRandomStream.VRandCone(AimDir, ConeHalfAngle, ConeHalfAngle);
    const FVector EndTrace = StartTrace + ShootDir * InstantConfig.WeaponRange;

    const FHitResult Impact = WeaponTrace(StartTrace, EndTrace);
    ProcessInstantHit(Impact, StartTrace, ShootDir, RandomSeed, CurrentSpread);

    CurrentFiringSpread = FMath::Min(InstantConfig.FiringSpreadMax, CurrentFiringSpread + InstantConfig.FiringSpreadIncrement);
}