2006-03-20 17:12:09 +00:00
/*
2014-06-01 17:55:23 +00:00
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING . txt .
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below .
* See COPYING . txt for license details .
2006-03-20 17:12:09 +00:00
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ( " PARALLAX " ) . PARALLAX , IN DISTRIBUTING THE CODE TO
END - USERS , AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN , GRANTS A
ROYALTY - FREE , PERPETUAL LICENSE TO SUCH END - USERS FOR USE BY SUCH END - USERS
IN USING , DISPLAYING , AND CREATING DERIVATIVE WORKS THEREOF , SO LONG AS
SUCH USE , DISPLAY OR CREATION IS FOR NON - COMMERCIAL , ROYALTY OR REVENUE
FREE PURPOSES . IN NO EVENT SHALL THE END - USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE - BEARING PURPOSES . THE END - USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE .
COPYRIGHT 1993 - 1999 PARALLAX SOFTWARE CORPORATION . ALL RIGHTS RESERVED .
*/
# include "wall.h"
# include "text.h"
# include "fireball.h"
# include "textures.h"
# include "newdemo.h"
# include "multi.h"
2013-12-26 04:18:28 +00:00
# include "gameseq.h"
2014-02-02 05:30:48 +00:00
# include "physfs-serial.h"
2013-12-26 04:18:28 +00:00
# include "gameseg.h"
2013-03-03 01:03:33 +00:00
# include "hudmsg.h"
2006-03-20 17:12:09 +00:00
# include "effects.h"
2018-12-30 00:43:57 +00:00
# include "d_enumerate.h"
2020-08-10 03:45:14 +00:00
# include "d_levelstate.h"
2019-05-04 18:27:36 +00:00
# include "d_range.h"
2014-04-27 23:12:34 +00:00
# include "compiler-range_for.h"
2022-12-31 16:21:47 +00:00
# include "partial_range.h"
2014-10-02 03:02:34 +00:00
# include "segiter.h"
2020-07-05 23:34:33 +00:00
# include "d_zip.h"
2014-04-27 23:12:34 +00:00
2006-03-20 17:12:09 +00:00
// Special door on boss level which is locked if not in multiplayer...sorry for this awful solution --MK.
# define BOSS_LOCKED_DOOR_LEVEL 7
# define BOSS_LOCKED_DOOR_SEG 595
2022-06-05 17:44:52 +00:00
# define BOSS_LOCKED_DOOR_SIDE sidenum_t::WFRONT
2006-03-20 17:12:09 +00:00
2015-12-22 04:18:50 +00:00
namespace dcx {
2014-02-02 05:30:48 +00:00
unsigned Num_wall_anims ;
2015-12-22 04:18:50 +00:00
}
2006-03-20 17:12:09 +00:00
2015-12-22 04:18:50 +00:00
namespace dsx {
2016-11-26 22:51:48 +00:00
namespace {
struct ad_removal_predicate
{
bool operator ( ) ( active_door & ) const ;
} ;
2016-11-26 22:51:48 +00:00
struct find_active_door_predicate
{
const wallnum_t wall_num ;
explicit find_active_door_predicate ( const wallnum_t i ) :
wall_num ( i )
{
}
bool operator ( ) ( active_door & d ) const {
if ( d . front_wallnum [ 0 ] = = wall_num )
return true ;
if ( d . back_wallnum [ 0 ] = = wall_num )
return true ;
if ( d . n_parts ! = 2 )
return false ;
if ( d . front_wallnum [ 1 ] = = wall_num )
return true ;
if ( d . back_wallnum [ 1 ] = = wall_num )
return true ;
return false ;
}
} ;
2016-11-26 22:51:48 +00:00
}
2015-12-22 04:18:50 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
# include "collide.h"
2015-12-22 04:18:50 +00:00
namespace dsx {
2017-10-14 17:10:30 +00:00
constexpr std : : integral_constant < unsigned , F1_0 > CLOAKING_WALL_TIME { } ;
2006-03-20 17:12:09 +00:00
2016-11-12 18:10:09 +00:00
namespace {
struct cwframe
{
wall & w ;
2020-05-02 21:18:42 +00:00
std : : array < uvl , 4 > & uvls ;
2018-12-30 00:43:57 +00:00
cwframe ( fvmsegptr & vmsegptr , wall & wr ) :
2016-11-12 18:10:10 +00:00
w ( wr ) ,
2018-12-13 02:31:38 +00:00
uvls ( vmsegptr ( w . segnum ) - > unique_segment : : sides [ w . sidenum ] . uvls )
2016-11-12 18:10:09 +00:00
{
}
} ;
2016-11-12 18:10:10 +00:00
struct cwresult
{
bool remove ;
bool record ;
cwresult ( ) = default ;
explicit cwresult ( bool r ) :
remove ( false ) , record ( r )
{
}
} ;
2016-11-19 17:24:54 +00:00
struct cw_removal_predicate
{
2018-12-30 00:43:57 +00:00
fvmsegptr & vmsegptr ;
wall_array & Walls ;
2016-11-19 17:24:54 +00:00
unsigned num_cloaking_walls = 0 ;
bool operator ( ) ( cloaking_wall & d ) ;
2018-12-31 04:46:16 +00:00
cw_removal_predicate ( fvmsegptr & v , wall_array & w ) :
vmsegptr ( v ) , Walls ( w )
{
}
2016-11-19 17:24:54 +00:00
} ;
2016-11-19 17:24:54 +00:00
struct find_cloaked_wall_predicate
{
2017-06-10 03:31:02 +00:00
const vmwallidx_t w ;
find_cloaked_wall_predicate ( const vmwallidx_t i ) :
2016-11-19 17:24:54 +00:00
w ( i )
{
}
bool operator ( ) ( const cloaking_wall & cw ) const
{
return cw . front_wallnum = = w | | cw . back_wallnum = = w ;
}
} ;
2016-11-12 18:10:09 +00:00
}
2015-12-22 04:18:50 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2020-12-19 16:13:26 +00:00
namespace {
2018-06-24 05:06:15 +00:00
static std : : pair < uint_fast32_t , uint_fast32_t > get_transparency_check_values ( const unique_side & side )
2015-01-12 00:26:03 +00:00
{
2020-08-24 01:31:28 +00:00
if ( const auto masked_tmap_num2 = static_cast < uint_fast32_t > ( get_texture_index ( side . tmap_num2 ) ) )
2015-01-12 00:26:03 +00:00
return { masked_tmap_num2 , BM_FLAG_SUPER_TRANSPARENT } ;
2020-09-11 03:08:02 +00:00
return { get_texture_index ( side . tmap_num ) , BM_FLAG_TRANSPARENT } ;
2015-01-12 00:26:03 +00:00
}
2006-03-20 17:12:09 +00:00
// This function determines whether the current segment/side is transparent
// 1 = YES
// 0 = NO
2018-06-24 05:06:15 +00:00
static uint_fast32_t check_transparency ( const GameBitmaps_array & GameBitmaps , const Textures_array & Textures , const unique_side & side )
2006-03-20 17:12:09 +00:00
{
2018-06-24 05:06:15 +00:00
const auto & & v = get_transparency_check_values ( side ) ;
2022-12-31 16:21:47 +00:00
return GameBitmaps [ Textures [ v . first ] ] . get_flag_mask ( v . second ) ;
2006-03-20 17:12:09 +00:00
}
2020-12-19 16:13:26 +00:00
}
2006-03-20 17:12:09 +00:00
//-----------------------------------------------------------------
// This function checks whether we can fly through the given side.
// In other words, whether or not we have a 'doorway'
// Flags:
2023-02-18 14:30:59 +00:00
// WALL_IS_DOORWAY_FLAG::FLY 1
// WALL_IS_DOORWAY_FLAG::RENDER 2
// WALL_IS_DOORWAY_FLAG::RENDPAST 4
2006-03-20 17:12:09 +00:00
// Return values:
2023-02-18 14:30:59 +00:00
// wall_is_doorway_result::wall 2 // 0/1/0 wall
// wall_is_doorway_result::transparent_wall 6 // 0/1/1 transparent wall
// wall_is_doorway_result::illusory_wall 3 // 1/1/0 illusory wall
// wall_is_doorway_result::transillusory_wall 7 // 1/1/1 transparent illusory wall
// wall_is_doorway_result::no_wall 5 // 1/0/1 no wall, can fly through
2016-08-25 04:05:32 +00:00
namespace dsx {
2018-06-24 05:06:15 +00:00
2020-12-19 16:13:26 +00:00
namespace {
2023-01-29 20:42:03 +00:00
static wall_is_doorway_result wall_is_doorway ( const GameBitmaps_array & GameBitmaps , const Textures_array & Textures , fvcwallptr & vcwallptr , const shared_side & sside , const unique_side & uside )
2006-03-20 17:12:09 +00:00
{
2018-06-24 05:06:15 +00:00
auto & w = * vcwallptr ( sside . wall_num ) ;
2015-01-12 00:26:03 +00:00
const auto type = w . type ;
2006-03-20 17:12:09 +00:00
if ( type = = WALL_OPEN )
2023-02-18 14:30:59 +00:00
return wall_is_doorway_result : : no_wall ;
2006-03-20 17:12:09 +00:00
2015-01-12 00:26:03 +00:00
# if defined(DXX_BUILD_DESCENT_II)
if ( unlikely ( type = = WALL_CLOAKED ) )
2023-02-18 14:30:59 +00:00
return wall_is_doorway_result : : cloaked_wall ;
2015-01-12 00:26:03 +00:00
# endif
const auto flags = w . flags ;
2006-03-20 17:12:09 +00:00
if ( type = = WALL_ILLUSION ) {
2021-11-01 03:37:19 +00:00
if ( flags & wall_flag : : illusion_off )
2023-02-18 14:30:59 +00:00
return wall_is_doorway_result : : no_wall ;
2006-03-20 17:12:09 +00:00
else {
2018-06-24 05:06:15 +00:00
if ( check_transparency ( GameBitmaps , Textures , uside ) )
2023-02-18 14:30:59 +00:00
return wall_is_doorway_result : : transillusory_wall ;
2006-03-20 17:12:09 +00:00
else
2023-02-18 14:30:59 +00:00
return wall_is_doorway_result : : illusory_wall ;
2006-03-20 17:12:09 +00:00
}
}
if ( type = = WALL_BLASTABLE ) {
2021-11-01 03:37:19 +00:00
if ( flags & wall_flag : : blasted )
2023-02-18 14:30:59 +00:00
return wall_is_doorway_result : : transillusory_wall ;
2006-03-20 17:12:09 +00:00
}
2018-06-20 04:16:06 +00:00
else
{
2021-11-01 03:37:19 +00:00
if ( unlikely ( flags & wall_flag : : door_opened ) )
2023-02-18 14:30:59 +00:00
return wall_is_doorway_result : : transillusory_wall ;
2021-11-01 03:37:19 +00:00
if ( likely ( type = = WALL_DOOR ) & & unlikely ( w . state = = wall_state : : opening ) )
2023-02-18 14:30:59 +00:00
return wall_is_doorway_result : : transparent_wall ;
2018-06-20 04:16:06 +00:00
}
2006-03-20 17:12:09 +00:00
// If none of the above flags are set, there is no doorway.
2018-06-24 05:06:15 +00:00
if ( check_transparency ( GameBitmaps , Textures , uside ) )
2023-02-18 14:30:59 +00:00
return wall_is_doorway_result : : transparent_wall ;
2006-03-20 17:12:09 +00:00
else
2023-02-18 14:30:59 +00:00
return wall_is_doorway_result : : wall ; // There are children behind the door.
2006-03-20 17:12:09 +00:00
}
2018-12-30 00:43:57 +00:00
2020-12-19 16:13:26 +00:00
}
2023-01-29 20:42:03 +00:00
wall_is_doorway_result WALL_IS_DOORWAY ( const GameBitmaps_array & GameBitmaps , const Textures_array & Textures , fvcwallptr & vcwallptr , const cscusegment seg , const sidenum_t side )
2018-12-30 00:43:57 +00:00
{
2019-12-27 02:02:23 +00:00
const auto child = seg . s . children [ side ] ;
2018-12-30 00:43:57 +00:00
if ( unlikely ( child = = segment_none ) )
2023-02-18 14:30:59 +00:00
return wall_is_doorway_result : : wall ;
2018-12-30 00:43:57 +00:00
if ( unlikely ( child = = segment_exit ) )
2023-02-18 14:30:59 +00:00
return wall_is_doorway_result : : external ;
2019-12-27 02:02:23 +00:00
auto & sside = seg . s . sides [ side ] ;
2018-12-30 00:43:57 +00:00
if ( likely ( sside . wall_num = = wall_none ) )
2023-02-18 14:30:59 +00:00
return wall_is_doorway_result : : no_wall ;
2019-12-27 02:02:23 +00:00
auto & uside = seg . u . sides [ side ] ;
2018-12-30 00:43:57 +00:00
return wall_is_doorway ( GameBitmaps , Textures , vcwallptr , sside , uside ) ;
}
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
//-----------------------------------------------------------------
// Initializes all the walls (in other words, no special walls)
2022-04-17 22:27:19 +00:00
void wall_init ( d_level_unique_wall_subsystem_state & LevelUniqueWallSubsystemState )
2006-03-20 17:12:09 +00:00
{
2018-08-12 21:08:07 +00:00
init_exploding_walls ( ) ;
2018-12-30 00:43:58 +00:00
auto & Walls = LevelUniqueWallSubsystemState . Walls ;
2016-02-06 22:12:54 +00:00
Walls . set_count ( 0 ) ;
2014-04-27 23:12:34 +00:00
range_for ( auto & w , Walls )
{
w . segnum = segment_none ;
2022-01-09 15:25:42 +00:00
w . sidenum = side_none ;
2014-04-27 23:12:34 +00:00
w . type = WALL_NORMAL ;
2021-11-01 03:37:19 +00:00
w . flags = { } ;
2014-04-27 23:12:34 +00:00
w . hps = 0 ;
2021-09-04 12:17:14 +00:00
w . trigger = trigger_none ;
2014-04-27 23:12:34 +00:00
w . clip_num = - 1 ;
2020-12-26 21:17:29 +00:00
w . linked_wall = wall_none ;
2014-04-27 23:12:34 +00:00
}
2018-12-30 00:43:58 +00:00
auto & ActiveDoors = LevelUniqueWallSubsystemState . ActiveDoors ;
2016-12-10 17:51:08 +00:00
ActiveDoors . set_count ( 0 ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2018-12-30 00:43:58 +00:00
auto & CloakingWalls = LevelUniqueWallSubsystemState . CloakingWalls ;
2016-11-19 17:24:54 +00:00
CloakingWalls . set_count ( 0 ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
# endif
//set the tmap_num or tmap_num2 field for a wall/door
2022-01-15 20:39:10 +00:00
void wall_set_tmap_num ( const wclip & anim , const vmsegptridx_t seg , const sidenum_t side , const vmsegptridx_t csegp , const sidenum_t cside , const unsigned frame_num )
2006-03-20 17:12:09 +00:00
{
2018-08-12 21:08:07 +00:00
const auto newdemo_state = Newdemo_state ;
if ( newdemo_state = = ND_STATE_PLAYBACK )
return ;
2006-03-20 17:12:09 +00:00
2018-08-12 21:08:07 +00:00
const auto tmap = anim . frames [ frame_num ] ;
2018-12-13 02:31:38 +00:00
auto & uside = seg - > unique_segment : : sides [ side ] ;
auto & cuside = csegp - > unique_segment : : sides [ cside ] ;
2018-08-12 21:08:07 +00:00
if ( anim . flags & WCF_TMAP1 ) {
2020-09-11 03:08:02 +00:00
const texture1_value t1 { tmap } ;
if ( t1 ! = uside . tmap_num | | t1 ! = cuside . tmap_num )
2010-07-15 06:09:23 +00:00
{
2020-09-11 03:08:02 +00:00
uside . tmap_num = cuside . tmap_num = t1 ;
2018-08-12 21:08:07 +00:00
if ( newdemo_state = = ND_STATE_RECORDING )
2020-09-11 03:08:02 +00:00
newdemo_record_wall_set_tmap_num1 ( seg , side , csegp , cside , t1 ) ;
2010-07-15 06:09:23 +00:00
}
2006-03-20 17:12:09 +00:00
} else {
2020-08-24 01:31:28 +00:00
const texture2_value t2 { tmap } ;
if ( t2 ! = uside . tmap_num2 | | t2 ! = cuside . tmap_num2 )
2010-07-15 06:09:23 +00:00
{
2020-08-24 01:31:28 +00:00
uside . tmap_num2 = cuside . tmap_num2 = t2 ;
2018-08-12 21:08:07 +00:00
if ( newdemo_state = = ND_STATE_RECORDING )
2020-08-24 01:31:28 +00:00
newdemo_record_wall_set_tmap_num2 ( seg , side , csegp , cside , t2 ) ;
2010-07-15 06:09:23 +00:00
}
2006-03-20 17:12:09 +00:00
}
}
2020-12-19 16:13:26 +00:00
}
namespace {
2006-03-20 17:12:09 +00:00
// -------------------------------------------------------------------------------
//when the wall has used all its hitpoints, this will destroy it
2022-01-15 20:39:10 +00:00
static void blast_blastable_wall ( const vmsegptridx_t seg , const sidenum_t side , wall & w0 )
2006-03-20 17:12:09 +00:00
{
2020-05-17 23:35:25 +00:00
auto & LevelSharedVertexState = LevelSharedSegmentState . get_vertex_state ( ) ;
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
2020-05-17 23:35:25 +00:00
auto & Vertices = LevelSharedVertexState . get_vertices ( ) ;
2019-03-03 00:31:08 +00:00
auto & vmobjptr = Objects . vmptr ;
2018-12-13 02:31:38 +00:00
auto & sside = seg - > shared_segment : : sides [ side ] ;
const auto wall_num = sside . wall_num ;
2016-02-12 04:02:28 +00:00
w0 . hps = - 1 ; //say it's blasted
2006-03-20 17:12:09 +00:00
2020-08-24 01:31:28 +00:00
const auto & & csegp = seg . absolute_sibling ( seg - > shared_segment : : children [ side ] ) ;
2014-09-06 04:06:18 +00:00
auto Connectside = find_connect_side ( seg , csegp ) ;
2016-01-03 20:21:35 +00:00
Assert ( Connectside ! = side_none ) ;
2018-12-13 02:31:38 +00:00
const auto cwall_num = csegp - > shared_segment : : sides [ Connectside ] . wall_num ;
2018-12-30 00:43:58 +00:00
auto & Walls = LevelUniqueWallSubsystemState . Walls ;
2020-04-04 19:30:22 +00:00
auto & WallAnims = GameSharedState . WallAnims ;
2018-12-30 00:43:58 +00:00
auto & imwallptr = Walls . imptr ;
2017-06-10 03:31:02 +00:00
const auto & & w1 = imwallptr ( cwall_num ) ;
2016-02-12 04:02:28 +00:00
if ( w1 )
2018-08-04 17:52:57 +00:00
LevelUniqueStuckObjectState . kill_stuck_objects ( vmobjptr , cwall_num ) ;
2018-12-13 02:31:38 +00:00
LevelUniqueStuckObjectState . kill_stuck_objects ( vmobjptr , wall_num ) ;
2016-07-21 01:43:21 +00:00
flush_fcd_cache ( ) ;
2006-03-20 17:12:09 +00:00
2016-02-12 04:02:28 +00:00
const auto a = w0 . clip_num ;
2020-04-04 19:30:22 +00:00
auto & wa = WallAnims [ a ] ;
2006-03-20 17:12:09 +00:00
//if this is an exploding wall, explode it
2020-04-04 19:30:22 +00:00
if ( wa . flags & WCF_EXPLODES )
2018-12-30 00:43:57 +00:00
{
auto & vcvertptr = Vertices . vcptr ;
2022-01-09 15:25:42 +00:00
explode_wall ( vcvertptr , seg , static_cast < sidenum_t > ( side ) , w0 ) ;
2018-12-30 00:43:57 +00:00
}
2006-03-20 17:12:09 +00:00
else {
//if not exploding, set final frame, and make door passable
2020-04-04 19:30:22 +00:00
const auto n = wa . num_frames ;
2021-11-01 03:37:19 +00:00
w0 . flags | = wall_flag : : blasted ;
2016-02-12 04:02:28 +00:00
if ( w1 )
2021-11-01 03:37:19 +00:00
w1 - > flags | = wall_flag : : blasted ;
2020-04-04 19:30:22 +00:00
wall_set_tmap_num ( wa , seg , side , csegp , Connectside , n - 1 ) ;
2006-03-20 17:12:09 +00:00
}
}
2020-12-19 16:13:26 +00:00
}
2013-06-10 22:17:00 +00:00
2006-03-20 17:12:09 +00:00
//-----------------------------------------------------------------
// Destroys a blastable wall.
2022-01-15 20:39:10 +00:00
void wall_destroy ( const vmsegptridx_t seg , const sidenum_t side )
2006-03-20 17:12:09 +00:00
{
2018-12-30 00:43:58 +00:00
auto & Walls = LevelUniqueWallSubsystemState . Walls ;
auto & vmwallptr = Walls . vmptr ;
2018-12-13 02:31:38 +00:00
auto & w = * vmwallptr ( seg - > shared_segment : : sides [ side ] . wall_num ) ;
2016-02-12 04:02:28 +00:00
if ( w . type = = WALL_BLASTABLE )
2018-12-30 00:43:58 +00:00
blast_blastable_wall ( seg , side , w ) ;
2006-03-20 17:12:09 +00:00
else
Error ( " Hey bub, you are trying to destroy an indestructable wall. " ) ;
}
//-----------------------------------------------------------------
// Deteriorate appearance of wall. (Changes bitmap (paste-ons))
2022-01-15 20:39:10 +00:00
void wall_damage ( const vmsegptridx_t seg , const sidenum_t side , fix damage )
2006-03-20 17:12:09 +00:00
{
2020-04-04 19:30:22 +00:00
auto & WallAnims = GameSharedState . WallAnims ;
2016-02-12 04:02:28 +00:00
int i ;
2006-03-20 17:12:09 +00:00
2018-12-13 02:31:38 +00:00
auto & sside = seg - > shared_segment : : sides [ side ] ;
const auto wall_num = sside . wall_num ;
if ( wall_num = = wall_none ) {
2006-03-20 17:12:09 +00:00
return ;
}
2018-12-30 00:43:58 +00:00
auto & Walls = LevelUniqueWallSubsystemState . Walls ;
auto & vmwallptr = Walls . vmptr ;
2018-12-13 02:31:38 +00:00
auto & w0 = * vmwallptr ( wall_num ) ;
2016-02-12 04:02:28 +00:00
if ( w0 . type ! = WALL_BLASTABLE )
2006-03-20 17:12:09 +00:00
return ;
2006-03-20 16:43:15 +00:00
2021-11-01 03:37:19 +00:00
if ( ! ( w0 . flags & wall_flag : : blasted ) & & w0 . hps > = 0 )
2006-03-20 17:12:09 +00:00
{
2020-08-24 01:31:28 +00:00
const auto & & csegp = seg . absolute_sibling ( seg - > shared_segment : : children [ side ] ) ;
2014-09-06 04:06:18 +00:00
auto Connectside = find_connect_side ( seg , csegp ) ;
2016-01-03 20:21:35 +00:00
Assert ( Connectside ! = side_none ) ;
2018-12-13 02:31:38 +00:00
const auto cwall_num = csegp - > shared_segment : : sides [ Connectside ] . wall_num ;
2018-12-30 00:43:58 +00:00
auto & imwallptr = Walls . imptr ;
2018-08-12 21:08:07 +00:00
if ( const auto & & w1p = imwallptr ( cwall_num ) )
{
auto & w1 = * w1p ;
w1 . hps - = damage ;
}
2016-02-12 04:02:28 +00:00
w0 . hps - = damage ;
const auto a = w0 . clip_num ;
const auto n = WallAnims [ a ] . num_frames ;
2006-03-20 17:12:09 +00:00
2016-02-12 04:02:28 +00:00
if ( w0 . hps < WALL_HPS * 1 / n ) {
2018-12-30 00:43:58 +00:00
blast_blastable_wall ( seg , side , w0 ) ;
2006-03-20 17:12:09 +00:00
if ( Game_mode & GM_MULTI )
2016-02-12 04:02:28 +00:00
multi_send_door_open ( seg , side , w0 . flags ) ;
2006-03-20 17:12:09 +00:00
}
else
for ( i = 0 ; i < n ; i + + )
2016-02-12 04:02:28 +00:00
if ( w0 . hps < WALL_HPS * ( n - i ) / n )
{
2018-08-12 21:08:07 +00:00
wall_set_tmap_num ( WallAnims [ a ] , seg , side , csegp , Connectside , i ) ;
2006-03-20 17:12:09 +00:00
}
}
}
//-----------------------------------------------------------------
// Opens a door
2016-08-25 04:05:32 +00:00
namespace dsx {
2022-01-15 20:39:10 +00:00
void wall_open_door ( const vmsegptridx_t seg , const sidenum_t side )
2006-03-20 17:12:09 +00:00
{
2020-05-17 23:35:25 +00:00
auto & LevelSharedVertexState = LevelSharedSegmentState . get_vertex_state ( ) ;
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
2020-05-17 23:35:25 +00:00
auto & Vertices = LevelSharedVertexState . get_vertices ( ) ;
2020-04-04 19:30:22 +00:00
auto & WallAnims = GameSharedState . WallAnims ;
2019-03-03 00:31:08 +00:00
auto & vmobjptr = Objects . vmptr ;
2006-03-20 17:12:09 +00:00
active_door * d ;
2018-12-13 02:31:38 +00:00
auto & sside = seg - > shared_segment : : sides [ side ] ;
const auto wall_num = sside . wall_num ;
2018-12-30 00:43:58 +00:00
auto & Walls = LevelUniqueWallSubsystemState . Walls ;
auto & vmwallptr = Walls . vmptr ;
2017-06-10 03:31:02 +00:00
wall * const w = vmwallptr ( wall_num ) ;
2018-12-13 02:31:38 +00:00
LevelUniqueStuckObjectState . kill_stuck_objects ( vmobjptr , wall_num ) ;
2006-03-20 17:12:09 +00:00
2021-11-01 03:37:19 +00:00
if ( w - > state = = wall_state : : opening | | //already opening
w - > state = = wall_state : : waiting ) //open, waiting to close
2013-03-03 01:03:33 +00:00
return ;
# if defined(DXX_BUILD_DESCENT_II)
2021-11-01 03:37:19 +00:00
if ( w - > state = = wall_state : : open ) //open, & staying open
2006-03-20 17:12:09 +00:00
return ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2018-12-30 00:43:58 +00:00
auto & ActiveDoors = LevelUniqueWallSubsystemState . ActiveDoors ;
auto & vmactdoorptr = ActiveDoors . vmptr ;
2021-11-01 03:37:19 +00:00
if ( w - > state = = wall_state : : closing ) { //closing, so reuse door
2022-12-31 16:21:47 +00:00
const auto & & r = ranges : : subrange ( vmactdoorptr ) ;
2022-10-09 23:15:20 +00:00
const auto & & re = r . end ( ) ;
2022-10-31 00:51:32 +00:00
const auto & & i = ranges : : find_if ( r . begin ( ) , re , find_active_door_predicate ( wall_num ) ) ;
2022-10-09 23:15:20 +00:00
if ( i = = re ) // likely in demo playback or multiplayer
2010-07-15 05:40:25 +00:00
{
2016-12-10 17:51:08 +00:00
const auto c = ActiveDoors . get_count ( ) ;
ActiveDoors . set_count ( c + 1 ) ;
2017-06-10 03:31:02 +00:00
d = vmactdoorptr ( static_cast < actdoornum_t > ( c ) ) ;
2010-07-15 05:40:25 +00:00
d - > time = 0 ;
}
else
{
2018-06-06 04:53:45 +00:00
d = * i ;
2010-07-15 05:40:25 +00:00
d - > time = WallAnims [ w - > clip_num ] . play_time - d - > time ;
if ( d - > time < 0 )
d - > time = 0 ;
}
2006-03-20 17:12:09 +00:00
}
else { //create new door
2021-11-01 03:37:19 +00:00
Assert ( w - > state = = wall_state : : closed ) ;
2016-12-10 17:51:08 +00:00
const auto i = ActiveDoors . get_count ( ) ;
ActiveDoors . set_count ( i + 1 ) ;
2017-06-10 03:31:02 +00:00
d = vmactdoorptr ( static_cast < actdoornum_t > ( i ) ) ;
2006-03-20 17:12:09 +00:00
d - > time = 0 ;
}
2021-11-01 03:37:19 +00:00
w - > state = wall_state : : opening ;
2006-03-20 17:12:09 +00:00
// So that door can't be shot while opening
2020-08-24 01:31:28 +00:00
const auto & & csegp = vcsegptr ( seg - > shared_segment : : children [ side ] ) ;
2014-09-06 04:06:18 +00:00
auto Connectside = find_connect_side ( seg , csegp ) ;
2016-01-03 20:21:35 +00:00
if ( Connectside ! = side_none )
2008-10-16 17:27:02 +00:00
{
2018-12-13 02:31:38 +00:00
const auto cwall_num = csegp - > shared_segment : : sides [ Connectside ] . wall_num ;
2018-12-30 00:43:58 +00:00
auto & imwallptr = Walls . imptr ;
2017-06-10 03:31:02 +00:00
if ( const auto & & w1 = imwallptr ( cwall_num ) )
2008-10-16 17:27:02 +00:00
{
2021-11-01 03:37:19 +00:00
w1 - > state = wall_state : : opening ;
2008-10-16 17:27:02 +00:00
d - > back_wallnum [ 0 ] = cwall_num ;
}
2018-12-13 02:31:38 +00:00
d - > front_wallnum [ 0 ] = seg - > shared_segment : : sides [ side ] . wall_num ;
2008-10-16 17:27:02 +00:00
}
else
2022-06-05 17:44:52 +00:00
con_printf ( CON_URGENT , " Illegal Connectside %i in wall_open_door on segment %hu. Trying to hop over. Please check your level! " , underlying_value ( side ) , seg . get_unchecked_index ( ) ) ;
2006-03-20 17:12:09 +00:00
if ( Newdemo_state = = ND_STATE_RECORDING ) {
2014-10-02 03:02:34 +00:00
newdemo_record_door_opening ( seg , side ) ;
2006-03-20 17:12:09 +00:00
}
2016-02-06 22:12:54 +00:00
if ( w - > linked_wall ! = wall_none )
{
2017-06-10 03:31:02 +00:00
wall * const w2 = vmwallptr ( w - > linked_wall ) ;
2006-03-20 17:12:09 +00:00
2018-12-13 02:31:38 +00:00
Assert ( w2 - > linked_wall = = seg - > shared_segment : : sides [ side ] . wall_num ) ;
2006-03-20 17:12:09 +00:00
//Assert(!(w2->flags & WALL_DOOR_OPENING || w2->flags & WALL_DOOR_OPENED));
2021-11-01 03:37:19 +00:00
w2 - > state = wall_state : : opening ;
2006-03-20 17:12:09 +00:00
2015-07-12 01:04:21 +00:00
const auto & & seg2 = vcsegptridx ( w2 - > segnum ) ;
2020-08-24 01:31:28 +00:00
const auto & & csegp2 = vcsegptr ( seg2 - > shared_segment : : children [ w2 - > sidenum ] ) ;
2020-01-24 01:12:43 +00:00
Connectside = find_connect_side ( seg2 , csegp2 ) ;
2016-01-03 20:21:35 +00:00
Assert ( Connectside ! = side_none ) ;
2020-01-24 01:12:43 +00:00
const auto cwall_num = csegp2 - > shared_segment : : sides [ Connectside ] . wall_num ;
2018-12-30 00:43:58 +00:00
auto & imwallptr = Walls . imptr ;
2017-06-10 03:31:02 +00:00
if ( const auto & & w3 = imwallptr ( cwall_num ) )
2021-11-01 03:37:19 +00:00
w3 - > state = wall_state : : opening ;
2006-03-20 17:12:09 +00:00
d - > n_parts = 2 ;
d - > front_wallnum [ 1 ] = w - > linked_wall ;
d - > back_wallnum [ 1 ] = cwall_num ;
}
else
d - > n_parts = 1 ;
if ( Newdemo_state ! = ND_STATE_PLAYBACK )
{
// NOTE THE LINK TO ABOVE!!!!
2018-12-30 00:43:57 +00:00
auto & vcvertptr = Vertices . vcptr ;
2018-03-12 03:43:47 +00:00
const auto & & cp = compute_center_point_on_side ( vcvertptr , seg , side ) ;
2020-04-04 19:30:22 +00:00
const auto open_sound = WallAnims [ w - > clip_num ] . open_sound ;
if ( open_sound > - 1 )
digi_link_sound_to_pos ( open_sound , seg , side , cp , 0 , F1_0 ) ;
2006-03-20 17:12:09 +00:00
}
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
//-----------------------------------------------------------------
// start the transition from closed -> open wall
2022-01-15 20:39:10 +00:00
void start_wall_cloak ( const vmsegptridx_t seg , const sidenum_t side )
2006-03-20 17:12:09 +00:00
{
2020-05-17 23:35:25 +00:00
auto & LevelSharedVertexState = LevelSharedSegmentState . get_vertex_state ( ) ;
auto & Vertices = LevelSharedVertexState . get_vertices ( ) ;
2006-03-20 17:12:09 +00:00
cloaking_wall * d ;
if ( Newdemo_state = = ND_STATE_PLAYBACK ) return ;
2018-12-30 00:43:58 +00:00
auto & Walls = LevelUniqueWallSubsystemState . Walls ;
const auto & & w = Walls . vmptridx ( seg - > shared_segment : : sides [ side ] . wall_num ) ;
2006-03-20 17:12:09 +00:00
2021-11-01 03:37:19 +00:00
if ( w - > type = = WALL_OPEN | | w - > state = = wall_state : : cloaking ) //already open or cloaking
2006-03-20 17:12:09 +00:00
return ;
2015-07-18 21:01:55 +00:00
const auto & & csegp = vcsegptr ( seg - > children [ side ] ) ;
2014-09-06 04:06:18 +00:00
auto Connectside = find_connect_side ( seg , csegp ) ;
2016-01-03 20:21:35 +00:00
Assert ( Connectside ! = side_none ) ;
2018-12-13 02:31:38 +00:00
const auto cwall_num = csegp - > shared_segment : : sides [ Connectside ] . wall_num ;
2006-03-20 17:12:09 +00:00
2018-12-30 00:43:58 +00:00
auto & CloakingWalls = LevelUniqueWallSubsystemState . CloakingWalls ;
2021-11-01 03:37:19 +00:00
if ( w - > state = = wall_state : : decloaking )
{ //decloaking, so reuse door
2022-12-31 16:21:47 +00:00
const auto & & r = ranges : : subrange ( CloakingWalls . vmptr ) ;
2022-10-09 23:15:20 +00:00
const auto & & re = r . end ( ) ;
2022-10-31 00:51:32 +00:00
const auto & & i = ranges : : find_if ( r . begin ( ) , re , find_cloaked_wall_predicate ( w ) ) ;
2022-10-09 23:15:20 +00:00
if ( i = = re )
2016-11-19 17:24:54 +00:00
{
d_debugbreak ( ) ;
return ;
2006-03-20 17:12:09 +00:00
}
2018-06-06 04:53:45 +00:00
d = * i ;
2016-11-19 17:24:54 +00:00
d - > time = CLOAKING_WALL_TIME - d - > time ;
2006-03-20 17:12:09 +00:00
}
2021-11-01 03:37:19 +00:00
else if ( w - > state = = wall_state : : closed ) { //create new door
2023-01-29 20:42:03 +00:00
const auto c = CloakingWalls . get_count ( ) ;
2016-11-19 17:24:54 +00:00
if ( c > = CloakingWalls . size ( ) )
{
2006-03-20 17:12:09 +00:00
Int3 ( ) ; //ran out of cloaking wall slots
w - > type = WALL_OPEN ;
2022-01-15 20:39:10 +00:00
if ( const auto & & w1 = Walls . vmptr . check_untrusted ( cwall_num ) )
( * w1 ) - > type = WALL_OPEN ;
2006-03-20 17:12:09 +00:00
return ;
}
2016-11-19 17:24:54 +00:00
CloakingWalls . set_count ( c + 1 ) ;
2023-01-29 20:42:03 +00:00
d = CloakingWalls . vmptr ( clwallnum_t { c } ) ;
2016-11-19 17:24:54 +00:00
d - > time = 0 ;
2006-03-20 17:12:09 +00:00
}
else {
Int3 ( ) ; //unexpected wall state
return ;
}
2021-11-01 03:37:19 +00:00
w - > state = wall_state : : cloaking ;
2018-12-30 00:43:58 +00:00
if ( const auto & & w1 = Walls . imptr ( cwall_num ) )
2021-11-01 03:37:19 +00:00
w1 - > state = wall_state : : cloaking ;
2006-03-20 17:12:09 +00:00
2018-12-13 02:31:38 +00:00
d - > front_wallnum = seg - > shared_segment : : sides [ side ] . wall_num ;
2006-03-20 17:12:09 +00:00
d - > back_wallnum = cwall_num ;
2016-02-06 22:12:54 +00:00
Assert ( w - > linked_wall = = wall_none ) ;
2006-03-20 17:12:09 +00:00
if ( Newdemo_state ! = ND_STATE_PLAYBACK ) {
2018-12-30 00:43:57 +00:00
auto & vcvertptr = Vertices . vcptr ;
2018-03-12 03:43:47 +00:00
const auto & & cp = compute_center_point_on_side ( vcvertptr , seg , side ) ;
2014-10-02 03:02:34 +00:00
digi_link_sound_to_pos ( SOUND_WALL_CLOAK_ON , seg , side , cp , 0 , F1_0 ) ;
2006-03-20 17:12:09 +00:00
}
2020-07-05 23:34:33 +00:00
for ( auto & & [ front_ls , back_ls , s0_uvls , s1_uvls ] : zip (
d - > front_ls ,
d - > back_ls ,
seg - > unique_segment : : sides [ side ] . uvls ,
csegp - > unique_segment : : sides [ Connectside ] . uvls
) )
2016-02-12 04:02:28 +00:00
{
2020-07-05 23:34:33 +00:00
front_ls = s0_uvls . l ;
back_ls = s1_uvls . l ;
2006-03-20 17:12:09 +00:00
}
}
//-----------------------------------------------------------------
// start the transition from open -> closed wall
2022-01-15 20:39:10 +00:00
void start_wall_decloak ( const vmsegptridx_t seg , const sidenum_t side )
2006-03-20 17:12:09 +00:00
{
2020-05-17 23:35:25 +00:00
auto & LevelSharedVertexState = LevelSharedSegmentState . get_vertex_state ( ) ;
auto & Vertices = LevelSharedVertexState . get_vertices ( ) ;
2006-03-20 17:12:09 +00:00
cloaking_wall * d ;
if ( Newdemo_state = = ND_STATE_PLAYBACK ) return ;
2018-12-13 02:31:38 +00:00
auto & sside = seg - > shared_segment : : sides [ side ] ;
assert ( sside . wall_num ! = wall_none ) ; //Opening door on illegal wall
2006-03-20 17:12:09 +00:00
2018-12-30 00:43:58 +00:00
auto & Walls = LevelUniqueWallSubsystemState . Walls ;
const auto & & w = Walls . vmptridx ( sside . wall_num ) ;
2006-03-20 17:12:09 +00:00
2021-11-01 03:37:19 +00:00
if ( w - > type = = WALL_CLOSED | | w - > state = = wall_state : : decloaking ) //already closed or decloaking
2006-03-20 17:12:09 +00:00
return ;
2018-12-30 00:43:58 +00:00
auto & CloakingWalls = LevelUniqueWallSubsystemState . CloakingWalls ;
2021-11-01 03:37:19 +00:00
if ( w - > state = = wall_state : : cloaking ) { //cloaking, so reuse door
2022-12-31 16:21:47 +00:00
const auto & & r = ranges : : subrange ( CloakingWalls . vmptr ) ;
2022-10-09 23:15:20 +00:00
const auto & & re = r . end ( ) ;
2022-10-31 00:51:32 +00:00
const auto & & i = ranges : : find_if ( r . begin ( ) , re , find_cloaked_wall_predicate ( w ) ) ;
2022-10-09 23:15:20 +00:00
if ( i = = re )
2016-11-19 17:24:54 +00:00
{
d_debugbreak ( ) ;
return ;
2006-03-20 17:12:09 +00:00
}
2018-06-06 04:53:45 +00:00
d = * i ;
2016-11-19 17:24:54 +00:00
d - > time = CLOAKING_WALL_TIME - d - > time ;
2006-03-20 17:12:09 +00:00
}
2021-11-01 03:37:19 +00:00
else if ( w - > state = = wall_state : : closed ) { //create new door
2023-01-29 20:42:03 +00:00
const auto c = CloakingWalls . get_count ( ) ;
2016-11-19 17:24:54 +00:00
if ( c > = CloakingWalls . size ( ) )
{
2006-03-20 17:12:09 +00:00
Int3 ( ) ; //ran out of cloaking wall slots
/* what is this _doing_ here?
w - > type = WALL_CLOSED ;
Walls [ csegp - > sides [ Connectside ] . wall_num ] . type = WALL_CLOSED ;
*/
return ;
}
2016-11-19 17:24:54 +00:00
CloakingWalls . set_count ( c + 1 ) ;
2023-01-29 20:42:03 +00:00
d = CloakingWalls . vmptr ( clwallnum_t { c } ) ;
2016-11-19 17:24:54 +00:00
d - > time = 0 ;
2006-03-20 17:12:09 +00:00
}
else {
Int3 ( ) ; //unexpected wall state
return ;
}
2021-11-01 03:37:19 +00:00
w - > state = wall_state : : decloaking ;
2006-03-20 17:12:09 +00:00
// So that door can't be shot while opening
2015-07-18 21:01:55 +00:00
const auto & & csegp = vcsegptr ( seg - > children [ side ] ) ;
2014-09-06 04:06:18 +00:00
auto Connectside = find_connect_side ( seg , csegp ) ;
2016-01-03 20:21:35 +00:00
Assert ( Connectside ! = side_none ) ;
2018-12-13 02:31:38 +00:00
auto & csside = csegp - > shared_segment : : sides [ Connectside ] ;
const auto cwall_num = csside . wall_num ;
2018-12-30 00:43:58 +00:00
if ( const auto & & w1 = Walls . imptr ( cwall_num ) )
2021-11-01 03:37:19 +00:00
w1 - > state = wall_state : : decloaking ;
2006-03-20 17:12:09 +00:00
2018-12-13 02:31:38 +00:00
d - > front_wallnum = seg - > shared_segment : : sides [ side ] . wall_num ;
d - > back_wallnum = csside . wall_num ;
2016-02-06 22:12:54 +00:00
Assert ( w - > linked_wall = = wall_none ) ;
2006-03-20 17:12:09 +00:00
if ( Newdemo_state ! = ND_STATE_PLAYBACK ) {
2018-12-30 00:43:57 +00:00
auto & vcvertptr = Vertices . vcptr ;
2018-03-12 03:43:47 +00:00
const auto & & cp = compute_center_point_on_side ( vcvertptr , seg , side ) ;
2014-10-02 03:02:34 +00:00
digi_link_sound_to_pos ( SOUND_WALL_CLOAK_OFF , seg , side , cp , 0 , F1_0 ) ;
2006-03-20 17:12:09 +00:00
}
2020-07-05 23:34:33 +00:00
for ( auto & & [ front_ls , back_ls , s0_uvls , s1_uvls ] : zip (
d - > front_ls ,
d - > back_ls ,
seg - > unique_segment : : sides [ side ] . uvls ,
csegp - > unique_segment : : sides [ Connectside ] . uvls
) )
2016-02-12 04:02:28 +00:00
{
2020-07-05 23:34:33 +00:00
front_ls = s0_uvls . l ;
back_ls = s1_uvls . l ;
2006-03-20 17:12:09 +00:00
}
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
//-----------------------------------------------------------------
// This function closes the specified door and restores the closed
// door texture. This is called when the animation is done
2018-12-30 00:43:57 +00:00
void wall_close_door_ref ( fvmsegptridx & vmsegptridx , wall_array & Walls , const wall_animations_array & WallAnims , active_door & d )
2006-03-20 17:12:09 +00:00
{
2016-11-26 22:51:48 +00:00
range_for ( const auto p , partial_const_range ( d . front_wallnum , d . n_parts ) )
2016-02-12 04:02:28 +00:00
{
2018-12-30 00:43:57 +00:00
wall & w = * Walls . vmptr ( p ) ;
2006-03-20 17:12:09 +00:00
2018-12-30 00:43:57 +00:00
const auto & & seg = vmsegptridx ( w . segnum ) ;
const auto side = w . sidenum ;
2021-11-01 03:37:19 +00:00
w . state = wall_state : : closed ;
2013-03-03 01:03:33 +00:00
2018-12-13 02:31:38 +00:00
assert ( seg - > shared_segment : : sides [ side ] . wall_num ! = wall_none ) ; //Closing door on illegal wall
2013-03-03 01:03:33 +00:00
2020-08-24 01:31:28 +00:00
const auto & & csegp = seg . absolute_sibling ( seg - > shared_segment : : children [ side ] ) ;
2014-09-06 04:06:18 +00:00
auto Connectside = find_connect_side ( seg , csegp ) ;
2016-01-03 20:21:35 +00:00
Assert ( Connectside ! = side_none ) ;
2018-12-13 02:31:38 +00:00
const auto cwall_num = csegp - > shared_segment : : sides [ Connectside ] . wall_num ;
2018-12-30 00:43:57 +00:00
if ( const auto & & w1 = Walls . imptr ( cwall_num ) )
2021-11-01 03:37:19 +00:00
w1 - > state = wall_state : : closed ;
2013-03-03 01:03:33 +00:00
2018-12-30 00:43:57 +00:00
wall_set_tmap_num ( WallAnims [ w . clip_num ] , seg , side , csegp , Connectside , 0 ) ;
2006-03-20 17:12:09 +00:00
}
}
2020-12-19 16:13:26 +00:00
}
namespace dcx {
namespace {
2022-02-19 14:52:17 +00:00
static unsigned check_poke ( fvcvertptr & vcvertptr , const object_base & obj , const shared_segment & seg , const sidenum_t side )
2006-03-20 17:12:09 +00:00
{
//note: don't let objects with zero size block door
2018-08-12 21:08:07 +00:00
if ( ! obj . size )
2015-07-12 01:04:17 +00:00
return 0 ;
2022-02-19 14:52:17 +00:00
return get_seg_masks ( vcvertptr , obj . pos , seg , obj . size ) . sidemask & build_sidemask ( side ) ; //pokes through side!
2006-03-20 17:12:09 +00:00
}
2020-12-19 16:13:26 +00:00
}
}
2016-08-25 04:05:32 +00:00
namespace dsx {
2020-12-19 16:13:26 +00:00
namespace {
2022-02-19 14:52:17 +00:00
static unsigned is_door_side_obstructed ( fvcobjptridx & vcobjptridx , fvcsegptr & vcsegptr , const cscusegment seg , const sidenum_t side )
2015-07-12 01:04:17 +00:00
{
2020-05-17 23:35:25 +00:00
auto & LevelSharedVertexState = LevelSharedSegmentState . get_vertex_state ( ) ;
2018-12-30 00:43:57 +00:00
auto & Vertices = LevelSharedVertexState . get_vertices ( ) ;
auto & vcvertptr = Vertices . vcptr ;
2018-08-12 21:08:07 +00:00
range_for ( const object_base & obj , objects_in ( seg , vcobjptridx , vcsegptr ) )
2016-02-06 22:12:55 +00:00
{
# if defined(DXX_BUILD_DESCENT_II)
2018-08-12 21:08:07 +00:00
if ( obj . type = = OBJ_WEAPON )
2016-02-06 22:12:55 +00:00
continue ;
2018-08-12 21:08:07 +00:00
if ( obj . type = = OBJ_FIREBALL )
2016-02-06 22:12:55 +00:00
continue ;
# endif
2018-08-12 21:08:07 +00:00
if ( const auto obstructed = check_poke ( vcvertptr , obj , seg , side ) )
return obstructed ;
2016-02-06 22:12:55 +00:00
}
2018-08-12 21:08:07 +00:00
return 0 ;
2016-08-25 04:05:32 +00:00
}
2015-07-12 01:04:17 +00:00
2018-08-12 21:08:07 +00:00
//returns true if door is obstructed (& thus cannot close)
2022-01-15 20:39:10 +00:00
static unsigned is_door_obstructed ( fvcobjptridx & vcobjptridx , fvcsegptr & vcsegptr , const vcsegptridx_t seg , const sidenum_t side )
2006-03-20 17:12:09 +00:00
{
2018-08-12 21:08:07 +00:00
if ( const auto obstructed = is_door_side_obstructed ( vcobjptridx , vcsegptr , seg , side ) )
return obstructed ;
2020-08-24 01:31:28 +00:00
const auto & & csegp = vcsegptr ( seg - > shared_segment : : children [ side ] ) ;
2016-01-03 20:21:35 +00:00
const auto & & Connectside = find_connect_side ( seg , csegp ) ;
Assert ( Connectside ! = side_none ) ;
2006-03-20 17:12:09 +00:00
//go through each object in each of two segments, and see if
//it pokes into the connecting seg
2018-08-12 21:08:07 +00:00
return is_door_side_obstructed ( vcobjptridx , vcsegptr , csegp , Connectside ) ;
2006-03-20 17:12:09 +00:00
}
2020-12-19 16:13:26 +00:00
}
2016-02-06 22:12:55 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
//-----------------------------------------------------------------
// Closes a door
2022-01-15 20:39:10 +00:00
void wall_close_door ( wall_array & Walls , const vmsegptridx_t seg , const sidenum_t side )
2006-03-20 17:12:09 +00:00
{
2020-05-17 23:35:25 +00:00
auto & LevelSharedVertexState = LevelSharedSegmentState . get_vertex_state ( ) ;
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
2020-05-17 23:35:25 +00:00
auto & Vertices = LevelSharedVertexState . get_vertices ( ) ;
2020-04-04 19:30:22 +00:00
auto & WallAnims = GameSharedState . WallAnims ;
2019-03-03 00:31:08 +00:00
auto & vcobjptridx = Objects . vcptridx ;
2006-03-20 17:12:09 +00:00
active_door * d ;
2018-12-13 02:31:38 +00:00
const auto wall_num = seg - > shared_segment : : sides [ side ] . wall_num ;
2018-12-30 00:43:58 +00:00
wall * const w = Walls . vmptr ( wall_num ) ;
2021-11-01 03:37:19 +00:00
if ( ( w - > state = = wall_state : : closing ) | | //already closing
( w - > state = = wall_state : : waiting ) | | //open, waiting to close
( w - > state = = wall_state : : closed ) ) //closed
2006-03-20 17:12:09 +00:00
return ;
2018-08-12 21:08:07 +00:00
if ( is_door_obstructed ( vcobjptridx , vcsegptr , seg , side ) )
2006-03-20 17:12:09 +00:00
return ;
2018-12-30 00:43:58 +00:00
auto & ActiveDoors = LevelUniqueWallSubsystemState . ActiveDoors ;
auto & vmactdoorptr = ActiveDoors . vmptr ;
2021-11-01 03:37:19 +00:00
if ( w - > state = = wall_state : : opening )
{ //reuse door
2022-12-31 16:21:47 +00:00
const auto & & r = ranges : : subrange ( vmactdoorptr ) ;
2022-10-09 23:15:20 +00:00
const auto & & re = r . end ( ) ;
2022-10-31 00:51:32 +00:00
const auto & & i = ranges : : find_if ( r . begin ( ) , re , find_active_door_predicate ( wall_num ) ) ;
2022-10-09 23:15:20 +00:00
if ( i = = re )
2016-11-26 22:51:48 +00:00
{
d_debugbreak ( ) ;
return ;
2006-03-20 17:12:09 +00:00
}
2018-06-06 04:53:45 +00:00
d = * i ;
2006-03-20 17:12:09 +00:00
d - > time = WallAnims [ w - > clip_num ] . play_time - d - > time ;
if ( d - > time < 0 )
d - > time = 0 ;
2013-03-03 01:03:33 +00:00
2006-03-20 17:12:09 +00:00
}
else { //create new door
2021-11-01 03:37:19 +00:00
assert ( w - > state = = wall_state : : open ) ;
2016-12-10 17:51:08 +00:00
const auto i = ActiveDoors . get_count ( ) ;
ActiveDoors . set_count ( i + 1 ) ;
2017-06-10 03:31:02 +00:00
d = vmactdoorptr ( static_cast < actdoornum_t > ( i ) ) ;
2006-03-20 17:12:09 +00:00
d - > time = 0 ;
}
2021-11-01 03:37:19 +00:00
w - > state = wall_state : : closing ;
2006-03-20 17:12:09 +00:00
// So that door can't be shot while opening
2015-07-18 21:01:55 +00:00
const auto & & csegp = vcsegptr ( seg - > children [ side ] ) ;
2016-01-03 20:21:35 +00:00
const auto & & Connectside = find_connect_side ( seg , csegp ) ;
Assert ( Connectside ! = side_none ) ;
2018-12-13 02:31:38 +00:00
const auto cwall_num = csegp - > shared_segment : : sides [ Connectside ] . wall_num ;
2018-12-30 00:43:58 +00:00
if ( const auto & & w1 = Walls . imptr ( cwall_num ) )
2021-11-01 03:37:19 +00:00
w1 - > state = wall_state : : closing ;
2006-03-20 17:12:09 +00:00
2018-12-13 02:31:38 +00:00
d - > front_wallnum [ 0 ] = seg - > shared_segment : : sides [ side ] . wall_num ;
2006-03-20 17:12:09 +00:00
d - > back_wallnum [ 0 ] = cwall_num ;
if ( Newdemo_state = = ND_STATE_RECORDING ) {
2014-10-02 03:02:34 +00:00
newdemo_record_door_opening ( seg , side ) ;
2006-03-20 17:12:09 +00:00
}
2016-02-06 22:12:54 +00:00
if ( w - > linked_wall ! = wall_none )
{
2006-03-20 17:12:09 +00:00
Int3 ( ) ; //don't think we ever used linked walls
}
else
d - > n_parts = 1 ;
if ( Newdemo_state ! = ND_STATE_PLAYBACK )
{
// NOTE THE LINK TO ABOVE!!!!
2018-12-30 00:43:57 +00:00
auto & vcvertptr = Vertices . vcptr ;
2018-03-12 03:43:47 +00:00
const auto & & cp = compute_center_point_on_side ( vcvertptr , seg , side ) ;
2020-04-04 19:30:22 +00:00
const auto open_sound = WallAnims [ w - > clip_num ] . open_sound ;
if ( open_sound > - 1 )
digi_link_sound_to_pos ( open_sound , seg , side , cp , 0 , F1_0 ) ;
2006-03-20 17:12:09 +00:00
}
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2020-12-19 16:13:26 +00:00
namespace {
2006-03-20 17:12:09 +00:00
//-----------------------------------------------------------------
// Animates opening of a door.
// Called in the game loop.
2016-11-26 22:51:47 +00:00
static bool do_door_open ( active_door & d )
2006-03-20 17:12:09 +00:00
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
2020-04-04 19:30:22 +00:00
auto & WallAnims = GameSharedState . WallAnims ;
2019-03-03 00:31:08 +00:00
auto & vmobjptr = Objects . vmptr ;
2016-11-26 22:51:47 +00:00
bool remove = false ;
2018-12-30 00:43:58 +00:00
auto & Walls = LevelUniqueWallSubsystemState . Walls ;
auto & vmwallptr = Walls . vmptr ;
2016-11-26 22:51:47 +00:00
for ( unsigned p = 0 ; p < d . n_parts ; + + p )
{
2006-03-20 17:12:09 +00:00
fix time_elapsed , time_total , one_frame ;
2017-06-17 23:05:16 +00:00
int i ;
2013-03-03 01:03:33 +00:00
2018-09-19 02:13:29 +00:00
wall & w = * vmwallptr ( d . front_wallnum [ p ] ) ;
2018-08-04 17:52:57 +00:00
LevelUniqueStuckObjectState . kill_stuck_objects ( vmobjptr , d . front_wallnum [ p ] ) ;
LevelUniqueStuckObjectState . kill_stuck_objects ( vmobjptr , d . back_wallnum [ p ] ) ;
2006-03-20 17:12:09 +00:00
2018-09-19 02:13:29 +00:00
const auto & & seg = vmsegptridx ( w . segnum ) ;
2022-01-15 20:39:10 +00:00
const auto side = w . sidenum ;
2013-03-03 01:03:33 +00:00
2018-12-13 02:31:38 +00:00
if ( seg - > shared_segment : : sides [ side ] . wall_num = = wall_none )
2008-10-16 17:27:02 +00:00
{
2022-06-05 17:44:52 +00:00
con_printf ( CON_URGENT , " Trying to do_door_open on illegal wall %i. Please check your level! " , underlying_value ( side ) ) ;
2008-10-16 17:27:02 +00:00
continue ;
}
2013-03-03 01:03:33 +00:00
2020-08-24 01:31:28 +00:00
const auto & & csegp = seg . absolute_sibling ( seg - > shared_segment : : children [ side ] ) ;
2016-01-03 20:21:35 +00:00
const auto & & Connectside = find_connect_side ( seg , csegp ) ;
Assert ( Connectside ! = side_none ) ;
2006-03-20 17:12:09 +00:00
2016-11-26 22:51:47 +00:00
d . time + = FrameTime ;
2013-03-03 01:03:33 +00:00
2016-11-26 22:51:47 +00:00
time_elapsed = d . time ;
2018-09-19 02:13:29 +00:00
auto & wa = WallAnims [ w . clip_num ] ;
const auto n = wa . num_frames ;
time_total = wa . play_time ;
2013-03-03 01:03:33 +00:00
2006-03-20 17:12:09 +00:00
one_frame = time_total / n ;
2013-03-03 01:03:33 +00:00
2006-03-20 17:12:09 +00:00
i = time_elapsed / one_frame ;
2013-03-03 01:03:33 +00:00
2006-03-20 17:12:09 +00:00
if ( i < n )
2020-04-04 19:30:22 +00:00
wall_set_tmap_num ( wa , seg , side , csegp , Connectside , i ) ;
2013-03-03 01:03:33 +00:00
2018-12-13 02:31:38 +00:00
const auto cwall_num = csegp - > shared_segment : : sides [ Connectside ] . wall_num ;
2018-09-19 02:13:29 +00:00
auto & w1 = * vmwallptr ( cwall_num ) ;
2006-03-20 17:12:09 +00:00
if ( i > n / 2 ) {
2021-11-01 03:37:19 +00:00
w . flags | = wall_flag : : door_opened ;
w1 . flags | = wall_flag : : door_opened ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
2006-03-20 17:12:09 +00:00
if ( i > = n - 1 ) {
2020-04-04 19:30:22 +00:00
wall_set_tmap_num ( wa , seg , side , csegp , Connectside , n - 1 ) ;
2006-03-20 17:12:09 +00:00
// If our door is not automatic just remove it from the list.
2021-11-01 03:37:19 +00:00
if ( ! ( w . flags & wall_flag : : door_auto ) )
{
2016-11-26 22:51:47 +00:00
remove = true ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2021-11-01 03:37:19 +00:00
w . state = wall_state : : open ;
w1 . state = wall_state : : open ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
else {
2021-11-01 03:37:19 +00:00
w . state = wall_state : : waiting ;
w1 . state = wall_state : : waiting ;
2006-03-20 17:12:09 +00:00
}
}
}
2016-07-21 01:43:21 +00:00
flush_fcd_cache ( ) ;
2016-11-26 22:51:47 +00:00
return remove ;
2016-08-25 04:05:32 +00:00
}
2006-03-20 17:12:09 +00:00
//-----------------------------------------------------------------
// Animates and processes the closing of a door.
// Called from the game loop.
2016-11-26 22:51:48 +00:00
static bool do_door_close ( active_door & d )
2006-03-20 17:12:09 +00:00
{
2020-05-17 23:35:25 +00:00
auto & LevelSharedVertexState = LevelSharedSegmentState . get_vertex_state ( ) ;
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
2020-05-17 23:35:25 +00:00
auto & Vertices = LevelSharedVertexState . get_vertices ( ) ;
2019-03-03 00:31:08 +00:00
auto & vcobjptridx = Objects . vcptridx ;
2018-12-30 00:43:58 +00:00
auto & Walls = LevelUniqueWallSubsystemState . Walls ;
2020-04-04 19:30:22 +00:00
auto & WallAnims = GameSharedState . WallAnims ;
2018-12-30 00:43:58 +00:00
auto & vmwallptr = Walls . vmptr ;
2017-06-10 03:31:02 +00:00
auto & w0 = * vmwallptr ( d . front_wallnum [ 0 ] ) ;
2020-01-24 01:12:43 +00:00
const auto & & seg0 = vmsegptridx ( w0 . segnum ) ;
2015-07-13 01:09:37 +00:00
2006-03-20 17:12:09 +00:00
//check for objects in doorway before closing
2021-11-01 03:37:19 +00:00
if ( w0 . flags & wall_flag : : door_auto )
2020-01-24 01:12:43 +00:00
if ( is_door_obstructed ( vcobjptridx , vcsegptr , seg0 , w0 . sidenum ) )
2018-08-12 21:08:07 +00:00
{
2016-02-06 22:12:55 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2016-05-21 17:24:50 +00:00
digi_kill_sound_linked_to_segment ( w0 . segnum , w0 . sidenum , - 1 ) ;
2020-01-24 01:12:43 +00:00
wall_open_door ( seg0 , w0 . sidenum ) ; //re-open door
2016-02-06 22:12:55 +00:00
# endif
2016-11-26 22:51:47 +00:00
return false ;
2006-03-20 17:12:09 +00:00
}
2016-11-26 22:51:47 +00:00
bool played_sound = false ;
2016-11-26 22:51:47 +00:00
bool remove = false ;
2016-11-26 22:51:47 +00:00
range_for ( const auto p , partial_const_range ( d . front_wallnum , d . n_parts ) )
2016-11-26 22:51:47 +00:00
{
2006-03-20 17:12:09 +00:00
fix time_elapsed , time_total , one_frame ;
2017-06-17 23:05:16 +00:00
int i ;
2013-03-03 01:03:33 +00:00
2017-06-10 03:31:02 +00:00
auto & wp = * vmwallptr ( p ) ;
2006-03-20 17:12:09 +00:00
2020-01-24 01:12:43 +00:00
const auto & & seg = vmsegptridx ( wp . segnum ) ;
2022-01-15 20:39:10 +00:00
const auto side = wp . sidenum ;
2013-03-03 01:03:33 +00:00
2018-12-13 02:31:38 +00:00
if ( seg - > shared_segment : : sides [ side ] . wall_num = = wall_none ) {
2016-11-26 22:51:47 +00:00
return false ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
2016-02-06 22:12:55 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2006-03-20 17:12:09 +00:00
//if here, must be auto door
//don't assert here, because now we have triggers to close non-auto doors
2021-11-01 03:37:19 +00:00
assert ( wp . flags & wall_flag : : door_auto ) ;
2016-02-06 22:12:55 +00:00
# endif
2013-03-03 01:03:33 +00:00
2006-03-20 17:12:09 +00:00
// Otherwise, close it.
2020-08-24 01:31:28 +00:00
const auto & & csegp = seg . absolute_sibling ( seg - > shared_segment : : children [ side ] ) ;
2016-01-03 20:21:35 +00:00
const auto & & Connectside = find_connect_side ( seg , csegp ) ;
Assert ( Connectside ! = side_none ) ;
2013-03-03 01:03:33 +00:00
2006-03-20 17:12:09 +00:00
2020-04-04 19:30:22 +00:00
auto & wa = WallAnims [ wp . clip_num ] ;
2006-03-20 17:12:09 +00:00
if ( Newdemo_state ! = ND_STATE_PLAYBACK )
2016-11-26 22:51:47 +00:00
{
2006-03-20 17:12:09 +00:00
// NOTE THE LINK TO ABOVE!!
2016-11-26 22:51:47 +00:00
if ( ! played_sound ) //only play one sound for linked doors
{
played_sound = true ;
2016-11-26 22:51:47 +00:00
if ( d . time = = 0 )
{ //first time
2020-04-04 19:30:22 +00:00
const auto close_sound = wa . close_sound ;
if ( close_sound > - 1 )
2016-11-26 22:51:47 +00:00
{
2018-12-30 00:43:57 +00:00
auto & vcvertptr = Vertices . vcptr ;
2020-04-04 19:30:22 +00:00
digi_link_sound_to_pos ( close_sound , seg , side , compute_center_point_on_side ( vcvertptr , seg , side ) , 0 , F1_0 ) ;
2016-11-26 22:51:47 +00:00
}
2006-03-20 17:12:09 +00:00
}
2016-11-26 22:51:47 +00:00
}
}
2013-03-03 01:03:33 +00:00
2016-11-26 22:51:47 +00:00
d . time + = FrameTime ;
2006-03-20 17:12:09 +00:00
2016-11-26 22:51:47 +00:00
time_elapsed = d . time ;
2020-04-04 19:30:22 +00:00
const auto n = wa . num_frames ;
time_total = wa . play_time ;
2013-03-03 01:03:33 +00:00
2006-03-20 17:12:09 +00:00
one_frame = time_total / n ;
2013-03-03 01:03:33 +00:00
2006-03-20 17:12:09 +00:00
i = n - time_elapsed / one_frame - 1 ;
2013-03-03 01:03:33 +00:00
2018-12-13 02:31:38 +00:00
const auto cwall_num = csegp - > shared_segment : : sides [ Connectside ] . wall_num ;
2017-06-10 03:31:02 +00:00
auto & w1 = * vmwallptr ( cwall_num ) ;
2006-03-20 17:12:09 +00:00
if ( i < n / 2 ) {
2021-11-01 03:37:19 +00:00
wp . flags & = ~ wall_flag : : door_opened ;
w1 . flags & = ~ wall_flag : : door_opened ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
2006-03-20 17:12:09 +00:00
// Animate door.
if ( i > 0 ) {
2020-04-04 19:30:22 +00:00
wall_set_tmap_num ( wa , seg , side , csegp , Connectside , i ) ;
2006-03-20 17:12:09 +00:00
2021-11-01 03:37:19 +00:00
wp . state = wall_state : : closing ;
w1 . state = wall_state : : closing ;
2006-03-20 17:12:09 +00:00
} else
2016-11-26 22:51:47 +00:00
remove = true ;
2006-03-20 17:12:09 +00:00
}
2020-01-24 01:12:43 +00:00
if ( remove )
wall_close_door_ref ( Segments . vmptridx , Walls , WallAnims , d ) ;
2016-11-26 22:51:47 +00:00
return remove ;
2006-03-20 17:12:09 +00:00
}
2022-01-15 20:39:10 +00:00
static std : : pair < wall * , wall * > wall_illusion_op ( fvmwallptr & vmwallptr , const vcsegptridx_t seg , const sidenum_t side )
2016-01-09 16:38:10 +00:00
{
2018-12-13 02:31:38 +00:00
const auto wall0 = seg - > shared_segment : : sides [ side ] . wall_num ;
2016-01-09 16:38:10 +00:00
if ( wall0 = = wall_none )
2018-12-13 02:31:38 +00:00
return { nullptr , nullptr } ;
2020-08-24 01:31:28 +00:00
const shared_segment & csegp = * seg . absolute_sibling ( seg - > shared_segment : : children [ side ] ) ;
2016-01-09 16:38:10 +00:00
const auto & & cside = find_connect_side ( seg , csegp ) ;
if ( cside = = side_none )
{
assert ( cside ! = side_none ) ;
2018-12-13 02:31:38 +00:00
return { nullptr , nullptr } ;
2016-01-09 16:38:10 +00:00
}
2018-12-13 02:31:38 +00:00
const auto wall1 = csegp . sides [ cside ] . wall_num ;
2018-12-13 02:31:38 +00:00
if ( wall1 = = wall_none )
return { nullptr , nullptr } ;
return { vmwallptr ( wall0 ) , vmwallptr ( wall1 ) } ;
}
template < typename F >
2022-01-15 20:39:10 +00:00
static void wall_illusion_op ( fvmwallptr & vmwallptr , const vcsegptridx_t seg , const sidenum_t side , const F op )
2018-12-13 02:31:38 +00:00
{
const auto & & r = wall_illusion_op ( vmwallptr , seg , side ) ;
if ( r . first )
{
op ( * r . first ) ;
op ( * r . second ) ;
}
2016-01-09 16:38:10 +00:00
}
2006-03-20 17:12:09 +00:00
2020-12-19 16:13:26 +00:00
}
2006-03-20 17:12:09 +00:00
//-----------------------------------------------------------------
// Turns off an illusionary wall (This will be used primarily for
// wall switches or triggers that can turn on/off illusionary walls.)
2022-01-15 20:39:10 +00:00
void wall_illusion_off ( fvmwallptr & vmwallptr , const vcsegptridx_t seg , const sidenum_t side )
2006-03-20 17:12:09 +00:00
{
2018-12-13 02:31:38 +00:00
const auto & & op = [ ] ( wall & w ) {
2021-11-01 03:37:19 +00:00
w . flags | = wall_flag : : illusion_off ;
2016-01-09 16:38:10 +00:00
} ;
2018-12-13 02:31:38 +00:00
wall_illusion_op ( vmwallptr , seg , side , op ) ;
2006-03-20 17:12:09 +00:00
}
//-----------------------------------------------------------------
// Turns on an illusionary wall (This will be used primarily for
// wall switches or triggers that can turn on/off illusionary walls.)
2022-01-15 20:39:10 +00:00
void wall_illusion_on ( fvmwallptr & vmwallptr , const vcsegptridx_t seg , const sidenum_t side )
2006-03-20 17:12:09 +00:00
{
2018-12-13 02:31:38 +00:00
const auto & & op = [ ] ( wall & w ) {
2021-11-01 03:37:19 +00:00
w . flags & = ~ wall_flag : : illusion_off ;
2016-01-09 16:38:10 +00:00
} ;
2018-12-13 02:31:38 +00:00
wall_illusion_op ( vmwallptr , seg , side , op ) ;
}
2006-03-20 17:12:09 +00:00
}
2020-12-19 16:13:26 +00:00
namespace {
2006-03-20 17:12:09 +00:00
// -----------------------------------------------------------------------------
// Allowed to open the normally locked special boss door if in multiplayer mode.
2022-06-05 17:44:52 +00:00
static int special_boss_opening_allowed ( const segnum_t segnum , const sidenum_t sidenum )
2006-03-20 17:12:09 +00:00
{
if ( Game_mode & GM_MULTI )
return ( Current_level_num = = BOSS_LOCKED_DOOR_LEVEL ) & & ( segnum = = BOSS_LOCKED_DOOR_SEG ) & & ( sidenum = = BOSS_LOCKED_DOOR_SIDE ) ;
else
return 0 ;
}
2020-12-19 16:13:26 +00:00
}
2006-03-20 17:12:09 +00:00
//-----------------------------------------------------------------
// Determines what happens when a wall is shot
//returns info about wall. see wall.h for codes
//obj is the object that hit...either a weapon or the player himself
//playernum is the number the player who hit the wall or fired the weapon,
//or -1 if a robot fired the weapon
2016-08-25 04:05:32 +00:00
namespace dsx {
2022-01-15 20:39:10 +00:00
wall_hit_process_t wall_hit_process ( const player_flags powerup_flags , const vmsegptridx_t seg , const sidenum_t side , const fix damage , const unsigned playernum , const object & obj )
2006-03-20 17:12:09 +00:00
{
fix show_message ;
// If it is not a "wall" then just return.
2018-12-13 02:31:38 +00:00
const auto wall_num = seg - > shared_segment : : sides [ side ] . wall_num ;
if ( wall_num = = wall_none )
2015-10-24 03:13:11 +00:00
return wall_hit_process_t : : WHP_NOT_SPECIAL ;
2006-03-20 17:12:09 +00:00
2018-12-30 00:43:58 +00:00
auto & Walls = LevelUniqueWallSubsystemState . Walls ;
auto & vmwallptr = Walls . vmptr ;
2018-12-13 02:31:38 +00:00
wall * const w = vmwallptr ( wall_num ) ;
2006-03-20 17:12:09 +00:00
if ( Newdemo_state = = ND_STATE_RECORDING )
2014-10-02 03:02:34 +00:00
newdemo_record_wall_hit_process ( seg , side , damage , playernum ) ;
2006-03-20 17:12:09 +00:00
if ( w - > type = = WALL_BLASTABLE ) {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2018-09-19 02:13:29 +00:00
if ( obj . ctype . laser_info . parent_type = = OBJ_PLAYER )
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
wall_damage ( seg , side , damage ) ;
2015-10-24 03:13:11 +00:00
return wall_hit_process_t : : WHP_BLASTABLE ;
2006-03-20 17:12:09 +00:00
}
if ( playernum ! = Player_num ) //return if was robot fire
2015-10-24 03:13:11 +00:00
return wall_hit_process_t : : WHP_NOT_SPECIAL ;
2006-03-20 17:12:09 +00:00
// Determine whether player is moving forward. If not, don't say negative
// messages because he probably didn't intentionally hit the door.
2018-09-19 02:13:29 +00:00
if ( obj . type = = OBJ_PLAYER )
show_message = ( vm_vec_dot ( obj . orient . fvec , obj . mtype . phys_info . velocity ) > 0 ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2018-09-19 02:13:29 +00:00
else if ( obj . type = = OBJ_ROBOT )
2006-03-20 17:12:09 +00:00
show_message = 0 ;
2018-09-19 02:13:29 +00:00
else if ( obj . type = = OBJ_WEAPON & & obj . ctype . laser_info . parent_type = = OBJ_ROBOT )
2006-03-20 17:12:09 +00:00
show_message = 0 ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
else
show_message = 1 ;
2015-10-24 03:13:11 +00:00
/* Set key_color only after the type matches, since TXT_* are macros
* that trigger a load from memory . Use operator , ( ) to suppress the
* truth test on the second branch since the compiler cannot prove
* that the loaded value will always be non - null .
*/
const char * key_color ;
if (
2020-12-19 16:13:26 +00:00
( w - > keys = = wall_key : : blue & & ( key_color = TXT_BLUE , true ) ) | |
( w - > keys = = wall_key : : gold & & ( key_color = TXT_YELLOW , true ) ) | |
( w - > keys = = wall_key : : red & & ( key_color = TXT_RED , true ) )
2015-10-24 03:13:11 +00:00
)
2015-11-07 21:55:59 +00:00
{
2016-10-02 00:34:46 +00:00
if ( ! ( powerup_flags & static_cast < PLAYER_FLAG > ( w - > keys ) ) )
2015-10-24 03:13:11 +00:00
{
2020-12-19 16:13:26 +00:00
static_assert ( static_cast < unsigned > ( wall_key : : blue ) = = static_cast < unsigned > ( PLAYER_FLAGS_BLUE_KEY ) , " BLUE key flag mismatch " ) ;
static_assert ( static_cast < unsigned > ( wall_key : : gold ) = = static_cast < unsigned > ( PLAYER_FLAGS_GOLD_KEY ) , " GOLD key flag mismatch " ) ;
static_assert ( static_cast < unsigned > ( wall_key : : red ) = = static_cast < unsigned > ( PLAYER_FLAGS_RED_KEY ) , " RED key flag mismatch " ) ;
2006-03-20 17:12:09 +00:00
if ( show_message )
2015-10-24 03:13:11 +00:00
HUD_init_message ( HM_DEFAULT , " %s %s " , key_color , TXT_ACCESS_DENIED ) ;
2015-10-24 03:13:11 +00:00
return wall_hit_process_t : : WHP_NO_KEY ;
2006-03-20 17:12:09 +00:00
}
2015-11-07 21:55:59 +00:00
}
2006-03-20 17:12:09 +00:00
if ( w - > type = = WALL_DOOR )
{
2021-11-01 03:37:19 +00:00
if ( ( w - > flags & wall_flag : : door_locked ) & & ! special_boss_opening_allowed ( seg , side ) )
{
2006-03-20 17:12:09 +00:00
if ( show_message )
2013-06-23 16:27:34 +00:00
HUD_init_message_literal ( HM_DEFAULT , TXT_CANT_OPEN_DOOR ) ;
2015-10-24 03:13:11 +00:00
return wall_hit_process_t : : WHP_NO_KEY ;
2006-03-20 17:12:09 +00:00
}
else {
2021-11-01 03:37:19 +00:00
if ( w - > state ! = wall_state : : opening )
2006-03-20 17:12:09 +00:00
{
wall_open_door ( seg , side ) ;
if ( Game_mode & GM_MULTI )
2013-03-03 01:03:33 +00:00
{
# if defined(DXX_BUILD_DESCENT_I)
2021-11-01 03:37:19 +00:00
const wall_flags flags { } ;
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2021-11-01 03:37:19 +00:00
const auto flags = w - > flags ;
2013-03-03 01:03:33 +00:00
# endif
2014-10-02 03:02:34 +00:00
multi_send_door_open ( seg , side , flags ) ;
2013-03-03 01:03:33 +00:00
}
2006-03-20 17:12:09 +00:00
}
2015-10-24 03:13:11 +00:00
return wall_hit_process_t : : WHP_DOOR ;
2006-03-20 17:12:09 +00:00
}
}
2015-10-24 03:13:11 +00:00
return wall_hit_process_t : : WHP_NOT_SPECIAL ; //default is treat like normal wall
2006-03-20 17:12:09 +00:00
}
//-----------------------------------------------------------------
// Opens doors/destroys wall/shuts off triggers.
2022-01-15 20:39:10 +00:00
void wall_toggle ( fvmwallptr & vmwallptr , const vmsegptridx_t segp , const sidenum_t side )
2006-03-20 17:12:09 +00:00
{
2015-05-28 03:08:39 +00:00
if ( side > = MAX_SIDES_PER_SEGMENT )
2006-03-20 17:12:09 +00:00
{
2007-03-25 09:18:22 +00:00
# ifndef NDEBUG
2022-06-05 17:44:52 +00:00
Warning ( " Can't toggle side %u of segment %d (%u)! \n " , underlying_value ( side ) , segp . get_unchecked_index ( ) , Highest_segment_index ) ;
2007-03-25 09:18:22 +00:00
# endif
2006-03-20 17:12:09 +00:00
return ;
}
2018-12-13 02:31:38 +00:00
const auto wall_num = segp - > shared_segment : : sides [ side ] . wall_num ;
2017-11-25 01:56:51 +00:00
if ( wall_num = = wall_none )
{
2022-06-05 17:44:52 +00:00
LevelError ( " Ignoring attempt to toggle wall in segment %hu, side %u: no wall exists there. " , segp . get_unchecked_index ( ) , underlying_value ( side ) ) ;
2006-03-20 17:12:09 +00:00
return ;
}
if ( Newdemo_state = = ND_STATE_RECORDING )
2015-05-28 03:08:40 +00:00
newdemo_record_wall_toggle ( segp , side ) ;
2006-03-20 17:12:09 +00:00
2017-06-10 03:31:02 +00:00
wall * const w = vmwallptr ( wall_num ) ;
2016-02-12 04:02:28 +00:00
if ( w - > type = = WALL_BLASTABLE )
2015-05-28 03:08:39 +00:00
wall_destroy ( segp , side ) ;
2006-03-20 17:12:09 +00:00
2021-11-01 03:37:19 +00:00
if ( w - > type = = WALL_DOOR & & w - > state = = wall_state : : closed )
2015-05-28 03:08:39 +00:00
wall_open_door ( segp , side ) ;
2006-03-20 17:12:09 +00:00
}
2016-11-26 22:51:48 +00:00
bool ad_removal_predicate : : operator ( ) ( active_door & d ) const
{
2019-03-03 00:31:08 +00:00
# if defined(DXX_BUILD_DESCENT_II)
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vcobjptridx = Objects . vcptridx ;
# endif
2018-12-30 00:43:58 +00:00
auto & Walls = LevelUniqueWallSubsystemState . Walls ;
wall & w = * Walls . vmptr ( d . front_wallnum [ 0 ] ) ;
2021-11-01 03:37:19 +00:00
if ( w . state = = wall_state : : opening )
2016-11-26 22:51:48 +00:00
return do_door_open ( d ) ;
2021-11-01 03:37:19 +00:00
else if ( w . state = = wall_state : : closing )
2016-11-26 22:51:48 +00:00
return do_door_close ( d ) ;
2021-11-01 03:37:19 +00:00
else if ( w . state = = wall_state : : waiting )
{
2016-11-26 22:51:48 +00:00
d . time + = FrameTime ;
// set flags to fix occasional netgame problem where door is waiting to close but open flag isn't set
2021-11-01 03:37:19 +00:00
w . flags | = wall_flag : : door_opened ;
2018-12-30 00:43:58 +00:00
if ( wall * const w1 = Walls . imptr ( d . back_wallnum [ 0 ] ) )
2021-11-01 03:37:19 +00:00
w1 - > flags | = wall_flag : : door_opened ;
2016-11-26 22:51:48 +00:00
if ( d . time > DOOR_WAIT_TIME )
# if defined(DXX_BUILD_DESCENT_II)
2018-08-12 21:08:07 +00:00
if ( ! is_door_obstructed ( vcobjptridx , vcsegptr , vcsegptridx ( w . segnum ) , w . sidenum ) )
2016-11-26 22:51:48 +00:00
# endif
{
2021-11-01 03:37:19 +00:00
w . state = wall_state : : closing ;
2016-11-26 22:51:48 +00:00
d . time = 0 ;
}
}
return false ;
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2020-12-19 16:13:26 +00:00
namespace {
2020-05-02 21:18:42 +00:00
static void copy_cloaking_wall_light_to_wall ( std : : array < uvl , 4 > & back_uvls , std : : array < uvl , 4 > & front_uvls , const cloaking_wall & d )
2016-11-12 18:10:10 +00:00
{
2019-05-04 18:27:36 +00:00
range_for ( const uint_fast32_t i , xrange ( 4u ) )
2016-11-12 18:10:10 +00:00
{
back_uvls [ i ] . l = d . back_ls [ i ] ;
front_uvls [ i ] . l = d . front_ls [ i ] ;
}
}
2020-05-02 21:18:42 +00:00
static void scale_cloaking_wall_light_to_wall ( std : : array < uvl , 4 > & back_uvls , std : : array < uvl , 4 > & front_uvls , const cloaking_wall & d , const fix light_scale )
2016-11-12 18:10:10 +00:00
{
2019-05-04 18:27:36 +00:00
range_for ( const uint_fast32_t i , xrange ( 4u ) )
2016-11-12 18:10:10 +00:00
{
back_uvls [ i ] . l = fixmul ( d . back_ls [ i ] , light_scale ) ;
front_uvls [ i ] . l = fixmul ( d . front_ls [ i ] , light_scale ) ;
}
}
2016-11-12 18:10:10 +00:00
static cwresult do_cloaking_wall_frame ( const bool initial , cloaking_wall & d , const cwframe front , const cwframe back )
2006-03-20 17:12:09 +00:00
{
2016-11-12 18:10:10 +00:00
cwresult r ( initial ) ;
2016-11-12 18:10:10 +00:00
if ( d . time > CLOAKING_WALL_TIME ) {
2016-11-12 18:10:09 +00:00
front . w . type = back . w . type = WALL_OPEN ;
2021-11-01 03:37:19 +00:00
front . w . state = back . w . state = wall_state : : closed ; //why closed? why not?
2016-11-12 18:10:10 +00:00
r . remove = true ;
2006-03-20 17:12:09 +00:00
}
2016-11-12 18:10:10 +00:00
else if ( d . time > CLOAKING_WALL_TIME / 2 ) {
const int8_t cloak_value = ( ( d . time - CLOAKING_WALL_TIME / 2 ) * ( GR_FADE_LEVELS - 2 ) ) / ( CLOAKING_WALL_TIME / 2 ) ;
2016-11-12 18:10:09 +00:00
if ( front . w . cloak_value ! = cloak_value )
{
2016-11-12 18:10:10 +00:00
r . record = true ;
2016-11-12 18:10:09 +00:00
front . w . cloak_value = back . w . cloak_value = cloak_value ;
}
2006-03-20 17:12:09 +00:00
2016-11-12 18:10:10 +00:00
if ( front . w . type ! = WALL_CLOAKED )
2016-11-12 18:10:09 +00:00
{ //just switched
2016-11-12 18:10:09 +00:00
front . w . type = back . w . type = WALL_CLOAKED ;
2016-11-12 18:10:10 +00:00
copy_cloaking_wall_light_to_wall ( back . uvls , front . uvls , d ) ;
2006-03-20 17:12:09 +00:00
}
}
else { //fading out
fix light_scale ;
2016-11-12 18:10:10 +00:00
light_scale = fixdiv ( CLOAKING_WALL_TIME / 2 - d . time , CLOAKING_WALL_TIME / 2 ) ;
2016-11-12 18:10:10 +00:00
scale_cloaking_wall_light_to_wall ( back . uvls , front . uvls , d , light_scale ) ;
2006-03-20 17:12:09 +00:00
}
2016-11-12 18:10:10 +00:00
return r ;
2006-03-20 17:12:09 +00:00
}
2016-11-12 18:10:10 +00:00
static cwresult do_decloaking_wall_frame ( const bool initial , cloaking_wall & d , const cwframe front , const cwframe back )
2006-03-20 17:12:09 +00:00
{
2016-11-12 18:10:10 +00:00
cwresult r ( initial ) ;
2016-11-12 18:10:10 +00:00
if ( d . time > CLOAKING_WALL_TIME ) {
2006-03-20 17:12:09 +00:00
2021-11-01 03:37:19 +00:00
back . w . state = wall_state : : closed ;
front . w . state = wall_state : : closed ;
2016-11-12 18:10:10 +00:00
copy_cloaking_wall_light_to_wall ( back . uvls , front . uvls , d ) ;
2016-11-12 18:10:10 +00:00
r . remove = true ;
2006-03-20 17:12:09 +00:00
}
2016-11-12 18:10:10 +00:00
else if ( d . time > CLOAKING_WALL_TIME / 2 ) { //fading in
2006-03-20 17:12:09 +00:00
fix light_scale ;
2016-11-12 18:10:09 +00:00
front . w . type = back . w . type = WALL_CLOSED ;
2006-03-20 17:12:09 +00:00
2016-11-12 18:10:10 +00:00
light_scale = fixdiv ( d . time - CLOAKING_WALL_TIME / 2 , CLOAKING_WALL_TIME / 2 ) ;
2016-11-12 18:10:10 +00:00
scale_cloaking_wall_light_to_wall ( back . uvls , front . uvls , d , light_scale ) ;
2006-03-20 17:12:09 +00:00
}
else { //cloaking in
2016-11-12 18:10:10 +00:00
const int8_t cloak_value = ( ( CLOAKING_WALL_TIME / 2 - d . time ) * ( GR_FADE_LEVELS - 2 ) ) / ( CLOAKING_WALL_TIME / 2 ) ;
2016-11-12 18:10:09 +00:00
if ( front . w . cloak_value ! = cloak_value )
{
front . w . cloak_value = back . w . cloak_value = cloak_value ;
2016-11-12 18:10:10 +00:00
r . record = true ;
2016-11-12 18:10:09 +00:00
}
2016-11-12 18:10:09 +00:00
front . w . type = WALL_CLOAKED ;
2016-11-12 18:10:09 +00:00
back . w . type = WALL_CLOAKED ;
2006-03-20 17:12:09 +00:00
}
2016-11-12 18:10:10 +00:00
return r ;
2006-03-20 17:12:09 +00:00
}
2016-11-19 17:24:54 +00:00
bool cw_removal_predicate : : operator ( ) ( cloaking_wall & d )
{
2018-12-30 00:43:57 +00:00
const cwframe front ( vmsegptr , * Walls . vmptr ( d . front_wallnum ) ) ;
const auto & & wpback = Walls . imptr ( d . back_wallnum ) ;
const cwframe back = ( wpback ? cwframe ( vmsegptr , * wpback ) : front ) ;
2016-11-19 17:24:54 +00:00
const bool initial = ( d . time = = 0 ) ;
d . time + = FrameTime ;
cwresult r ;
2021-11-01 03:37:19 +00:00
if ( front . w . state = = wall_state : : cloaking )
2016-11-19 17:24:54 +00:00
r = do_cloaking_wall_frame ( initial , d , front , back ) ;
2021-11-01 03:37:19 +00:00
else if ( front . w . state = = wall_state : : decloaking )
2016-11-19 17:24:54 +00:00
r = do_decloaking_wall_frame ( initial , d , front , back ) ;
else
{
d_debugbreak ( ) ; //unexpected wall state
return false ;
}
if ( r . record )
{
// check if the actual cloak_value changed in this frame to prevent redundant recordings and wasted bytes
if ( Newdemo_state = = ND_STATE_RECORDING & & r . record )
newdemo_record_cloaking_wall ( d . front_wallnum , d . back_wallnum , front . w . type , front . w . state , front . w . cloak_value , front . uvls [ 0 ] . l , front . uvls [ 1 ] . l , front . uvls [ 2 ] . l , front . uvls [ 3 ] . l ) ;
}
if ( ! r . remove )
+ + num_cloaking_walls ;
return r . remove ;
}
2020-12-19 16:13:26 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2020-12-19 16:13:26 +00:00
namespace {
2022-07-09 13:39:29 +00:00
static void process_exploding_walls ( const d_robot_info_array & Robot_info )
2006-03-20 17:12:09 +00:00
{
2018-12-01 01:58:37 +00:00
if ( Newdemo_state = = ND_STATE_PLAYBACK )
return ;
2018-08-12 21:08:07 +00:00
if ( unsigned num_exploding_walls = Num_exploding_walls )
{
2018-12-30 00:43:58 +00:00
auto & Walls = LevelUniqueWallSubsystemState . Walls ;
2018-08-12 21:08:07 +00:00
range_for ( auto & & wp , Walls . vmptr )
{
auto & w1 = * wp ;
2021-11-01 03:37:19 +00:00
if ( w1 . flags & wall_flag : : exploding )
2018-08-12 21:08:07 +00:00
{
assert ( num_exploding_walls ) ;
2022-07-09 13:39:29 +00:00
const auto n = do_exploding_wall_frame ( Robot_info , w1 ) ;
2019-06-20 04:02:27 +00:00
num_exploding_walls - = n ;
if ( ! num_exploding_walls )
2018-08-12 21:08:07 +00:00
break ;
}
}
}
2018-12-01 01:58:37 +00:00
}
2018-08-12 21:08:07 +00:00
2020-12-19 16:13:26 +00:00
}
2022-07-09 13:39:29 +00:00
void wall_frame_process ( const d_robot_info_array & Robot_info )
2018-12-01 01:58:37 +00:00
{
2022-07-09 13:39:29 +00:00
process_exploding_walls ( Robot_info ) ;
2016-11-26 22:51:48 +00:00
{
2018-12-30 00:43:58 +00:00
auto & ActiveDoors = LevelUniqueWallSubsystemState . ActiveDoors ;
2016-12-11 23:47:40 +00:00
const auto & & r = partial_range ( ActiveDoors , ActiveDoors . get_count ( ) ) ;
2016-11-26 22:51:48 +00:00
auto & & i = std : : remove_if ( r . begin ( ) , r . end ( ) , ad_removal_predicate ( ) ) ;
2016-12-10 17:51:08 +00:00
ActiveDoors . set_count ( std : : distance ( r . begin ( ) , i ) ) ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2016-11-12 18:10:09 +00:00
if ( Newdemo_state ! = ND_STATE_PLAYBACK )
2016-11-19 17:24:54 +00:00
{
2018-12-30 00:43:58 +00:00
auto & CloakingWalls = LevelUniqueWallSubsystemState . CloakingWalls ;
2016-12-11 23:47:40 +00:00
const auto & & r = partial_range ( CloakingWalls , CloakingWalls . get_count ( ) ) ;
2018-12-30 00:43:58 +00:00
auto & Walls = LevelUniqueWallSubsystemState . Walls ;
2018-12-30 00:43:57 +00:00
cw_removal_predicate rp { Segments . vmptr , Walls } ;
2016-11-19 17:24:54 +00:00
std : : remove_if ( r . begin ( ) , r . end ( ) , std : : ref ( rp ) ) ;
2016-11-19 17:24:54 +00:00
CloakingWalls . set_count ( rp . num_cloaking_walls ) ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
2018-08-04 17:52:57 +00:00
d_level_unique_stuck_object_state LevelUniqueStuckObjectState ;
2006-03-20 17:12:09 +00:00
// An object got stuck in a door (like a flare).
// Add global entry.
2022-01-09 15:25:42 +00:00
void d_level_unique_stuck_object_state : : add_stuck_object ( fvcwallptr & vcwallptr , const vmobjptridx_t objp , const shared_segment & segp , const sidenum_t sidenum )
2006-03-20 17:12:09 +00:00
{
2018-09-19 02:13:29 +00:00
const auto wallnum = segp . sides [ sidenum ] . wall_num ;
2015-07-21 02:57:27 +00:00
if ( wallnum ! = wall_none )
{
2021-11-01 03:37:19 +00:00
if ( vcwallptr ( wallnum ) - > flags & wall_flag : : blasted )
2018-08-04 17:52:57 +00:00
{
2006-03-20 17:12:09 +00:00
objp - > flags | = OF_SHOULD_BE_DEAD ;
2018-08-04 17:52:57 +00:00
return ;
}
2018-08-04 17:52:57 +00:00
if ( Num_stuck_objects > = Stuck_objects . size ( ) )
2015-07-21 02:57:27 +00:00
{
2018-08-04 17:52:57 +00:00
assert ( Num_stuck_objects < = Stuck_objects . size ( ) ) ;
con_printf ( CON_NORMAL , " %s:%u: all stuck objects are busy; terminating %hu early " , __FILE__ , __LINE__ , objp . get_unchecked_index ( ) ) ;
objp - > flags | = OF_SHOULD_BE_DEAD ;
return ;
2006-03-20 17:12:09 +00:00
}
2018-08-04 17:52:57 +00:00
auto & so = Stuck_objects [ Num_stuck_objects + + ] ;
so . wallnum = wallnum ;
so . objnum = objp ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
}
2006-03-20 17:12:09 +00:00
2018-08-04 17:52:57 +00:00
void d_level_unique_stuck_object_state : : remove_stuck_object ( const vcobjidx_t obj )
2006-03-20 17:12:09 +00:00
{
2018-08-04 17:52:57 +00:00
auto & & pr = partial_range ( Stuck_objects , Num_stuck_objects ) ;
2022-10-09 23:15:20 +00:00
auto & & pre = pr . end ( ) ;
2018-08-04 17:52:57 +00:00
const auto predicate = [ obj ] ( const stuckobj & so ) { return so . objnum = = obj ; } ;
2022-10-31 00:51:32 +00:00
const auto & & i = ranges : : find_if ( pr . begin ( ) , pre , predicate ) ;
2022-10-09 23:15:20 +00:00
if ( i = = pre )
2018-08-04 17:52:57 +00:00
/* Objects enter this function if they are able to become stuck,
* without regard to whether they actually are stuck . If the
* object terminated without being stuck in a wall , then it will
* not be found in Stuck_objects .
*/
2006-03-20 17:12:09 +00:00
return ;
2018-08-04 17:52:57 +00:00
/* If pr.begin() == pr.end(), then i == pr.end(), and this line
* cannot be reached .
*
* If pr . begin ( ) ! = pr . end ( ) , then prev ( pr . end ( ) ) must point to a
* valid element .
*
* Move that valid element to the location vacated by the removed
* object . This may be a self - move if the removed object is the
* last object .
*/
auto & last_element = * std : : prev ( pr . end ( ) ) ;
static_assert ( std : : is_trivially_move_assignable < stuckobj > : : value , " stuckobj move may require a check to prevent self-move " ) ;
* i = std : : move ( last_element ) ;
DXX_POISON_VAR ( last_element . wallnum , 0xcc ) ;
DXX_POISON_VAR ( last_element . objnum , 0xcc ) ;
- - Num_stuck_objects ;
2006-03-20 17:12:09 +00:00
}
// ----------------------------------------------------------------------------------------------------
// Door with wall index wallnum is opening, kill all objects stuck in it.
2018-08-04 17:52:57 +00:00
void d_level_unique_stuck_object_state : : kill_stuck_objects ( fvmobjptr & vmobjptr , const vcwallidx_t wallnum )
2006-03-20 17:12:09 +00:00
{
2016-07-21 01:43:22 +00:00
if ( ! Num_stuck_objects )
2006-03-20 17:12:09 +00:00
return ;
2018-08-04 17:52:57 +00:00
auto & & pr = partial_range ( Stuck_objects , Num_stuck_objects ) ;
2018-08-04 17:52:57 +00:00
const auto predicate = [ & vmobjptr , wallnum ] ( const stuckobj & so )
{
2018-08-04 17:52:57 +00:00
if ( so . wallnum ! = wallnum )
return false ;
auto & obj = * vmobjptr ( so . objnum ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
# define DXX_WEAPON_LIFELEFT F1_0 / 4
# elif defined(DXX_BUILD_DESCENT_II)
# define DXX_WEAPON_LIFELEFT F1_0 / 8
# endif
2018-08-04 17:52:57 +00:00
assert ( obj . type = = OBJ_WEAPON ) ;
2020-08-10 03:45:13 +00:00
assert ( obj . movement_source = = object : : movement_type : : physics ) ;
2018-08-04 17:52:57 +00:00
assert ( obj . mtype . phys_info . flags & PF_STICK ) ;
obj . lifeleft = DXX_WEAPON_LIFELEFT ;
return true ;
} ;
const auto i = std : : remove_if ( pr . begin ( ) , pr . end ( ) , predicate ) ;
static_assert ( std : : is_trivially_destructible < stuckobj > : : value , " stuckobj destructor not called " ) ;
Num_stuck_objects = std : : distance ( pr . begin ( ) , i ) ;
2020-05-02 21:18:42 +00:00
std : : array < stuckobj , 1 > empty ;
2018-08-04 17:52:57 +00:00
DXX_POISON_VAR ( empty , 0xcc ) ;
std : : fill ( i , pr . end ( ) , empty [ 0 ] ) ;
2006-03-20 17:12:09 +00:00
}
2018-12-30 00:43:57 +00:00
2016-08-25 04:05:32 +00:00
}
2006-03-20 17:12:09 +00:00
2018-08-04 17:52:57 +00:00
namespace dcx {
2006-03-20 17:12:09 +00:00
// -----------------------------------------------------------------------------------
// Initialize stuck objects array. Called at start of level
2018-08-04 17:52:57 +00:00
void d_level_unique_stuck_object_state : : init_stuck_objects ( )
2006-03-20 17:12:09 +00:00
{
2018-08-04 17:52:57 +00:00
DXX_POISON_VAR ( Stuck_objects , 0xcc ) ;
2006-03-20 17:12:09 +00:00
Num_stuck_objects = 0 ;
}
2018-08-04 17:52:57 +00:00
}
2006-03-20 17:12:09 +00:00
2016-10-15 00:53:22 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// -----------------------------------------------------------------------------------
# define MAX_BLAST_GLASS_DEPTH 5
2018-12-30 00:43:57 +00:00
namespace dsx {
namespace {
class blast_nearby_glass_context
2006-03-20 17:12:09 +00:00
{
2018-12-30 00:43:59 +00:00
using TmapInfo_array = d_level_unique_tmap_info_state : : TmapInfo_array ;
2018-12-30 00:43:57 +00:00
const object & obj ;
const fix damage ;
const d_eclip_array & Effects ;
const GameBitmaps_array & GameBitmaps ;
const Textures_array & Textures ;
const TmapInfo_array & TmapInfo ;
const d_vclip_array & Vclip ;
fvcvertptr & vcvertptr ;
fvcwallptr & vcwallptr ;
visited_segment_bitarray_t visited ;
2020-08-24 01:31:28 +00:00
unsigned can_blast ( texture2_value tmap_num2 ) const ;
2018-12-30 00:43:57 +00:00
public :
blast_nearby_glass_context ( const object & obj , const fix damage , const d_eclip_array & Effects , const GameBitmaps_array & GameBitmaps , const Textures_array & Textures , const TmapInfo_array & TmapInfo , const d_vclip_array & Vclip , fvcvertptr & vcvertptr , fvcwallptr & vcwallptr ) :
obj ( obj ) , damage ( damage ) , Effects ( Effects ) , GameBitmaps ( GameBitmaps ) ,
Textures ( Textures ) , TmapInfo ( TmapInfo ) , Vclip ( Vclip ) ,
vcvertptr ( vcvertptr ) , vcwallptr ( vcwallptr ) , visited { }
{
}
blast_nearby_glass_context ( const blast_nearby_glass_context & ) = delete ;
blast_nearby_glass_context & operator = ( const blast_nearby_glass_context & ) = delete ;
void process_segment ( vmsegptridx_t segp , unsigned steps_remaining ) ;
} ;
2006-03-20 17:12:09 +00:00
2020-08-24 01:31:28 +00:00
unsigned blast_nearby_glass_context : : can_blast ( const texture2_value tmap_num2 ) const
2018-12-30 00:43:57 +00:00
{
2020-08-24 01:31:28 +00:00
const auto tm = get_texture_index ( tmap_num2 ) ; //tm without flags
2018-12-30 00:43:57 +00:00
auto & ti = TmapInfo [ tm ] ;
const auto ec = ti . eclip_num ;
if ( ec = = eclip_none )
{
return ti . destroyed ! = - 1 ;
}
else
{
auto & e = Effects [ ec ] ;
return e . dest_bm_num ! = ~ 0u & & ! ( e . flags & EF_ONE_SHOT ) ;
}
}
2006-03-20 17:12:09 +00:00
2018-12-30 00:43:57 +00:00
void blast_nearby_glass_context : : process_segment ( const vmsegptridx_t segp , const unsigned steps_remaining )
{
visited [ segp ] = true ;
2006-03-20 17:12:09 +00:00
2018-12-30 00:43:57 +00:00
const auto & objp = obj ;
2021-06-28 03:37:50 +00:00
for ( const auto & & [ sidenum , uside ] : enumerate ( segp - > unique_segment : : sides ) )
2018-12-30 00:43:57 +00:00
{
2006-03-20 17:12:09 +00:00
fix dist ;
// Process only walls which have glass.
2020-08-24 01:31:28 +00:00
if ( const auto tmap_num2 = uside . tmap_num2 ; tmap_num2 ! = texture2_value : : None )
2018-12-30 00:43:57 +00:00
{
if ( can_blast ( tmap_num2 ) )
2018-03-31 21:53:01 +00:00
{
2018-03-12 03:43:47 +00:00
const auto & & pnt = compute_center_point_on_side ( vcvertptr , segp , sidenum ) ;
2016-04-23 17:59:47 +00:00
dist = vm_vec_dist_quick ( pnt , objp . pos ) ;
2006-03-20 17:12:09 +00:00
if ( dist < damage / 2 ) {
2023-01-29 20:42:03 +00:00
dist = find_connected_distance ( pnt , segp , objp . pos , segp . absolute_sibling ( objp . segnum ) , MAX_BLAST_GLASS_DEPTH , wall_is_doorway_mask : : rendpast ) ;
2006-03-20 17:12:09 +00:00
if ( ( dist > 0 ) & & ( dist < damage / 2 ) )
2018-11-01 02:41:30 +00:00
{
assert ( objp . type = = OBJ_WEAPON ) ;
2022-01-09 15:25:42 +00:00
check_effect_blowup ( LevelSharedSegmentState . DestructibleLights , Vclip , segp , static_cast < sidenum_t > ( sidenum ) , pnt , objp . ctype . laser_info , 1 , 0 ) ;
2018-11-01 02:41:30 +00:00
}
2006-03-20 17:12:09 +00:00
}
}
}
}
2018-12-30 00:43:57 +00:00
const unsigned next_steps_remaining = steps_remaining - 1 ;
if ( ! next_steps_remaining )
return ;
2021-06-28 03:37:50 +00:00
for ( const auto & & [ i , segnum ] : enumerate ( segp - > children ) )
2018-12-30 00:43:57 +00:00
{
2013-12-26 22:21:16 +00:00
if ( segnum ! = segment_none ) {
2006-03-20 17:12:09 +00:00
if ( ! visited [ segnum ] ) {
2022-01-15 20:39:10 +00:00
if ( WALL_IS_DOORWAY ( GameBitmaps , Textures , vcwallptr , segp , static_cast < sidenum_t > ( i ) ) & WALL_IS_DOORWAY_FLAG : : fly )
2018-06-24 05:06:15 +00:00
{
2018-12-30 00:43:57 +00:00
process_segment ( segp . absolute_sibling ( segnum ) , next_steps_remaining ) ;
2006-03-20 17:12:09 +00:00
}
}
}
}
}
2014-02-02 05:30:48 +00:00
struct d1wclip
{
2018-12-30 00:43:57 +00:00
wclip * const wc ;
2014-02-02 05:30:48 +00:00
d1wclip ( wclip & w ) : wc ( & w ) { }
} ;
DEFINE_SERIAL_UDT_TO_MESSAGE ( d1wclip , dwc , ( dwc . wc - > play_time , dwc . wc - > num_frames , dwc . wc - > d1_frames , dwc . wc - > open_sound , dwc . wc - > close_sound , dwc . wc - > flags , dwc . wc - > filename , serial : : pad < 1 > ( ) ) ) ;
ASSERT_SERIAL_UDT_MESSAGE_SIZE ( d1wclip , 26 + ( sizeof ( int16_t ) * MAX_CLIP_FRAMES_D1 ) ) ;
2018-12-30 00:43:57 +00:00
}
// -----------------------------------------------------------------------------------
// objp is going to detonate
// blast nearby monitors, lights, maybe other things
void blast_nearby_glass ( const object & objp , const fix damage )
{
2020-05-17 23:35:25 +00:00
auto & LevelSharedVertexState = LevelSharedSegmentState . get_vertex_state ( ) ;
2019-03-03 00:31:09 +00:00
auto & Effects = LevelUniqueEffectsClipState . Effects ;
2018-12-30 00:43:59 +00:00
auto & TmapInfo = LevelUniqueTmapInfoState . TmapInfo ;
2018-12-30 00:43:57 +00:00
auto & Vertices = LevelSharedVertexState . get_vertices ( ) ;
auto & vcvertptr = Vertices . vcptr ;
2018-12-30 00:43:58 +00:00
auto & Walls = LevelUniqueWallSubsystemState . Walls ;
auto & vcwallptr = Walls . vcptr ;
2018-12-30 00:43:57 +00:00
blast_nearby_glass_context { objp , damage , Effects , GameBitmaps , Textures , TmapInfo , Vclip , vcvertptr , vcwallptr } . process_segment ( vmsegptridx ( objp . segnum ) , MAX_BLAST_GLASS_DEPTH ) ;
}
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2014-02-02 05:30:48 +00:00
DEFINE_SERIAL_UDT_TO_MESSAGE ( wclip , wc , ( wc . play_time , wc . num_frames , wc . frames , wc . open_sound , wc . close_sound , wc . flags , wc . filename , serial : : pad < 1 > ( ) ) ) ;
ASSERT_SERIAL_UDT_MESSAGE_SIZE ( wclip , 26 + ( sizeof ( int16_t ) * MAX_CLIP_FRAMES ) ) ;
2020-12-19 16:13:26 +00:00
namespace dsx {
2006-03-20 17:12:09 +00:00
/*
2016-01-09 16:38:14 +00:00
* reads a wclip structure from a PHYSFS_File
2006-03-20 17:12:09 +00:00
*/
2016-01-09 16:38:14 +00:00
void wclip_read ( PHYSFS_File * fp , wclip & wc )
2006-03-20 17:12:09 +00:00
{
2014-02-02 05:30:48 +00:00
PHYSFSX_serialize_read ( fp , wc ) ;
}
2015-12-04 03:36:31 +00:00
#if 0
2016-01-09 16:38:14 +00:00
void wclip_write ( PHYSFS_File * fp , const wclip & wc )
2014-02-02 05:30:48 +00:00
{
PHYSFSX_serialize_write ( fp , wc ) ;
2006-03-20 17:12:09 +00:00
}
2015-12-04 03:36:31 +00:00
# endif
2020-12-19 16:13:26 +00:00
}
2006-03-20 17:12:09 +00:00
2014-04-27 03:09:11 +00:00
struct wrap_v16_wall
{
const wall * w ;
wrap_v16_wall ( const wall & t ) : w ( & t ) { }
} ;
# define _SERIAL_UDT_WALL_V16_MEMBERS(P) (P type, P flags, P hps, P trigger, P clip_num, P keys)
DEFINE_SERIAL_UDT_TO_MESSAGE ( v16_wall , w , _SERIAL_UDT_WALL_V16_MEMBERS ( w . ) ) ;
DEFINE_SERIAL_UDT_TO_MESSAGE ( wrap_v16_wall , w , _SERIAL_UDT_WALL_V16_MEMBERS ( w . w - > ) ) ;
ASSERT_SERIAL_UDT_MESSAGE_SIZE ( wrap_v16_wall , 9 ) ;
2006-03-20 17:12:09 +00:00
/*
2016-01-09 16:38:14 +00:00
* reads a v16_wall structure from a PHYSFS_File
2006-03-20 17:12:09 +00:00
*/
2016-01-09 16:38:14 +00:00
void v16_wall_read ( PHYSFS_File * fp , v16_wall & w )
2006-03-20 17:12:09 +00:00
{
2014-04-27 03:09:11 +00:00
PHYSFSX_serialize_read ( fp , w ) ;
2006-03-20 17:12:09 +00:00
}
2014-04-27 03:09:11 +00:00
struct wrap_v19_wall
{
const wall * w ;
wrap_v19_wall ( const wall & t ) : w ( & t ) { }
} ;
2020-12-26 21:17:29 +00:00
DEFINE_SERIAL_UDT_TO_MESSAGE ( v19_wall , w , ( w . segnum , serial : : pad < 2 > ( ) , w . sidenum , w . type , w . flags , w . hps , w . trigger , w . clip_num , w . keys , w . linked_wall , serial : : pad < 2 > ( ) ) ) ;
2015-01-12 00:26:02 +00:00
DEFINE_SERIAL_UDT_TO_MESSAGE ( wrap_v19_wall , w , ( w . w - > segnum , serial : : pad < 2 > ( ) , w . w - > sidenum , serial : : pad < 3 > ( ) , w . w - > type , w . w - > flags , w . w - > hps , w . w - > trigger , w . w - > clip_num , w . w - > keys , w . w - > linked_wall , serial : : pad < 2 > ( ) ) ) ;
2014-04-27 03:09:11 +00:00
ASSERT_SERIAL_UDT_MESSAGE_SIZE ( v19_wall , 21 ) ;
ASSERT_SERIAL_UDT_MESSAGE_SIZE ( wrap_v19_wall , 21 ) ;
2006-03-20 17:12:09 +00:00
/*
2016-01-09 16:38:14 +00:00
* reads a v19_wall structure from a PHYSFS_File
2006-03-20 17:12:09 +00:00
*/
2016-01-09 16:38:14 +00:00
void v19_wall_read ( PHYSFS_File * fp , v19_wall & w )
2006-03-20 17:12:09 +00:00
{
2014-04-27 03:09:11 +00:00
PHYSFSX_serialize_read ( fp , w ) ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2014-04-27 03:09:11 +00:00
# define _SERIAL_UDT_WALL_D2X_MEMBERS serial::pad<2>()
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2014-04-27 03:09:11 +00:00
# define _SERIAL_UDT_WALL_D2X_MEMBERS w.controlling_trigger, w.cloak_value
2013-03-03 01:03:33 +00:00
# endif
2020-12-26 21:17:29 +00:00
DEFINE_SERIAL_UDT_TO_MESSAGE ( wall , w , ( serial : : sign_extend < int > ( w . segnum ) , w . sidenum , serial : : pad < 3 , 0 > ( ) , w . hps , w . linked_wall , serial : : pad < 2 , 0 > ( ) , w . type , w . flags , w . state , w . trigger , w . clip_num , w . keys , _SERIAL_UDT_WALL_D2X_MEMBERS ) ) ;
2014-04-27 03:09:11 +00:00
ASSERT_SERIAL_UDT_MESSAGE_SIZE ( wall , 24 ) ;
2009-10-05 02:51:37 +00:00
2020-12-19 16:13:26 +00:00
namespace dsx {
2009-10-05 02:51:37 +00:00
/*
2016-01-09 16:38:14 +00:00
* reads a wall structure from a PHYSFS_File
2009-10-05 02:51:37 +00:00
*/
2016-01-09 16:38:14 +00:00
void wall_read ( PHYSFS_File * fp , wall & w )
2009-10-05 02:51:37 +00:00
{
2014-04-27 03:09:11 +00:00
PHYSFSX_serialize_read ( fp , w ) ;
2021-11-01 03:37:19 +00:00
w . flags & = ~ wall_flag : : exploding ;
2009-10-05 02:51:37 +00:00
}
2020-12-19 16:13:26 +00:00
}
2014-04-27 02:39:44 +00:00
DEFINE_SERIAL_UDT_TO_MESSAGE ( active_door , d , ( d . n_parts , d . front_wallnum , d . back_wallnum , d . time ) ) ;
ASSERT_SERIAL_UDT_MESSAGE_SIZE ( active_door , 16 ) ;
2006-03-20 17:12:09 +00:00
/*
2016-01-09 16:38:14 +00:00
* reads an active_door structure from a PHYSFS_File
2006-03-20 17:12:09 +00:00
*/
2016-01-09 16:38:14 +00:00
void active_door_read ( PHYSFS_File * fp , active_door & ad )
2009-10-05 02:51:37 +00:00
{
2014-04-27 02:39:44 +00:00
PHYSFSX_serialize_read ( fp , ad ) ;
2009-10-05 02:51:37 +00:00
}
2016-01-09 16:38:14 +00:00
void active_door_write ( PHYSFS_File * fp , const active_door & ad )
2009-10-05 02:51:37 +00:00
{
2014-04-27 02:39:44 +00:00
PHYSFSX_serialize_write ( fp , ad ) ;
2009-10-05 02:51:37 +00:00
}
2020-12-19 16:13:26 +00:00
namespace dsx {
2016-01-09 16:38:14 +00:00
void wall_write ( PHYSFS_File * fp , const wall & w , short version )
2006-03-20 17:12:09 +00:00
{
2014-04-27 03:09:11 +00:00
if ( version < = 16 )
PHYSFSX_serialize_write < wrap_v16_wall > ( fp , w ) ;
else if ( version < = 19 )
PHYSFSX_serialize_write < wrap_v19_wall > ( fp , w ) ;
2006-03-20 17:12:09 +00:00
else
2014-04-27 03:09:11 +00:00
PHYSFSX_serialize_write ( fp , w ) ;
2006-03-20 17:12:09 +00:00
}
2009-10-05 02:51:37 +00:00
2020-12-19 16:13:26 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2020-12-19 16:13:26 +00:00
DEFINE_SERIAL_UDT_TO_MESSAGE ( dsx : : cloaking_wall , cw , ( cw . front_wallnum , cw . back_wallnum , cw . front_ls , cw . back_ls , cw . time ) ) ;
ASSERT_SERIAL_UDT_MESSAGE_SIZE ( dsx : : cloaking_wall , 40 ) ;
2014-01-12 23:00:43 +00:00
2018-12-30 00:43:57 +00:00
namespace dsx {
2016-01-09 16:38:14 +00:00
void cloaking_wall_read ( cloaking_wall & cw , PHYSFS_File * fp )
2009-10-05 02:51:37 +00:00
{
2014-01-12 23:00:43 +00:00
PHYSFSX_serialize_read ( fp , cw ) ;
}
2016-01-09 16:38:14 +00:00
void cloaking_wall_write ( const cloaking_wall & cw , PHYSFS_File * fp )
2014-01-12 23:00:43 +00:00
{
PHYSFSX_serialize_write ( fp , cw ) ;
2009-10-05 02:51:37 +00:00
}
2018-12-30 00:43:57 +00:00
}
2013-03-03 01:03:33 +00:00
# endif