dxx-rebirth/similar/main/wall.cpp
Kp 7d926f0741 Remove bogus remove_obsolete_stuck_objects lifeleft change
This assignment looks bogus.  If the signature saved when the object
became stuck is different from the signature of the object currently
using that slot, then remove_obsolete_stuck_objects kills the object
using that slot.  Signatures are defined to change when the object slot
is freed and reused.  Therefore, this statement kills the new object
that took the slot of the obsolete object.  This new object may be
important, such as a spawned robot or a weapon.
2016-07-21 01:43:21 +00:00

1625 lines
45 KiB
C++

/*
* 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.
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 "player.h"
#include "text.h"
#include "fireball.h"
#include "textures.h"
#include "newdemo.h"
#include "multi.h"
#include "gameseq.h"
#include "physfs-serial.h"
#include "gameseg.h"
#include "hudmsg.h"
#include "laser.h" // For seeing if a flare is stuck in a wall.
#include "effects.h"
#include "compiler-range_for.h"
#include "partial_range.h"
#include "segiter.h"
// 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
#define BOSS_LOCKED_DOOR_SIDE 5
namespace dcx {
unsigned Num_wall_anims;
unsigned Num_open_doors; // Number of open doors
}
namespace dsx {
array<wclip, MAX_WALL_ANIMS> WallAnims; // Wall animations
array<active_door, MAX_DOORS> ActiveDoors;
}
#if defined(DXX_BUILD_DESCENT_II)
#include "collide.h"
namespace dsx {
#define CLOAKING_WALL_TIME f1_0
array<cloaking_wall, MAX_CLOAKING_WALLS> CloakingWalls;
unsigned Num_cloaking_walls;
}
#endif
static std::pair<uint_fast32_t, uint_fast32_t> get_transparency_check_values(const side &side)
{
if (uint_fast32_t masked_tmap_num2 = side.tmap_num2 & 0x3FFF)
return {masked_tmap_num2, BM_FLAG_SUPER_TRANSPARENT};
return {side.tmap_num, BM_FLAG_TRANSPARENT};
}
// This function determines whether the current segment/side is transparent
// 1 = YES
// 0 = NO
static uint_fast32_t check_transparency(const side &side)
{
auto v = get_transparency_check_values(side);
return GameBitmaps[Textures[v.first].index].bm_flags & v.second;
}
//-----------------------------------------------------------------
// This function checks whether we can fly through the given side.
// In other words, whether or not we have a 'doorway'
// Flags:
// WID_FLY_FLAG 1
// WID_RENDER_FLAG 2
// WID_RENDPAST_FLAG 4
// Return values:
// WID_WALL 2 // 0/1/0 wall
// WID_TRANSPARENT_WALL 6 // 0/1/1 transparent wall
// WID_ILLUSORY_WALL 3 // 1/1/0 illusory wall
// WID_TRANSILLUSORY_WALL 7 // 1/1/1 transparent illusory wall
// WID_NO_WALL 5 // 1/0/1 no wall, can fly through
WALL_IS_DOORWAY_result_t wall_is_doorway(const side &side)
{
auto &w = *vcwallptr(side.wall_num);
const auto type = w.type;
if (type == WALL_OPEN)
return WID_NO_WALL;
#if defined(DXX_BUILD_DESCENT_II)
if (unlikely(type == WALL_CLOAKED))
return WID_CLOAKED_WALL;
#endif
const auto flags = w.flags;
if (type == WALL_ILLUSION) {
if (flags & WALL_ILLUSION_OFF)
return WID_NO_WALL;
else {
if (check_transparency(side))
return WID_TRANSILLUSORY_WALL;
else
return WID_ILLUSORY_WALL;
}
}
if (type == WALL_BLASTABLE) {
if (flags & WALL_BLASTED)
return WID_TRANSILLUSORY_WALL;
if (check_transparency(side))
return WID_TRANSPARENT_WALL;
else
return WID_WALL;
}
if (unlikely(flags & WALL_DOOR_OPENED))
return WID_TRANSILLUSORY_WALL;
if (likely(type == WALL_DOOR) && unlikely(w.state == WALL_DOOR_OPENING))
return WID_TRANSPARENT_WALL;
// If none of the above flags are set, there is no doorway.
if (check_transparency(side))
return WID_TRANSPARENT_WALL;
else
return WID_WALL; // There are children behind the door.
}
#ifdef EDITOR
//-----------------------------------------------------------------
// Initializes all the walls (in other words, no special walls)
void wall_init()
{
Walls.set_count(0);
range_for (auto &w, Walls)
{
w.segnum = segment_none;
w.sidenum = -1;
w.type = WALL_NORMAL;
w.flags = 0;
w.hps = 0;
w.trigger = -1;
w.clip_num = -1;
w.linked_wall = -1;
}
Num_open_doors = 0;
#if defined(DXX_BUILD_DESCENT_II)
Num_cloaking_walls = 0;
#endif
}
#endif
//set the tmap_num or tmap_num2 field for a wall/door
void wall_set_tmap_num(const vsegptridx_t seg,int side,const vsegptridx_t csegp,int cside,int anim_num,int frame_num)
{
wclip *anim = &WallAnims[anim_num];
int tmap = anim->frames[frame_num];
if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
if (anim->flags & WCF_TMAP1) {
if (tmap != seg->sides[side].tmap_num || tmap != csegp->sides[cside].tmap_num)
{
seg->sides[side].tmap_num = csegp->sides[cside].tmap_num = tmap;
if ( Newdemo_state == ND_STATE_RECORDING )
newdemo_record_wall_set_tmap_num1(seg,side,csegp,cside,tmap);
}
} else {
Assert(tmap!=0 && seg->sides[side].tmap_num2!=0);
if (tmap != seg->sides[side].tmap_num2 || tmap != csegp->sides[cside].tmap_num2)
{
seg->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = tmap;
if ( Newdemo_state == ND_STATE_RECORDING )
newdemo_record_wall_set_tmap_num2(seg,side,csegp,cside,tmap);
}
}
}
// -------------------------------------------------------------------------------
//when the wall has used all its hitpoints, this will destroy it
static void blast_blastable_wall(const vsegptridx_t seg, int side)
{
auto &w0 = *vwallptr(seg->sides[side].wall_num);
w0.hps = -1; //say it's blasted
const auto &&csegp = seg.absolute_sibling(seg->children[side]);
auto Connectside = find_connect_side(seg, csegp);
Assert(Connectside != side_none);
const auto cwall_num = csegp->sides[Connectside].wall_num;
const auto &&w1 = wallptr(cwall_num);
kill_stuck_objects(seg->sides[side].wall_num);
if (w1)
kill_stuck_objects(cwall_num);
const auto a = w0.clip_num;
//if this is an exploding wall, explode it
if (WallAnims[a].flags & WCF_EXPLODES)
explode_wall(seg,side);
else {
//if not exploding, set final frame, and make door passable
const auto n = WallAnims[a].num_frames;
w0.flags |= WALL_BLASTED;
if (w1)
w1->flags |= WALL_BLASTED;
wall_set_tmap_num(seg,side,csegp,Connectside,a,n-1);
}
}
//-----------------------------------------------------------------
// Destroys a blastable wall.
void wall_destroy(const vsegptridx_t seg, int side)
{
auto &w = *vwallptr(seg->sides[side].wall_num);
if (w.type == WALL_BLASTABLE)
blast_blastable_wall( seg, side );
else
Error("Hey bub, you are trying to destroy an indestructable wall.");
}
//-----------------------------------------------------------------
// Deteriorate appearance of wall. (Changes bitmap (paste-ons))
void wall_damage(const vsegptridx_t seg, int side, fix damage)
{
int i;
if (seg->sides[side].wall_num == wall_none) {
return;
}
auto &w0 = *vwallptr(seg->sides[side].wall_num);
if (w0.type != WALL_BLASTABLE)
return;
if (!(w0.flags & WALL_BLASTED) && w0.hps >= 0)
{
const auto &&csegp = seg.absolute_sibling(seg->children[side]);
auto Connectside = find_connect_side(seg, csegp);
Assert(Connectside != side_none);
const auto cwall_num = csegp->sides[Connectside].wall_num;
if (const auto &&w1 = wallptr(cwall_num))
w1->hps -= damage;
w0.hps -= damage;
const auto a = w0.clip_num;
const auto n = WallAnims[a].num_frames;
if (w0.hps < WALL_HPS*1/n) {
blast_blastable_wall( seg, side );
if (Game_mode & GM_MULTI)
multi_send_door_open(seg, side, w0.flags);
}
else
for (i=0;i<n;i++)
if (w0.hps < WALL_HPS*(n-i)/n)
{
wall_set_tmap_num(seg,side,csegp,Connectside,a,i);
}
}
}
//-----------------------------------------------------------------
// Opens a door
void wall_open_door(const vsegptridx_t seg, int side)
{
active_door *d;
const auto wall_num = seg->sides[side].wall_num;
wall *const w = vwallptr(wall_num);
//kill_stuck_objects(seg->sides[side].wall_num);
if ((w->state == WALL_DOOR_OPENING) || //already opening
(w->state == WALL_DOOR_WAITING)) //open, waiting to close
return;
#if defined(DXX_BUILD_DESCENT_II)
if (w->state == WALL_DOOR_OPEN) //open, & staying open
return;
#endif
if (w->state == WALL_DOOR_CLOSING) { //closing, so reuse door
int i;
d = NULL;
for (i=0;i<Num_open_doors;i++) { //find door
d = &ActiveDoors[i];
if (d->front_wallnum[0]==wall_num || d->back_wallnum[0]==wall_num ||
(d->n_parts==2 && (d->front_wallnum[1]==wall_num || d->back_wallnum[1]==wall_num)))
break;
}
if (i>=Num_open_doors) // likely in demo playback or multiplayer
{
d = &ActiveDoors[Num_open_doors];
d->time = 0;
Num_open_doors++;
Assert( Num_open_doors < MAX_DOORS );
}
else
{
Assert( d!=NULL ); // Get John!
d->time = WallAnims[w->clip_num].play_time - d->time;
if (d->time < 0)
d->time = 0;
}
}
else { //create new door
Assert(w->state == WALL_DOOR_CLOSED);
d = &ActiveDoors[Num_open_doors];
d->time = 0;
Num_open_doors++;
Assert( Num_open_doors < MAX_DOORS );
}
w->state = WALL_DOOR_OPENING;
// So that door can't be shot while opening
const auto &&csegp = vcsegptr(seg->children[side]);
auto Connectside = find_connect_side(seg, csegp);
if (Connectside != side_none)
{
const auto cwall_num = csegp->sides[Connectside].wall_num;
if (const auto &&w1 = wallptr(cwall_num))
{
w1->state = WALL_DOOR_OPENING;
d->back_wallnum[0] = cwall_num;
}
d->front_wallnum[0] = seg->sides[side].wall_num;
}
else
con_printf(CON_URGENT, "Illegal Connectside %i in wall_open_door. Trying to hop over. Please check your level!", side);
if (Newdemo_state == ND_STATE_RECORDING) {
newdemo_record_door_opening(seg, side);
}
if (w->linked_wall != wall_none)
{
wall *const w2 = vwallptr(w->linked_wall);
Assert(w2->linked_wall == seg->sides[side].wall_num);
//Assert(!(w2->flags & WALL_DOOR_OPENING || w2->flags & WALL_DOOR_OPENED));
w2->state = WALL_DOOR_OPENING;
const auto &&seg2 = vcsegptridx(w2->segnum);
Connectside = find_connect_side(seg2, vcsegptr(seg2->children[w2->sidenum]));
Assert(Connectside != side_none);
const auto cwall_num = csegp->sides[Connectside].wall_num;
if (const auto &&w3 = wallptr(cwall_num))
w3->state = WALL_DOOR_OPENING;
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!!!!
const auto cp = compute_center_point_on_side(seg, side );
if (WallAnims[w->clip_num].open_sound > -1 )
digi_link_sound_to_pos( WallAnims[w->clip_num].open_sound, seg, side, cp, 0, F1_0 );
}
}
#if defined(DXX_BUILD_DESCENT_I)
// This function closes the specified door and restores the closed
// door texture. This is called when the animation is done
void wall_close_door(int door_num)
{
active_door *d;
int i;
d = &ActiveDoors[door_num];
range_for (const auto p, partial_const_range(d->front_wallnum, d->n_parts))
{
wall *const w = vwallptr(p);
int side;
const auto &&seg = vsegptridx(w->segnum);
side = w->sidenum;
Assert(seg->sides[side].wall_num != wall_none); //Closing door on illegal wall
const auto &&csegp = seg.absolute_sibling(seg->children[side]);
auto Connectside = find_connect_side(seg, csegp);
Assert(Connectside != side_none);
w->state = WALL_DOOR_CLOSED;
vwallptr(csegp->sides[Connectside].wall_num)->state = WALL_DOOR_CLOSED;
wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,0);
}
for (i=door_num;i<Num_open_doors;i++)
ActiveDoors[i] = ActiveDoors[i+1];
Num_open_doors--;
}
#endif
#if defined(DXX_BUILD_DESCENT_II)
//-----------------------------------------------------------------
// start the transition from closed -> open wall
void start_wall_cloak(const vsegptridx_t seg, int side)
{
cloaking_wall *d;
if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
const auto &&w = vwallptridx(seg->sides[side].wall_num);
if (w->type == WALL_OPEN || w->state == WALL_DOOR_CLOAKING) //already open or cloaking
return;
const auto &&csegp = vcsegptr(seg->children[side]);
auto Connectside = find_connect_side(seg, csegp);
Assert(Connectside != side_none);
const auto cwall_num = csegp->sides[Connectside].wall_num;
if (w->state == WALL_DOOR_DECLOAKING) { //decloaking, so reuse door
d = NULL;
for (unsigned i = 0; i < Num_cloaking_walls; ++i)
{ //find door
d = &CloakingWalls[i];
if (d->front_wallnum == w || d->back_wallnum == w)
{
d->time = CLOAKING_WALL_TIME - d->time;
break;
}
}
Assert( d!=NULL ); // Get John!
}
else if (w->state == WALL_DOOR_CLOSED) { //create new door
d = &CloakingWalls[Num_cloaking_walls];
d->time = 0;
if (Num_cloaking_walls >= MAX_CLOAKING_WALLS) { //no more!
Int3(); //ran out of cloaking wall slots
w->type = WALL_OPEN;
if (const auto &&w1 = wallptr(cwall_num))
w1->type = WALL_OPEN;
return;
}
Num_cloaking_walls++;
}
else {
Int3(); //unexpected wall state
return;
}
w->state = WALL_DOOR_CLOAKING;
if (const auto &&w1 = wallptr(cwall_num))
w1->state = WALL_DOOR_CLOAKING;
d->front_wallnum = seg->sides[side].wall_num;
d->back_wallnum = cwall_num;
Assert(w->linked_wall == wall_none);
if ( Newdemo_state != ND_STATE_PLAYBACK ) {
const auto cp = compute_center_point_on_side(seg, side );
digi_link_sound_to_pos( SOUND_WALL_CLOAK_ON, seg, side, cp, 0, F1_0 );
}
auto &df = d->front_ls;
auto &db = d->back_ls;
auto &s0_uvls = seg->sides[side].uvls;
auto &s1_uvls = csegp->sides[Connectside].uvls;
for (unsigned i = 0; i < 4; ++i)
{
df[i] = s0_uvls[i].l;
db[i] = s1_uvls[i].l;
}
}
//-----------------------------------------------------------------
// start the transition from open -> closed wall
void start_wall_decloak(const vsegptridx_t seg, int side)
{
cloaking_wall *d;
if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
Assert(seg->sides[side].wall_num != wall_none); //Opening door on illegal wall
const auto &&w = vwallptridx(seg->sides[side].wall_num);
if (w->type == WALL_CLOSED || w->state == WALL_DOOR_DECLOAKING) //already closed or decloaking
return;
if (w->state == WALL_DOOR_CLOAKING) { //cloaking, so reuse door
d = NULL;
for (unsigned i = 0; i < Num_cloaking_walls; ++i)
{ //find door
d = &CloakingWalls[i];
if (d->front_wallnum == w || d->back_wallnum == w)
{
d->time = CLOAKING_WALL_TIME - d->time;
break;
}
}
Assert( d!=NULL ); // Get John!
}
else if (w->state == WALL_DOOR_CLOSED) { //create new door
d = &CloakingWalls[Num_cloaking_walls];
d->time = 0;
if (Num_cloaking_walls >= MAX_CLOAKING_WALLS) { //no more!
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;
}
Num_cloaking_walls++;
}
else {
Int3(); //unexpected wall state
return;
}
w->state = WALL_DOOR_DECLOAKING;
// So that door can't be shot while opening
const auto &&csegp = vcsegptr(seg->children[side]);
auto Connectside = find_connect_side(seg, csegp);
Assert(Connectside != side_none);
const auto cwall_num = csegp->sides[Connectside].wall_num;
if (const auto &&w1 = wallptr(cwall_num))
w1->state = WALL_DOOR_DECLOAKING;
d->front_wallnum = seg->sides[side].wall_num;
d->back_wallnum = csegp->sides[Connectside].wall_num;
Assert(w->linked_wall == wall_none);
if ( Newdemo_state != ND_STATE_PLAYBACK ) {
const auto cp = compute_center_point_on_side(seg, side );
digi_link_sound_to_pos( SOUND_WALL_CLOAK_OFF, seg, side, cp, 0, F1_0 );
}
auto &df = d->front_ls;
auto &db = d->back_ls;
auto &s0_uvls = seg->sides[side].uvls;
auto &s1_uvls = csegp->sides[Connectside].uvls;
for (unsigned i = 0; i < 4; ++i)
{
df[i] = s0_uvls[i].l;
db[i] = s1_uvls[i].l;
}
}
#endif
//-----------------------------------------------------------------
// This function closes the specified door and restores the closed
// door texture. This is called when the animation is done
void wall_close_door_num(int door_num)
{
active_door *d;
int i;
d = &ActiveDoors[door_num];
range_for (const auto p, partial_const_range(d->front_wallnum, d->n_parts))
{
int side;
wall *const w = vwallptr(p);
const auto &&seg = vsegptridx(w->segnum);
side = w->sidenum;
w->state = WALL_DOOR_CLOSED;
Assert(seg->sides[side].wall_num != wall_none); //Closing door on illegal wall
const auto &&csegp = seg.absolute_sibling(seg->children[side]);
auto Connectside = find_connect_side(seg, csegp);
Assert(Connectside != side_none);
const auto cwall_num = csegp->sides[Connectside].wall_num;
if (const auto &&w1 = wallptr(cwall_num))
w1->state = WALL_DOOR_CLOSED;
wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,0);
}
for (i=door_num;i<Num_open_doors;i++)
ActiveDoors[i] = ActiveDoors[i+1];
Num_open_doors--;
}
static uint8_t check_poke(const vcobjptr_t obj, const vcsegptr_t segnum,int side)
{
//note: don't let objects with zero size block door
if (!obj->size)
return 0;
return get_seg_masks(obj->pos, segnum, obj->size).sidemask & (1 << side); //pokes through side!
}
static int is_door_side_free(const vcsegptr_t seg, int side)
{
range_for (const auto &&obj, objects_in(seg))
{
#if defined(DXX_BUILD_DESCENT_II)
if (obj->type == OBJ_WEAPON)
continue;
if (obj->type == OBJ_FIREBALL)
continue;
#endif
if (check_poke(obj,seg,side))
return 0; //not free
}
return 1;
}
//returns true of door in unobjstructed (& thus can close)
static int is_door_free(const vcsegptridx_t seg,int side)
{
if (!is_door_side_free(seg, side))
return 0;
const auto &&csegp = vcsegptr(seg->children[side]);
const auto &&Connectside = find_connect_side(seg, csegp);
Assert(Connectside != side_none);
//go through each object in each of two segments, and see if
//it pokes into the connecting seg
return is_door_side_free(csegp, Connectside);
}
#if defined(DXX_BUILD_DESCENT_II)
//-----------------------------------------------------------------
// Closes a door
void wall_close_door(const vsegptridx_t seg, int side)
{
active_door *d;
const auto wall_num = seg->sides[side].wall_num;
wall *const w = vwallptr(wall_num);
if ((w->state == WALL_DOOR_CLOSING) || //already closing
(w->state == WALL_DOOR_WAITING) || //open, waiting to close
(w->state == WALL_DOOR_CLOSED)) //closed
return;
if (!is_door_free(seg,side))
return;
if (w->state == WALL_DOOR_OPENING) { //reuse door
int i;
d = NULL;
for (i=0;i<Num_open_doors;i++) { //find door
d = &ActiveDoors[i];
if (d->front_wallnum[0]==wall_num || d->back_wallnum[0]==wall_num ||
(d->n_parts==2 && (d->front_wallnum[1]==wall_num || d->back_wallnum[1]==wall_num)))
break;
}
Assert(i<Num_open_doors); //didn't find door!
Assert( d!=NULL ); // Get John!
d->time = WallAnims[w->clip_num].play_time - d->time;
if (d->time < 0)
d->time = 0;
}
else { //create new door
Assert(w->state == WALL_DOOR_OPEN);
d = &ActiveDoors[Num_open_doors];
d->time = 0;
Num_open_doors++;
Assert( Num_open_doors < MAX_DOORS );
}
w->state = WALL_DOOR_CLOSING;
// So that door can't be shot while opening
const auto &&csegp = vcsegptr(seg->children[side]);
const auto &&Connectside = find_connect_side(seg, csegp);
Assert(Connectside != side_none);
const auto cwall_num = csegp->sides[Connectside].wall_num;
if (const auto &&w1 = wallptr(cwall_num))
w1->state = WALL_DOOR_CLOSING;
d->front_wallnum[0] = seg->sides[side].wall_num;
d->back_wallnum[0] = cwall_num;
if (Newdemo_state == ND_STATE_RECORDING) {
newdemo_record_door_opening(seg, side);
}
if (w->linked_wall != wall_none)
{
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!!!!
const auto cp = compute_center_point_on_side(seg, side );
if (WallAnims[w->clip_num].open_sound > -1 )
digi_link_sound_to_pos( WallAnims[w->clip_num].open_sound, seg, side, cp, 0, F1_0 );
}
}
#endif
//-----------------------------------------------------------------
// Animates opening of a door.
// Called in the game loop.
void do_door_open(int door_num)
{
int p;
active_door *d;
// Assert(door_num != -1); //Trying to do_door_open on illegal door
if (door_num == -1)
return;
d = &ActiveDoors[door_num];
for (p=0;p<d->n_parts;p++) {
int side;
fix time_elapsed, time_total, one_frame;
int i, n;
wall *const w = vwallptr(d->front_wallnum[p]);
kill_stuck_objects(d->front_wallnum[p]);
kill_stuck_objects(d->back_wallnum[p]);
const auto &&seg = vsegptridx(w->segnum);
side = w->sidenum;
// Assert(seg->sides[side].wall_num != -1); //Trying to do_door_open on illegal wall
if (seg->sides[side].wall_num == wall_none)
{
con_printf(CON_URGENT, "Trying to do_door_open on illegal wall %i. Please check your level!",side);
continue;
}
const auto &&csegp = seg.absolute_sibling(seg->children[side]);
const auto &&Connectside = find_connect_side(seg, csegp);
Assert(Connectside != side_none);
d->time += FrameTime;
time_elapsed = d->time;
n = WallAnims[w->clip_num].num_frames;
time_total = WallAnims[w->clip_num].play_time;
one_frame = time_total/n;
i = time_elapsed/one_frame;
if (i < n)
wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,i);
const auto cwall_num = csegp->sides[Connectside].wall_num;
const auto &&w1 = vwallptr(cwall_num);
if (i> n/2) {
w->flags |= WALL_DOOR_OPENED;
w1->flags |= WALL_DOOR_OPENED;
}
if (i >= n-1) {
wall_set_tmap_num(seg,side,csegp,Connectside,w->clip_num,n-1);
// If our door is not automatic just remove it from the list.
if (!(w->flags & WALL_DOOR_AUTO)) {
for (i=door_num;i<Num_open_doors;i++)
ActiveDoors[i] = ActiveDoors[i+1];
Num_open_doors--;
#if defined(DXX_BUILD_DESCENT_II)
w->state = WALL_DOOR_OPEN;
w1->state = WALL_DOOR_OPEN;
#endif
}
else {
w->state = WALL_DOOR_WAITING;
w1->state = WALL_DOOR_WAITING;
ActiveDoors[Num_open_doors].time = 0; //counts up
}
}
}
}
//-----------------------------------------------------------------
// Animates and processes the closing of a door.
// Called from the game loop.
void do_door_close(int door_num)
{
int p;
active_door *d;
Assert(door_num != -1); //Trying to do_door_open on illegal door
d = &ActiveDoors[door_num];
auto &w0 = *vwallptr(d->front_wallnum[0]);
const auto &&wsegp = vsegptridx(w0.segnum);
//check for objects in doorway before closing
if (w0.flags & WALL_DOOR_AUTO)
if (!is_door_free(wsegp, w0.sidenum)) {
#if defined(DXX_BUILD_DESCENT_II)
digi_kill_sound_linked_to_segment(w0.segnum, w0.sidenum, -1);
wall_open_door(wsegp, w0.sidenum); //re-open door
#endif
return;
}
for (p=0;p<d->n_parts;p++) {
int side;
fix time_elapsed, time_total, one_frame;
int i, n;
auto &wp = *vwallptr(d->front_wallnum[p]);
const auto &seg = wsegp;
side = wp.sidenum;
if (seg->sides[side].wall_num == wall_none) {
return;
}
#if defined(DXX_BUILD_DESCENT_I)
//if here, must be auto door
//don't assert here, because now we have triggers to close non-auto doors
Assert(wp.flags & WALL_DOOR_AUTO);
#endif
// Otherwise, close it.
const auto &&csegp = seg.absolute_sibling(seg->children[side]);
const auto &&Connectside = find_connect_side(seg, csegp);
Assert(Connectside != side_none);
if ( Newdemo_state != ND_STATE_PLAYBACK )
// NOTE THE LINK TO ABOVE!!
if (p==0) //only play one sound for linked doors
if ( d->time==0 ) { //first time
const auto cp = compute_center_point_on_side(seg, side );
if (WallAnims[wp.clip_num].close_sound > -1 )
digi_link_sound_to_pos(WallAnims[wp.clip_num].close_sound, seg, side, cp, 0, F1_0);
}
d->time += FrameTime;
time_elapsed = d->time;
n = WallAnims[wp.clip_num].num_frames;
time_total = WallAnims[wp.clip_num].play_time;
one_frame = time_total/n;
i = n-time_elapsed/one_frame-1;
const auto cwall_num = csegp->sides[Connectside].wall_num;
auto &w1 = *vwallptr(cwall_num);
if (i < n/2) {
wp.flags &= ~WALL_DOOR_OPENED;
w1.flags &= ~WALL_DOOR_OPENED;
}
// Animate door.
if (i > 0) {
wall_set_tmap_num(seg, side, csegp, Connectside, wp.clip_num, i);
wp.state = WALL_DOOR_CLOSING;
w1.state = WALL_DOOR_CLOSING;
ActiveDoors[Num_open_doors].time = 0; //counts up
} else
wall_close_door_num(door_num);
}
}
template <typename F>
static void wall_illusion_op(const vsegptridx_t seg, unsigned side, F op)
{
const auto wall0 = seg->sides[side].wall_num;
if (wall0 == wall_none)
return;
const auto &&csegp = vcsegptr(seg->children[side]);
const auto &&cside = find_connect_side(seg, csegp);
if (cside == side_none)
{
assert(cside != side_none);
return;
}
const auto wall1 = csegp->sides[cside].wall_num;
op(wall0);
op(wall1);
}
//-----------------------------------------------------------------
// Turns off an illusionary wall (This will be used primarily for
// wall switches or triggers that can turn on/off illusionary walls.)
void wall_illusion_off(const vsegptridx_t seg, int side)
{
const auto op = [](const wallnum_t wall_num) {
vwallptr(wall_num)->flags |= WALL_ILLUSION_OFF;
#if defined(DXX_BUILD_DESCENT_II)
kill_stuck_objects(wall_num);
#endif
};
wall_illusion_op(seg, side, op);
}
//-----------------------------------------------------------------
// Turns on an illusionary wall (This will be used primarily for
// wall switches or triggers that can turn on/off illusionary walls.)
void wall_illusion_on(const vsegptridx_t seg, int side)
{
const auto op = [](const wallnum_t wall_num) {
vwallptr(wall_num)->flags &= ~WALL_ILLUSION_OFF;
};
wall_illusion_op(seg, side, op);
}
// -----------------------------------------------------------------------------
// Allowed to open the normally locked special boss door if in multiplayer mode.
static int special_boss_opening_allowed(segnum_t segnum, int sidenum)
{
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;
}
//-----------------------------------------------------------------
// 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
wall_hit_process_t wall_hit_process(const vsegptridx_t seg, int side, fix damage, int playernum, const vobjptr_t obj)
{
fix show_message;
// If it is not a "wall" then just return.
if ( seg->sides[side].wall_num == wall_none )
return wall_hit_process_t::WHP_NOT_SPECIAL;
wall *const w = vwallptr(seg->sides[side].wall_num);
if ( Newdemo_state == ND_STATE_RECORDING )
newdemo_record_wall_hit_process( seg, side, damage, playernum );
if (w->type == WALL_BLASTABLE) {
#if defined(DXX_BUILD_DESCENT_II)
if (obj->ctype.laser_info.parent_type == OBJ_PLAYER)
#endif
wall_damage(seg, side, damage);
return wall_hit_process_t::WHP_BLASTABLE;
}
if (playernum != Player_num) //return if was robot fire
return wall_hit_process_t::WHP_NOT_SPECIAL;
Assert( playernum > -1 );
// Determine whether player is moving forward. If not, don't say negative
// messages because he probably didn't intentionally hit the door.
if (obj->type == OBJ_PLAYER)
show_message = (vm_vec_dot(obj->orient.fvec, obj->mtype.phys_info.velocity) > 0);
#if defined(DXX_BUILD_DESCENT_II)
else if (obj->type == OBJ_ROBOT)
show_message = 0;
else if ((obj->type == OBJ_WEAPON) && (obj->ctype.laser_info.parent_type == OBJ_ROBOT))
show_message = 0;
#endif
else
show_message = 1;
/* 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 (
(w->keys == KEY_BLUE && (key_color = TXT_BLUE, true)) ||
(w->keys == KEY_GOLD && (key_color = TXT_YELLOW, true)) ||
(w->keys == KEY_RED && (key_color = TXT_RED, true))
)
{
const auto &objp = get_local_plrobj();
const auto &player_info = objp.ctype.player_info;
if (!(player_info.powerup_flags & static_cast<PLAYER_FLAG>(w->keys)))
{
static_assert(KEY_BLUE == static_cast<unsigned>(PLAYER_FLAGS_BLUE_KEY), "BLUE key flag mismatch");
static_assert(KEY_GOLD == static_cast<unsigned>(PLAYER_FLAGS_GOLD_KEY), "GOLD key flag mismatch");
static_assert(KEY_RED == static_cast<unsigned>(PLAYER_FLAGS_RED_KEY), "RED key flag mismatch");
if (show_message)
HUD_init_message(HM_DEFAULT, "%s %s",key_color,TXT_ACCESS_DENIED);
return wall_hit_process_t::WHP_NO_KEY;
}
}
if (w->type == WALL_DOOR)
{
if ((w->flags & WALL_DOOR_LOCKED ) && !(special_boss_opening_allowed(seg, side)) ) {
if (show_message)
HUD_init_message_literal(HM_DEFAULT, TXT_CANT_OPEN_DOOR);
return wall_hit_process_t::WHP_NO_KEY;
}
else {
if (w->state != WALL_DOOR_OPENING)
{
wall_open_door(seg, side);
if (Game_mode & GM_MULTI)
{
int flags;
#if defined(DXX_BUILD_DESCENT_I)
flags = 0;
#elif defined(DXX_BUILD_DESCENT_II)
flags = w->flags;
#endif
multi_send_door_open(seg, side,flags);
}
}
return wall_hit_process_t::WHP_DOOR;
}
}
return wall_hit_process_t::WHP_NOT_SPECIAL; //default is treat like normal wall
}
//-----------------------------------------------------------------
// Opens doors/destroys wall/shuts off triggers.
void wall_toggle(const vsegptridx_t segp, unsigned side)
{
if (side >= MAX_SIDES_PER_SEGMENT)
{
#ifndef NDEBUG
Warning("Can't toggle side %u of segment %d (%u)!\n", side, static_cast<segnum_t>(segp), Highest_segment_index);
#endif
return;
}
auto wall_num = segp->sides[side].wall_num;
if (wall_num == wall_none) {
return;
}
if ( Newdemo_state == ND_STATE_RECORDING )
newdemo_record_wall_toggle(segp, side);
wall *const w = vwallptr(wall_num);
if (w->type == WALL_BLASTABLE)
wall_destroy(segp, side);
if (w->type == WALL_DOOR && w->state == WALL_DOOR_CLOSED)
wall_open_door(segp, side);
}
//-----------------------------------------------------------------
// Tidy up Walls array for load/save purposes.
void reset_walls()
{
range_for (auto &w, partial_range(Walls, Num_walls, MAX_WALLS))
{
w.type = WALL_NORMAL;
w.flags = 0;
w.hps = 0;
w.trigger = -1;
w.clip_num = -1;
}
}
#if defined(DXX_BUILD_DESCENT_II)
static void do_cloaking_wall_frame(int cloaking_wall_num)
{
cloaking_wall *d;
sbyte old_cloak; // for demo recording
if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
d = &CloakingWalls[cloaking_wall_num];
wall *const wfront = vwallptr(d->front_wallnum);
wall *const wback = wallptr(d->back_wallnum);
old_cloak = wfront->cloak_value;
d->time += FrameTime;
if (d->time > CLOAKING_WALL_TIME) {
int i;
wfront->type = WALL_OPEN;
wfront->state = WALL_DOOR_CLOSED; //why closed? why not?
if (wback) {
wback->type = WALL_OPEN;
wback->state = WALL_DOOR_CLOSED; //why closed? why not?
}
for (i=cloaking_wall_num;i<Num_cloaking_walls;i++)
CloakingWalls[i] = CloakingWalls[i+1];
Num_cloaking_walls--;
}
else if (d->time > CLOAKING_WALL_TIME/2) {
int old_type=wfront->type;
wfront->cloak_value = ((d->time - CLOAKING_WALL_TIME/2) * (GR_FADE_LEVELS-2)) / (CLOAKING_WALL_TIME/2);
if (wback)
wback->cloak_value = wfront->cloak_value;
if (old_type != WALL_CLOAKED) { //just switched
int i;
wfront->type = WALL_CLOAKED;
if (wback)
wback->type = WALL_CLOAKED;
for (i=0;i<4;i++) {
Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = d->front_ls[i];
if (wback)
Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = d->back_ls[i];
}
}
}
else { //fading out
fix light_scale;
int i;
light_scale = fixdiv(CLOAKING_WALL_TIME/2-d->time,CLOAKING_WALL_TIME/2);
for (i=0;i<4;i++) {
Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = fixmul(d->front_ls[i],light_scale);
if (wback)
Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = fixmul(d->back_ls[i],light_scale);
}
}
// check if the actual cloak_value changed in this frame to prevent redundant recordings and wasted bytes
if ( Newdemo_state == ND_STATE_RECORDING && (wfront->cloak_value != old_cloak || d->time == FrameTime) )
newdemo_record_cloaking_wall(d->front_wallnum, d->back_wallnum, wfront->type, wfront->state, wfront->cloak_value, Segments[wfront->segnum].sides[wfront->sidenum].uvls[0].l, Segments[wfront->segnum].sides[wfront->sidenum].uvls[1].l, Segments[wfront->segnum].sides[wfront->sidenum].uvls[2].l, Segments[wfront->segnum].sides[wfront->sidenum].uvls[3].l);
}
static void do_decloaking_wall_frame(int cloaking_wall_num)
{
cloaking_wall *d;
sbyte old_cloak; // for demo recording
if ( Newdemo_state==ND_STATE_PLAYBACK ) return;
d = &CloakingWalls[cloaking_wall_num];
wall *const wfront = vwallptr(d->front_wallnum);
wall *const wback = wallptr(d->back_wallnum);
old_cloak = wfront->cloak_value;
d->time += FrameTime;
if (d->time > CLOAKING_WALL_TIME) {
int i;
wfront->state = WALL_DOOR_CLOSED;
if (wback)
wback->state = WALL_DOOR_CLOSED;
for (i=0;i<4;i++) {
Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = d->front_ls[i];
if (wback)
Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = d->back_ls[i];
}
for (i=cloaking_wall_num;i<Num_cloaking_walls;i++)
CloakingWalls[i] = CloakingWalls[i+1];
Num_cloaking_walls--;
}
else if (d->time > CLOAKING_WALL_TIME/2) { //fading in
fix light_scale;
int i;
wfront->type = wback->type = WALL_CLOSED;
light_scale = fixdiv(d->time-CLOAKING_WALL_TIME/2,CLOAKING_WALL_TIME/2);
for (i=0;i<4;i++) {
Segments[wfront->segnum].sides[wfront->sidenum].uvls[i].l = fixmul(d->front_ls[i],light_scale);
if (wback)
Segments[wback->segnum].sides[wback->sidenum].uvls[i].l = fixmul(d->back_ls[i],light_scale);
}
}
else { //cloaking in
wfront->cloak_value = ((CLOAKING_WALL_TIME/2 - d->time) * (GR_FADE_LEVELS-2)) / (CLOAKING_WALL_TIME/2);
wfront->type = WALL_CLOAKED;
if (wback) {
wback->cloak_value = wfront->cloak_value;
wback->type = WALL_CLOAKED;
}
}
// check if the actual cloak_value changed in this frame to prevent redundant recordings and wasted bytes
if ( Newdemo_state == ND_STATE_RECORDING && (wfront->cloak_value != old_cloak || d->time == FrameTime) )
newdemo_record_cloaking_wall(d->front_wallnum, d->back_wallnum, wfront->type, wfront->state, wfront->cloak_value, Segments[wfront->segnum].sides[wfront->sidenum].uvls[0].l, Segments[wfront->segnum].sides[wfront->sidenum].uvls[1].l, Segments[wfront->segnum].sides[wfront->sidenum].uvls[2].l, Segments[wfront->segnum].sides[wfront->sidenum].uvls[3].l);
}
#endif
void wall_frame_process()
{
int i;
for (i=0;i<Num_open_doors;i++) {
active_door *d;
d = &ActiveDoors[i];
wall *const w = vwallptr(d->front_wallnum[0]);
if (w->state == WALL_DOOR_OPENING)
do_door_open(i);
else if (w->state == WALL_DOOR_CLOSING)
do_door_close(i);
else if (w->state == WALL_DOOR_WAITING) {
d->time += FrameTime;
// set flags to fix occasional netgame problem where door is waiting to close but open flag isn't set
w->flags |= WALL_DOOR_OPENED;
if (wall *const w1 = wallptr(d->back_wallnum[0]))
w1->flags |= WALL_DOOR_OPENED;
if (d->time > DOOR_WAIT_TIME)
#if defined(DXX_BUILD_DESCENT_II)
if (is_door_free(vcsegptridx(w->segnum), w->sidenum))
#endif
{
w->state = WALL_DOOR_CLOSING;
d->time = 0;
}
}
#if defined(DXX_BUILD_DESCENT_II)
else if (w->state == WALL_DOOR_CLOSED || w->state == WALL_DOOR_OPEN) {
//this shouldn't happen. if the wall is in one of these states,
//there shouldn't be an activedoor entry for it. So we'll kill
//the activedoor entry. Tres simple.
int t;
Int3(); //a bad thing has happened, but I'll try to fix it up
for (t=i;t<Num_open_doors;t++)
ActiveDoors[t] = ActiveDoors[t+1];
Num_open_doors--;
}
#endif
}
#if defined(DXX_BUILD_DESCENT_II)
for (i=0;i<Num_cloaking_walls;i++) {
cloaking_wall *d;
d = &CloakingWalls[i];
wall *const w = vwallptr(d->front_wallnum);
if (w->state == WALL_DOOR_CLOAKING)
do_cloaking_wall_frame(i);
else if (w->state == WALL_DOOR_DECLOAKING)
do_decloaking_wall_frame(i);
#ifdef _DEBUG
else
Int3(); //unexpected wall state
#endif
}
#endif
}
static unsigned Num_stuck_objects;
static array<stuckobj, 32> Stuck_objects;
// An object got stuck in a door (like a flare).
// Add global entry.
void add_stuck_object(const vobjptridx_t objp, const vsegptr_t segp, int sidenum)
{
const auto wallnum = segp->sides[sidenum].wall_num;
if (wallnum != wall_none)
{
if (vwallptr(wallnum)->flags & WALL_BLASTED)
objp->flags |= OF_SHOULD_BE_DEAD;
range_for (auto &i, Stuck_objects)
{
if (i.wallnum == wall_none)
{
i.wallnum = wallnum;
i.objnum = objp;
i.signature = objp->signature;
Num_stuck_objects++;
break;
}
}
}
}
#if defined(DXX_BUILD_DESCENT_I)
// --------------------------------------------------------------------------------------------------
// Look at the list of stuck objects, clean up in case an object has gone away, but not been removed here.
// Removes up to one/frame.
void remove_obsolete_stuck_objects(void)
{
int objnum;
objnum = d_tick_count % Stuck_objects.size();
if (Stuck_objects[objnum].wallnum != wall_none)
if ((Stuck_objects[objnum].wallnum == 0) || (vcobjptr(Stuck_objects[objnum].objnum)->signature != Stuck_objects[objnum].signature))
{
Num_stuck_objects--;
Stuck_objects[objnum].wallnum = wall_none;
}
}
#endif
#if defined(DXX_BUILD_DESCENT_II)
// --------------------------------------------------------------------------------------------------
// Look at the list of stuck objects, clean up in case an object has gone away, but not been removed here.
// Removes up to one/frame.
void remove_obsolete_stuck_objects(void)
{
int objnum;
// Safety and efficiency code. If no stuck objects, should never get inside the IF, but this is faster.
if (!Num_stuck_objects)
return;
objnum = d_tick_count % Stuck_objects.size();
if (Stuck_objects[objnum].wallnum != wall_none)
if (vcwallptr(Stuck_objects[objnum].wallnum)->state != WALL_DOOR_CLOSED ||
vcobjptr(Stuck_objects[objnum].objnum)->signature != Stuck_objects[objnum].signature)
{
Num_stuck_objects--;
Stuck_objects[objnum].wallnum = wall_none;
}
}
#endif
// ----------------------------------------------------------------------------------------------------
// Door with wall index wallnum is opening, kill all objects stuck in it.
void kill_stuck_objects(int wallnum)
{
if (wallnum < 0 || Num_stuck_objects == 0) {
return;
}
unsigned n = 0;
range_for (auto &i, Stuck_objects)
{
if (i.wallnum == wallnum)
{
i.wallnum = wall_none;
const auto &&objp = vobjptr(i.objnum);
if (objp->type == OBJ_WEAPON) {
#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
objp->lifeleft = DXX_WEAPON_LIFELEFT;
}
}
else if (i.wallnum != wall_none)
{
++n;
}
}
Num_stuck_objects = n;
// Ok, this is awful, but we need to do things whenever a door opens/closes/disappears, etc.
#if defined(DXX_BUILD_DESCENT_II)
flush_fcd_cache();
#endif
}
#if defined(DXX_BUILD_DESCENT_II)
// -----------------------------------------------------------------------------------
// Initialize stuck objects array. Called at start of level
void init_stuck_objects(void)
{
range_for (auto &i, Stuck_objects)
i.wallnum = wall_none;
Num_stuck_objects = 0;
}
// -----------------------------------------------------------------------------------
// Clear out all stuck objects. Called for a new ship
void clear_stuck_objects(void)
{
range_for (auto &i, Stuck_objects)
{
if (i.wallnum != wall_none)
{
const auto &&objp = vobjptr(i.objnum);
if (objp->type == OBJ_WEAPON && get_weapon_id(objp) == weapon_id_type::FLARE_ID)
objp->lifeleft = F1_0/8;
i.wallnum = wall_none;
Num_stuck_objects--;
}
}
Assert(Num_stuck_objects == 0);
}
// -----------------------------------------------------------------------------------
#define MAX_BLAST_GLASS_DEPTH 5
static void bng_process_segment(const object &objp, fix damage, const vsegptridx_t segp, int depth, visited_segment_bitarray_t &visited)
{
visited[segp] = true;
int i, sidenum;
if (depth > MAX_BLAST_GLASS_DEPTH)
return;
depth++;
for (sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
int tm;
fix dist;
// Process only walls which have glass.
if ((tm=segp->sides[sidenum].tmap_num2) != 0) {
int ec, db;
tm &= 0x3fff; //tm without flags
if ((((ec=TmapInfo[tm].eclip_num)!=-1) && ((db=Effects[ec].dest_bm_num)!=-1 && !(Effects[ec].flags&EF_ONE_SHOT))) || (ec==-1 && (TmapInfo[tm].destroyed!=-1))) {
const auto pnt = compute_center_point_on_side(segp, sidenum);
dist = vm_vec_dist_quick(pnt, objp.pos);
if (dist < damage/2) {
dist = find_connected_distance(pnt, segp, objp.pos, segp.absolute_sibling(objp.segnum), MAX_BLAST_GLASS_DEPTH, WID_RENDPAST_FLAG);
if ((dist > 0) && (dist < damage/2))
check_effect_blowup(segp, sidenum, pnt, vcobjptr(objp.ctype.laser_info.parent_num)->ctype.laser_info, 1, 0);
}
}
}
}
for (i=0; i<MAX_SIDES_PER_SEGMENT; i++) {
auto segnum = segp->children[i];
if (segnum != segment_none) {
if (!visited[segnum]) {
if (WALL_IS_DOORWAY(segp, i) & WID_FLY_FLAG) {
bng_process_segment(objp, damage, segp.absolute_sibling(segnum), depth, visited);
}
}
}
}
}
// -----------------------------------------------------------------------------------
// objp is going to detonate
// blast nearby monitors, lights, maybe other things
void blast_nearby_glass(const object &objp, fix damage)
{
visited_segment_bitarray_t visited;
bng_process_segment(objp, damage, vsegptridx(objp.segnum), 0, visited);
}
struct d1wclip
{
wclip *wc;
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));
#endif
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));
/*
* reads a wclip structure from a PHYSFS_File
*/
void wclip_read(PHYSFS_File *fp, wclip &wc)
{
PHYSFSX_serialize_read(fp, wc);
}
#if 0
void wclip_write(PHYSFS_File *fp, const wclip &wc)
{
PHYSFSX_serialize_write(fp, wc);
}
#endif
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);
/*
* reads a v16_wall structure from a PHYSFS_File
*/
void v16_wall_read(PHYSFS_File *fp, v16_wall &w)
{
PHYSFSX_serialize_read(fp, w);
}
struct wrap_v19_wall
{
const wall *w;
wrap_v19_wall(const wall &t) : w(&t) {}
};
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));
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>()));
ASSERT_SERIAL_UDT_MESSAGE_SIZE(v19_wall, 21);
ASSERT_SERIAL_UDT_MESSAGE_SIZE(wrap_v19_wall, 21);
/*
* reads a v19_wall structure from a PHYSFS_File
*/
void v19_wall_read(PHYSFS_File *fp, v19_wall &w)
{
PHYSFSX_serialize_read(fp, w);
}
#if defined(DXX_BUILD_DESCENT_I)
#define _SERIAL_UDT_WALL_D2X_MEMBERS serial::pad<2>()
#elif defined(DXX_BUILD_DESCENT_II)
#define _SERIAL_UDT_WALL_D2X_MEMBERS w.controlling_trigger, w.cloak_value
#endif
DEFINE_SERIAL_UDT_TO_MESSAGE(wall, w, (w.segnum, serial::pad<2>(), w.sidenum, serial::pad<3>(), w.hps, w.linked_wall, serial::pad<2>(), w.type, w.flags, w.state, w.trigger, w.clip_num, w.keys, _SERIAL_UDT_WALL_D2X_MEMBERS));
ASSERT_SERIAL_UDT_MESSAGE_SIZE(wall, 24);
/*
* reads a wall structure from a PHYSFS_File
*/
void wall_read(PHYSFS_File *fp, wall &w)
{
PHYSFSX_serialize_read(fp, w);
}
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);
/*
* reads an active_door structure from a PHYSFS_File
*/
void active_door_read(PHYSFS_File *fp, active_door &ad)
{
PHYSFSX_serialize_read(fp, ad);
}
void active_door_write(PHYSFS_File *fp, const active_door &ad)
{
PHYSFSX_serialize_write(fp, ad);
}
void wall_write(PHYSFS_File *fp, const wall &w, short version)
{
if (version <= 16)
PHYSFSX_serialize_write<wrap_v16_wall>(fp, w);
else if (version <= 19)
PHYSFSX_serialize_write<wrap_v19_wall>(fp, w);
else
PHYSFSX_serialize_write(fp, w);
}
#if defined(DXX_BUILD_DESCENT_II)
DEFINE_SERIAL_UDT_TO_MESSAGE(cloaking_wall, cw, (cw.front_wallnum, cw.back_wallnum, cw.front_ls, cw.back_ls, cw.time));
ASSERT_SERIAL_UDT_MESSAGE_SIZE(cloaking_wall, 40);
void cloaking_wall_read(cloaking_wall &cw, PHYSFS_File *fp)
{
PHYSFSX_serialize_read(fp, cw);
}
void cloaking_wall_write(const cloaking_wall &cw, PHYSFS_File *fp)
{
PHYSFSX_serialize_write(fp, cw);
}
#endif