mirror of
https://github.com/1dot13/source.git
synced 2026-08-05 14:00:23 +02:00
Flashlight changes (#81)
* Flashlight changes: - Fixed the lighted area being way too large in the diagonal directions. - Added a minimum range to simulate a flashlight user not illuminating its feet. - Added angle based calculations instead of the hardcoded one. * Flashlight changes, MR feedback.
This commit is contained in:
+163
-71
@@ -13,6 +13,7 @@
|
||||
#include "Animation Data.h"
|
||||
#include "Animation Control.h"
|
||||
#include "container.h"
|
||||
#define _USE_MATH_DEFINES // for C
|
||||
#include <math.h>
|
||||
#include "pathai.h"
|
||||
#include "Random.h"
|
||||
@@ -17688,78 +17689,13 @@ void SOLDIERTYPE::HandleFlashLights( )
|
||||
fLightChanged = TRUE;
|
||||
}
|
||||
|
||||
// not possible to get this bonus on a roof, due to our lighting system
|
||||
if ( !this->pathing.bLevel )
|
||||
{
|
||||
UINT8 flashlightrange = this->GetBestEquippedFlashLightRange( );
|
||||
if ( AddBestFlashLight() )
|
||||
{
|
||||
// take note: we own a light source
|
||||
this->usSoldierFlagMask |= SOLDIER_LIGHT_OWNER;
|
||||
|
||||
// if no flashlight is found, this will be 0
|
||||
if ( flashlightrange )
|
||||
{
|
||||
// the range at which we create additional light sources to the side
|
||||
UINT8 firstexpand = 8;
|
||||
UINT8 secondexpand = 12;
|
||||
|
||||
// depending on our direction, alter range
|
||||
if ( this->ubDirection == NORTHEAST || this->ubDirection == NORTHWEST || this->ubDirection == SOUTHEAST || this->ubDirection == SOUTHWEST )
|
||||
{
|
||||
flashlightrange = sqrt( (FLOAT)flashlightrange*(FLOAT)flashlightrange / 2.0f );
|
||||
firstexpand = sqrt( (FLOAT)firstexpand*(FLOAT)firstexpand / 2.0f );
|
||||
secondexpand = sqrt( (FLOAT)secondexpand*(FLOAT)secondexpand / 2.0f );
|
||||
}
|
||||
|
||||
// we determine the height of the next tile in our direction. Because of the way structures are handled, we sometimes have to take the very tile we're occupying right now
|
||||
INT32 nextGridNoinSight = this->sGridNo;
|
||||
|
||||
for ( UINT8 i = 0; i < flashlightrange; ++i )
|
||||
{
|
||||
nextGridNoinSight = NewGridNo( nextGridNoinSight, DirectionInc( this->ubDirection ) );
|
||||
|
||||
if ( SoldierToVirtualSoldierLineOfSightTest( this, nextGridNoinSight, this->pathing.bLevel, gAnimControl[this->usAnimState].ubEndHeight, FALSE ) )
|
||||
CreatePersonalLight( nextGridNoinSight, this->ubID );
|
||||
|
||||
// after a certain range, add new lights to the side to simulate a light cone
|
||||
if ( i > firstexpand )
|
||||
{
|
||||
INT8 sidedir1 = (this->ubDirection + 2) % NUM_WORLD_DIRECTIONS;
|
||||
INT8 sidedir2 = (this->ubDirection - 2) % NUM_WORLD_DIRECTIONS;
|
||||
|
||||
INT32 sideGridNo1 = NewGridNo( nextGridNoinSight, DirectionInc( sidedir1 ) );
|
||||
sideGridNo1 = NewGridNo( sideGridNo1, DirectionInc( sidedir1 ) );
|
||||
|
||||
if ( SoldierToVirtualSoldierLineOfSightTest( this, sideGridNo1, this->pathing.bLevel, gAnimControl[this->usAnimState].ubEndHeight, FALSE, NO_DISTANCE_LIMIT ) )
|
||||
CreatePersonalLight( sideGridNo1, this->ubID );
|
||||
|
||||
if ( i > secondexpand )
|
||||
{
|
||||
sideGridNo1 = NewGridNo( sideGridNo1, DirectionInc( sidedir1 ) );
|
||||
|
||||
if ( SoldierToVirtualSoldierLineOfSightTest( this, sideGridNo1, this->pathing.bLevel, gAnimControl[this->usAnimState].ubEndHeight, FALSE, NO_DISTANCE_LIMIT ) )
|
||||
CreatePersonalLight( sideGridNo1, this->ubID );
|
||||
}
|
||||
|
||||
INT32 sideGridNo2 = NewGridNo( nextGridNoinSight, DirectionInc( sidedir2 ) );
|
||||
sideGridNo2 = NewGridNo( sideGridNo2, DirectionInc( sidedir2 ) );
|
||||
|
||||
if ( SoldierToVirtualSoldierLineOfSightTest( this, sideGridNo2, this->pathing.bLevel, gAnimControl[this->usAnimState].ubEndHeight, FALSE, NO_DISTANCE_LIMIT ) )
|
||||
CreatePersonalLight( sideGridNo2, this->ubID );
|
||||
|
||||
if ( i > secondexpand )
|
||||
{
|
||||
sideGridNo2 = NewGridNo( sideGridNo2, DirectionInc( sidedir2 ) );
|
||||
|
||||
if ( SoldierToVirtualSoldierLineOfSightTest( this, sideGridNo2, this->pathing.bLevel, gAnimControl[this->usAnimState].ubEndHeight, FALSE, NO_DISTANCE_LIMIT ) )
|
||||
CreatePersonalLight( sideGridNo2, this->ubID );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// take note: we own a light source
|
||||
this->usSoldierFlagMask |= SOLDIER_LIGHT_OWNER;
|
||||
|
||||
fLightChanged = TRUE;
|
||||
}
|
||||
}
|
||||
fLightChanged = TRUE;
|
||||
}
|
||||
|
||||
if ( fLightChanged )
|
||||
{
|
||||
@@ -17805,6 +17741,162 @@ UINT8 SOLDIERTYPE::GetBestEquippedFlashLightRange( )
|
||||
return(bestrange);
|
||||
}
|
||||
|
||||
bool SOLDIERTYPE::AddBestFlashLight()
|
||||
{
|
||||
// not possible to get this bonus on a roof, due to our lighting system
|
||||
if ( this->pathing.bLevel != 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UINT8 maxRange = this->GetBestEquippedFlashLightRange();
|
||||
if ( maxRange < 1 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// we don't use the flashlight to run better at night (light up our shoes), we use it to find enemies!
|
||||
UINT8 minRange = 4;
|
||||
if ( minRange > maxRange )
|
||||
{
|
||||
minRange = maxRange;
|
||||
}
|
||||
|
||||
float maxAngle = 45;
|
||||
maxAngle *= PI / 180 / 2; // convert to rad and halven
|
||||
|
||||
auto forward = DirectionInc(this->ubDirection);
|
||||
auto left = DirectionInc(DirectionIfTurnedClockwise(this->ubDirection, 6));
|
||||
auto leftLeft = DirectionInc(DirectionIfTurnedClockwise(this->ubDirection, 5));
|
||||
auto right = DirectionInc(DirectionIfTurnedClockwise(this->ubDirection, 2));
|
||||
auto rightRight = DirectionInc(DirectionIfTurnedClockwise(this->ubDirection, 3));
|
||||
|
||||
bool isDiagonal = this->ubDirection == NORTHEAST || this->ubDirection == NORTHWEST || this->ubDirection == SOUTHEAST || this->ubDirection == SOUTHWEST;
|
||||
|
||||
struct position_2d
|
||||
{
|
||||
INT16 x, y;
|
||||
|
||||
position_2d(INT32 gridNo)
|
||||
{
|
||||
ConvertGridNoToXY(gridNo, &x, &y);
|
||||
}
|
||||
position_2d(INT16 _x, INT16 _y) : x{_x}, y{_y}
|
||||
{
|
||||
}
|
||||
};
|
||||
struct vector_2d
|
||||
{
|
||||
INT16 dx, dy;
|
||||
float length;
|
||||
|
||||
vector_2d(INT8 direction)
|
||||
{
|
||||
ConvertDirectionToVectorInXY(direction, &dx, &dy);
|
||||
length = CalcLength(dx, dy);
|
||||
}
|
||||
vector_2d(position_2d from, position_2d to)
|
||||
{
|
||||
dx = to.x - from.x;
|
||||
dy = to.y - from.y;
|
||||
length = CalcLength(dx, dy);
|
||||
}
|
||||
vector_2d(INT16 _dx, INT16 _dy) : dx{_dx}, dy{_dy}
|
||||
{
|
||||
length = CalcLength(dx, dy);
|
||||
}
|
||||
|
||||
float GetAngle( vector_2d other )
|
||||
{
|
||||
auto dot = dx * other.dx + dy * other.dy;
|
||||
return acos(dot / (length * other.length));
|
||||
}
|
||||
|
||||
static float CalcLength(float dx, float dy)
|
||||
{
|
||||
return sqrt(powf(dx, 2) + powf(dy, 2));
|
||||
}
|
||||
};
|
||||
|
||||
position_2d soldierPos(this->sGridNo);
|
||||
vector_2d soldierDir(this->ubDirection);
|
||||
|
||||
auto is_in_area = [&](INT32 sGridNoToTest) -> bool
|
||||
{
|
||||
vector_2d v(soldierPos, position_2d(sGridNoToTest));
|
||||
|
||||
if (v.length > maxRange)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (v.length < minRange)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto coneAngle = soldierDir.GetAngle( v );
|
||||
if (coneAngle > maxAngle)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
auto add_light_if_in_line_of_sight = [&, this]( INT32 sGridNoToTest, bool allowSkip ) -> void
|
||||
{
|
||||
if (allowSkip) // improve performance by skipping 3/4 of the lights
|
||||
{
|
||||
INT16 sXPos, sYPos;
|
||||
ConvertGridNoToXY( sGridNoToTest, &sXPos, &sYPos );
|
||||
if (!(sXPos % 2 == 0 && sYPos % 2 == 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( SoldierToVirtualSoldierLineOfSightTest( this, sGridNoToTest, this->pathing.bLevel, gAnimControl[this->usAnimState].ubEndHeight, false, NO_DISTANCE_LIMIT ) )
|
||||
{
|
||||
CreatePersonalLight( sGridNoToTest, this->ubID );
|
||||
}
|
||||
};
|
||||
|
||||
auto travel_direction_to_add_light = [&]( INT32 startingGridNo, INT16 directionIncrementer )
|
||||
{
|
||||
for ( auto currentGridNo = startingGridNo; !OutOfBounds( currentGridNo, -1 ) && is_in_area( currentGridNo ); currentGridNo += directionIncrementer )
|
||||
{
|
||||
add_light_if_in_line_of_sight( currentGridNo, true);
|
||||
}
|
||||
};
|
||||
|
||||
for ( auto currentGridNo = this->sGridNo; !OutOfBounds( currentGridNo, -1 ); currentGridNo += forward )
|
||||
{
|
||||
vector_2d v(soldierPos, position_2d(currentGridNo));
|
||||
if ( v.length < minRange )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (v.length > maxRange)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
add_light_if_in_line_of_sight( currentGridNo, false );
|
||||
|
||||
travel_direction_to_add_light( currentGridNo, left );
|
||||
travel_direction_to_add_light( currentGridNo, right );
|
||||
|
||||
if ( isDiagonal )
|
||||
{
|
||||
travel_direction_to_add_light( NewGridNo( currentGridNo, leftLeft ), left );
|
||||
travel_direction_to_add_light( NewGridNo( currentGridNo, rightRight ), right );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Flugente: soldier profiles
|
||||
// retrieves the correct sub-array
|
||||
INT8 SOLDIERTYPE::GetSoldierProfileType( UINT8 usTeam )
|
||||
|
||||
@@ -1935,7 +1935,8 @@ public:
|
||||
//void AddDrugValues(UINT8 uDrugType, UINT8 usEffect, UINT8 usTravelRate, UINT8 usSideEffect );
|
||||
|
||||
void HandleFlashLights();
|
||||
UINT8 GetBestEquippedFlashLightRange();
|
||||
bool AddBestFlashLight();
|
||||
UINT8 GetBestEquippedFlashLightRange();
|
||||
|
||||
// Flugente: soldier profiles
|
||||
INT8 GetSoldierProfileType(UINT8 usTeam); // retrieves the correct sub-array
|
||||
|
||||
@@ -91,6 +91,11 @@ UINT8 gOneCCDirection[ NUM_WORLD_DIRECTIONS ] =
|
||||
WEST
|
||||
};
|
||||
|
||||
UINT8 DirectionIfTurnedClockwise(UINT8 facing, UINT8 times)
|
||||
{
|
||||
return (facing + times) % NUM_WORLD_DIRECTIONS;
|
||||
}
|
||||
|
||||
// DIRECTION FACING DIRECTION WE WANT TO GOTO
|
||||
UINT8 gPurpendicularDirection[ NUM_WORLD_DIRECTIONS ][ NUM_WORLD_DIRECTIONS ] =
|
||||
{
|
||||
@@ -912,6 +917,15 @@ INT16 sDeltaScreenX, sDeltaScreenY;
|
||||
|
||||
}
|
||||
|
||||
INT16 DirectionToVectorX[NUM_WORLD_DIRECTIONS] = {0, 1, 1, 1, 0, -1, -1, -1};
|
||||
INT16 DirectionToVectorY[NUM_WORLD_DIRECTIONS] = {-1, -1, 0, 1, 1, 1, 0, -1};
|
||||
void ConvertDirectionToVectorInXY( UINT8 ubDirection, INT16* sXDir, INT16* sYDir )
|
||||
{
|
||||
Assert(ubDirection >= 0 && ubDirection < NUM_WORLD_DIRECTIONS);
|
||||
*sXDir = DirectionToVectorX[ubDirection];
|
||||
*sYDir = DirectionToVectorY[ubDirection];
|
||||
}
|
||||
|
||||
void ConvertGridNoToXY( INT32 sGridNo, INT16 *sXPos, INT16 *sYPos )
|
||||
{
|
||||
*sYPos = sGridNo / WORLD_COLS;
|
||||
|
||||
@@ -30,6 +30,8 @@ extern UINT8 gTwoCDirection[ NUM_WORLD_DIRECTIONS ];
|
||||
extern UINT8 gOneCDirection[ NUM_WORLD_DIRECTIONS ];
|
||||
extern UINT8 gOneCCDirection[ NUM_WORLD_DIRECTIONS ];
|
||||
|
||||
UINT8 DirectionIfTurnedClockwise(UINT8 facing, UINT8 times);
|
||||
|
||||
extern UINT8 gPurpendicularDirection[ NUM_WORLD_DIRECTIONS ][ NUM_WORLD_DIRECTIONS ];
|
||||
|
||||
// Macros
|
||||
@@ -40,6 +42,7 @@ extern UINT8 gPurpendicularDirection[ NUM_WORLD_DIRECTIONS ][ NUM_WORLD_DIRECTIO
|
||||
|
||||
#define GETWORLDINDEXFROMWORLDCOORDS( y, x ) ( (INT32) ( x / CELL_X_SIZE ) ) + WORLD_COLS * ( (INT32) ( y / CELL_Y_SIZE ) )
|
||||
|
||||
void ConvertDirectionToVectorInXY(UINT8 ubDirection, INT16* sXDir, INT16* sYDir);
|
||||
void ConvertGridNoToXY( INT32 sGridNo, INT16 *sXPos, INT16 *sYPos );
|
||||
void ConvertGridNoToCellXY( INT32 sGridNo, INT16 *sXPos, INT16 *sYPos );
|
||||
void ConvertGridNoToCenterCellXY( INT32 sGridNo, INT16 *sXPos, INT16 *sYPos );
|
||||
|
||||
Reference in New Issue
Block a user