2018-10-28 22:08:38 +00:00
# include "p_spec.h"
# include "g_levellocals.h"
# include "p_destructible.h"
# include "v_text.h"
# include "actor.h"
# include "p_trace.h"
# include "p_lnspec.h"
# include "r_sky.h"
# include "p_local.h"
# include "p_maputl.h"
# include "c_cvars.h"
2018-10-31 20:40:08 +00:00
# include "serializer.h"
2018-10-28 22:08:38 +00:00
//==========================================================================
//
// [ZZ] Geometry damage logic callbacks
//
//==========================================================================
void P_SetHealthGroupHealth ( int group , int health )
{
FHealthGroup * grp = P_GetHealthGroup ( group ) ;
if ( ! grp ) return ;
grp - > health = health ;
// now set health of all linked objects to the same as this one
for ( unsigned i = 0 ; i < grp - > lines . Size ( ) ; i + + )
{
line_t * lline = grp - > lines [ i ] ;
lline - > health = health ;
}
//
for ( unsigned i = 0 ; i < grp - > sectors . Size ( ) ; i + + )
{
sector_t * lsector = grp - > sectors [ i ] ;
if ( lsector - > healthceilinggroup = = group )
lsector - > healthceiling = health ;
if ( lsector - > healthfloorgroup = = group )
lsector - > healthfloor = health ;
2018-11-04 04:53:37 +00:00
if ( lsector - > health3dgroup = = group )
lsector - > health3d = health ;
2018-10-28 22:08:38 +00:00
}
}
void P_DamageHealthGroup ( FHealthGroup * grp , void * object , AActor * source , int damage , FName damagetype , int side , int part , DVector3 position )
{
if ( ! grp ) return ;
int group = grp - > id ;
// now set health of all linked objects to the same as this one
for ( unsigned i = 0 ; i < grp - > lines . Size ( ) ; i + + )
{
line_t * lline = grp - > lines [ i ] ;
if ( lline = = object )
continue ;
lline - > health = grp - > health + damage ;
P_DamageLinedef ( lline , source , damage , damagetype , side , position , false ) ;
}
//
for ( unsigned i = 0 ; i < grp - > sectors . Size ( ) ; i + + )
{
sector_t * lsector = grp - > sectors [ i ] ;
if ( lsector - > healthceilinggroup = = group & & ( lsector ! = object | | part ! = SECPART_Ceiling ) )
{
lsector - > healthceiling = grp - > health + damage ;
P_DamageSector ( lsector , source , damage , damagetype , SECPART_Ceiling , position , false ) ;
}
if ( lsector - > healthfloorgroup = = group & & ( lsector ! = object | | part ! = SECPART_Floor ) )
{
lsector - > healthfloor = grp - > health + damage ;
P_DamageSector ( lsector , source , damage , damagetype , SECPART_Floor , position , false ) ;
}
2018-11-04 04:53:37 +00:00
if ( lsector - > health3dgroup = = group & & ( lsector ! = object | | part ! = SECPART_3D ) )
{
lsector - > health3d = grp - > health + damage ;
P_DamageSector ( lsector , source , damage , damagetype , SECPART_3D , position , false ) ;
}
2018-10-28 22:08:38 +00:00
}
}
void P_DamageLinedef ( line_t * line , AActor * source , int damage , FName damagetype , int side , DVector3 position , bool dogroups )
{
2018-11-04 04:53:37 +00:00
if ( damage < 0 ) damage = 0 ;
if ( ! damage ) return ;
2018-10-28 22:08:38 +00:00
line - > health - = damage ;
if ( line - > health < 0 ) line - > health = 0 ;
// callbacks here
// first off, call special if needed
if ( ( line - > activation & SPAC_Damage ) | | ( ( line - > activation & SPAC_Death ) & & ! line - > health ) )
{
int activateon = SPAC_Damage ;
if ( ! line - > health ) activateon | = SPAC_Death ;
P_ActivateLine ( line , source , side , activateon & line - > activation , & position ) ;
}
if ( dogroups & & line - > healthgroup )
{
FHealthGroup * grp = P_GetHealthGroup ( line - > healthgroup ) ;
if ( grp )
grp - > health = line - > health ;
P_DamageHealthGroup ( grp , line , source , damage , damagetype , side , - 1 , position ) ;
}
//Printf("P_DamageLinedef: %d damage (type=%s, source=%p), new health = %d\n", damage, damagetype.GetChars(), source, line->health);
}
void P_DamageSector ( sector_t * sector , AActor * source , int damage , FName damagetype , int part , DVector3 position , bool dogroups )
{
2018-11-04 04:53:37 +00:00
if ( damage < 0 ) damage = 0 ;
if ( ! damage ) return ;
int * sectorhealth ;
int group ;
int dmg ;
int dth ;
switch ( part )
{
case SECPART_Ceiling :
sectorhealth = & sector - > healthceiling ;
group = sector - > healthceilinggroup ;
dmg = SECSPAC_DamageCeiling ;
dth = SECSPAC_DeathCeiling ;
break ;
case SECPART_Floor :
sectorhealth = & sector - > healthfloor ;
group = sector - > healthfloorgroup ;
dmg = SECSPAC_DamageFloor ;
dth = SECSPAC_DeathFloor ;
break ;
case SECPART_3D :
sectorhealth = & sector - > health3d ;
group = sector - > health3dgroup ;
dmg = SECSPAC_Damage3D ;
dth = SECSPAC_Death3D ;
break ;
default :
return ;
}
int newhealth = * sectorhealth - damage ;
2018-10-28 22:08:38 +00:00
if ( newhealth < 0 ) newhealth = 0 ;
2018-11-04 04:53:37 +00:00
* sectorhealth = newhealth ;
2018-10-28 22:08:38 +00:00
// callbacks here
if ( sector - > SecActTarget )
{
sector - > TriggerSectorActions ( source , dmg ) ;
if ( newhealth = = 0 )
sector - > TriggerSectorActions ( source , dth ) ;
}
if ( dogroups & & group )
{
FHealthGroup * grp = P_GetHealthGroup ( group ) ;
if ( grp )
grp - > health = newhealth ;
P_DamageHealthGroup ( grp , sector , source , damage , damagetype , 0 , part , position ) ;
}
//Printf("P_DamageSector: %d damage (type=%s, position=%s, source=%p), new health = %d\n", damage, damagetype.GetChars(), (part == SECPART_Ceiling) ? "ceiling" : "floor", source, newhealth);
}
//===========================================================================
//
//
//
//===========================================================================
FHealthGroup * P_GetHealthGroup ( int id )
{
FHealthGroup * grp = level . healthGroups . CheckKey ( id ) ;
return grp ;
}
FHealthGroup * P_GetHealthGroupOrNew ( int id , int health )
{
FHealthGroup * grp = level . healthGroups . CheckKey ( id ) ;
if ( ! grp )
{
FHealthGroup newgrp ;
newgrp . id = id ;
newgrp . health = health ;
grp = & ( level . healthGroups . Insert ( id , newgrp ) ) ;
}
return grp ;
}
void P_InitHealthGroups ( )
{
level . healthGroups . Clear ( ) ;
TArray < int > groupsInError ;
for ( unsigned i = 0 ; i < level . lines . Size ( ) ; i + + )
{
line_t * lline = & level . lines [ i ] ;
if ( lline - > healthgroup > 0 )
{
FHealthGroup * grp = P_GetHealthGroupOrNew ( lline - > healthgroup , lline - > health ) ;
if ( grp - > health ! = lline - > health )
{
Printf ( TEXTCOLOR_RED " Warning: line %d in health group %d has different starting health (line health = %d, group health = %d) \n " , i , lline - > healthgroup , lline - > health , grp - > health ) ;
groupsInError . Push ( lline - > healthgroup ) ;
if ( lline - > health > grp - > health )
grp - > health = lline - > health ;
}
grp - > lines . Push ( lline ) ;
}
}
for ( unsigned i = 0 ; i < level . sectors . Size ( ) ; i + + )
{
sector_t * lsector = & level . sectors [ i ] ;
if ( lsector - > healthceilinggroup > 0 )
{
FHealthGroup * grp = P_GetHealthGroupOrNew ( lsector - > healthceilinggroup , lsector - > healthceiling ) ;
if ( grp - > health ! = lsector - > healthceiling )
{
Printf ( TEXTCOLOR_RED " Warning: sector ceiling %d in health group %d has different starting health (ceiling health = %d, group health = %d) \n " , i , lsector - > healthceilinggroup , lsector - > healthceiling , grp - > health ) ;
groupsInError . Push ( lsector - > healthceilinggroup ) ;
if ( lsector - > healthceiling > grp - > health )
grp - > health = lsector - > healthceiling ;
}
grp - > sectors . Push ( lsector ) ;
}
if ( lsector - > healthfloorgroup > 0 )
{
FHealthGroup * grp = P_GetHealthGroupOrNew ( lsector - > healthfloorgroup , lsector - > healthfloor ) ;
if ( grp - > health ! = lsector - > healthfloor )
{
Printf ( TEXTCOLOR_RED " Warning: sector floor %d in health group %d has different starting health (floor health = %d, group health = %d) \n " , i , lsector - > healthfloorgroup , lsector - > healthfloor , grp - > health ) ;
groupsInError . Push ( lsector - > healthfloorgroup ) ;
if ( lsector - > healthfloor > grp - > health )
grp - > health = lsector - > healthfloor ;
}
if ( lsector - > healthceilinggroup ! = lsector - > healthfloorgroup )
grp - > sectors . Push ( lsector ) ;
}
}
for ( unsigned i = 0 ; i < groupsInError . Size ( ) ; i + + )
{
FHealthGroup * grp = P_GetHealthGroup ( groupsInError [ i ] ) ;
Printf ( TEXTCOLOR_GOLD " Health group %d is using the highest found health value of %d " , groupsInError [ i ] , grp - > health ) ;
}
}
//==========================================================================
//
// P_GeometryLineAttack
//
// Applies hitscan damage to geometry (lines and sectors with health>0)
//==========================================================================
void P_GeometryLineAttack ( FTraceResults & trace , AActor * thing , int damage , FName damageType )
{
// [ZZ] hitscan geometry damage logic
//
2018-11-04 04:53:37 +00:00
// check 3d floor, but still allow the wall to take generic damage
if ( trace . HitType = = TRACE_HitWall & & trace . Tier = = TIER_FFloor )
{
if ( trace . ffloor & & trace . ffloor - > model & & trace . ffloor - > model - > health3d )
P_DamageSector ( trace . ffloor - > model , thing , damage , damageType , SECPART_3D , trace . HitPos ) ;
}
2018-10-28 22:08:38 +00:00
if ( trace . HitType = = TRACE_HitWall & & P_CheckLinedefVulnerable ( trace . Line , trace . Side ) )
{
if ( trace . Tier = = TIER_Lower | | trace . Tier = = TIER_Upper ) // process back sector health if any
{
sector_t * backsector = ( trace . Line - > sidedef [ ! trace . Side ] ? trace . Line - > sidedef [ ! trace . Side ] - > sector : nullptr ) ;
int sectorhealth = 0 ;
if ( backsector & & trace . Tier = = TIER_Lower & & backsector - > healthfloor > 0 & & P_CheckLinedefVulnerable ( trace . Line , trace . Side , SECPART_Floor ) )
sectorhealth = backsector - > healthfloor ;
if ( backsector & & trace . Tier = = TIER_Upper & & backsector - > healthceiling > 0 & & P_CheckLinedefVulnerable ( trace . Line , trace . Side , SECPART_Ceiling ) )
sectorhealth = backsector - > healthceiling ;
if ( sectorhealth > 0 )
{
P_DamageSector ( backsector , thing , damage , damageType , ( trace . Tier = = TIER_Upper ) ? SECPART_Ceiling : SECPART_Floor , trace . HitPos ) ;
}
}
// always process linedef health if any
if ( trace . Line - > health > 0 )
{
P_DamageLinedef ( trace . Line , thing , damage , damageType , trace . Side , trace . HitPos ) ;
}
// fake floors are not handled
}
else if ( trace . HitType = = TRACE_HitFloor | | trace . HitType = = TRACE_HitCeiling )
{
2018-11-04 04:53:37 +00:00
// check for 3d floors. if a 3d floor was hit, it'll block any interaction with the sector planes at the same point, if present.
// i.e. if there are 3d floors at the same height as the sector's real floor/ceiling, and they blocked the shot, then it won't damage.
bool hit3dfloors = false ;
sector_t * sector = trace . Sector ;
for ( auto f : sector - > e - > XFloor . ffloors )
{
if ( ! ( f - > flags & FF_EXISTS ) ) continue ;
if ( ! ( f - > flags & FF_SOLID ) | | ( f - > flags & FF_SHOOTTHROUGH ) ) continue ;
if ( ! f - > model ) continue ;
if ( trace . HitType = = TRACE_HitFloor & & fabs ( f - > top . plane - > ZatPoint ( trace . HitPos . XY ( ) ) - trace . HitPos . Z ) < = EQUAL_EPSILON )
{
if ( f - > model - > health3d )
P_DamageSector ( f - > model , thing , damage , damageType , SECPART_3D , trace . HitPos ) ;
hit3dfloors = true ;
}
else if ( trace . HitType = = TRACE_HitCeiling & & fabs ( f - > bottom . plane - > ZatPoint ( trace . HitPos . XY ( ) ) - trace . HitPos . Z ) < = EQUAL_EPSILON )
{
if ( f - > model - > health3d )
P_DamageSector ( f - > model , thing , damage , damageType , SECPART_3D , trace . HitPos ) ;
hit3dfloors = true ;
}
}
if ( hit3dfloors )
return ;
2018-10-28 22:08:38 +00:00
int sectorhealth = 0 ;
if ( trace . HitType = = TRACE_HitFloor & & trace . Sector - > healthfloor > 0 & & P_CheckSectorVulnerable ( trace . Sector , SECPART_Floor ) )
sectorhealth = trace . Sector - > healthfloor ;
if ( trace . HitType = = TRACE_HitCeiling & & trace . Sector - > healthceiling > 0 & & P_CheckSectorVulnerable ( trace . Sector , SECPART_Ceiling ) )
sectorhealth = trace . Sector - > healthceiling ;
if ( sectorhealth > 0 )
{
P_DamageSector ( trace . Sector , thing , damage , damageType , ( trace . HitType = = TRACE_HitCeiling ) ? SECPART_Ceiling : SECPART_Floor , trace . HitPos ) ;
}
}
}
//==========================================================================
//
// P_GeometryRadiusAttack
//
// Applies radius damage to destructible geometry (lines and sectors with health>0)
//==========================================================================
struct pgra_data_t
{
DVector3 pos ;
line_t * line ;
sector_t * sector ;
int secpart ;
} ;
// we use this horizontally for 2D wall distance, and in a bit fancier way for 3D wall distance
static DVector2 PGRA_ClosestPointOnLine2D ( DVector2 x , DVector2 p1 , DVector2 p2 )
{
DVector2 p2p1 = ( p2 - p1 ) ;
DVector2 xp1 = ( x - p1 ) ;
double r = p2p1 | xp1 ;
double len = p2p1 . Length ( ) ;
r / = len ;
if ( r < 0 )
return p1 ;
if ( r > len )
return p2 ;
return p1 + p2p1 . Unit ( ) * r ;
}
2018-11-04 04:53:37 +00:00
static bool PGRA_CheckExplosionBlocked ( DVector3 pt , DVector3 check , sector_t * checksector )
2018-10-28 22:08:38 +00:00
{
// simple solid geometry sight check between "check" and "pt"
// expected - Trace hits nothing
2018-11-04 04:53:37 +00:00
check . Z + = EQUAL_EPSILON ; // this is so that floor under the rocket doesn't block explosion
2018-10-28 22:08:38 +00:00
DVector3 ptVec = ( pt - check ) ;
double ptDst = ptVec . Length ( ) - 0.5 ;
ptVec . MakeUnit ( ) ;
FTraceResults res ;
2018-11-04 04:53:37 +00:00
bool isblocked = Trace ( check , checksector , ptVec , ptDst , 0 , ML_BLOCKEVERYTHING , nullptr , res ) ;
return isblocked ;
}
static void PGRA_InsertIfCloser ( TMap < int , pgra_data_t > & damageGroupPos , int group , DVector3 pt , DVector3 check , sector_t * checksector , sector_t * sector , line_t * line , int secpart )
{
if ( PGRA_CheckExplosionBlocked ( pt , check , checksector ) )
return ;
2018-10-28 22:08:38 +00:00
pgra_data_t * existing = damageGroupPos . CheckKey ( group ) ;
// not present or distance is closer
if ( ! existing | | ( ( ( * existing ) . pos - check ) . Length ( ) > ( pt - check ) . Length ( ) ) )
{
if ( ! existing ) existing = & damageGroupPos . Insert ( group , pgra_data_t ( ) ) ;
existing - > pos = pt ;
existing - > line = line ;
existing - > sector = sector ;
existing - > secpart = secpart ;
}
}
EXTERN_CVAR ( Float , splashfactor ) ;
void P_GeometryRadiusAttack ( AActor * bombspot , AActor * bombsource , int bombdamage , int bombdistance , FName damagetype , int fulldamagedistance )
{
TMap < int , pgra_data_t > damageGroupPos ;
double bombdistancefloat = 1. / ( double ) ( bombdistance - fulldamagedistance ) ;
// now, this is not entirely correct... but sector actions still _do_ require a valid source actor to trigger anything
if ( ! bombspot )
return ;
if ( ! bombsource )
bombsource = bombspot ;
// check current sector floor and ceiling. this is the only *sector* checked, otherwise only use lines
// a bit imprecise, but should work
sector_t * srcsector = bombspot - > Sector ;
if ( srcsector - > healthceiling > 0 )
{
double dstceiling = srcsector - > ceilingplane . Normal ( ) | ( bombspot - > Pos ( ) + srcsector - > ceilingplane . Normal ( ) * srcsector - > ceilingplane . D ) ;
DVector3 spotTo = bombspot - > Pos ( ) - srcsector - > ceilingplane . Normal ( ) * dstceiling ;
int grp = srcsector - > healthceilinggroup ;
if ( grp < = 0 )
2018-11-04 04:53:37 +00:00
grp = 0x80000000 | ( srcsector - > sectornum & 0x0FFFFFFF ) ;
2018-10-28 22:08:38 +00:00
PGRA_InsertIfCloser ( damageGroupPos , grp , spotTo , bombspot - > Pos ( ) , srcsector , srcsector , nullptr , SECPART_Ceiling ) ;
}
if ( srcsector - > healthfloor > 0 )
{
double dstfloor = srcsector - > floorplane . Normal ( ) | ( bombspot - > Pos ( ) + srcsector - > floorplane . Normal ( ) * srcsector - > floorplane . D ) ;
DVector3 spotTo = bombspot - > Pos ( ) - srcsector - > floorplane . Normal ( ) * dstfloor ;
int grp = srcsector - > healthfloorgroup ;
if ( grp < = 0 )
2018-11-04 04:53:37 +00:00
grp = 0x40000000 | ( srcsector - > sectornum & 0x0FFFFFFF ) ;
2018-10-28 22:08:38 +00:00
PGRA_InsertIfCloser ( damageGroupPos , grp , spotTo , bombspot - > Pos ( ) , srcsector , srcsector , nullptr , SECPART_Floor ) ;
}
2018-11-04 04:53:37 +00:00
for ( auto f : srcsector - > e - > XFloor . ffloors )
{
if ( ! ( f - > flags & FF_EXISTS ) ) continue ;
if ( ! ( f - > flags & FF_SOLID ) ) continue ;
if ( ! f - > model | | ! f - > model - > health3d ) continue ;
double ff_top = f - > top . plane - > ZatPoint ( bombspot - > Pos ( ) ) ;
double ff_bottom = f - > bottom . plane - > ZatPoint ( bombspot - > Pos ( ) ) ;
if ( ff_top < ff_bottom ) // ignore eldritch geometry
continue ;
int grp = f - > model - > health3dgroup ;
if ( grp < = 0 )
grp = 0x20000000 | ( f - > model - > sectornum & 0x0FFFFFFF ) ;
DVector3 spotTo ;
if ( bombspot - > Z ( ) < ff_bottom ) // use bottom plane
{
double dst = f - > bottom . plane - > Normal ( ) | ( bombspot - > Pos ( ) + f - > bottom . plane - > Normal ( ) * f - > bottom . plane - > D ) ;
spotTo = bombspot - > Pos ( ) - f - > bottom . plane - > Normal ( ) * dst ;
}
else if ( bombspot - > Z ( ) > ff_top ) // use top plane
{
double dst = f - > top . plane - > Normal ( ) | ( bombspot - > Pos ( ) + f - > top . plane - > Normal ( ) * f - > top . plane - > D ) ;
spotTo = bombspot - > Pos ( ) - f - > top . plane - > Normal ( ) * dst ;
}
else // explosion right inside the floor. do 100% damage
{
spotTo = bombspot - > Pos ( ) ;
}
PGRA_InsertIfCloser ( damageGroupPos , grp , spotTo , bombspot - > Pos ( ) , srcsector , f - > model , nullptr , SECPART_3D ) ;
}
2018-10-28 22:08:38 +00:00
// enumerate all lines around
2018-11-04 04:53:37 +00:00
FBoundingBox bombbox ( bombspot - > X ( ) , bombspot - > Y ( ) , bombdistance ) ;
2018-10-28 22:08:38 +00:00
FBlockLinesIterator it ( bombbox ) ;
line_t * ln ;
int vc = validcount ;
TArray < line_t * > lines ;
while ( ( ln = it . Next ( ) ) ) // iterator and Trace both use validcount and interfere with each other
lines . Push ( ln ) ;
2018-10-31 18:13:54 +00:00
for ( unsigned i = 0 ; i < lines . Size ( ) ; i + + )
2018-10-28 22:08:38 +00:00
{
ln = lines [ i ] ;
DVector2 pos2d = bombspot - > Pos ( ) . XY ( ) ;
int sd = P_PointOnLineSide ( pos2d , ln ) ;
side_t * side = ln - > sidedef [ sd ] ;
if ( ! side ) continue ;
sector_t * sector = side - > sector ;
side_t * otherside = ln - > sidedef [ ! sd ] ;
sector_t * othersector = otherside ? otherside - > sector : nullptr ;
2018-11-04 04:53:37 +00:00
if ( ! ln - > health & & ( ! othersector | | ( ! othersector - > healthfloor & & ! othersector - > healthceiling & & ! othersector - > e - > XFloor . ffloors . Size ( ) ) ) )
2018-10-28 22:08:38 +00:00
continue ; // non-interactive geometry
DVector2 to2d = PGRA_ClosestPointOnLine2D ( bombspot - > Pos ( ) . XY ( ) , side - > V1 ( ) - > p , side - > V2 ( ) - > p ) ;
// this gives us x/y
double distto2d = ( to2d - pos2d ) . Length ( ) ;
double z_top1 , z_top2 , z_bottom1 , z_bottom2 ; // here, z_top1 is closest to the ceiling, and z_bottom1 is closest to the floor.
z_top1 = sector - > ceilingplane . ZatPoint ( to2d ) ;
2018-11-04 04:53:37 +00:00
z_top2 = othersector ? othersector - > ceilingplane . ZatPoint ( to2d ) : z_top1 ;
2018-10-28 22:08:38 +00:00
z_bottom1 = sector - > floorplane . ZatPoint ( to2d ) ;
2018-11-04 04:53:37 +00:00
z_bottom2 = othersector ? othersector - > floorplane . ZatPoint ( to2d ) : z_bottom1 ;
2018-10-28 22:08:38 +00:00
DVector3 to3d_fullheight ( to2d . X , to2d . Y , clamp ( bombspot - > Z ( ) , z_bottom1 , z_top1 ) ) ;
if ( ln - > health & & P_CheckLinedefVulnerable ( ln , sd ) )
{
bool cantdamage = false ;
2018-11-04 04:53:37 +00:00
bool linefullheight = ! othersector | | ! ! ( ln - > flags & ( ML_BLOCKEVERYTHING ) ) ;
2018-10-28 22:08:38 +00:00
// decide specific position to affect on a line.
if ( ! linefullheight )
{
z_top2 = othersector - > ceilingplane . ZatPoint ( to2d ) ;
z_bottom2 = othersector - > floorplane . ZatPoint ( to2d ) ;
double bombz = bombspot - > Z ( ) ;
if ( z_top2 > z_top1 )
z_top2 = z_top1 ;
if ( z_bottom2 < z_bottom1 )
z_bottom2 = z_bottom1 ;
if ( bombz < = z_top2 & & bombz > = z_bottom2 ) // between top and bottom opening
to3d_fullheight . Z = ( z_top2 - bombz < bombz - z_bottom2 ) ? z_top2 : z_bottom2 ;
else if ( bombz < z_bottom2 & & bombz > = z_bottom1 )
to3d_fullheight . Z = clamp ( bombz , z_bottom1 , z_bottom2 ) ;
else if ( bombz > z_top2 & & bombz < = z_top1 )
to3d_fullheight . Z = clamp ( bombz , z_top2 , z_top1 ) ;
else cantdamage = true ;
}
if ( ! cantdamage )
{
if ( ln - > healthgroup )
{
PGRA_InsertIfCloser ( damageGroupPos , ln - > healthgroup , to3d_fullheight , bombspot - > Pos ( ) , srcsector , nullptr , ln , - 1 ) ;
}
2018-11-04 04:53:37 +00:00
else if ( ! PGRA_CheckExplosionBlocked ( to3d_fullheight , bombspot - > Pos ( ) , srcsector ) )
2018-10-28 22:08:38 +00:00
{
// otherwise just damage line
double dst = ( to3d_fullheight - bombspot - > Pos ( ) ) . Length ( ) ;
int damage = 0 ;
if ( dst < bombdistance )
{
dst = clamp < double > ( dst - ( double ) fulldamagedistance , 0 , dst ) ;
damage = ( int ) ( ( double ) bombdamage * ( 1. - dst * bombdistancefloat ) ) ;
if ( bombsource = = bombspot )
damage = ( int ) ( damage * splashfactor ) ;
}
P_DamageLinedef ( ln , bombsource , damage , damagetype , sd , to3d_fullheight ) ;
}
}
}
if ( othersector & & othersector - > healthceiling & & P_CheckLinedefVulnerable ( ln , sd , SECPART_Ceiling ) )
{
if ( z_top2 < z_top1 ) // we have front side to hit against
{
DVector3 to3d_upper ( to2d . X , to2d . Y , clamp ( bombspot - > Z ( ) , z_top2 , z_top1 ) ) ;
int grp = othersector - > healthceilinggroup ;
if ( grp < = 0 )
2018-11-04 04:53:37 +00:00
grp = 0x80000000 | ( othersector - > sectornum & 0x0FFFFFFF ) ;
2018-10-28 22:08:38 +00:00
PGRA_InsertIfCloser ( damageGroupPos , grp , to3d_upper , bombspot - > Pos ( ) , srcsector , othersector , nullptr , SECPART_Ceiling ) ;
}
}
if ( othersector & & othersector - > healthfloor & & P_CheckLinedefVulnerable ( ln , sd , SECPART_Floor ) )
{
if ( z_bottom2 > z_bottom1 ) // we have front side to hit against
{
DVector3 to3d_lower ( to2d . X , to2d . Y , clamp ( bombspot - > Z ( ) , z_bottom1 , z_bottom2 ) ) ;
int grp = othersector - > healthfloorgroup ;
if ( grp < = 0 )
2018-11-04 04:53:37 +00:00
grp = 0x40000000 | ( othersector - > sectornum & 0x0FFFFFFF ) ;
2018-10-28 22:08:38 +00:00
PGRA_InsertIfCloser ( damageGroupPos , grp , to3d_lower , bombspot - > Pos ( ) , srcsector , othersector , nullptr , SECPART_Floor ) ;
}
}
2018-11-04 04:53:37 +00:00
// check 3d floors
2018-11-05 15:56:49 +00:00
if ( othersector )
2018-11-04 04:53:37 +00:00
{
2018-11-05 15:56:49 +00:00
for ( auto f : othersector - > e - > XFloor . ffloors )
{
if ( ! ( f - > flags & FF_EXISTS ) ) continue ;
if ( ! ( f - > flags & FF_SOLID ) ) continue ;
2018-11-04 04:53:37 +00:00
2018-11-05 15:56:49 +00:00
if ( ! f - > model | | ! f - > model - > health3d ) continue ;
2018-11-04 04:53:37 +00:00
2018-11-05 15:56:49 +00:00
// 3d floors over real ceiling, or under real floor, are ignored
double z_ff_top = clamp ( f - > top . plane - > ZatPoint ( to2d ) , z_bottom2 , z_top2 ) ;
double z_ff_bottom = clamp ( f - > bottom . plane - > ZatPoint ( to2d ) , z_bottom2 , z_top2 ) ;
if ( z_ff_top < z_ff_bottom )
continue ; // also ignore eldritch geometry
2018-11-04 04:53:37 +00:00
2018-11-05 15:56:49 +00:00
DVector3 to3d_ffloor ( to2d . X , to2d . Y , clamp ( bombspot - > Z ( ) , z_ff_bottom , z_ff_top ) ) ;
int grp = f - > model - > health3dgroup ;
if ( grp < = 0 )
grp = 0x20000000 | ( f - > model - > sectornum & 0x0FFFFFFF ) ;
PGRA_InsertIfCloser ( damageGroupPos , grp , to3d_ffloor , bombspot - > Pos ( ) , srcsector , f - > model , nullptr , SECPART_3D ) ;
}
2018-11-04 04:53:37 +00:00
}
2018-10-28 22:08:38 +00:00
}
// damage health groups and sectors.
// if group & 0x80000000, this is sector ceiling without health group
// if grpup & 0x40000000, this is sector floor without health group
// otherwise this is some object in health group
TMap < int , pgra_data_t > : : ConstIterator damageGroupIt ( damageGroupPos ) ;
TMap < int , pgra_data_t > : : ConstPair * damageGroupPair ;
while ( damageGroupIt . NextPair ( damageGroupPair ) )
{
DVector3 pos = damageGroupPair - > Value . pos ;
double dst = ( pos - bombspot - > Pos ( ) ) . Length ( ) ;
int damage = 0 ;
if ( dst < bombdistance )
{
dst = clamp < double > ( dst - ( double ) fulldamagedistance , 0 , dst ) ;
damage = ( int ) ( ( double ) bombdamage * ( 1. - dst * bombdistancefloat ) ) ;
if ( bombsource = = bombspot )
damage = ( int ) ( damage * splashfactor ) ;
}
// for debug
//P_SpawnParticle(damageGroupPair->Value.pos, DVector3(), DVector3(), PalEntry(0xFF, 0x00, 0x00), 1.0, 35, 5.0, 0.0, 0.0, 0);
int grp = damageGroupPair - > Key ;
if ( grp & 0x80000000 ) // sector ceiling
{
assert ( damageGroupPair - > Value . sector ! = nullptr ) ;
P_DamageSector ( damageGroupPair - > Value . sector , bombsource , damage , damagetype , SECPART_Ceiling , pos ) ;
}
else if ( grp & 0x40000000 ) // sector floor
{
assert ( damageGroupPair - > Value . sector ! = nullptr ) ;
P_DamageSector ( damageGroupPair - > Value . sector , bombsource , damage , damagetype , SECPART_Floor , pos ) ;
}
2018-11-04 04:53:37 +00:00
else if ( grp & 0x20000000 ) // sector 3d
{
assert ( damageGroupPair - > Value . sector ! = nullptr ) ;
P_DamageSector ( damageGroupPair - > Value . sector , bombsource , damage , damagetype , SECPART_3D , pos ) ;
}
2018-10-28 22:08:38 +00:00
else
{
assert ( ( damageGroupPair - > Value . sector ! = nullptr ) ! = ( damageGroupPair - > Value . line ! = nullptr ) ) ;
if ( damageGroupPair - > Value . line ! = nullptr )
P_DamageLinedef ( damageGroupPair - > Value . line , bombsource , damage , damagetype , P_PointOnLineSide ( pos . XY ( ) , damageGroupPair - > Value . line ) , pos ) ;
else P_DamageSector ( damageGroupPair - > Value . sector , bombsource , damage , damagetype , damageGroupPair - > Value . secpart , pos ) ;
}
}
}
2018-11-04 04:53:37 +00:00
//==========================================================================
//
// P_ProjectileHitLinedef
//
// Called if P_ExplodeMissile was called against a wall.
//==========================================================================
2018-11-05 15:56:49 +00:00
bool P_ProjectileHitLinedef ( AActor * mo , line_t * line )
2018-11-04 04:53:37 +00:00
{
2018-11-05 15:56:49 +00:00
bool washit = false ;
2018-11-04 04:53:37 +00:00
// detect 3d floor hit
if ( mo - > Blocking3DFloor )
{
if ( mo - > Blocking3DFloor - > health3d > 0 )
2018-11-05 15:56:49 +00:00
{
2018-11-04 04:53:37 +00:00
P_DamageSector ( mo - > Blocking3DFloor , mo , mo - > GetMissileDamage ( ( mo - > flags4 & MF4_STRIFEDAMAGE ) ? 3 : 7 , 1 ) , mo - > DamageType , SECPART_3D , mo - > Pos ( ) ) ;
2018-11-05 15:56:49 +00:00
washit = true ;
}
2018-11-04 04:53:37 +00:00
}
int wside = P_PointOnLineSide ( mo - > Pos ( ) , line ) ;
int oside = ! wside ;
side_t * otherside = line - > sidedef [ oside ] ;
// check if hit upper or lower part
if ( otherside )
{
sector_t * othersector = otherside - > sector ;
// find closest pos from line to MO.
// this logic is so that steep slopes work correctly (value at the line is used, instead of value below the rocket actor)
DVector2 moRelPos = line - > v1 - > p - mo - > Pos ( ) . XY ( ) ;
DVector2 lineNormal = line - > delta . Rotated90CW ( ) . Unit ( ) ;
double moRelDst = lineNormal | moRelPos ;
DVector2 moPos = mo - > Pos ( ) . XY ( ) - lineNormal * fabs ( moRelDst ) ;
double otherfloorz = othersector - > floorplane . ZatPoint ( moPos ) ;
double otherceilingz = othersector - > ceilingplane . ZatPoint ( moPos ) ;
double zbottom = mo - > Pos ( ) . Z ;
double ztop = mo - > Pos ( ) . Z + mo - > Height ;
if ( zbottom < ( otherfloorz + EQUAL_EPSILON ) & & othersector - > healthfloor > 0 & & P_CheckLinedefVulnerable ( line , wside , SECPART_Floor ) )
{
P_DamageSector ( othersector , mo , mo - > GetMissileDamage ( ( mo - > flags4 & MF4_STRIFEDAMAGE ) ? 3 : 7 , 1 ) , mo - > DamageType , SECPART_Floor , mo - > Pos ( ) ) ;
2018-11-05 15:56:49 +00:00
washit = true ;
2018-11-04 04:53:37 +00:00
}
if ( ztop > ( otherceilingz - EQUAL_EPSILON ) & & othersector - > healthceiling > 0 & & P_CheckLinedefVulnerable ( line , wside , SECPART_Ceiling ) )
{
P_DamageSector ( othersector , mo , mo - > GetMissileDamage ( ( mo - > flags4 & MF4_STRIFEDAMAGE ) ? 3 : 7 , 1 ) , mo - > DamageType , SECPART_Ceiling , mo - > Pos ( ) ) ;
2018-11-05 15:56:49 +00:00
washit = true ;
2018-11-04 04:53:37 +00:00
}
}
if ( line - > health > 0 & & P_CheckLinedefVulnerable ( line , wside ) )
{
P_DamageLinedef ( line , mo , mo - > GetMissileDamage ( ( mo - > flags4 & MF4_STRIFEDAMAGE ) ? 3 : 7 , 1 ) , mo - > DamageType , wside , mo - > Pos ( ) ) ;
2018-11-05 15:56:49 +00:00
washit = true ;
2018-11-04 04:53:37 +00:00
}
2018-11-05 15:56:49 +00:00
return washit ;
2018-11-04 04:53:37 +00:00
}
2018-11-05 15:56:49 +00:00
// part = -1 means "detect from blocking"
bool P_ProjectileHitPlane ( AActor * mo , int part )
2018-11-04 04:53:37 +00:00
{
2018-11-05 15:56:49 +00:00
if ( part < 0 )
{
if ( mo - > BlockingCeiling )
part = SECPART_Ceiling ;
else if ( mo - > BlockingFloor )
part = SECPART_Floor ;
}
2018-11-04 04:53:37 +00:00
// detect 3d floor hit
if ( mo - > Blocking3DFloor )
{
if ( mo - > Blocking3DFloor - > health3d > 0 )
2018-11-05 15:56:49 +00:00
{
2018-11-04 04:53:37 +00:00
P_DamageSector ( mo - > Blocking3DFloor , mo , mo - > GetMissileDamage ( ( mo - > flags4 & MF4_STRIFEDAMAGE ) ? 3 : 7 , 1 ) , mo - > DamageType , SECPART_3D , mo - > Pos ( ) ) ;
2018-11-05 15:56:49 +00:00
return true ;
}
return false ;
2018-11-04 04:53:37 +00:00
}
if ( part = = SECPART_Floor & & mo - > Sector - > healthfloor > 0 & & P_CheckSectorVulnerable ( mo - > Sector , SECPART_Floor ) )
2018-11-05 15:56:49 +00:00
{
2018-11-04 04:53:37 +00:00
P_DamageSector ( mo - > Sector , mo , mo - > GetMissileDamage ( ( mo - > flags4 & MF4_STRIFEDAMAGE ) ? 3 : 7 , 1 ) , mo - > DamageType , SECPART_Floor , mo - > Pos ( ) ) ;
2018-11-05 15:56:49 +00:00
return true ;
}
2018-11-04 04:53:37 +00:00
else if ( part = = SECPART_Ceiling & & mo - > Sector - > healthceiling > 0 & & P_CheckSectorVulnerable ( mo - > Sector , SECPART_Ceiling ) )
2018-11-05 15:56:49 +00:00
{
2018-11-04 04:53:37 +00:00
P_DamageSector ( mo - > Sector , mo , mo - > GetMissileDamage ( ( mo - > flags4 & MF4_STRIFEDAMAGE ) ? 3 : 7 , 1 ) , mo - > DamageType , SECPART_Ceiling , mo - > Pos ( ) ) ;
2018-11-05 15:56:49 +00:00
return true ;
}
return false ;
2018-11-04 04:53:37 +00:00
}
2018-10-28 22:08:38 +00:00
//==========================================================================
//
// P_CheckLinedefVulnerable
//
// If sectorpart is <0: returns if linedef is damageable directly
// If sectorpart is valid (SECPART_ enum): returns if sector on sidedef is
// damageable through this line at specified sectorpart
//==========================================================================
bool P_CheckLinedefVulnerable ( line_t * line , int side , int sectorpart )
{
if ( line - > special = = Line_Horizon )
return false ;
side_t * sidedef = line - > sidedef [ side ] ;
if ( ! sidedef )
return false ;
return true ;
}
//==========================================================================
//
// P_CheckSectorVulnerable
//
// Returns true if sector's floor or ceiling is directly damageable
//==========================================================================
bool P_CheckSectorVulnerable ( sector_t * sector , int part )
{
2018-11-04 04:53:37 +00:00
if ( part = = SECPART_3D )
return true ;
2018-10-28 22:08:38 +00:00
FTextureID texture = sector - > GetTexture ( ( part = = SECPART_Ceiling ) ? sector_t : : ceiling : sector_t : : floor ) ;
secplane_t * plane = ( part = = SECPART_Ceiling ) ? & sector - > ceilingplane : & sector - > floorplane ;
if ( texture = = skyflatnum )
return false ;
return true ;
2018-10-31 20:40:08 +00:00
}
///
FSerializer & Serialize ( FSerializer & arc , const char * key , FHealthGroup & g , FHealthGroup * def )
{
if ( arc . BeginObject ( key ) )
{
arc ( " id " , g . id )
( " health " , g . health )
. EndObject ( ) ;
}
return arc ;
}
void P_SerializeHealthGroups ( FSerializer & arc )
{
// todo : stuff
if ( arc . BeginArray ( " healthgroups " ) )
{
if ( arc . isReading ( ) )
{
TArray < FHealthGroup > readGroups ;
int sz = arc . ArraySize ( ) ;
for ( int i = 0 ; i < sz ; i + + )
{
FHealthGroup grp ;
arc ( nullptr , grp ) ;
FHealthGroup * existinggrp = P_GetHealthGroup ( grp . id ) ;
if ( ! existinggrp )
continue ;
existinggrp - > health = grp . health ;
}
}
else
{
TMap < int , FHealthGroup > : : ConstIterator it ( level . healthGroups ) ;
TMap < int , FHealthGroup > : : ConstPair * pair ;
while ( it . NextPair ( pair ) )
{
FHealthGroup grp = pair - > Value ;
arc ( nullptr , grp ) ;
}
}
arc . EndArray ( ) ;
}
2018-11-04 04:53:37 +00:00
}