Fix bomb damage radius

* Change version to 1.3.0.3
* Fix bomb radius not accurate by using a box
* When creating a point somewhere we now store its in-game coordinates for the bomb intersection test
* SpatialPartitioningHelper.cs is not yet used, except for the BoxIntersects-Method
*
This commit is contained in:
Mathias Lui 2022-11-27 22:03:13 +01:00
parent 4fe796769e
commit 3e94f4c622
4 changed files with 231 additions and 6 deletions

View file

@ -11,8 +11,8 @@
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>27.ico</ApplicationIcon>
<FileVersion>1.3.0.1</FileVersion>
<AssemblyVersion>1.3.0.1</AssemblyVersion>
<FileVersion>1.3.0.3</FileVersion>
<AssemblyVersion>1.3.0.3</AssemblyVersion>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Properties\AssemblyInfo.cs" />

View file

@ -32,6 +32,12 @@ namespace Damage_Calculator
private readonly string placeholderText = "None";
private static readonly double eyeLevelStanding = 64.093811;
private static readonly double eyeLevelCrouching = 46.076218;
private static readonly int heightStanding = 72;
private static readonly int heightCrouching = 54;
private static readonly int playerWidth = 32;
/// <summary>
/// Holds current in-game mouse coordinates when hovering over the map
/// </summary>
@ -1026,6 +1032,7 @@ namespace Damage_Calculator
// left and right point for the X and Y coordinates (in pixels) so we gotta convert those
Point[] points = this.connectingLine.Tag as Point[];
// These are not in-game coordinates yet, but the offset from the top left -- but in units instead of pixels
double leftX = this.getUnitsFromPixels(points[0].X);
double leftY = this.getUnitsFromPixels(points[0].Y);
double leftZ;
@ -1034,10 +1041,29 @@ namespace Damage_Calculator
double rightY = this.getUnitsFromPixels(points[1].Y);
double rightZ;
// Make the left and right point in-game coordinates accessible to everyone outside this function
Point playerCoords = this.getGameCoordsFromPoint(points[1].X, points[1].Y);
this.playerPoint.X = playerCoords.X;
this.playerPoint.Y = playerCoords.Y;
Point targetOrBombCoords = this.getGameCoordsFromPoint(points[0].X, points[0].Y);
if (this.bombPoint != null)
{
this.bombPoint.X = targetOrBombCoords.X;
this.bombPoint.Y = targetOrBombCoords.Y;
}
if(this.targetPoint != null)
{
this.targetPoint.X = targetOrBombCoords.X;
this.targetPoint.Y = targetOrBombCoords.Y;
}
// Manage height
if (this.playerPoint.AssociatedAreaID < 0 ||
((this.DrawMode == eDrawMode.Shooting && this.targetPoint.AssociatedAreaID < 0)
|| (this.DrawMode == eDrawMode.Bomb && this.bombPoint.AssociatedAreaID < 0)))
{
// One of the points has no area ID, and thus no Z coordinate
leftZ = 0;
rightZ = 0;
}
@ -1051,13 +1077,14 @@ namespace Damage_Calculator
double diffPixels2D = Math.Sqrt(Math.Pow(leftX - rightX, 2) + Math.Pow(leftY - rightY, 2));
double unitsDifference2D = this.getUnitsFromPixels(diffPixels2D);
if (this.DrawMode == eDrawMode.Bomb)
{
// Add the appropriate eye level
if (radioPlayerStanding.IsChecked == true)
rightZ += 64.093811;
rightZ += eyeLevelStanding;
else if(radioPlayerCrouched.IsChecked == true)
rightZ += 46.076218;
rightZ += eyeLevelCrouching;
}
// Add Z height to calculation, unless a point has no area ID associated, then it stays 2D
@ -1172,11 +1199,46 @@ namespace Damage_Calculator
private void calculateAndUpdateBombDamage()
{
// This is the maximum damage, and the height of the bell curve
double flDamage = this.loadedMap.BombDamage; // 500 is hard-coded as the default, and also the default in the hammer editor, can be overridden as a map creator
double flBombRadius = flDamage * 3.5d;
// First we need to check if the player is within the bomb radius, this isn't done with a circle, but with a box that has a side length of 2r
// So basically it's the bounding box, so if you're not directly above, below, left or right of the bomb, the radius increases a bit
// Also its calculated via the bounding box of the player which is 32x32 units in the horizontal axes
// Get mins and maxs of player hitbox
// Mins is X - 16, Y - 16, Z
Vector3 playerMins = new Vector3 { X = (float)(this.playerPoint.X - (playerWidth / 2d)), Y = (float)(this.playerPoint.Y - (playerWidth / 2d)), Z = (float)this.playerPoint.Z };
// Head height is not eye level, crouching is smaller, otherwise use standing height
float headHeight = (float)(this.radioPlayerCrouched.IsChecked == true ? heightCrouching : heightStanding);
// Maxs is X + 16, Y + 16, Z + head height
Vector3 playerMaxs = new Vector3 { X = (float)(this.playerPoint.X + (playerWidth / 2d)), Y = (float)(this.playerPoint.Y + (playerWidth / 2d)), Z = (float)this.playerPoint.Z + headHeight };
// Get mins and maxs of bomb radius
Vector3 bombMins = new Vector3 { X = (float)(this.bombPoint.X - flBombRadius), Y = (float)(this.bombPoint.Y - flBombRadius), Z = (float)(this.bombPoint.Z - flBombRadius) };
Vector3 bombMaxs = new Vector3 { X = (float)(this.bombPoint.X + flBombRadius), Y = (float)(this.bombPoint.Y + flBombRadius), Z = (float)(this.bombPoint.Z + flBombRadius) };
// Check if the two boxes intersect
bool playerIsInRange = SpatialPartitioningHelper.BoxIntersects(bombMins, bombMaxs, playerMins, playerMaxs);
if (!playerIsInRange)
{
txtResult.Text = txtResultArmor.Text = "0";
return;
}
// Now we can calculate the damage...
const double damagePercentage = 1.0d;
double flDamage = this.loadedMap.BombDamage; // 500 - default, if radius is not written on the map https://i.imgur.com/mUSaTHj.png
double flBombRadius = flDamage * 3.5d;
// From player origin + eye level, to the bomb
double flDistanceToLocalPlayer = (double)this.unitsDistance;// ((c4bomb origin + viewoffset) - (localplayer origin + viewoffset))
// This defines the width of the curve, a smaller value gives a steeper curve and a faster falloff
double fSigma = flBombRadius / 3.0d;
double fGaussianFalloff = Math.Exp(-flDistanceToLocalPlayer * flDistanceToLocalPlayer / (2.0d * fSigma * fSigma));
double flAdjustedDamage = flDamage * fGaussianFalloff * damagePercentage;

View file

@ -0,0 +1,150 @@
using SteamShared.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.RightsManagement;
using System.Text;
using System.Threading.Tasks;
namespace Damage_Calculator
{
internal class Voxel
{
private uint x;
private uint y;
private uint z;
private uint voxelId;
public uint X
{
get => this.x;
set
{
this.x = value & 0x7FF; // Maximum value is 11 bits, make double sure
this.updateVoxelId();
}
}
public uint Y
{
get => this.y;
set
{
this.y = value & 0x7FF; // Maximum value is 11 bits, make double sure
this.updateVoxelId();
}
}
public uint Z
{
get => this.z;
set
{
this.z = value & 0x3FF; // Maximum value is 10 bits, make double sure
this.updateVoxelId();
}
}
public uint VoxelId
{
get => this.voxelId;
set
{
this.voxelId = value;
// VoxelID consists of X=11, Y=11 and Z=10 bits in little endian like this ZZZZZZZZZZYYYYYYYYYYYXXXXXXXXXXX
// so extract them
// No need to bitshift since it's already on the lowest bit, but & it so that Y and Z are 0
this.x = value & 0x7FF;
// & it to only get the Y value in the middle and then shift it to the rightmost spot
this.y = (value & 0x3FF800) >> 11;
// This has no values higher than it so no & is needed, but shift it to the rightmost spot, the lower values will be discarded anyways
this.z = value >> 22;
}
}
private void updateVoxelId()
{
this.voxelId = 0;
this.voxelId |= this.x;
this.voxelId |= this.y << 11;
this.voxelId |= this.z << 22;
}
}
internal static class SpatialPartitioningHelper
{
const int MAX_COORD_INTEGER = 16384;
const int MIN_COORD_INTEGER = -MAX_COORD_INTEGER;
const float MAX_COORD_FLOAT = MAX_COORD_INTEGER;
const float MIN_COORD_FLOAT = -MAX_COORD_FLOAT;
const int COORD_EXTENT = 2 * MAX_COORD_INTEGER;
const int SPHASH_LEVEL_SKIP = 2;
const int SPHASH_VOXEL_SIZE = 256; // Must be power of 2
const int SPHASH_VOXEL_SHIFT = 8;
const float SPHASH_EPS = 0.03125f;
static readonly Vector3 voxelOrigin = new Vector3 { X = MIN_COORD_FLOAT, Y = MIN_COORD_FLOAT, Z = MIN_COORD_FLOAT };
static int levelShift;
static int levelCount;
static int GetVoxelSize(int level)
{
return SPHASH_VOXEL_SIZE << (SPHASH_LEVEL_SKIP * level);
}
static void UpdateLevelShift(int level)
{
levelShift = SPHASH_VOXEL_SHIFT + (SPHASH_LEVEL_SKIP * level);
}
static void UpdateLevelCount()
{
levelCount = 0;
while (ComputeVoxelCountAtLevel(levelCount) > 2)
{
// From level 0 to 3 it will be 128, 32, 8, 2
++levelCount;
}
// And then add one to have the count instead of the maximum level index
++levelCount;
}
static int ComputeVoxelCountAtLevel(int level)
{
int nVoxelCount = COORD_EXTENT >> SPHASH_VOXEL_SHIFT;
nVoxelCount >>= (SPHASH_LEVEL_SKIP * level);
return (nVoxelCount > 0) ? nVoxelCount : 1;
}
static Vector3 VoxelIndexFromPoint(Vector3 worldPoint)
{
Vector3 voxel = new Vector3();
voxel.X = (int)(worldPoint.X - voxelOrigin.X) >> levelShift;
voxel.Y = (int)(worldPoint.Y - voxelOrigin.Y) >> levelShift;
voxel.Z = (int)(worldPoint.Z - voxelOrigin.Z) >> levelShift;
return voxel;
}
public static bool BoxIntersects(Vector3 boxMins, Vector3 boxMaxs, Vector3 otherMins, Vector3 otherMaxs)
{
return (otherMins.X <= boxMaxs.X) && (otherMaxs.X >= boxMins.X) &&
(otherMins.Y <= boxMaxs.Y) && (otherMaxs.Y >= boxMins.Y) &&
(otherMins.Z <= boxMaxs.Z) && (otherMaxs.Z >= boxMins.Z);
}
}
}

View file

@ -14,6 +14,19 @@ namespace SteamShared.Models
public double PercentageY { get; set; }
/// <summary>
/// The in-game X-coordinate.
/// </summary>
public double X { get; set; }
/// <summary>
/// The in-game Y-coordinate.
/// </summary>
public double Y { get; set; }
/// <summary>
/// The in-game Z-coordinate.
/// </summary>
public double Z { get; set; }
public int AssociatedAreaID { get; set; } = -1;