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:
CptMoore
2023-01-18 20:52:12 +02:00
committed by GitHub
parent d6896c9234
commit 624c0f3bc4
4 changed files with 182 additions and 72 deletions
+163 -71
View File
@@ -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 )
+2 -1
View File
@@ -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
+14
View File
@@ -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;
+3
View File
@@ -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 );