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 .
*/
/*
*
* object rendering
*
*/
2012-11-11 22:12:51 +00:00
# include <algorithm>
2015-09-09 03:27:52 +00:00
# include <cstdlib>
2006-03-20 17:12:09 +00:00
# include <stdio.h>
# include "inferno.h"
# include "game.h"
# include "gr.h"
# include "bm.h"
# include "3d.h"
# include "segment.h"
# include "texmap.h"
# include "laser.h"
# include "key.h"
# include "gameseg.h"
# include "textures.h"
# include "object.h"
2013-09-24 01:59:09 +00:00
# include "controls.h"
2006-03-20 17:12:09 +00:00
# include "physics.h"
2011-02-23 16:46:39 +00:00
# include "slew.h"
2006-03-20 17:12:09 +00:00
# include "render.h"
# include "wall.h"
# include "vclip.h"
2015-04-02 02:36:57 +00:00
# include "robot.h"
2013-12-18 00:07:46 +00:00
# include "interp.h"
2006-03-20 17:12:09 +00:00
# include "fireball.h"
# include "laser.h"
2012-07-07 18:35:06 +00:00
# include "dxxerror.h"
2006-03-20 17:12:09 +00:00
# include "ai.h"
# include "morph.h"
# include "cntrlcen.h"
# include "powerup.h"
# include "fuelcen.h"
# include "endlevel.h"
2015-04-19 04:18:51 +00:00
# include "hudmsg.h"
2006-03-20 17:12:09 +00:00
# include "sounds.h"
# include "collide.h"
# include "lighting.h"
# include "newdemo.h"
# include "player.h"
# include "weapon.h"
# include "gauges.h"
# include "multi.h"
# include "text.h"
# include "piggy.h"
# include "switch.h"
# include "gameseq.h"
2008-04-13 00:28:36 +00:00
# include "playsave.h"
2012-05-14 17:06:28 +00:00
# include "timer.h"
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
# include "editor/editor.h"
# endif
2013-12-25 23:47:30 +00:00
# include "compiler-range_for.h"
2019-05-04 18:27:37 +00:00
# include "d_range.h"
2020-08-10 03:45:14 +00:00
# include "d_levelstate.h"
2022-07-02 18:10:45 +00:00
# include "d_underlying_value.h"
2014-07-12 16:28:05 +00:00
# include "partial_range.h"
2020-05-02 21:18:42 +00:00
# include <utility>
2013-12-25 23:47:30 +00:00
2012-11-11 22:12:51 +00:00
using std : : min ;
using std : : max ;
2015-12-22 04:18:50 +00:00
namespace dsx {
2022-04-17 22:27:19 +00:00
namespace {
2018-03-12 03:43:46 +00:00
static void obj_detach_all ( object_array & Objects , object_base & parent ) ;
2022-06-05 17:44:52 +00:00
static objnum_t obj_detach_one ( object_array & Objects , object & sub ) ;
2006-03-20 17:12:09 +00:00
2019-08-15 01:34:22 +00:00
static int is_proximity_bomb_or_any_smart_mine ( const weapon_id_type id )
{
const auto r = is_proximity_bomb_or_player_smart_mine ( id ) ;
# if defined(DXX_BUILD_DESCENT_II)
if ( r )
return r ;
// superprox dropped by robots have their own ID not considered by is_proximity_bomb_or_player_smart_mine() and since that function is used in many other places, I didn't feel safe to add this weapon type in it
if ( id = = weapon_id_type : : ROBOT_SUPERPROX_ID )
return 1 ;
# endif
return r ;
}
2022-04-17 22:27:19 +00:00
}
2006-03-20 17:12:09 +00:00
/*
* Global variables
*/
object * ConsoleObject ; //the object that is the player
# ifndef RELEASE
//set viewer object to next object in array
2022-06-05 17:44:53 +00:00
void object_goto_next_viewer ( const object_array & Objects , const object * & viewer )
2006-03-20 17:12:09 +00:00
{
2022-06-05 17:44:53 +00:00
const auto initial_viewer = viewer ;
const object * const oe = * Objects . vcptr . end ( ) ;
/* Preconditions:
* - There exists an integer ` i ` such that ` i < MAX_OBJECTS ` and
* ` & Objects [ i ] = = initial_viewer ` .
* - There exists at least one object not of type ` OBJ_NONE ` .
* If the first precondition holds , then :
* - The test for ` candidate_object ! = initial_viewer ` will always terminate the
* loop , even if there are no other objects of type other than
* ` OBJ_NONE ` .
* If both preconditions hold , then :
* - The loop will always find and return a value .
*/
unsigned limit = 0 ;
for ( auto candidate_object = initial_viewer ; + + candidate_object ! = initial_viewer & & limit < MAX_OBJECTS ; + + limit )
2014-10-12 23:05:46 +00:00
{
2022-06-05 17:44:53 +00:00
if ( unlikely ( candidate_object = = oe ) )
/* Wrap around to the beginning. This is only computed if
* necessary , so if an object can be found without wrapping , then
* ` begin ( ) ` never needs to be called .
*/
candidate_object = * Objects . vcptr . begin ( ) ;
auto & objp = * candidate_object ;
2018-06-24 05:06:15 +00:00
if ( objp . type ! = OBJ_NONE )
2015-12-03 03:26:49 +00:00
{
2022-06-05 17:44:53 +00:00
viewer = & objp ;
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
2019-08-15 01:34:22 +00:00
imobjptridx_t obj_find_first_of_type ( fvmobjptridx & vmobjptridx , const object_type_t type )
2013-02-21 00:20:26 +00:00
{
2017-06-10 03:31:02 +00:00
range_for ( const auto & & i , vmobjptridx )
2014-08-23 23:53:56 +00:00
{
if ( i - > type = = type )
return i ;
}
return object_none ;
2013-02-21 00:20:26 +00:00
}
2006-03-20 17:12:09 +00:00
2016-04-06 03:34:14 +00:00
}
namespace dcx {
2019-12-16 01:56:59 +00:00
icobjidx_t laser_info : : get_last_hitobj ( ) const
{
if ( ! hitobj_count )
/* If no elements, return object_none */
return object_none ;
/* Return the most recently written element. `hitobj_pos`
* indicates the element to write next , so return
* hitobj_values [ hitobj_pos - 1 ] . When hitobj_pos = = 0 , the
* most recently written element is at the end of the array , not
* before the beginning of the array .
*/
if ( ! hitobj_pos )
return hitobj_values . back ( ) ;
return hitobj_values [ hitobj_pos - 1 ] ;
}
2022-06-05 17:44:52 +00:00
}
namespace dsx {
2006-03-20 17:12:09 +00:00
//draw an object that has one bitmap & doesn't rotate
2022-06-05 17:44:52 +00:00
void draw_object_blob ( GameBitmaps_array & GameBitmaps , const object_base & Viewer , grs_canvas & canvas , const object_base & obj , const bitmap_index bmi )
2006-03-20 17:12:09 +00:00
{
2016-04-06 03:34:13 +00:00
auto & bm = GameBitmaps [ bmi . index ] ;
2006-03-20 17:12:09 +00:00
PIGGY_PAGE_IN ( bmi ) ;
2016-04-06 03:34:14 +00:00
const auto osize = obj . size ;
2011-02-23 16:46:39 +00:00
// draw these with slight offset to viewer preventing too much ugly clipping
2016-04-06 03:34:14 +00:00
auto pos = obj . pos ;
if ( obj . type = = OBJ_FIREBALL & & get_fireball_id ( obj ) = = VCLIP_VOLATILE_WALL_HIT )
2011-02-23 16:46:39 +00:00
{
vms_vector offs_vec ;
2022-06-05 17:44:52 +00:00
vm_vec_normalized_dir_quick ( offs_vec , Viewer . pos , pos ) ;
2014-09-28 21:43:14 +00:00
vm_vec_scale_add2 ( pos , offs_vec , F1_0 ) ;
2007-01-29 23:31:20 +00:00
}
2011-02-23 16:46:39 +00:00
2016-04-06 03:34:13 +00:00
using wh = std : : pair < fix , fix > ;
const auto bm_w = bm . bm_w ;
const auto bm_h = bm . bm_h ;
const auto p = ( bm_w > bm_h )
? wh ( osize , fixmuldiv ( osize , bm_h , bm_w ) )
: wh ( fixmuldiv ( osize , bm_w , bm_h ) , osize ) ;
2017-03-11 19:56:24 +00:00
g3_draw_bitmap ( canvas , pos , p . first , p . second , bm ) ;
2006-03-20 17:12:09 +00:00
}
//draw an object that is a texture-mapped rod
2019-04-13 18:00:07 +00:00
void draw_object_tmap_rod ( grs_canvas & canvas , const d_level_unique_light_state * const LevelUniqueLightState , const vcobjptridx_t obj , const bitmap_index bitmapi )
2006-03-20 17:12:09 +00:00
{
2011-04-07 20:32:51 +00:00
g3s_lrgb light ;
2006-03-20 17:12:09 +00:00
PIGGY_PAGE_IN ( bitmapi ) ;
2014-11-13 03:34:42 +00:00
auto & bitmap = GameBitmaps [ bitmapi . index ] ;
2006-03-20 17:12:09 +00:00
2014-11-02 03:43:47 +00:00
const auto delta = vm_vec_copy_scale ( obj - > orient . uvec , obj - > size ) ;
2006-03-20 17:12:09 +00:00
2014-10-30 03:11:06 +00:00
const auto top_v = vm_vec_add ( obj - > pos , delta ) ;
2014-10-29 03:24:31 +00:00
const auto bot_v = vm_vec_sub ( obj - > pos , delta ) ;
2006-03-20 17:12:09 +00:00
2014-11-13 03:41:29 +00:00
const auto top_p = g3_rotate_point ( top_v ) ;
const auto bot_p = g3_rotate_point ( bot_v ) ;
2006-03-20 17:12:09 +00:00
2019-04-13 18:00:07 +00:00
if ( LevelUniqueLightState )
2011-04-07 20:32:51 +00:00
{
2019-04-13 18:00:07 +00:00
light = compute_object_light ( * LevelUniqueLightState , obj ) ;
2011-04-07 20:32:51 +00:00
}
2006-03-20 17:12:09 +00:00
else
2011-04-07 20:32:51 +00:00
{
light . r = light . g = light . b = f1_0 ;
}
2017-02-11 21:42:40 +00:00
g3_draw_rod_tmap ( canvas , bitmap , bot_p , obj - > size , top_p , obj - > size , light ) ;
2006-03-20 17:12:09 +00:00
}
//used for robot engine glow
# define MAX_VELOCITY i2f(50)
//what darkening level to use when cloaked
# define CLOAKED_FADE_LEVEL 28
# define CLOAK_FADEIN_DURATION_PLAYER F2_0
# define CLOAK_FADEOUT_DURATION_PLAYER F2_0
# define CLOAK_FADEIN_DURATION_ROBOT F1_0
# define CLOAK_FADEOUT_DURATION_ROBOT F1_0
2022-07-09 13:39:29 +00:00
namespace {
2006-03-20 17:12:09 +00:00
//do special cloaked render
2018-06-24 05:06:15 +00:00
static void draw_cloaked_object ( grs_canvas & canvas , const object_base & obj , const g3s_lrgb light , glow_values_t glow , const fix64 cloak_start_time , const fix total_cloaked_time , const fix Cloak_fadein_duration , const fix Cloak_fadeout_duration )
2006-03-20 17:12:09 +00:00
{
2015-11-19 03:23:34 +00:00
fix cloak_delta_time ;
2006-03-20 17:12:09 +00:00
fix light_scale = F1_0 ;
int cloak_value = 0 ;
int fading = 0 ; //if true, fading, else cloaking
2010-12-22 00:17:59 +00:00
cloak_delta_time = GameTime64 - cloak_start_time ;
2006-03-20 17:12:09 +00:00
if ( cloak_delta_time < Cloak_fadein_duration / 2 ) {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
light_scale = Cloak_fadein_duration / 2 - cloak_delta_time ;
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
light_scale = fixdiv ( Cloak_fadein_duration / 2 - cloak_delta_time , Cloak_fadein_duration / 2 ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
fading = 1 ;
}
else if ( cloak_delta_time < Cloak_fadein_duration ) {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
cloak_value = f2i ( ( cloak_delta_time - Cloak_fadein_duration / 2 ) * CLOAKED_FADE_LEVEL ) ;
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
cloak_value = f2i ( fixdiv ( cloak_delta_time - Cloak_fadein_duration / 2 , Cloak_fadein_duration / 2 ) * CLOAKED_FADE_LEVEL ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2015-11-19 03:23:34 +00:00
} else if ( GameTime64 < ( cloak_start_time + total_cloaked_time ) - Cloak_fadeout_duration ) {
2006-03-20 17:12:09 +00:00
static int cloak_delta = 0 , cloak_dir = 1 ;
static fix cloak_timer = 0 ;
//note, if more than one cloaked object is visible at once, the
//pulse rate will change!
cloak_timer - = FrameTime ;
while ( cloak_timer < 0 ) {
cloak_timer + = Cloak_fadeout_duration / 12 ;
cloak_delta + = cloak_dir ;
if ( cloak_delta = = 0 | | cloak_delta = = 4 )
cloak_dir = - cloak_dir ;
}
cloak_value = CLOAKED_FADE_LEVEL - cloak_delta ;
2015-11-19 03:23:34 +00:00
} else if ( GameTime64 < ( cloak_start_time + total_cloaked_time ) - Cloak_fadeout_duration / 2 ) {
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
cloak_value = f2i ( ( total_cloaked_time - Cloak_fadeout_duration / 2 - cloak_delta_time ) * CLOAKED_FADE_LEVEL ) ;
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
cloak_value = f2i ( fixdiv ( total_cloaked_time - Cloak_fadeout_duration / 2 - cloak_delta_time , Cloak_fadeout_duration / 2 ) * CLOAKED_FADE_LEVEL ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
} else {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
light_scale = Cloak_fadeout_duration / 2 - ( total_cloaked_time - cloak_delta_time ) ;
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
light_scale = fixdiv ( Cloak_fadeout_duration / 2 - ( total_cloaked_time - cloak_delta_time ) , Cloak_fadeout_duration / 2 ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
fading = 1 ;
}
2015-02-14 22:48:29 +00:00
alternate_textures alt_textures ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
if ( fading )
# endif
{
2018-06-24 05:06:15 +00:00
const unsigned ati = static_cast < unsigned > ( obj . rtype . pobj_info . alt_textures ) - 1 ;
2015-10-09 02:46:09 +00:00
if ( ati < multi_player_textures . size ( ) )
2013-03-03 01:03:33 +00:00
{
2015-10-09 02:46:09 +00:00
alt_textures = multi_player_textures [ ati ] ;
2013-03-03 01:03:33 +00:00
}
}
2006-03-20 17:12:09 +00:00
2022-10-09 23:15:20 +00:00
auto & Polygon_models = LevelSharedPolygonModelState . Polygon_models ;
2006-03-20 17:12:09 +00:00
if ( fading ) {
2011-04-07 20:32:51 +00:00
g3s_lrgb new_light ;
2006-03-20 17:12:09 +00:00
2011-04-07 20:32:51 +00:00
new_light . r = fixmul ( light . r , light_scale ) ;
new_light . g = fixmul ( light . g , light_scale ) ;
new_light . b = fixmul ( light . b , light_scale ) ;
2006-03-20 17:12:09 +00:00
glow [ 0 ] = fixmul ( glow [ 0 ] , light_scale ) ;
2022-10-09 23:15:20 +00:00
draw_polygon_model ( Polygon_models , canvas , obj . pos ,
2018-06-24 05:06:15 +00:00
obj . orient ,
obj . rtype . pobj_info . anim_angles ,
obj . rtype . pobj_info . model_num , obj . rtype . pobj_info . subobj_flags ,
2006-03-20 17:12:09 +00:00
new_light ,
2013-12-18 00:07:46 +00:00
& glow ,
2006-03-20 17:12:09 +00:00
alt_textures ) ;
}
else {
2021-09-19 10:53:48 +00:00
gr_settransblend ( canvas , static_cast < gr_fade_level > ( cloak_value ) , gr_blend : : normal ) ;
2014-08-08 02:52:35 +00:00
g3_set_special_render ( draw_tmap_flat ) ; //use special flat drawer
2022-10-09 23:15:20 +00:00
draw_polygon_model ( Polygon_models , canvas , obj . pos ,
2018-06-24 05:06:15 +00:00
obj . orient ,
obj . rtype . pobj_info . anim_angles ,
obj . rtype . pobj_info . model_num , obj . rtype . pobj_info . subobj_flags ,
2006-03-20 17:12:09 +00:00
light ,
2013-12-18 00:07:46 +00:00
& glow ,
2013-03-03 01:03:33 +00:00
alt_textures ) ;
2015-01-22 03:34:20 +00:00
g3_set_special_render ( draw_tmap ) ;
2020-07-16 02:31:04 +00:00
gr_settransblend ( canvas , GR_FADE_OFF , gr_blend : : normal ) ;
2006-03-20 17:12:09 +00:00
}
}
//draw an object which renders as a polygon model
2019-04-13 18:00:07 +00:00
static void draw_polygon_object ( grs_canvas & canvas , const d_level_unique_light_state & LevelUniqueLightState , const vcobjptridx_t obj )
2006-03-20 17:12:09 +00:00
{
2019-08-06 02:59:40 +00:00
auto & BossUniqueState = LevelUniqueObjectState . BossState ;
2018-12-30 00:43:59 +00:00
auto & Robot_info = LevelSharedRobotInfoState . Robot_info ;
2011-04-07 20:32:51 +00:00
g3s_lrgb light ;
2013-12-18 00:07:46 +00:00
glow_values_t engine_glow_value ;
engine_glow_value [ 0 ] = 0 ;
# if defined(DXX_BUILD_DESCENT_II)
engine_glow_value [ 1 ] = - 1 ; //element 0 is for engine glow, 1 for headlight
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
// If option set for bright players in netgame, brighten them!
2015-11-14 18:17:20 +00:00
light = unlikely ( Netgame . BrightPlayers & & ( Game_mode & GM_MULTI ) & & obj - > type = = OBJ_PLAYER )
? g3s_lrgb { F1_0 * 2 , F1_0 * 2 , F1_0 * 2 }
2019-04-13 18:00:07 +00:00
: compute_object_light ( LevelUniqueLightState , obj ) ;
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
//make robots brighter according to robot glow field
if ( obj - > type = = OBJ_ROBOT )
2011-04-07 20:32:51 +00:00
{
2015-11-14 18:17:20 +00:00
const auto glow = Robot_info [ get_robot_id ( obj ) ] . glow < < 12 ;
light . r + = glow ; //convert 4:4 to 16:16
light . g + = glow ; //convert 4:4 to 16:16
light . b + = glow ; //convert 4:4 to 16:16
2011-04-07 20:32:51 +00:00
}
2006-03-20 17:12:09 +00:00
2015-11-14 18:17:20 +00:00
if ( ( obj - > type = = OBJ_WEAPON & &
2015-12-03 03:26:49 +00:00
get_weapon_id ( obj ) = = weapon_id_type : : FLARE_ID ) | |
2015-11-14 18:17:20 +00:00
obj - > type = = OBJ_MARKER
)
2011-04-07 20:32:51 +00:00
{
light . r + = F1_0 * 2 ;
light . g + = F1_0 * 2 ;
light . b + = F1_0 * 2 ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2015-12-04 03:36:31 +00:00
push_interpolation_method imsave ( 1 , true ) ;
2006-03-20 17:12:09 +00:00
//set engine glow value
engine_glow_value [ 0 ] = f1_0 / 5 ;
2020-08-10 03:45:13 +00:00
if ( obj - > movement_source = = object : : movement_type : : physics ) {
2006-03-20 17:12:09 +00:00
2013-10-07 23:52:33 +00:00
if ( obj - > mtype . phys_info . flags & PF_USES_THRUST & & obj - > type = = OBJ_PLAYER & & get_player_id ( obj ) = = Player_num ) {
2021-09-19 10:53:48 +00:00
const auto thrust_mag = vm_vec_mag_quick ( obj - > mtype . phys_info . thrust ) ;
2006-03-20 17:12:09 +00:00
engine_glow_value [ 0 ] + = ( fixdiv ( thrust_mag , Player_ship - > max_thrust ) * 4 ) / 5 ;
}
else {
2021-09-19 10:53:48 +00:00
const auto speed = vm_vec_mag_quick ( obj - > mtype . phys_info . velocity ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
engine_glow_value [ 0 ] + = ( fixdiv ( speed , MAX_VELOCITY ) * 4 ) / 5 ;
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
engine_glow_value [ 0 ] + = ( fixdiv ( speed , MAX_VELOCITY ) * 3 ) / 5 ;
2013-03-03 01:03:33 +00:00
# endif
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
//set value for player headlight
if ( obj - > type = = OBJ_PLAYER ) {
2015-11-07 21:55:59 +00:00
auto & player_flags = obj - > ctype . player_info . powerup_flags ;
if ( player_flags & PLAYER_FLAGS_HEADLIGHT & & ! Endlevel_sequence )
if ( player_flags & PLAYER_FLAGS_HEADLIGHT_ON )
2006-03-20 17:12:09 +00:00
engine_glow_value [ 1 ] = - 2 ; //draw white!
else
engine_glow_value [ 1 ] = - 1 ; //draw normal color (grey)
else
engine_glow_value [ 1 ] = - 3 ; //don't draw
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2022-10-09 23:15:20 +00:00
auto & Polygon_models = LevelSharedPolygonModelState . Polygon_models ;
2006-03-20 17:12:09 +00:00
if ( obj - > rtype . pobj_info . tmap_override ! = - 1 ) {
2020-05-02 21:18:42 +00:00
std : : array < bitmap_index , 12 > bm_ptrs ;
2006-03-20 17:12:09 +00:00
2015-02-14 22:48:29 +00:00
//fill whole array, in case simple model needs more
bm_ptrs . fill ( Textures [ obj - > rtype . pobj_info . tmap_override ] ) ;
2022-10-09 23:15:20 +00:00
draw_polygon_model ( Polygon_models , canvas , obj - > pos ,
2017-08-11 23:43:54 +00:00
obj - > orient ,
2013-03-03 01:03:33 +00:00
obj - > rtype . pobj_info . anim_angles ,
2006-03-20 17:12:09 +00:00
obj - > rtype . pobj_info . model_num ,
obj - > rtype . pobj_info . subobj_flags ,
light ,
2013-12-18 00:07:46 +00:00
& engine_glow_value ,
2006-03-20 17:12:09 +00:00
bm_ptrs ) ;
}
else {
2015-11-19 03:23:34 +00:00
std : : pair < fix64 , fix > cloak_duration ;
2015-11-19 03:23:34 +00:00
std : : pair < fix , fix > cloak_fade ;
2015-11-07 21:55:59 +00:00
if ( obj - > type = = OBJ_PLAYER & & ( obj - > ctype . player_info . powerup_flags & PLAYER_FLAGS_CLOAKED ) )
2015-11-07 21:56:00 +00:00
{
2015-11-14 18:17:21 +00:00
auto & cloak_time = obj - > ctype . player_info . cloak_time ;
2015-11-19 03:23:34 +00:00
cloak_duration = { cloak_time , CLOAK_TIME_MAX } ;
2015-11-19 03:23:34 +00:00
cloak_fade = { CLOAK_FADEIN_DURATION_PLAYER , CLOAK_FADEOUT_DURATION_PLAYER } ;
2015-11-07 21:56:00 +00:00
}
2006-03-20 17:12:09 +00:00
else if ( ( obj - > type = = OBJ_ROBOT ) & & ( obj - > ctype . ai_info . CLOAKED ) ) {
2013-10-07 23:52:33 +00:00
if ( Robot_info [ get_robot_id ( obj ) ] . boss_flag )
2019-08-06 02:59:40 +00:00
cloak_duration = { BossUniqueState . Boss_cloak_start_time , Boss_cloak_duration } ;
2006-03-20 17:12:09 +00:00
else
2015-11-19 03:23:34 +00:00
cloak_duration = { GameTime64 - F1_0 * 10 , F1_0 * 20 } ;
2015-11-19 03:23:34 +00:00
cloak_fade = { CLOAK_FADEIN_DURATION_ROBOT , CLOAK_FADEOUT_DURATION_ROBOT } ;
2006-03-20 17:12:09 +00:00
} else {
2015-02-14 22:48:29 +00:00
alternate_textures alt_textures ;
2015-10-09 02:46:09 +00:00
const unsigned ati = static_cast < unsigned > ( obj - > rtype . pobj_info . alt_textures ) - 1 ;
if ( ati < multi_player_textures . size ( ) )
alt_textures = multi_player_textures [ ati ] ;
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2016-11-19 18:09:26 +00:00
if ( obj - > type = = OBJ_ROBOT )
{
2006-03-20 17:12:09 +00:00
// Snipers get bright when they fire.
2017-03-18 18:07:39 +00:00
if ( obj - > ctype . ai_info . ail . next_fire < F1_0 / 8 ) {
2015-04-02 02:36:57 +00:00
if ( obj - > ctype . ai_info . behavior = = ai_behavior : : AIB_SNIPE )
2011-04-07 20:32:51 +00:00
{
light . r = 2 * light . r + F1_0 ;
light . g = 2 * light . g + F1_0 ;
light . b = 2 * light . b + F1_0 ;
}
2006-03-20 17:12:09 +00:00
}
2016-11-19 18:09:26 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2011-02-17 23:17:51 +00:00
2015-11-14 18:17:21 +00:00
const auto is_weapon_with_inner_model = ( obj - > type = = OBJ_WEAPON & & Weapon_info [ get_weapon_id ( obj ) ] . model_num_inner > - 1 ) ;
bool draw_simple_model ;
if ( is_weapon_with_inner_model )
{
2020-07-16 02:31:04 +00:00
gr_settransblend ( canvas , GR_FADE_OFF , gr_blend : : additive_a ) ;
2015-11-14 18:17:21 +00:00
draw_simple_model = static_cast < fix > ( vm_vec_dist_quick ( Viewer - > pos , obj - > pos ) ) < Simple_model_threshhold_scale * F1_0 * 2 ;
if ( draw_simple_model )
2022-10-09 23:15:20 +00:00
draw_polygon_model ( Polygon_models , canvas , obj - > pos ,
2017-08-11 23:43:54 +00:00
obj - > orient ,
2013-03-03 01:03:33 +00:00
obj - > rtype . pobj_info . anim_angles ,
2013-10-07 23:52:33 +00:00
Weapon_info [ get_weapon_id ( obj ) ] . model_num_inner ,
2006-03-20 17:12:09 +00:00
obj - > rtype . pobj_info . subobj_flags ,
light ,
2013-12-18 00:07:46 +00:00
& engine_glow_value ,
2006-03-20 17:12:09 +00:00
alt_textures ) ;
}
2011-02-17 23:17:51 +00:00
2022-10-09 23:15:20 +00:00
draw_polygon_model ( Polygon_models , canvas , obj - > pos ,
2017-08-11 23:43:54 +00:00
obj - > orient ,
2013-03-03 01:03:33 +00:00
obj - > rtype . pobj_info . anim_angles , obj - > rtype . pobj_info . model_num ,
2011-02-17 23:17:51 +00:00
obj - > rtype . pobj_info . subobj_flags ,
light ,
2013-12-18 00:07:46 +00:00
& engine_glow_value ,
2011-02-17 23:17:51 +00:00
alt_textures ) ;
2011-02-23 16:46:39 +00:00
2015-11-14 18:17:21 +00:00
if ( is_weapon_with_inner_model )
{
2016-09-24 18:06:11 +00:00
# if !DXX_USE_OGL // in software rendering must draw inner model last
2020-07-16 02:31:04 +00:00
gr_settransblend ( canvas , GR_FADE_OFF , gr_blend : : additive_a ) ;
2015-11-14 18:17:21 +00:00
if ( draw_simple_model )
2022-10-09 23:15:20 +00:00
draw_polygon_model ( Polygon_models , canvas , obj - > pos ,
2017-08-11 23:43:54 +00:00
obj - > orient ,
2013-03-03 01:03:33 +00:00
obj - > rtype . pobj_info . anim_angles ,
2011-04-07 20:32:51 +00:00
Weapon_info [ obj - > id ] . model_num_inner ,
obj - > rtype . pobj_info . subobj_flags ,
light ,
2013-12-18 00:07:46 +00:00
& engine_glow_value ,
2011-04-07 20:32:51 +00:00
alt_textures ) ;
# endif
2020-07-16 02:31:04 +00:00
gr_settransblend ( canvas , GR_FADE_OFF , gr_blend : : normal ) ;
2015-11-14 18:17:21 +00:00
}
2015-11-07 21:56:00 +00:00
return ;
2006-03-20 17:12:09 +00:00
}
2017-03-11 19:56:27 +00:00
draw_cloaked_object ( canvas , obj , light , engine_glow_value , cloak_duration . first , cloak_duration . second , cloak_fade . first , cloak_fade . second ) ;
2006-03-20 17:12:09 +00:00
}
}
2015-12-22 04:18:50 +00:00
}
2022-07-09 13:39:29 +00:00
}
2006-03-20 17:12:09 +00:00
//------------------------------------------------------------------------------
// These variables are used to keep a list of the 3 closest robots to the viewer.
// The code works like this: Every time render object is called with a polygon model,
// it finds the distance of that robot to the viewer. If this distance if within 10
// segments of the viewer, it does the following: If there aren't already 3 robots in
// the closet-robots list, it just sticks that object into the list along with its distance.
// If the list already contains 3 robots, then it finds the robot in that list that is
// farthest from the viewer. If that object is farther than the object currently being
// rendered, then the new object takes over that far object's slot. *Then* after all
// objects are rendered, object_render_targets is called an it draws a target on top
// of all the objects.
//091494: #define MAX_CLOSE_ROBOTS 3
//--unused-- static int Object_draw_lock_boxes = 0;
//091494: static int Object_num_close = 0;
//091494: static object * Object_close_ones[MAX_CLOSE_ROBOTS];
//091494: static fix Object_close_distance[MAX_CLOSE_ROBOTS];
//091494: set_close_objects(object *obj)
//091494: {
//091494: fix dist;
//091494:
//091494: if ( (obj->type != OBJ_ROBOT) || (Object_draw_lock_boxes==0) )
//091494: return;
//091494:
//091494: // The following code keeps a list of the 10 closest robots to the
//091494: // viewer. See comments in front of this function for how this works.
//091494: dist = vm_vec_dist( &obj->pos, &Viewer->pos );
//091494: if ( dist < i2f(20*10) ) {
//091494: if ( Object_num_close < MAX_CLOSE_ROBOTS ) {
//091494: Object_close_ones[Object_num_close] = obj;
//091494: Object_close_distance[Object_num_close] = dist;
//091494: Object_num_close++;
//091494: } else {
//091494: int i, farthest_robot;
//091494: fix farthest_distance;
//091494: // Find the farthest robot in the list
//091494: farthest_robot = 0;
//091494: farthest_distance = Object_close_distance[0];
//091494: for (i=1; i<Object_num_close; i++ ) {
//091494: if ( Object_close_distance[i] > farthest_distance ) {
//091494: farthest_distance = Object_close_distance[i];
//091494: farthest_robot = i;
//091494: }
//091494: }
//091494: // If this object is closer to the viewer than
//091494: // the farthest in the list, replace the farthest with this object.
//091494: if ( farthest_distance > dist ) {
//091494: Object_close_ones[farthest_robot] = obj;
//091494: Object_close_distance[farthest_robot] = dist;
//091494: }
//091494: }
//091494: }
//091494: }
2015-12-22 04:18:50 +00:00
namespace dcx {
2013-12-29 04:28:07 +00:00
objnum_t Player_fired_laser_this_frame = object_none ;
2019-12-22 05:34:08 +00:00
2022-04-17 22:27:19 +00:00
namespace {
2019-12-22 05:34:08 +00:00
static bool predicate_debris ( const object_base & o )
{
return o . type = = OBJ_DEBRIS ;
}
static bool predicate_flare ( const object_base & o )
{
return ( o . type = = OBJ_WEAPON ) & & ( get_weapon_id ( o ) = = weapon_id_type : : FLARE_ID ) ;
}
static bool predicate_nonflare_weapon ( const object_base & o )
{
return ( o . type = = OBJ_WEAPON ) & & ( get_weapon_id ( o ) ! = weapon_id_type : : FLARE_ID ) ;
}
2015-12-22 04:18:50 +00:00
}
2006-03-20 17:12:09 +00:00
2022-04-17 22:27:19 +00:00
}
2006-03-20 17:12:09 +00:00
2015-12-22 04:18:50 +00:00
namespace dsx {
2019-12-22 05:34:08 +00:00
2022-04-17 22:27:19 +00:00
namespace {
2019-12-22 05:34:08 +00:00
static bool predicate_fireball ( const object & o )
{
return o . type = = OBJ_FIREBALL & & o . ctype . expl_info . delete_objnum = = object_none ;
}
2006-03-20 17:12:09 +00:00
// -----------------------------------------------------------------------------
//this routine checks to see if an robot rendered near the middle of
//the screen, and if so and the player had fired, "warns" the robot
2016-04-23 17:59:47 +00:00
static void set_robot_location_info ( object & objp )
2006-03-20 17:12:09 +00:00
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vcobjptr = Objects . vcptr ;
2013-12-26 22:21:16 +00:00
if ( Player_fired_laser_this_frame ! = object_none ) {
2016-04-23 17:59:47 +00:00
const auto & & temp = g3_rotate_point ( objp . pos ) ;
2022-10-02 19:51:35 +00:00
if ( ( temp . p3_codes & clipping_code : : behind ) ! = clipping_code : : None ) //robot behind the screen
2006-03-20 17:12:09 +00:00
return ;
//the code below to check for object near the center of the screen
//completely ignores z, which may not be good
if ( ( abs ( temp . p3_x ) < F1_0 * 4 ) & & ( abs ( temp . p3_y ) < F1_0 * 4 ) ) {
2016-04-23 17:59:47 +00:00
objp . ctype . ai_info . danger_laser_num = Player_fired_laser_this_frame ;
objp . ctype . ai_info . danger_laser_signature = vcobjptr ( Player_fired_laser_this_frame ) - > signature ;
2006-03-20 17:12:09 +00:00
}
}
}
2022-07-09 13:39:29 +00:00
static const object * Viewer_save ;
2022-04-17 22:27:19 +00:00
}
2006-03-20 17:12:09 +00:00
// ------------------------------------------------------------------------------------------------------------------
2017-06-10 03:31:02 +00:00
void create_small_fireball_on_object ( const vmobjptridx_t objp , fix size_scale , int sound_flag )
2006-03-20 17:12:09 +00:00
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
2006-03-20 17:12:09 +00:00
fix size ;
2014-11-04 01:33:45 +00:00
vms_vector pos ;
2006-03-20 17:12:09 +00:00
pos = objp - > pos ;
2014-11-04 01:33:45 +00:00
auto rand_vec = make_random_vector ( ) ;
2006-03-20 17:12:09 +00:00
2014-09-28 21:11:05 +00:00
vm_vec_scale ( rand_vec , objp - > size / 2 ) ;
2006-03-20 17:12:09 +00:00
2014-09-28 21:43:00 +00:00
vm_vec_add2 ( pos , rand_vec ) ;
2006-03-20 17:12:09 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
size = fixmul ( size_scale , F1_0 + d_rand ( ) * 4 ) ;
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
size = fixmul ( size_scale , F1_0 / 2 + d_rand ( ) * 4 / 2 ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2018-09-19 02:13:30 +00:00
const auto & & segnum = find_point_seg ( LevelSharedSegmentState , LevelUniqueSegmentState , pos , Segments . vmptridx ( objp - > segnum ) ) ;
2013-12-26 22:21:16 +00:00
if ( segnum ! = segment_none ) {
2014-10-26 21:36:25 +00:00
auto expl_obj = object_create_explosion ( segnum , pos , size , VCLIP_SMALL_EXPLOSION ) ;
2006-03-20 17:12:09 +00:00
if ( ! expl_obj )
return ;
2018-03-12 03:43:46 +00:00
obj_attach ( Objects , objp , expl_obj ) ;
2006-03-20 17:12:09 +00:00
if ( d_rand ( ) < 8192 ) {
fix vol = F1_0 / 2 ;
if ( objp - > type = = OBJ_ROBOT )
vol * = 2 ;
2008-11-20 18:02:42 +00:00
if ( sound_flag )
2018-05-13 03:14:34 +00:00
digi_link_sound_to_object ( SOUND_EXPLODING_WALL , objp , 0 , vol , sound_stack : : allow_stacking ) ;
2006-03-20 17:12:09 +00:00
}
}
}
// -- mk, 02/05/95 -- #define VCLIP_INVULNERABILITY_EFFECT VCLIP_SMALL_EXPLOSION
// -- mk, 02/05/95 --
// -- mk, 02/05/95 -- // -----------------------------------------------------------------------------
// -- mk, 02/05/95 -- void do_player_invulnerability_effect(object *objp)
// -- mk, 02/05/95 -- {
// -- mk, 02/05/95 -- if (d_rand() < FrameTime*8) {
// -- mk, 02/05/95 -- create_vclip_on_object(objp, F1_0, VCLIP_INVULNERABILITY_EFFECT);
// -- mk, 02/05/95 -- }
// -- mk, 02/05/95 -- }
// -----------------------------------------------------------------------------
// Render an object. Calls one of several routines based on type
2019-04-13 18:00:07 +00:00
void render_object ( grs_canvas & canvas , const d_level_unique_light_state & LevelUniqueLightState , const vmobjptridx_t obj )
2006-03-20 17:12:09 +00:00
{
2015-10-18 21:01:19 +00:00
if ( unlikely ( obj = = Viewer ) )
2011-02-23 16:46:39 +00:00
return ;
2015-10-18 21:01:19 +00:00
if ( unlikely ( obj - > type = = OBJ_NONE ) )
2011-02-23 16:46:39 +00:00
{
2006-03-20 17:12:09 +00:00
Int3 ( ) ;
return ;
}
2016-09-24 18:06:11 +00:00
# if !DXX_USE_OGL
2020-05-02 21:18:42 +00:00
const auto mld_save = std : : exchange ( Max_linear_depth , Max_linear_depth_objects ) ;
2015-04-22 02:44:29 +00:00
# endif
2006-03-20 17:12:09 +00:00
2015-08-21 03:12:35 +00:00
bool alpha = false ;
2011-02-23 16:46:39 +00:00
switch ( obj - > render_type )
{
case RT_NONE :
break ; //doesn't render, like the player
2006-03-20 17:12:09 +00:00
case RT_POLYOBJ :
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-08-21 03:12:35 +00:00
if ( PlayerCfg . AlphaBlendMarkers & & obj - > type = = OBJ_MARKER ) // set nice transparency/blending for certrain objects
{
alpha = true ;
2021-09-19 10:53:48 +00:00
gr_settransblend ( canvas , gr_fade_level { 10 } , gr_blend : : additive_a ) ;
2015-08-21 03:12:35 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2019-04-13 18:00:07 +00:00
draw_polygon_object ( canvas , LevelUniqueLightState , obj ) ;
2006-03-20 17:12:09 +00:00
2011-02-23 16:46:39 +00:00
if ( obj - > type = = OBJ_ROBOT ) //"warn" robot if being shot at
2006-03-20 17:12:09 +00:00
set_robot_location_info ( obj ) ;
2011-02-23 16:46:39 +00:00
break ;
2006-03-20 17:12:09 +00:00
2011-02-23 16:46:39 +00:00
case RT_MORPH :
2019-04-13 18:00:07 +00:00
draw_morph_object ( canvas , LevelUniqueLightState , obj ) ;
2011-02-23 16:46:39 +00:00
break ;
2006-03-20 17:12:09 +00:00
2011-02-23 16:46:39 +00:00
case RT_FIREBALL :
2015-08-21 03:12:35 +00:00
if ( PlayerCfg . AlphaBlendFireballs ) // set nice transparency/blending for certrain objects
{
alpha = true ;
2020-07-16 02:31:04 +00:00
gr_settransblend ( canvas , GR_FADE_OFF , gr_blend : : additive_c ) ;
2015-08-21 03:12:35 +00:00
}
2006-03-20 17:12:09 +00:00
2018-10-21 00:24:07 +00:00
draw_fireball ( Vclip , canvas , obj ) ;
2006-03-20 17:12:09 +00:00
break ;
2011-02-23 16:46:39 +00:00
case RT_WEAPON_VCLIP :
2019-08-15 01:34:22 +00:00
if ( PlayerCfg . AlphaBlendWeapons & & ( ! is_proximity_bomb_or_any_smart_mine ( get_weapon_id ( obj ) )
2019-08-08 09:57:52 +00:00
) ) // set nice transparency/blending for certain objects
2015-08-21 03:12:35 +00:00
{
alpha = true ;
2021-09-19 10:53:48 +00:00
gr_settransblend ( canvas , gr_fade_level { 7 } , gr_blend : : additive_a ) ;
2015-08-21 03:12:35 +00:00
}
2006-03-20 17:12:09 +00:00
2018-10-21 00:24:07 +00:00
draw_weapon_vclip ( Vclip , Weapon_info , canvas , obj ) ;
2011-02-23 16:46:39 +00:00
break ;
2006-03-20 17:12:09 +00:00
2011-02-23 16:46:39 +00:00
case RT_HOSTAGE :
2019-04-13 18:00:07 +00:00
draw_hostage ( Vclip , canvas , LevelUniqueLightState , obj ) ;
2011-02-23 16:46:39 +00:00
break ;
2006-03-20 17:12:09 +00:00
2011-02-23 16:46:39 +00:00
case RT_POWERUP :
2015-08-21 03:12:35 +00:00
if ( PlayerCfg . AlphaBlendPowerups ) // set nice transparency/blending for certrain objects
2013-10-07 23:52:33 +00:00
switch ( get_powerup_id ( obj ) )
2011-02-23 16:46:39 +00:00
{
case POW_EXTRA_LIFE :
case POW_ENERGY :
case POW_SHIELD_BOOST :
case POW_CLOAK :
case POW_INVULNERABILITY :
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2011-02-23 16:46:39 +00:00
case POW_HOARD_ORB :
2013-03-03 01:03:33 +00:00
# endif
2015-08-21 03:12:35 +00:00
alpha = true ;
2021-09-19 10:53:48 +00:00
gr_settransblend ( canvas , gr_fade_level { 7 } , gr_blend : : additive_a ) ;
2011-02-23 16:46:39 +00:00
break ;
2015-04-19 04:18:53 +00:00
case POW_LASER :
case POW_KEY_BLUE :
case POW_KEY_RED :
case POW_KEY_GOLD :
case POW_MISSILE_1 :
case POW_MISSILE_4 :
case POW_QUAD_FIRE :
case POW_VULCAN_WEAPON :
case POW_SPREADFIRE_WEAPON :
case POW_PLASMA_WEAPON :
case POW_FUSION_WEAPON :
case POW_PROXIMITY_WEAPON :
case POW_HOMING_AMMO_1 :
case POW_HOMING_AMMO_4 :
case POW_SMARTBOMB_WEAPON :
case POW_MEGA_WEAPON :
case POW_VULCAN_AMMO :
case POW_TURBO :
case POW_MEGAWOW :
# if defined(DXX_BUILD_DESCENT_II)
case POW_FULL_MAP :
case POW_HEADLIGHT :
case POW_GAUSS_WEAPON :
case POW_HELIX_WEAPON :
case POW_PHOENIX_WEAPON :
case POW_OMEGA_WEAPON :
case POW_SUPER_LASER :
case POW_CONVERTER :
case POW_AMMO_RACK :
case POW_AFTERBURNER :
case POW_SMISSILE1_1 :
case POW_SMISSILE1_4 :
case POW_GUIDED_MISSILE_1 :
case POW_GUIDED_MISSILE_4 :
case POW_SMART_MINE :
case POW_MERCURY_MISSILE_1 :
case POW_MERCURY_MISSILE_4 :
case POW_EARTHSHAKER_MISSILE :
case POW_FLAG_BLUE :
case POW_FLAG_RED :
# endif
break ;
2011-02-23 16:46:39 +00:00
}
2018-10-21 00:24:07 +00:00
draw_powerup ( Vclip , canvas , obj ) ;
2011-02-23 16:46:39 +00:00
break ;
case RT_LASER :
2015-08-21 03:12:35 +00:00
if ( PlayerCfg . AlphaBlendLasers ) // set nice transparency/blending for certrain objects
{
alpha = true ;
2021-09-19 10:53:48 +00:00
gr_settransblend ( canvas , gr_fade_level { 7 } , gr_blend : : additive_a ) ;
2015-08-21 03:12:35 +00:00
}
2006-03-20 17:12:09 +00:00
2017-03-11 19:56:27 +00:00
Laser_render ( canvas , obj ) ;
2011-02-23 16:46:39 +00:00
break ;
2006-03-20 17:12:09 +00:00
2011-02-23 16:46:39 +00:00
default :
Error ( " Unknown render_type <%d> " , obj - > render_type ) ;
}
2006-03-20 17:12:09 +00:00
2015-08-21 03:12:35 +00:00
if ( alpha )
2020-07-16 02:31:04 +00:00
gr_settransblend ( canvas , GR_FADE_OFF , gr_blend : : normal ) ; // revert any transparency/blending setting back to normal
2006-03-20 17:12:09 +00:00
2011-02-23 16:46:39 +00:00
if ( obj - > render_type ! = RT_NONE & & Newdemo_state = = ND_STATE_RECORDING )
2010-07-15 05:40:25 +00:00
newdemo_record_render_object ( obj ) ;
2016-09-24 18:06:11 +00:00
# if !DXX_USE_OGL
2006-03-20 17:12:09 +00:00
Max_linear_depth = mld_save ;
2015-04-22 02:44:29 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
2022-06-05 17:44:52 +00:00
void reset_player_object ( object_base & ConsoleObject )
2006-03-20 17:12:09 +00:00
{
//Init physics
2022-06-05 17:44:52 +00:00
vm_vec_zero ( ConsoleObject . mtype . phys_info . velocity ) ;
vm_vec_zero ( ConsoleObject . mtype . phys_info . thrust ) ;
vm_vec_zero ( ConsoleObject . mtype . phys_info . rotvel ) ;
vm_vec_zero ( ConsoleObject . mtype . phys_info . rotthrust ) ;
ConsoleObject . mtype . phys_info . turnroll = 0 ;
ConsoleObject . mtype . phys_info . mass = Player_ship - > mass ;
ConsoleObject . mtype . phys_info . drag = Player_ship - > drag ;
2022-07-02 18:10:45 +00:00
ConsoleObject . mtype . phys_info . flags =
# ifndef DXX_FEATURE_DISABLE_SHIP_WIGGLE
PF_WIGGLE |
# endif
PF_TURNROLL | PF_LEVELLING | PF_USES_THRUST ;
2006-03-20 17:12:09 +00:00
//Init render info
2022-06-05 17:44:52 +00:00
ConsoleObject . render_type = RT_POLYOBJ ;
ConsoleObject . rtype . pobj_info . model_num = Player_ship - > model_num ; //what model is this?
ConsoleObject . rtype . pobj_info . subobj_flags = 0 ; //zero the flags
ConsoleObject . rtype . pobj_info . tmap_override = - 1 ; //no tmap override!
ConsoleObject . rtype . pobj_info . anim_angles = { } ;
2006-03-20 17:12:09 +00:00
// Clear misc
2022-06-05 17:44:52 +00:00
ConsoleObject . flags = 0 ;
2006-03-20 17:12:09 +00:00
}
//make object0 the player, setting all relevant fields
2022-06-05 17:44:52 +00:00
void init_player_object ( const d_level_shared_polygon_model_state & LevelSharedPolygonModelState , object_base & console )
2006-03-20 17:12:09 +00:00
{
2022-06-05 17:44:52 +00:00
console . type = OBJ_PLAYER ;
2015-07-12 01:04:21 +00:00
set_player_id ( console , 0 ) ; //no sub-types for player
2022-06-05 17:44:52 +00:00
console . signature = object_signature_t { 0 } ;
2018-12-30 00:43:59 +00:00
auto & Polygon_models = LevelSharedPolygonModelState . Polygon_models ;
2022-06-05 17:44:52 +00:00
console . size = Polygon_models [ Player_ship - > model_num ] . rad ;
console . control_source = object : : control_type : : slew ; //default is player slewing
console . movement_source = object : : movement_type : : physics ; //change this sometime
console . lifeleft = IMMORTAL_TIME ;
console . attached_obj = object_none ;
2022-06-05 17:44:52 +00:00
reset_player_object ( console ) ;
2006-03-20 17:12:09 +00:00
}
//sets up the free list & init player & whatever else
void init_objects ( )
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vmobjptr = Objects . vmptr ;
2015-12-03 03:26:49 +00:00
for ( objnum_t i = 0 ; i < MAX_OBJECTS ; + + i )
{
2018-12-30 00:43:57 +00:00
LevelUniqueObjectState . free_obj_list [ i ] = i ;
2017-06-10 03:31:02 +00:00
auto & obj = * vmobjptr ( i ) ;
2016-11-06 17:12:03 +00:00
DXX_POISON_VAR ( obj , 0xfd ) ;
obj . type = OBJ_NONE ;
2006-03-20 17:12:09 +00:00
}
2019-12-22 05:34:08 +00:00
range_for ( unique_segment & j , Segments )
2014-11-23 21:41:30 +00:00
j . objects = object_none ;
2006-03-20 17:12:09 +00:00
2018-09-25 03:44:09 +00:00
Viewer = ConsoleObject = & Objects . front ( ) ;
2006-03-20 17:12:09 +00:00
2022-06-05 17:44:52 +00:00
init_player_object ( LevelSharedPolygonModelState , * ConsoleObject ) ;
2018-03-12 03:43:46 +00:00
obj_link_unchecked ( Objects . vmptr , Objects . vmptridx ( ConsoleObject ) , Segments . vmptridx ( segment_first ) ) ; //put in the world in segment 0
2018-12-30 00:43:57 +00:00
LevelUniqueObjectState . num_objects = 1 ; //just the player
2016-01-09 16:38:12 +00:00
Objects . set_count ( 1 ) ;
2006-03-20 17:12:09 +00:00
}
//after calling init_object(), the network code has grabbed specific
//object slots without allocating them. Go though the objects & build
//the free list, then set the apporpriate globals
2018-12-30 00:43:57 +00:00
void special_reset_objects ( d_level_unique_object_state & LevelUniqueObjectState )
2006-03-20 17:12:09 +00:00
{
2018-03-12 03:43:46 +00:00
unsigned num_objects = MAX_OBJECTS ;
2006-03-20 17:12:09 +00:00
2018-12-30 00:43:57 +00:00
auto & Objects = LevelUniqueObjectState . get_objects ( ) ;
2016-01-09 16:38:12 +00:00
Objects . set_count ( 1 ) ;
2016-10-15 00:53:18 +00:00
assert ( Objects . front ( ) . type ! = OBJ_NONE ) ; //0 should be used
2006-03-20 17:12:09 +00:00
2018-12-30 00:43:57 +00:00
DXX_POISON_VAR ( LevelUniqueObjectState . free_obj_list , 0xfd ) ;
2015-12-03 03:26:49 +00:00
for ( objnum_t i = MAX_OBJECTS ; i - - ; )
2018-03-12 03:43:46 +00:00
if ( Objects . vcptr ( i ) - > type = = OBJ_NONE )
2018-12-30 00:43:57 +00:00
LevelUniqueObjectState . free_obj_list [ - - num_objects ] = i ;
2006-03-20 17:12:09 +00:00
else
if ( i > Highest_object_index )
2016-01-09 16:38:12 +00:00
Objects . set_count ( i + 1 ) ;
2018-12-30 00:43:57 +00:00
LevelUniqueObjectState . num_objects = num_objects ;
2006-03-20 17:12:09 +00:00
}
2022-04-17 22:27:19 +00:00
namespace {
2006-03-20 17:12:09 +00:00
//link the object into the list for its segment
2018-03-12 03:43:46 +00:00
void obj_link ( fvmobjptr & vmobjptr , const vmobjptridx_t obj , const vmsegptridx_t segnum )
2006-03-20 17:12:09 +00:00
{
2016-11-06 17:12:03 +00:00
assert ( obj - > segnum = = segment_none ) ;
assert ( obj - > next = = object_none ) ;
assert ( obj - > prev = = object_none ) ;
2018-03-12 03:43:46 +00:00
obj_link_unchecked ( vmobjptr , obj , segnum ) ;
2016-11-06 17:12:03 +00:00
}
2022-04-17 22:27:19 +00:00
}
2018-03-12 03:43:46 +00:00
void obj_link_unchecked ( fvmobjptr & vmobjptr , const vmobjptridx_t obj , const vmsegptridx_t segnum )
2016-11-06 17:12:03 +00:00
{
2006-03-20 17:12:09 +00:00
obj - > segnum = segnum ;
2020-08-10 03:45:13 +00:00
unique_segment & useg = segnum ;
obj - > next = std : : exchange ( useg . objects , obj ) ;
2013-12-26 22:21:16 +00:00
obj - > prev = object_none ;
2006-03-20 17:12:09 +00:00
2015-12-03 03:26:49 +00:00
if ( obj - > next ! = object_none )
2017-06-10 03:31:02 +00:00
vmobjptr ( obj - > next ) - > prev = obj ;
2006-03-20 17:12:09 +00:00
}
2018-03-12 03:43:46 +00:00
void obj_unlink ( fvmobjptr & vmobjptr , fvmsegptr & vmsegptr , object_base & obj )
2006-03-20 17:12:09 +00:00
{
2016-07-23 04:10:43 +00:00
const auto next = obj . next ;
2017-06-10 03:31:02 +00:00
/* It is a bug elsewhere if vmsegptr ever fails here. However, it is
2016-11-06 17:12:03 +00:00
* expensive to check , so only force verification in debug builds .
*
* In debug builds , always compute it , for the side effect of
* validating the segment number .
*
* In release builds , compute it when it is needed .
*/
# ifndef NDEBUG
2020-08-10 03:45:13 +00:00
unique_segment & segp = vmsegptr ( obj . segnum ) ;
2016-11-06 17:12:03 +00:00
# endif
2016-07-23 04:10:43 +00:00
( ( obj . prev = = object_none )
2016-11-06 17:12:03 +00:00
? (
# ifdef NDEBUG
2020-08-10 03:45:13 +00:00
static_cast < unique_segment & > ( vmsegptr ( obj . segnum ) )
2016-11-06 17:12:03 +00:00
# else
segp
# endif
2020-08-10 03:45:13 +00:00
) . objects
2017-06-10 03:31:02 +00:00
: vmobjptr ( obj . prev ) - > next ) = next ;
2006-03-20 17:12:09 +00:00
2016-07-23 04:10:43 +00:00
obj . segnum = segment_none ;
2006-03-20 17:12:09 +00:00
2016-07-23 04:10:43 +00:00
if ( next ! = object_none )
2017-06-10 03:31:02 +00:00
vmobjptr ( next ) - > prev = obj . prev ;
2016-11-06 17:12:03 +00:00
DXX_POISON_VAR ( obj . next , 0xfa ) ;
DXX_POISON_VAR ( obj . prev , 0xfa ) ;
2006-03-20 17:12:09 +00:00
}
//returns the number of a free object, updating Highest_object_index.
//Generally, obj_create() should be called to get an object, since it
//fills in important fields and does the linking.
//returns -1 if no free objects
2018-12-30 00:43:57 +00:00
imobjptridx_t obj_allocate ( d_level_unique_object_state & LevelUniqueObjectState )
2006-03-20 17:12:09 +00:00
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
2018-12-30 00:43:57 +00:00
if ( LevelUniqueObjectState . num_objects > = Objects . size ( ) )
2013-12-26 22:21:16 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
2018-12-30 00:43:57 +00:00
const auto objnum = LevelUniqueObjectState . free_obj_list [ LevelUniqueObjectState . num_objects + + ] ;
2018-03-12 03:43:46 +00:00
if ( objnum > = Objects . get_count ( ) )
{
2016-01-09 16:38:12 +00:00
Objects . set_count ( objnum + 1 ) ;
2006-03-20 17:12:09 +00:00
}
2018-03-12 03:43:46 +00:00
const auto & & r = Objects . vmptridx ( objnum ) ;
2016-11-10 04:22:18 +00:00
assert ( r - > type = = OBJ_NONE ) ;
return r ;
2006-03-20 17:12:09 +00:00
}
2022-04-17 22:27:19 +00:00
namespace {
2006-03-20 17:12:09 +00:00
//frees up an object. Generally, obj_delete() should be called to get
//rid of an object. This function deallocates the object entry after
//the object has been unlinked
2018-12-30 00:43:57 +00:00
static void obj_free ( d_level_unique_object_state & LevelUniqueObjectState , const vmobjidx_t objnum )
2006-03-20 17:12:09 +00:00
{
2018-12-30 00:43:57 +00:00
const auto num_objects = - - LevelUniqueObjectState . num_objects ;
assert ( num_objects < LevelUniqueObjectState . free_obj_list . size ( ) ) ;
LevelUniqueObjectState . free_obj_list [ num_objects ] = objnum ;
auto & Objects = LevelUniqueObjectState . get_objects ( ) ;
2006-03-20 17:12:09 +00:00
2018-03-12 03:43:46 +00:00
objnum_t o = objnum ;
if ( o = = Highest_object_index )
2012-12-02 23:47:41 +00:00
{
for ( ; ; )
{
- - o ;
2018-03-12 03:43:46 +00:00
if ( Objects . vcptr ( o ) - > type ! = OBJ_NONE )
2012-12-02 23:47:41 +00:00
break ;
if ( o = = 0 )
break ;
}
2016-01-09 16:38:12 +00:00
Objects . set_count ( o + 1 ) ;
2012-12-02 23:47:41 +00:00
}
2006-03-20 17:12:09 +00:00
}
//-----------------------------------------------------------------------------
// Scan the object list, freeing down to num_used objects
// Returns number of slots freed.
2014-09-20 23:47:27 +00:00
static void free_object_slots ( uint_fast32_t num_used )
2006-03-20 17:12:09 +00:00
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vmobjptr = Objects . vmptr ;
2020-05-02 21:18:42 +00:00
std : : array < object * , MAX_OBJECTS > obj_list ;
2014-07-12 16:28:05 +00:00
unsigned num_already_free , num_to_free , olind = 0 ;
2006-03-20 17:12:09 +00:00
num_already_free = MAX_OBJECTS - Highest_object_index - 1 ;
if ( MAX_OBJECTS - num_already_free < num_used )
2013-06-23 20:39:55 +00:00
return ;
2006-03-20 17:12:09 +00:00
2017-06-10 03:31:02 +00:00
range_for ( const auto & & objp , vmobjptr )
2014-10-12 23:05:46 +00:00
{
2015-06-13 22:42:18 +00:00
if ( objp - > flags & OF_SHOULD_BE_DEAD )
{
2006-03-20 17:12:09 +00:00
num_already_free + + ;
if ( MAX_OBJECTS - num_already_free < num_used )
2013-06-23 20:39:55 +00:00
return ;
2006-03-20 17:12:09 +00:00
} else
2015-06-13 22:42:18 +00:00
switch ( objp - > type )
{
2006-03-20 17:12:09 +00:00
case OBJ_NONE :
num_already_free + + ;
if ( MAX_OBJECTS - num_already_free < num_used )
2013-06-23 20:39:55 +00:00
return ;
2006-03-20 17:12:09 +00:00
break ;
case OBJ_WALL :
Int3 ( ) ; // This is curious. What is an object that is a wall?
break ;
case OBJ_FIREBALL :
case OBJ_WEAPON :
case OBJ_DEBRIS :
2015-06-13 22:42:18 +00:00
obj_list [ olind + + ] = objp ;
2006-03-20 17:12:09 +00:00
break ;
case OBJ_ROBOT :
case OBJ_HOSTAGE :
case OBJ_PLAYER :
case OBJ_CNTRLCEN :
case OBJ_CLUTTER :
case OBJ_GHOST :
case OBJ_LIGHT :
case OBJ_CAMERA :
case OBJ_POWERUP :
2016-11-20 23:12:00 +00:00
case OBJ_COOP :
case OBJ_MARKER :
2006-03-20 17:12:09 +00:00
break ;
}
}
num_to_free = MAX_OBJECTS - num_used - num_already_free ;
if ( num_to_free > olind ) {
num_to_free = olind ;
}
2014-07-12 16:28:05 +00:00
// Capture before num_to_free modified
2016-02-12 04:02:28 +00:00
const auto & & r = partial_const_range ( obj_list , num_to_free ) ;
2019-12-22 05:34:08 +00:00
auto l = [ & vmobjptr , & r , & num_to_free ] ( const auto predicate ) - > bool {
2015-07-12 01:04:21 +00:00
range_for ( const auto i , r )
{
2019-12-22 05:34:08 +00:00
auto & o = * vmobjptr ( i ) ;
2015-07-12 01:04:21 +00:00
if ( predicate ( o ) )
{
2019-12-22 05:34:08 +00:00
o . flags | = OF_SHOULD_BE_DEAD ;
2014-07-12 16:28:05 +00:00
if ( ! - - num_to_free )
return true ;
}
2015-07-12 01:04:21 +00:00
}
2014-07-12 16:28:05 +00:00
return false ;
} ;
2006-03-20 17:12:09 +00:00
2014-07-12 16:28:05 +00:00
if ( l ( predicate_debris ) )
2013-06-23 20:39:55 +00:00
return ;
2006-03-20 17:12:09 +00:00
2014-07-12 16:28:05 +00:00
if ( l ( predicate_fireball ) )
2013-06-23 20:39:55 +00:00
return ;
2006-03-20 17:12:09 +00:00
2014-07-12 16:28:05 +00:00
if ( l ( predicate_flare ) )
2013-06-23 20:39:55 +00:00
return ;
2006-03-20 17:12:09 +00:00
2014-07-12 16:28:05 +00:00
if ( l ( predicate_nonflare_weapon ) )
return ;
2006-03-20 17:12:09 +00:00
}
2022-04-17 22:27:19 +00:00
}
2006-03-20 17:12:09 +00:00
//-----------------------------------------------------------------------------
//initialize a new object. adds to the list for the given segment
//note that segnum is really just a suggestion, since this routine actually
//searches for the correct segment
//returns the object number
2022-07-02 18:10:45 +00:00
imobjptridx_t obj_create ( d_level_unique_object_state & LevelUniqueObjectState , const d_level_shared_segment_state & LevelSharedSegmentState , d_level_unique_segment_state & LevelUniqueSegmentState , const object_type_t type , const unsigned id , vmsegptridx_t segnum , const vms_vector & pos , const vms_matrix * const orient , const fix size , const typename object : : control_type ctype , const typename object : : movement_type mtype , const render_type_t rtype )
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 ( ) ;
2011-09-24 09:15:31 +00:00
// Some consistency checking. FIXME: Add more debug output here to probably trace all possible occurances back.
2020-08-06 03:47:56 +00:00
assert ( ctype < = object : : control_type : : cntrlcen ) ;
2006-03-20 17:12:09 +00:00
2018-12-30 00:43:57 +00:00
if ( type = = OBJ_DEBRIS & & LevelUniqueObjectState . Debris_object_count > = Max_debris_objects & & ! PERSISTENT_DEBRIS )
2013-12-26 22:21:16 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
2018-12-30 00:43:57 +00:00
auto & vcvertptr = Vertices . vcptr ;
2022-02-19 14:52:17 +00:00
if ( get_seg_masks ( vcvertptr , pos , segnum , 0 ) . centermask ! = sidemask_t { } )
2014-10-02 03:02:34 +00:00
{
2018-09-19 02:13:30 +00:00
const auto & & p = find_point_seg ( LevelSharedSegmentState , LevelUniqueSegmentState , pos , segnum ) ;
2014-10-02 03:02:34 +00:00
if ( p = = segment_none ) {
2013-12-26 22:21:16 +00:00
return object_none ; //don't create this object
2006-03-20 17:12:09 +00:00
}
2014-10-02 03:02:34 +00:00
segnum = p ;
}
2006-03-20 17:12:09 +00:00
// Find next free object
2018-12-30 00:43:57 +00:00
const auto & & obj = obj_allocate ( LevelUniqueObjectState ) ;
2006-03-20 17:12:09 +00:00
2014-01-10 04:00:34 +00:00
if ( obj = = object_none ) //no free objects
2016-10-29 23:16:15 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
// Zero out object structure to keep weird bugs from happening
// in uninitialized fields.
2019-07-07 22:00:02 +00:00
const auto signature = obj - > signature ;
/* Test the version in the object structure, not the local copy.
* This produces a more useful diagnostic from Valgrind if the
* test reports a problem .
*/
DXX_CHECK_VAR_IS_DEFINED ( obj - > signature ) ;
2014-07-04 04:15:04 +00:00
* obj = { } ;
2015-01-03 23:44:32 +00:00
// Tell Valgrind to warn on any uninitialized fields.
2016-11-06 17:12:03 +00:00
DXX_POISON_VAR ( * obj , 0xfd ) ;
2006-03-20 17:12:09 +00:00
2020-08-10 03:45:13 +00:00
obj - > signature = next ( signature ) ;
2015-09-20 17:24:25 +00:00
obj - > type = type ;
obj - > id = id ;
obj - > pos = pos ;
obj - > size = size ;
obj - > flags = 0 ;
2006-03-20 17:12:09 +00:00
//@@if (orient != NULL)
//@@ obj->orient = *orient;
obj - > orient = orient ? * orient : vmd_identity_matrix ;
2020-08-10 03:45:13 +00:00
obj - > control_source = ctype ;
2020-08-10 03:45:13 +00:00
obj - > movement_source = mtype ;
2006-03-20 17:12:09 +00:00
obj - > render_type = rtype ;
2015-09-20 17:24:25 +00:00
obj - > contains_count = 0 ;
obj - > matcen_creator = 0 ;
2006-03-20 17:12:09 +00:00
obj - > lifeleft = IMMORTAL_TIME ; //assume immortal
2013-12-26 22:21:16 +00:00
obj - > attached_obj = object_none ;
2006-03-20 17:12:09 +00:00
2020-08-10 03:45:13 +00:00
if ( obj - > control_source = = object : : control_type : : powerup )
2015-09-20 17:24:25 +00:00
{
2006-03-20 17:12:09 +00:00
obj - > ctype . powerup_info . count = 1 ;
2015-09-20 17:24:25 +00:00
obj - > ctype . powerup_info . flags = 0 ;
obj - > ctype . powerup_info . creation_time = GameTime64 ;
}
2006-03-20 17:12:09 +00:00
// Init physics info for this object
2020-08-10 03:45:13 +00:00
if ( obj - > movement_source = = object : : movement_type : : physics ) {
2015-12-22 04:18:51 +00:00
obj - > mtype . phys_info = { } ;
2006-03-20 17:12:09 +00:00
}
if ( obj - > render_type = = RT_POLYOBJ )
2015-09-20 17:24:25 +00:00
{
obj - > rtype . pobj_info . subobj_flags = 0 ;
2006-03-20 17:12:09 +00:00
obj - > rtype . pobj_info . tmap_override = - 1 ;
2015-09-20 17:24:25 +00:00
obj - > rtype . pobj_info . alt_textures = 0 ;
}
2006-03-20 17:12:09 +00:00
obj - > shields = 20 * F1_0 ;
2014-10-26 21:35:16 +00:00
{
2018-09-19 02:13:30 +00:00
const auto & & p = find_point_seg ( LevelSharedSegmentState , LevelUniqueSegmentState , pos , segnum ) ; //find correct segment
2014-10-26 21:35:16 +00:00
// Previously this was only an assert check. Now it is also
// checked at runtime.
segnum = p ;
}
2006-03-20 17:12:09 +00:00
2018-03-12 03:43:46 +00:00
obj_link_unchecked ( Objects . vmptr , obj , segnum ) ;
2006-03-20 17:12:09 +00:00
2020-08-10 03:45:13 +00:00
if ( obj - > control_source = = object : : control_type : : explosion )
2013-12-26 22:21:16 +00:00
obj - > ctype . expl_info . next_attach = obj - > ctype . expl_info . prev_attach = obj - > ctype . expl_info . attach_parent = object_none ;
2006-03-20 17:12:09 +00:00
if ( obj - > type = = OBJ_DEBRIS )
2018-12-30 00:43:57 +00:00
+ + LevelUniqueObjectState . Debris_object_count ;
2014-01-10 04:00:34 +00:00
return obj ;
2006-03-20 17:12:09 +00:00
}
2022-07-02 18:10:45 +00:00
imobjptridx_t obj_weapon_create ( d_level_unique_object_state & LevelUniqueObjectState , const d_level_shared_segment_state & LevelSharedSegmentState , d_level_unique_segment_state & LevelUniqueSegmentState , const weapon_info_array & Weapon_info , const unsigned id , const vmsegptridx_t segnum , const vms_vector & pos , const fix size , const render_type_t rtype )
{
constexpr auto ctype = object : : control_type : : weapon ;
constexpr auto mtype = object : : movement_type : : physics ;
constexpr const vms_matrix * orient = nullptr ;
const auto & & objp = obj_create ( LevelUniqueObjectState , LevelSharedSegmentState , LevelUniqueSegmentState , OBJ_WEAPON , id , segnum , pos , orient , size , ctype , mtype , rtype ) ;
if ( objp = = object_none )
return objp ;
auto & obj = * objp ;
// Set (or not) persistent bit in phys_info.
assert ( obj . control_source = = object : : control_type : : weapon ) ;
if ( Weapon_info [ id ] . persistent ! = weapon_info : : persistence_flag : : terminate_on_impact )
obj . mtype . phys_info . flags | = PF_PERSISTENT ;
obj . ctype . laser_info . creation_time = GameTime64 ;
obj . ctype . laser_info . clear_hitobj ( ) ;
obj . ctype . laser_info . multiplier = F1_0 ;
# if defined(DXX_BUILD_DESCENT_II)
obj . ctype . laser_info . last_afterburner_time = 0 ;
# endif
return objp ;
}
2006-03-20 17:12:09 +00:00
//create a copy of an object. returns new object number
2018-12-03 04:25:11 +00:00
imobjptridx_t obj_create_copy ( const object & srcobj , const vmsegptridx_t newsegnum )
2006-03-20 17:12:09 +00:00
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
2006-03-20 17:12:09 +00:00
// Find next free object
2018-12-30 00:43:57 +00:00
const auto & & obj = obj_allocate ( LevelUniqueObjectState ) ;
2006-03-20 17:12:09 +00:00
2014-01-12 19:14:16 +00:00
if ( obj = = object_none )
2016-10-29 23:16:15 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
2019-07-07 22:00:02 +00:00
const auto signature = obj - > signature ;
2016-10-29 23:16:18 +00:00
* obj = srcobj ;
2006-03-20 17:12:09 +00:00
2018-03-12 03:43:46 +00:00
obj_link_unchecked ( Objects . vmptr , obj , newsegnum ) ;
2020-08-10 03:45:13 +00:00
obj - > signature = next ( signature ) ;
2006-03-20 17:12:09 +00:00
//we probably should initialize sub-structures here
2014-01-12 19:14:16 +00:00
return obj ;
2006-03-20 17:12:09 +00:00
}
//remove object from the world
2018-12-30 00:43:57 +00:00
void obj_delete ( d_level_unique_object_state & LevelUniqueObjectState , segment_array & Segments , const vmobjptridx_t obj )
2006-03-20 17:12:09 +00:00
{
2018-12-30 00:43:57 +00:00
auto & Objects = LevelUniqueObjectState . get_objects ( ) ;
2006-03-20 17:12:09 +00:00
Assert ( obj - > type ! = OBJ_NONE ) ;
Assert ( obj ! = ConsoleObject ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-12-03 03:26:49 +00:00
if ( obj - > type = = OBJ_WEAPON & & get_weapon_id ( obj ) = = weapon_id_type : : GUIDEDMISS_ID & & obj - > ctype . laser_info . parent_type = = OBJ_PLAYER )
2006-10-02 13:16:09 +00:00
{
2018-09-09 01:00:39 +00:00
const auto pnum = get_player_id ( Objects . vcptr ( obj - > ctype . laser_info . parent_num ) ) ;
2018-12-30 00:43:57 +00:00
const auto & & gimobj = LevelUniqueObjectState . Guided_missile . get_player_active_guided_missile ( Objects . vmptridx , pnum ) ;
2018-10-08 03:58:48 +00:00
if ( gimobj = = obj )
{
2018-12-30 00:43:57 +00:00
LevelUniqueObjectState . Guided_missile . clear_player_active_guided_missile ( pnum ) ;
2018-10-08 03:58:48 +00:00
if ( pnum = = Player_num )
{
if ( ! PlayerCfg . GuidedInBigWindow )
2021-01-17 22:23:23 +00:00
do_cockpit_window_view ( gauge_inset_window_view : : secondary , weapon_box_user : : post_missile_static ) ;
2018-10-08 03:58:48 +00:00
if ( Newdemo_state = = ND_STATE_RECORDING )
newdemo_record_guided_end ( ) ;
}
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
if ( obj = = Viewer ) //deleting the viewer?
Viewer = ConsoleObject ; //..make the player the viewer
if ( obj - > flags & OF_ATTACHED ) //detach this from object
2018-03-12 03:43:46 +00:00
obj_detach_one ( Objects , obj ) ;
2006-03-20 17:12:09 +00:00
2013-12-26 22:21:16 +00:00
if ( obj - > attached_obj ! = object_none ) //detach all objects from this
2018-03-12 03:43:46 +00:00
obj_detach_all ( Objects , obj ) ;
2006-03-20 17:12:09 +00:00
if ( obj - > type = = OBJ_DEBRIS )
2018-12-30 00:43:57 +00:00
- - LevelUniqueObjectState . Debris_object_count ;
2006-03-20 17:12:09 +00:00
2020-08-10 03:45:13 +00:00
if ( obj - > movement_source = = object : : movement_type : : physics & & ( obj - > mtype . phys_info . flags & PF_STICK ) )
2018-08-04 17:52:57 +00:00
LevelUniqueStuckObjectState . remove_stuck_object ( obj ) ;
2018-03-12 03:43:46 +00:00
obj_unlink ( Objects . vmptr , Segments . vmptr , obj ) ;
2019-07-07 22:00:02 +00:00
const auto signature = obj - > signature ;
2016-11-06 17:12:03 +00:00
DXX_POISON_VAR ( * obj , 0xfa ) ;
2006-03-20 17:12:09 +00:00
obj - > type = OBJ_NONE ; //unused!
2019-07-07 22:00:02 +00:00
/* Preserve signature across the poison value. When the object slot
* is reused , the allocator will need the old signature so that the
* new one can be derived from it . No other sites should read it
* until that happens .
*/
obj - > signature = signature ;
2018-12-30 00:43:57 +00:00
obj_free ( LevelUniqueObjectState , obj ) ;
2006-03-20 17:12:09 +00:00
}
# define DEATH_SEQUENCE_LENGTH (F1_0*5)
# define DEATH_SEQUENCE_EXPLODE_TIME (F1_0*2)
2022-07-09 13:39:29 +00:00
object * Dead_player_camera ; // Object index of object watching deader.
2015-12-22 04:18:50 +00:00
}
namespace dcx {
player_dead_state Player_dead_state = player_dead_state : : no ; // If !0, then player is dead, but game continues so he can watch.
2022-07-09 13:39:29 +00:00
namespace {
2016-12-10 17:51:09 +00:00
static int Player_flags_save ;
static fix Camera_to_player_dist_goal = F1_0 * 4 ;
2020-08-06 03:47:56 +00:00
static typename object : : control_type Control_type_save ;
2020-05-02 21:18:43 +00:00
static render_type_t Render_type_save ;
2022-07-09 13:39:29 +00:00
}
2019-07-07 22:00:02 +00:00
unsigned laser_parent_is_matching_signature ( const laser_parent & l , const object_base & o )
{
if ( l . parent_type ! = o . type )
return 0 ;
return l . parent_signature = = o . signature ;
}
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 {
2022-04-17 22:27:19 +00:00
2006-03-20 17:12:09 +00:00
// ------------------------------------------------------------------------------------------------------------------
void dead_player_end ( void )
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vmobjptridx = Objects . vmptridx ;
2015-12-15 04:09:35 +00:00
if ( Player_dead_state = = player_dead_state : : no )
2006-03-20 17:12:09 +00:00
return ;
if ( Newdemo_state = = ND_STATE_RECORDING )
newdemo_record_restore_cockpit ( ) ;
2015-12-15 04:09:35 +00:00
Player_dead_state = player_dead_state : : no ;
2018-12-30 00:43:57 +00:00
obj_delete ( LevelUniqueObjectState , Segments , vmobjptridx ( Dead_player_camera ) ) ;
2006-03-20 17:12:09 +00:00
Dead_player_camera = NULL ;
2010-01-28 00:04:29 +00:00
select_cockpit ( PlayerCfg . CockpitMode [ 0 ] ) ;
2006-03-20 17:12:09 +00:00
Viewer = Viewer_save ;
ConsoleObject - > type = OBJ_PLAYER ;
ConsoleObject - > flags = Player_flags_save ;
2020-08-06 03:47:56 +00:00
assert ( Control_type_save = = object : : control_type : : flying | | Control_type_save = = object : : control_type : : slew ) ;
2006-03-20 17:12:09 +00:00
2020-08-10 03:45:13 +00:00
ConsoleObject - > control_source = Control_type_save ;
2006-03-20 17:12:09 +00:00
ConsoleObject - > render_type = Render_type_save ;
2016-10-02 00:34:40 +00:00
auto & player_info = ConsoleObject - > ctype . player_info ;
player_info . powerup_flags & = ~ PLAYER_FLAGS_INVULNERABLE ;
2016-12-10 17:51:09 +00:00
player_info . Player_eggs_dropped = false ;
2006-03-20 17:12:09 +00:00
}
2022-04-17 22:27:19 +00:00
namespace {
2006-03-20 17:12:09 +00:00
// ------------------------------------------------------------------------------------------------------------------
// Camera is less than size of player away from
2015-07-12 01:04:21 +00:00
static void set_camera_pos ( vms_vector & camera_pos , const vcobjptridx_t objp )
2006-03-20 17:12:09 +00:00
{
int count = 0 ;
fix camera_player_dist ;
fix far_scale ;
2014-11-01 14:57:37 +00:00
camera_player_dist = vm_vec_dist_quick ( camera_pos , objp - > pos ) ;
2006-03-20 17:12:09 +00:00
2015-12-22 04:18:50 +00:00
if ( camera_player_dist < Camera_to_player_dist_goal ) {
2006-03-20 17:12:09 +00:00
// Camera is too close to player object, so move it away.
fvi_info hit_data ;
2014-11-01 14:57:37 +00:00
auto player_camera_vec = vm_vec_sub ( camera_pos , objp - > pos ) ;
2006-03-20 17:12:09 +00:00
if ( ( player_camera_vec . x = = 0 ) & & ( player_camera_vec . y = = 0 ) & & ( player_camera_vec . z = = 0 ) )
player_camera_vec . x + = F1_0 / 16 ;
2022-06-05 17:44:52 +00:00
auto hit_type = fvi_hit_type : : Wall ;
2006-03-20 17:12:09 +00:00
far_scale = F1_0 ;
2022-06-05 17:44:52 +00:00
while ( hit_type ! = fvi_hit_type : : None & & count + + < 6 )
2022-05-24 02:32:58 +00:00
{
2014-09-28 21:11:04 +00:00
vm_vec_normalize_quick ( player_camera_vec ) ;
2014-09-28 21:11:05 +00:00
vm_vec_scale ( player_camera_vec , Camera_to_player_dist_goal ) ;
2006-03-20 17:12:09 +00:00
2014-10-30 03:11:06 +00:00
const auto closer_p1 = vm_vec_add ( objp - > pos , player_camera_vec ) ; // This is the actual point we want to put the camera at.
2014-09-28 21:11:05 +00:00
vm_vec_scale ( player_camera_vec , far_scale ) ; // ...but find a point 50% further away...
2014-10-30 03:11:06 +00:00
const auto local_p1 = vm_vec_add ( objp - > pos , player_camera_vec ) ; // ...so we won't have to do as many cuts.
2006-03-20 17:12:09 +00:00
2022-07-09 13:39:29 +00:00
hit_type = find_vector_intersection ( fvi_query {
objp - > pos ,
local_p1 ,
fvi_query : : unused_ignore_obj_list ,
2022-07-09 13:39:29 +00:00
fvi_query : : unused_LevelUniqueObjectState ,
2022-07-09 13:39:29 +00:00
fvi_query : : unused_Robot_info ,
2022-07-09 13:39:29 +00:00
0 ,
objp ,
} , objp - > segnum , 0 , hit_data ) ;
2022-06-05 17:44:52 +00:00
if ( hit_type = = fvi_hit_type : : None )
2022-05-24 02:32:58 +00:00
{
2014-11-01 14:57:37 +00:00
camera_pos = closer_p1 ;
2006-03-20 17:12:09 +00:00
} else {
2014-10-02 03:02:38 +00:00
make_random_vector ( player_camera_vec ) ;
2006-03-20 17:12:09 +00:00
far_scale = 3 * F1_0 / 2 ;
}
}
}
}
2022-04-17 22:27:19 +00:00
}
2006-03-20 17:12:09 +00:00
// ------------------------------------------------------------------------------------------------------------------
2022-07-09 13:39:29 +00:00
window_event_result dead_player_frame ( const d_robot_info_array & Robot_info )
2006-03-20 17:12:09 +00:00
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vcobjptridx = Objects . vcptridx ;
auto & vmobjptr = Objects . vmptr ;
auto & vmobjptridx = Objects . vmptridx ;
2008-04-13 13:14:19 +00:00
static fix time_dead = 0 ;
2006-03-20 17:12:09 +00:00
2015-12-15 04:09:35 +00:00
if ( Player_dead_state ! = player_dead_state : : no )
2008-04-13 13:14:19 +00:00
{
time_dead + = FrameTime ;
2006-03-20 17:12:09 +00:00
// If unable to create camera at time of death, create now.
if ( Dead_player_camera = = Viewer_save ) {
2015-09-15 02:48:04 +00:00
const auto & player = get_local_plrobj ( ) ;
2022-07-02 18:10:45 +00:00
const auto & & objnum = obj_create ( LevelUniqueObjectState , LevelSharedSegmentState , LevelUniqueSegmentState , OBJ_CAMERA , 0 , vmsegptridx ( player . segnum ) , player . pos , & player . orient , 0 , object : : control_type : : None , object : : movement_type : : None , RT_NONE ) ;
2006-03-20 17:12:09 +00:00
2013-12-26 22:21:16 +00:00
if ( objnum ! = object_none )
2014-09-08 03:24:48 +00:00
Viewer = Dead_player_camera = objnum ;
2006-03-20 17:12:09 +00:00
else {
Int3 ( ) ;
}
}
ConsoleObject - > mtype . phys_info . rotvel . x = max ( 0 , DEATH_SEQUENCE_EXPLODE_TIME - time_dead ) / 4 ;
ConsoleObject - > mtype . phys_info . rotvel . y = max ( 0 , DEATH_SEQUENCE_EXPLODE_TIME - time_dead ) / 2 ;
ConsoleObject - > mtype . phys_info . rotvel . z = max ( 0 , DEATH_SEQUENCE_EXPLODE_TIME - time_dead ) / 3 ;
Camera_to_player_dist_goal = min ( time_dead * 8 , F1_0 * 20 ) + ConsoleObject - > size ;
2015-07-12 01:04:21 +00:00
set_camera_pos ( Dead_player_camera - > pos , vcobjptridx ( ConsoleObject ) ) ;
2006-03-20 17:12:09 +00:00
// the following line uncommented by WraithX, 4-12-00
if ( time_dead < DEATH_SEQUENCE_EXPLODE_TIME + F1_0 * 2 )
{
2014-10-29 03:24:31 +00:00
const auto fvec = vm_vec_sub ( ConsoleObject - > pos , Dead_player_camera - > pos ) ;
2014-10-01 02:28:42 +00:00
vm_vector_2_matrix ( Dead_player_camera - > orient , fvec , nullptr , nullptr ) ;
2006-03-20 17:12:09 +00:00
Dead_player_camera - > mtype . phys_info = ConsoleObject - > mtype . phys_info ;
// the following "if" added by WraithX to get rid of camera "wiggle"
2015-11-06 03:51:10 +00:00
Dead_player_camera - > mtype . phys_info . flags & = ~ PF_WIGGLE ;
// end "if" added by WraithX, 4/13/00
2006-03-20 17:12:09 +00:00
// the following line uncommented by WraithX, 4-12-00
}
else
{
// the following line uncommented by WraithX, 4-11-00
2020-08-10 03:45:13 +00:00
Dead_player_camera - > movement_source = object : : movement_type : : physics ;
2006-03-20 17:12:09 +00:00
//Dead_player_camera->mtype.phys_info.rotvel.y = F1_0/8;
// the following line uncommented by WraithX, 4-12-00
}
// end addition by WX
if ( time_dead > DEATH_SEQUENCE_EXPLODE_TIME ) {
2016-01-09 16:38:10 +00:00
if ( Player_dead_state ! = player_dead_state : : exploded )
{
2016-12-10 17:51:09 +00:00
auto & player_info = get_local_plrobj ( ) . ctype . player_info ;
2020-05-02 21:18:42 +00:00
const auto hostages_lost = std : : exchange ( player_info . mission . hostages_on_board , 0 ) ;
2006-03-20 17:12:09 +00:00
2015-11-06 03:51:10 +00:00
if ( hostages_lost > 1 )
2016-03-09 02:18:52 +00:00
HUD_init_message ( HM_DEFAULT , TXT_SHIP_DESTROYED_2 , hostages_lost ) ;
2012-05-16 12:39:31 +00:00
else
2015-11-06 03:51:10 +00:00
HUD_init_message_literal ( HM_DEFAULT , hostages_lost = = 1 ? TXT_SHIP_DESTROYED_1 : TXT_SHIP_DESTROYED_0 ) ;
2006-03-20 17:12:09 +00:00
2016-01-09 16:38:10 +00:00
Player_dead_state = player_dead_state : : exploded ;
2006-03-20 17:12:09 +00:00
2017-06-10 03:31:02 +00:00
const auto cobjp = vmobjptridx ( ConsoleObject ) ;
2014-12-23 04:20:27 +00:00
drop_player_eggs ( cobjp ) ;
2016-12-10 17:51:09 +00:00
player_info . Player_eggs_dropped = true ;
2006-03-20 17:12:09 +00:00
if ( Game_mode & GM_MULTI )
{
2013-12-16 22:59:31 +00:00
multi_send_player_deres ( deres_explode ) ;
2006-03-20 17:12:09 +00:00
}
2022-07-09 13:39:29 +00:00
explode_badass_player ( Robot_info , cobjp ) ;
2006-03-20 17:12:09 +00:00
//is this next line needed, given the badass call above?
2022-07-09 13:39:29 +00:00
explode_object ( LevelUniqueObjectState , Robot_info , LevelSharedSegmentState , LevelUniqueSegmentState , cobjp , 0 ) ;
2006-03-20 17:12:09 +00:00
ConsoleObject - > flags & = ~ OF_SHOULD_BE_DEAD ; //don't really kill player
ConsoleObject - > render_type = RT_NONE ; //..just make him disappear
ConsoleObject - > type = OBJ_GHOST ; //..and kill intersections
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2016-10-02 00:34:40 +00:00
player_info . powerup_flags & = ~ PLAYER_FLAGS_HEADLIGHT_ON ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
} else {
if ( d_rand ( ) < FrameTime * 4 ) {
if ( Game_mode & GM_MULTI )
multi_send_create_explosion ( Player_num ) ;
2017-06-10 03:31:02 +00:00
create_small_fireball_on_object ( vmobjptridx ( ConsoleObject ) , F1_0 , 1 ) ;
2006-03-20 17:12:09 +00:00
}
}
2019-08-15 01:34:22 +00:00
if ( GameViewUniqueState . Death_sequence_aborted )
2013-03-03 01:03:33 +00:00
{
2016-12-10 17:51:09 +00:00
auto & player_info = get_local_plrobj ( ) . ctype . player_info ;
if ( ! player_info . Player_eggs_dropped ) {
player_info . Player_eggs_dropped = true ;
2017-06-10 03:31:02 +00:00
drop_player_eggs ( vmobjptridx ( ConsoleObject ) ) ;
2006-03-20 17:12:09 +00:00
if ( Game_mode & GM_MULTI )
{
2013-12-16 22:59:31 +00:00
multi_send_player_deres ( deres_explode ) ;
2006-03-20 17:12:09 +00:00
}
}
2016-12-24 08:50:29 +00:00
return DoPlayerDead ( ) ; //kill_player();
2006-03-20 17:12:09 +00:00
}
}
2008-06-04 19:30:43 +00:00
else
time_dead = 0 ;
2016-12-24 08:50:29 +00:00
return window_event_result : : handled ;
2006-03-20 17:12:09 +00:00
}
2022-04-17 22:27:19 +00:00
namespace {
2006-03-20 17:12:09 +00:00
// ------------------------------------------------------------------------------------------------------------------
2016-10-02 00:34:40 +00:00
static void start_player_death_sequence ( object & player )
2006-03-20 17:12:09 +00:00
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
# if defined(DXX_BUILD_DESCENT_II)
auto & vmobjptr = Objects . vmptr ;
# endif
auto & vmobjptridx = Objects . vmptridx ;
2016-04-23 17:59:47 +00:00
assert ( & player = = ConsoleObject ) ;
2015-12-15 04:09:35 +00:00
if ( Player_dead_state ! = player_dead_state : : no | |
Dead_player_camera ! = NULL | |
2022-07-16 15:26:12 +00:00
( ( Game_mode & GM_MULTI ) & & get_local_player ( ) . connected ! = player_connection_status : : playing ) )
2006-03-20 17:12:09 +00:00
return ;
//Assert(Dead_player_camera == NULL);
reset_rear_view ( ) ;
if ( ! ( Game_mode & GM_MULTI ) )
HUD_clear_messages ( ) ;
2019-08-15 01:34:22 +00:00
GameViewUniqueState . Death_sequence_aborted = 0 ;
2006-03-20 17:12:09 +00:00
if ( Game_mode & GM_MULTI )
{
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2012-06-07 23:41:31 +00:00
// If Hoard, increase number of orbs by 1. Only if you haven't killed yourself. This prevents cheating
2013-08-11 16:03:47 +00:00
if ( game_mode_hoard ( ) )
2016-07-03 00:54:15 +00:00
{
2016-10-02 00:34:40 +00:00
auto & player_info = player . ctype . player_info ;
2017-03-18 18:07:36 +00:00
auto & proximity = player_info . hoard . orbs ;
if ( proximity < player_info . max_hoard_orbs )
2015-07-25 23:10:46 +00:00
{
2019-03-03 00:31:08 +00:00
const auto is_bad_kill = [ & vmobjptr ] {
2015-11-07 21:55:59 +00:00
auto & lplr = get_local_player ( ) ;
auto & lplrobj = get_local_plrobj ( ) ;
const auto killer_objnum = lplrobj . ctype . player_info . killer_objnum ;
if ( killer_objnum = = lplr . objnum )
2015-07-25 23:10:46 +00:00
/* Self kill */
return true ;
if ( killer_objnum = = object_none )
/* Non-player kill */
return true ;
2017-06-10 03:31:02 +00:00
const auto & & killer_objp = vmobjptr ( killer_objnum ) ;
2015-11-26 02:56:56 +00:00
if ( killer_objp - > type ! = OBJ_PLAYER )
return true ;
2015-07-25 23:10:46 +00:00
if ( ! ( Game_mode & GM_TEAM ) )
return false ;
2015-11-26 02:56:56 +00:00
return get_team ( Player_num ) = = get_team ( get_player_id ( killer_objp ) ) ;
2015-07-25 23:10:46 +00:00
} ;
if ( ! is_bad_kill ( ) )
2016-07-03 00:54:15 +00:00
+ + proximity ;
2015-07-25 23:10:46 +00:00
}
2016-07-03 00:54:15 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2017-06-10 03:31:02 +00:00
multi_send_kill ( vmobjptridx ( get_local_player ( ) . objnum ) ) ;
2006-03-20 17:12:09 +00:00
}
PaletteRedAdd = 40 ;
2015-12-15 04:09:35 +00:00
Player_dead_state = player_dead_state : : yes ;
2006-03-20 17:12:09 +00:00
2016-04-23 17:59:47 +00:00
vm_vec_zero ( player . mtype . phys_info . rotthrust ) ;
vm_vec_zero ( player . mtype . phys_info . thrust ) ;
2006-03-20 17:12:09 +00:00
2022-07-02 18:10:45 +00:00
const auto & & objnum = obj_create ( LevelUniqueObjectState , LevelSharedSegmentState , LevelUniqueSegmentState , OBJ_CAMERA , 0 , vmsegptridx ( player . segnum ) , player . pos , & player . orient , 0 , object : : control_type : : None , object : : movement_type : : None , RT_NONE ) ;
2006-03-20 17:12:09 +00:00
Viewer_save = Viewer ;
2013-12-26 22:21:16 +00:00
if ( objnum ! = object_none )
2014-09-08 03:24:48 +00:00
Viewer = Dead_player_camera = objnum ;
2006-03-20 17:12:09 +00:00
else {
Int3 ( ) ;
2018-09-25 03:44:09 +00:00
Dead_player_camera = ConsoleObject ;
2006-03-20 17:12:09 +00:00
}
2022-02-12 18:57:12 +00:00
select_cockpit ( cockpit_mode_t : : letterbox ) ;
2006-03-20 17:12:09 +00:00
if ( Newdemo_state = = ND_STATE_RECORDING )
newdemo_record_letterbox ( ) ;
2016-04-23 17:59:47 +00:00
Player_flags_save = player . flags ;
2020-08-10 03:45:13 +00:00
Control_type_save = player . control_source ;
2016-04-23 17:59:47 +00:00
Render_type_save = player . render_type ;
2006-03-20 17:12:09 +00:00
2016-04-23 17:59:47 +00:00
player . flags & = ~ OF_SHOULD_BE_DEAD ;
2006-03-20 17:12:09 +00:00
// Players[Player_num].flags |= PLAYER_FLAGS_INVULNERABLE;
2020-08-10 03:45:13 +00:00
player . control_source = object : : control_type : : None ;
2006-03-20 17:12:09 +00:00
PALETTE_FLASH_SET ( 0 , 0 , 0 ) ;
}
// ------------------------------------------------------------------------------------------------------------------
2014-11-23 04:36:58 +00:00
static void obj_delete_all_that_should_be_dead ( )
2006-03-20 17:12:09 +00:00
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vmobjptridx = Objects . vmptridx ;
2013-12-29 04:28:07 +00:00
objnum_t local_dead_player_object = object_none ;
2006-03-20 17:12:09 +00:00
// Move all objects
2017-06-10 03:31:02 +00:00
range_for ( const auto & & objp , vmobjptridx )
2014-10-12 23:05:46 +00:00
{
2006-03-20 17:12:09 +00:00
if ( ( objp - > type ! = OBJ_NONE ) & & ( objp - > flags & OF_SHOULD_BE_DEAD ) ) {
Assert ( ! ( objp - > type = = OBJ_FIREBALL & & objp - > ctype . expl_info . delete_time ! = - 1 ) ) ;
if ( objp - > type = = OBJ_PLAYER ) {
2013-10-07 23:52:33 +00:00
if ( get_player_id ( objp ) = = Player_num ) {
2013-12-26 22:21:16 +00:00
if ( local_dead_player_object = = object_none ) {
2006-03-20 17:12:09 +00:00
start_player_death_sequence ( objp ) ;
2013-12-24 04:53:59 +00:00
local_dead_player_object = objp ;
2006-03-20 17:12:09 +00:00
} else
Int3 ( ) ; // Contact Mike: Illegal, killed player twice in this frame!
// Ok to continue, won't start death sequence again!
// kill_player();
}
} else {
2018-12-30 00:43:57 +00:00
obj_delete ( LevelUniqueObjectState , Segments , objp ) ;
2006-03-20 17:12:09 +00:00
}
}
}
}
2022-04-17 22:27:19 +00:00
}
2006-03-20 17:12:09 +00:00
//when an object has moved into a new segment, this function unlinks it
//from its old segment, and links it into the new segment
2018-03-12 03:43:46 +00:00
void obj_relink ( fvmobjptr & vmobjptr , fvmsegptr & vmsegptr , const vmobjptridx_t objnum , const vmsegptridx_t newsegnum )
2006-03-20 17:12:09 +00:00
{
2018-03-12 03:43:46 +00:00
obj_unlink ( vmobjptr , vmsegptr , objnum ) ;
2018-03-12 03:43:46 +00:00
obj_link_unchecked ( vmobjptr , objnum , newsegnum ) ;
2006-03-20 17:12:09 +00:00
}
2012-04-17 09:15:09 +00:00
// for getting out of messed up linking situations (i.e. caused by demo playback)
void obj_relink_all ( void )
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
2020-08-10 03:45:13 +00:00
for ( unique_segment & useg : vmsegptr )
2015-06-13 22:42:18 +00:00
{
2020-08-10 03:45:13 +00:00
useg . objects = object_none ;
2015-06-13 22:42:18 +00:00
}
2020-08-10 03:45:13 +00:00
2019-03-03 00:31:08 +00:00
range_for ( const auto & & obj , Objects . vmptridx )
2014-10-12 23:05:46 +00:00
{
2012-04-17 09:15:09 +00:00
if ( obj - > type ! = OBJ_NONE )
{
2016-11-06 17:12:03 +00:00
auto segnum = obj - > segnum ;
2012-04-17 09:15:09 +00:00
if ( segnum > Highest_segment_index )
2013-12-26 22:21:16 +00:00
segnum = segment_first ;
2018-03-12 03:43:46 +00:00
obj_link_unchecked ( Objects . vmptr , obj , Segments . vmptridx ( segnum ) ) ;
2012-04-17 09:15:09 +00:00
}
2014-10-12 23:05:46 +00:00
}
2012-04-17 09:15:09 +00:00
}
2022-04-17 22:27:19 +00:00
namespace {
2006-03-20 17:12:09 +00:00
//process a continuously-spinning object
2016-04-23 17:59:47 +00:00
static void spin_object ( object_base & obj )
2006-03-20 17:12:09 +00:00
{
vms_angvec rotangs ;
2020-08-10 03:45:13 +00:00
assert ( obj . movement_source = = object : : movement_type : : spinning ) ;
2006-03-20 17:12:09 +00:00
2016-04-23 17:59:47 +00:00
const fix frametime = FrameTime ;
rotangs . p = fixmul ( obj . mtype . spin_rate . x , frametime ) ;
rotangs . h = fixmul ( obj . mtype . spin_rate . y , frametime ) ;
rotangs . b = fixmul ( obj . mtype . spin_rate . z , frametime ) ;
2006-03-20 17:12:09 +00:00
2015-02-05 03:03:51 +00:00
const auto & & rotmat = vm_angles_2_matrix ( rotangs ) ;
2016-04-23 17:59:47 +00:00
obj . orient = vm_matrix_x_matrix ( obj . orient , rotmat ) ;
check_and_fix_matrix ( obj . orient ) ;
2006-03-20 17:12:09 +00:00
}
2022-04-17 22:27:19 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2022-07-23 20:58:10 +00:00
imobjidx_t d_guided_missile_indices : : get_player_active_guided_missile ( const playernum_t pnum ) const
2018-10-08 03:58:48 +00:00
{
return operator [ ] ( pnum ) ;
}
/* Place debug checks out of line so that they are shared among the
* template instantiations .
*/
bool d_guided_missile_indices : : debug_check_current_object ( const object_base & obj )
{
assert ( obj . type = = OBJ_WEAPON ) ;
const auto gmid = get_weapon_id ( obj ) ;
if ( obj . type ! = OBJ_WEAPON )
return false ;
assert ( gmid = = weapon_id_type : : GUIDEDMISS_ID ) ;
if ( gmid ! = weapon_id_type : : GUIDEDMISS_ID )
return false ;
return true ;
}
template < typename R , typename F >
2022-07-23 20:58:10 +00:00
R d_guided_missile_indices : : get_player_active_guided_missile_tmpl ( F & fobjptr , const playernum_t pnum ) const
2018-10-08 03:58:48 +00:00
{
const auto gmidx = get_player_active_guided_missile ( pnum ) ;
if ( gmidx = = object_none )
return object_none ;
auto & & gmobj = fobjptr ( gmidx ) ;
if ( ! debug_check_current_object ( gmobj ) )
return object_none ;
return gmobj ;
}
2022-07-23 20:58:10 +00:00
imobjptr_t d_guided_missile_indices : : get_player_active_guided_missile ( fvmobjptr & vmobjptr , const playernum_t pnum ) const
2018-10-08 03:58:48 +00:00
{
return this - > template get_player_active_guided_missile_tmpl < imobjptr_t > ( vmobjptr , pnum ) ;
}
2022-07-23 20:58:10 +00:00
imobjptridx_t d_guided_missile_indices : : get_player_active_guided_missile ( fvmobjptridx & vmobjptridx , const playernum_t pnum ) const
2018-10-08 03:58:48 +00:00
{
return this - > template get_player_active_guided_missile_tmpl < imobjptridx_t > ( vmobjptridx , pnum ) ;
}
2022-07-23 20:58:10 +00:00
void d_guided_missile_indices : : set_player_active_guided_missile ( const vmobjidx_t obji , const playernum_t pnum )
2018-10-08 03:58:48 +00:00
{
auto & i = operator [ ] ( pnum ) ;
i = obji ;
}
2022-07-23 20:58:10 +00:00
void d_guided_missile_indices : : clear_player_active_guided_missile ( const playernum_t pnum )
2018-10-08 03:58:48 +00:00
{
auto & i = operator [ ] ( pnum ) ;
i = object_none ;
}
2006-03-20 17:12:09 +00:00
int Drop_afterburner_blob_flag ; //ugly hack
//see if wall is volatile, and if so, cause damage to player
//returns true if player is in lava
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2022-04-17 22:27:19 +00:00
namespace {
2006-03-20 17:12:09 +00:00
//--------------------------------------------------------------------
//move an object for the current frame
2022-07-09 13:39:29 +00:00
static window_event_result object_move_one ( const d_level_shared_robot_info_state & LevelSharedRobotInfoState , const vmobjptridx_t obj , control_info & Controls )
2006-03-20 17:12:09 +00:00
{
2020-05-17 23:35:25 +00:00
# if defined(DXX_BUILD_DESCENT_II)
auto & LevelSharedVertexState = LevelSharedSegmentState . get_vertex_state ( ) ;
auto & Vertices = LevelSharedVertexState . get_vertices ( ) ;
# endif
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vmobjptr = Objects . vmptr ;
2015-07-12 01:04:21 +00:00
const auto previous_segment = obj - > segnum ;
2016-12-24 08:50:29 +00:00
auto result = window_event_result : : handled ;
2006-03-20 17:12:09 +00:00
2019-12-22 05:34:08 +00:00
const auto obj_previous_position = obj - > pos ; // Save the current position
2006-03-20 17:12:09 +00:00
2013-10-07 23:52:33 +00:00
if ( ( obj - > type = = OBJ_PLAYER ) & & ( Player_num = = get_player_id ( obj ) ) ) {
2017-06-10 03:31:02 +00:00
const auto & & segp = vmsegptr ( obj - > segnum ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2016-01-09 16:38:13 +00:00
if ( game_mode_capture_flag ( ) )
2018-12-30 00:43:57 +00:00
fuelcen_check_for_goal ( obj , segp ) ;
2016-01-09 16:38:13 +00:00
else if ( game_mode_hoard ( ) )
2018-12-30 00:43:57 +00:00
fuelcen_check_for_hoard_goal ( obj , segp ) ;
2006-03-20 17:12:09 +00:00
# endif
2018-12-30 00:43:57 +00:00
auto & player_info = obj - > ctype . player_info ;
2016-09-11 18:49:13 +00:00
auto & energy = player_info . energy ;
2016-07-03 00:54:16 +00:00
const fix fuel = fuelcen_give_fuel ( segp , INITIAL_ENERGY - energy ) ;
2006-03-20 17:12:09 +00:00
if ( fuel > 0 ) {
2016-07-03 00:54:16 +00:00
energy + = fuel ;
2006-03-20 17:12:09 +00:00
}
2022-06-05 17:44:52 +00:00
auto & pl_shields = obj - > shields ;
2016-07-03 00:54:16 +00:00
const fix shields = repaircen_give_shields ( segp , INITIAL_SHIELDS - pl_shields ) ;
2006-03-20 17:12:09 +00:00
if ( shields > 0 ) {
2016-07-03 00:54:16 +00:00
pl_shields + = shields ;
2006-03-20 17:12:09 +00:00
}
}
2017-09-30 18:00:15 +00:00
{
auto lifeleft = obj - > lifeleft ;
if ( lifeleft ! = IMMORTAL_TIME ) //if not immortal...
{
lifeleft - = FrameTime ; //...inevitable countdown towards death
# if defined(DXX_BUILD_DESCENT_II)
if ( obj - > type = = OBJ_MARKER )
{
if ( lifeleft < F1_0 * 1000 )
lifeleft + = F1_0 ; // Make sure this object doesn't go out.
}
# endif
obj - > lifeleft = lifeleft ;
}
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
Drop_afterburner_blob_flag = 0 ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2020-08-10 03:45:13 +00:00
switch ( obj - > control_source ) {
2006-03-20 17:12:09 +00:00
2020-08-06 03:47:56 +00:00
case object : : control_type : : None :
break ;
2006-03-20 17:12:09 +00:00
2020-08-06 03:47:56 +00:00
case object : : control_type : : flying :
2020-10-12 03:28:26 +00:00
read_flying_controls ( obj , Controls ) ;
2006-03-20 17:12:09 +00:00
break ;
2020-08-06 03:47:56 +00:00
case object : : control_type : : repaircen :
2017-06-07 02:44:54 +00:00
Int3 ( ) ;
// -- hey! these are no longer supported!! -- do_repair_sequence(obj);
break ;
2006-03-20 17:12:09 +00:00
2020-08-06 03:47:56 +00:00
case object : : control_type : : powerup :
2018-10-21 00:24:07 +00:00
do_powerup_frame ( Vclip , obj ) ;
2017-06-07 02:44:54 +00:00
break ;
2006-03-20 17:12:09 +00:00
2020-08-06 03:47:56 +00:00
case object : : control_type : : morph : //morph implies AI
2006-03-20 17:12:09 +00:00
do_morph_frame ( obj ) ;
//NOTE: FALLS INTO AI HERE!!!!
2022-01-09 15:25:42 +00:00
[[fallthrough]] ;
2006-03-20 17:12:09 +00:00
2020-08-06 03:47:56 +00:00
case object : : control_type : : ai :
//NOTE LINK TO object::control_type::morph ABOVE!!!
2016-12-24 08:50:29 +00:00
if ( Game_suspended & SUSP_ROBOTS ) return window_event_result : : ignored ;
2022-07-09 13:39:29 +00:00
do_ai_frame ( LevelSharedRobotInfoState , obj ) ;
2006-03-20 17:12:09 +00:00
break ;
2020-08-06 03:47:56 +00:00
case object : : control_type : : weapon :
2022-07-09 13:39:29 +00:00
Laser_do_weapon_sequence ( LevelSharedRobotInfoState . Robot_info , obj ) ;
2020-08-06 03:47:56 +00:00
break ;
case object : : control_type : : explosion :
2022-07-09 13:39:29 +00:00
do_explosion_sequence ( LevelSharedRobotInfoState . Robot_info , obj ) ;
2020-08-06 03:47:56 +00:00
break ;
2006-03-20 17:12:09 +00:00
2020-08-06 03:47:56 +00:00
case object : : control_type : : slew :
2019-02-02 18:36:39 +00:00
# ifdef RELEASE
2020-08-10 03:45:13 +00:00
obj - > control_source = object : : control_type : : None ;
2020-08-11 04:09:00 +00:00
con_printf ( CON_URGENT , DXX_STRINGIZE_FL ( __FILE__ , __LINE__ , " BUG: object %hu has control type object::control_type::slew, sig/type/id = %hu/%i/%i " ) , static_cast < objnum_t > ( obj ) , static_cast < uint16_t > ( obj - > signature ) , obj - > type , obj - > id ) ;
2019-02-02 18:36:39 +00:00
# else
2013-09-08 04:34:49 +00:00
if ( keyd_pressed [ KEY_PAD5 ] ) slew_stop ( ) ;
2006-03-20 17:12:09 +00:00
if ( keyd_pressed [ KEY_NUMLOCK ] ) {
2013-10-06 03:04:00 +00:00
slew_reset_orient ( ) ;
2006-03-20 17:12:09 +00:00
}
slew_frame ( 0 ) ; // Does velocity addition for us.
2019-02-02 18:36:39 +00:00
# endif
2006-03-20 17:12:09 +00:00
break ;
2020-08-06 03:47:56 +00:00
// case object::control_type::flythrough:
2006-03-20 17:12:09 +00:00
// do_flythrough(obj,0); // HACK:do_flythrough should operate on an object!!!!
// //check_object_seg(obj);
// return; // DON'T DO THE REST OF OBJECT STUFF SINCE THIS IS A SPECIAL CASE!!!
// break;
2020-08-06 03:47:56 +00:00
case object : : control_type : : debris :
2022-07-09 13:39:29 +00:00
do_debris_frame ( LevelSharedRobotInfoState . Robot_info , obj ) ;
2020-08-06 03:47:56 +00:00
break ;
2006-03-20 17:12:09 +00:00
2020-08-06 03:47:56 +00:00
case object : : control_type : : light :
break ; //doesn't do anything
2006-03-20 17:12:09 +00:00
2020-08-06 03:47:56 +00:00
case object : : control_type : : remote :
break ; //doesn't do anything
2006-03-20 17:12:09 +00:00
2020-08-06 03:47:56 +00:00
case object : : control_type : : cntrlcen :
2022-07-09 13:39:29 +00:00
do_controlcen_frame ( LevelSharedRobotInfoState . Robot_info , obj ) ;
2020-08-06 03:47:56 +00:00
break ;
2006-03-20 17:12:09 +00:00
default :
2020-08-10 03:45:13 +00:00
Error ( " Unknown control type %u in object %hu, sig/type/id = %i/%i/%i " , static_cast < unsigned > ( obj - > control_source ) , static_cast < objnum_t > ( obj ) , static_cast < uint16_t > ( obj - > signature ) , obj - > type , obj - > id ) ;
2006-03-20 17:12:09 +00:00
break ;
}
if ( obj - > lifeleft < 0 ) { // We died of old age
obj - > flags | = OF_SHOULD_BE_DEAD ;
2013-10-07 23:52:33 +00:00
if ( obj - > type = = OBJ_WEAPON & & Weapon_info [ get_weapon_id ( obj ) ] . damage_radius )
2022-07-09 13:39:29 +00:00
explode_badass_weapon ( LevelSharedRobotInfoState . Robot_info , obj , obj - > pos ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
else if ( obj - > type = = OBJ_ROBOT ) //make robots explode
2022-07-09 13:39:29 +00:00
explode_object ( LevelUniqueObjectState , LevelSharedRobotInfoState . Robot_info , LevelSharedSegmentState , LevelUniqueSegmentState , obj , 0 ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
2007-01-13 13:18:42 +00:00
if ( obj - > type = = OBJ_NONE | | obj - > flags & OF_SHOULD_BE_DEAD )
2016-12-24 08:50:29 +00:00
return window_event_result : : ignored ; // object has been deleted
2006-03-20 17:12:09 +00:00
2017-08-02 02:49:13 +00:00
bool prepare_seglist = false ;
phys_visited_seglist phys_visited_segs ;
2020-08-10 03:45:13 +00:00
switch ( obj - > movement_source ) {
2006-03-20 17:12:09 +00:00
2020-08-10 03:45:13 +00:00
case object : : movement_type : : None :
break ; //this doesn't move
2006-03-20 17:12:09 +00:00
2020-08-10 03:45:13 +00:00
case object : : movement_type : : physics : //move by physics
2022-07-09 13:39:29 +00:00
result = do_physics_sim ( LevelSharedRobotInfoState . Robot_info , obj , obj_previous_position , obj - > type = = OBJ_PLAYER ? ( prepare_seglist = true , phys_visited_segs . nsegs = 0 , & phys_visited_segs ) : nullptr ) ;
2017-08-02 02:49:13 +00:00
break ;
2006-03-20 17:12:09 +00:00
2020-08-10 03:45:13 +00:00
case object : : movement_type : : spinning :
spin_object ( obj ) ;
break ;
2006-03-20 17:12:09 +00:00
}
2018-12-30 00:43:58 +00:00
# if defined(DXX_BUILD_DESCENT_II)
auto & Walls = LevelUniqueWallSubsystemState . Walls ;
auto & vcwallptr = Walls . vcptr ;
# endif
2006-03-20 17:12:09 +00:00
// If player and moved to another segment, see if hit any triggers.
// also check in player under a lavafall
2017-08-02 02:49:13 +00:00
if ( prepare_seglist )
{
if ( previous_segment ! = obj - > segnum & & phys_visited_segs . nsegs > 1 )
2015-04-19 04:18:53 +00:00
{
2017-08-02 02:49:13 +00:00
auto seg0 = vmsegptridx ( phys_visited_segs . seglist [ 0 ] ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
int old_level = Current_level_num ;
# endif
2017-08-02 02:49:13 +00:00
range_for ( const auto i , partial_const_range ( phys_visited_segs . seglist , 1u , phys_visited_segs . nsegs ) )
2015-04-11 04:01:29 +00:00
{
2015-12-22 04:18:51 +00:00
const auto & & seg1 = seg0 . absolute_sibling ( i ) ;
2015-04-19 04:18:53 +00:00
const auto connect_side = find_connect_side ( seg1 , seg0 ) ;
2016-01-03 20:21:35 +00:00
if ( connect_side ! = side_none )
2015-04-19 04:18:53 +00:00
{
2016-12-24 08:50:29 +00:00
result = check_trigger ( seg0 , connect_side , get_local_plrobj ( ) , obj , 0 ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
//maybe we've gone on to the next level. if so, bail!
if ( Current_level_num ! = old_level )
2016-12-24 08:50:29 +00:00
return window_event_result : : ignored ;
2006-03-20 17:12:09 +00:00
# endif
2015-04-19 04:18:53 +00:00
}
seg0 = seg1 ;
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
{
2016-01-29 04:05:47 +00:00
bool under_lavafall = false ;
2006-03-20 17:12:09 +00:00
2016-07-24 04:04:25 +00:00
auto & playing = obj - > ctype . player_info . lavafall_hiss_playing ;
2016-01-29 04:05:47 +00:00
const auto & & segp = vcsegptr ( obj - > segnum ) ;
2018-12-30 00:43:57 +00:00
auto & vcvertptr = Vertices . vcptr ;
2022-02-19 14:52:17 +00:00
if ( const auto sidemask = get_seg_masks ( vcvertptr , obj - > pos , segp , obj - > size ) . sidemask ; sidemask ! = sidemask_t { } )
2016-01-29 04:05:47 +00:00
{
2022-01-09 15:25:42 +00:00
for ( const auto sidenum : MAX_SIDES_PER_SEGMENT )
2016-01-29 04:05:47 +00:00
{
2022-02-19 14:52:17 +00:00
if ( ! ( sidemask & build_sidemask ( sidenum ) ) )
2016-01-29 04:05:47 +00:00
continue ;
2018-12-13 02:31:38 +00:00
const auto wall_num = segp - > shared_segment : : sides [ sidenum ] . wall_num ;
2016-02-12 04:02:28 +00:00
if ( wall_num ! = wall_none & & vcwallptr ( wall_num ) - > type = = WALL_ILLUSION )
2016-01-29 04:05:47 +00:00
{
2018-12-13 02:31:38 +00:00
const auto type = check_volatile_wall ( obj , segp - > unique_segment : : sides [ sidenum ] ) ;
2016-01-29 04:05:47 +00:00
if ( type ! = volatile_wall_result : : none )
{
2006-03-20 17:12:09 +00:00
under_lavafall = 1 ;
2016-01-29 04:05:47 +00:00
if ( ! playing )
2015-11-27 03:56:13 +00:00
{
2016-01-29 04:05:47 +00:00
playing = 1 ;
const auto sound = ( type = = volatile_wall_result : : lava ) ? SOUND_LAVAFALL_HISS : SOUND_SHIP_IN_WATERFALL ;
2018-05-13 03:14:34 +00:00
digi_link_sound_to_object3 ( sound , obj , 1 , F1_0 , sound_stack : : allow_stacking , vm_distance { i2f ( 256 ) } , - 1 , - 1 ) ;
2016-01-29 04:05:47 +00:00
break ;
2006-03-20 17:12:09 +00:00
}
}
}
2016-01-29 04:05:47 +00:00
}
2006-03-20 17:12:09 +00:00
}
2016-01-29 04:05:47 +00:00
if ( ! under_lavafall & & playing )
{
playing = 0 ;
2014-01-11 22:50:07 +00:00
digi_kill_sound_linked_to_object ( obj ) ;
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
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
//see if guided missile has flown through exit trigger
2018-12-30 00:43:57 +00:00
if ( obj = = LevelUniqueObjectState . Guided_missile . get_player_active_guided_missile ( Player_num ) )
2018-10-08 03:58:48 +00:00
{
2006-03-20 17:12:09 +00:00
if ( previous_segment ! = obj - > segnum ) {
2015-07-12 01:04:21 +00:00
const auto & & psegp = vcsegptr ( previous_segment ) ;
const auto & & connect_side = find_connect_side ( vcsegptridx ( obj - > segnum ) , psegp ) ;
2016-01-03 20:21:35 +00:00
if ( connect_side ! = side_none )
{
2018-12-13 02:31:38 +00:00
const auto wall_num = psegp - > shared_segment : : sides [ connect_side ] . wall_num ;
2014-09-21 22:11:51 +00:00
if ( wall_num ! = wall_none ) {
2016-02-12 04:02:28 +00:00
auto trigger_num = vcwallptr ( wall_num ) - > trigger ;
2015-04-26 20:15:50 +00:00
if ( trigger_num ! = trigger_none )
2016-02-06 22:12:54 +00:00
{
2018-12-30 00:43:58 +00:00
auto & Triggers = LevelUniqueWallSubsystemState . Triggers ;
auto & vctrgptr = Triggers . vcptr ;
2016-02-06 22:12:54 +00:00
const auto & & t = vctrgptr ( trigger_num ) ;
2020-07-05 23:34:32 +00:00
if ( t - > type = = trigger_action : : normal_exit )
2018-10-08 03:58:48 +00:00
obj - > lifeleft = 0 ;
2016-02-06 22:12:54 +00:00
}
2006-03-20 17:12:09 +00:00
}
}
}
}
if ( Drop_afterburner_blob_flag ) {
Assert ( obj = = ConsoleObject ) ;
drop_afterburner_blobs ( obj , 2 , i2f ( 5 ) / 2 , - 1 ) ; // -1 means use default lifetime
if ( Game_mode & GM_MULTI )
multi_send_drop_blobs ( Player_num ) ;
Drop_afterburner_blob_flag = 0 ;
}
2013-10-07 23:52:33 +00:00
if ( ( obj - > type = = OBJ_WEAPON ) & & ( Weapon_info [ get_weapon_id ( obj ) ] . afterburner_size ) ) {
2014-09-28 21:11:03 +00:00
fix vel = vm_vec_mag_quick ( obj - > mtype . phys_info . velocity ) ;
2006-03-20 17:12:09 +00:00
fix delay , lifetime ;
if ( vel > F1_0 * 200 )
delay = F1_0 / 16 ;
else if ( vel > F1_0 * 40 )
delay = fixdiv ( F1_0 * 13 , vel ) ;
else
delay = F1_0 / 4 ;
lifetime = ( delay * 3 ) / 2 ;
if ( ! ( Game_mode & GM_MULTI ) ) {
delay / = 2 ;
lifetime * = 2 ;
}
2020-08-10 03:45:13 +00:00
assert ( obj - > control_source = = object : : control_type : : weapon ) ;
2013-08-24 22:09:58 +00:00
if ( ( obj - > ctype . laser_info . last_afterburner_time + delay < GameTime64 ) | | ( obj - > ctype . laser_info . last_afterburner_time > GameTime64 ) ) {
2013-10-07 23:52:33 +00:00
drop_afterburner_blobs ( obj , 1 , i2f ( Weapon_info [ get_weapon_id ( obj ) ] . afterburner_size ) / 16 , lifetime ) ;
2013-08-24 22:09:58 +00:00
obj - > ctype . laser_info . last_afterburner_time = GameTime64 ;
2006-03-20 17:12:09 +00:00
}
}
2016-12-24 08:50:29 +00:00
2013-03-03 01:03:33 +00:00
# endif
2017-01-13 03:19:19 +00:00
return result ;
2006-03-20 17:12:09 +00:00
}
//--------------------------------------------------------------------
//move all objects for the current frame
2022-07-09 13:39:29 +00:00
static window_event_result object_move_all ( const d_level_shared_robot_info_state & LevelSharedRobotInfoState )
2006-03-20 17:12:09 +00:00
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vmobjptridx = Objects . vmptridx ;
2016-12-24 08:50:29 +00:00
auto result = window_event_result : : ignored ;
2009-02-17 11:34:16 +00:00
if ( Highest_object_index > MAX_USED_OBJECTS )
free_object_slots ( MAX_USED_OBJECTS ) ; // Free all possible object slots.
2006-03-20 17:12:09 +00:00
obj_delete_all_that_should_be_dead ( ) ;
2008-04-13 00:28:36 +00:00
if ( PlayerCfg . AutoLeveling )
2006-03-20 17:12:09 +00:00
ConsoleObject - > mtype . phys_info . flags | = PF_LEVELLING ;
else
ConsoleObject - > mtype . phys_info . flags & = ~ PF_LEVELLING ;
// Move all objects
2017-06-10 03:31:02 +00:00
range_for ( const auto & & objp , vmobjptridx )
2014-10-12 23:05:46 +00:00
{
2006-03-20 17:12:09 +00:00
if ( ( objp - > type ! = OBJ_NONE ) & & ( ! ( objp - > flags & OF_SHOULD_BE_DEAD ) ) ) {
2022-07-09 13:39:29 +00:00
result = std : : max ( object_move_one ( LevelSharedRobotInfoState , objp , Controls ) , result ) ;
2006-03-20 17:12:09 +00:00
}
}
// check_duplicate_objects();
// remove_incorrect_objects();
2016-12-24 08:50:29 +00:00
return result ;
2006-03-20 17:12:09 +00:00
}
2022-04-17 22:27:19 +00:00
}
2022-07-09 13:39:29 +00:00
window_event_result game_move_all_objects ( const d_level_shared_robot_info_state & LevelSharedRobotInfoState )
2019-12-22 05:34:08 +00:00
{
LevelUniqueObjectState . last_console_player_position = ConsoleObject - > pos ;
2022-07-09 13:39:29 +00:00
return object_move_all ( LevelSharedRobotInfoState ) ;
2019-12-22 05:34:08 +00:00
}
2022-07-09 13:39:29 +00:00
window_event_result endlevel_move_all_objects ( const d_level_shared_robot_info_state & LevelSharedRobotInfoState )
2019-12-22 05:34:08 +00:00
{
2022-07-09 13:39:29 +00:00
return object_move_all ( LevelSharedRobotInfoState ) ;
2019-12-22 05:34:08 +00:00
}
2006-03-20 17:12:09 +00:00
//--unused-- // -----------------------------------------------------------
//--unused-- // Moved here from eobject.c on 02/09/94 by MK.
//--unused-- int find_last_obj(int i)
//--unused-- {
//--unused-- for (i=MAX_OBJECTS;--i>=0;)
//--unused-- if (Objects[i].type != OBJ_NONE) break;
//--unused--
//--unused-- return i;
//--unused--
//--unused-- }
//make object array non-sparse
void compress_objects ( void )
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vmobjptr = Objects . vmptr ;
auto & vmobjptridx = Objects . vmptridx ;
2006-03-20 17:12:09 +00:00
//last_i = find_last_obj(MAX_OBJECTS);
// Note: It's proper to do < (rather than <=) Highest_object_index here because we
// are just removing gaps, and the last object can't be a gap.
2014-11-26 03:39:21 +00:00
for ( objnum_t start_i = 0 ; start_i < Highest_object_index ; start_i + + )
2015-12-03 03:26:49 +00:00
{
2017-06-10 03:31:02 +00:00
const auto & & start_objp = vmobjptridx ( start_i ) ;
2015-12-03 03:26:49 +00:00
if ( start_objp - > type = = OBJ_NONE ) {
2016-01-09 16:38:12 +00:00
auto highest = Highest_object_index ;
2017-06-10 03:31:02 +00:00
const auto & & h = vmobjptr ( static_cast < objnum_t > ( highest ) ) ;
2014-11-26 03:39:21 +00:00
auto segnum_copy = h - > segnum ;
2006-03-20 17:12:09 +00:00
2018-03-12 03:43:46 +00:00
obj_unlink ( Objects . vmptr , Segments . vmptr , h ) ;
2006-03-20 17:12:09 +00:00
2015-12-03 03:26:49 +00:00
* start_objp = * h ;
2006-03-20 17:12:09 +00:00
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
if ( Cur_object_index = = Highest_object_index )
Cur_object_index = start_i ;
# endif
2014-11-26 03:39:21 +00:00
h - > type = OBJ_NONE ;
2006-03-20 17:12:09 +00:00
2018-03-12 03:43:46 +00:00
obj_link ( Objects . vmptr , start_objp , vmsegptridx ( segnum_copy ) ) ;
2006-03-20 17:12:09 +00:00
2017-06-10 03:31:02 +00:00
while ( vmobjptr ( static_cast < objnum_t > ( - - highest ) ) - > type = = OBJ_NONE )
2016-01-09 16:38:12 +00:00
{
}
Objects . set_count ( highest + 1 ) ;
2006-03-20 17:12:09 +00:00
//last_i = find_last_obj(last_i);
}
2015-12-03 03:26:49 +00:00
}
2018-12-30 00:43:57 +00:00
reset_objects ( LevelUniqueObjectState , LevelUniqueObjectState . num_objects ) ;
2006-03-20 17:12:09 +00:00
}
//called after load. Takes number of objects, and objects should be
//compressed. resets free list, marks unused objects as unused
2018-12-30 00:43:57 +00:00
void reset_objects ( d_level_unique_object_state & LevelUniqueObjectState , const unsigned n_objs )
2006-03-20 17:12:09 +00:00
{
2018-12-30 00:43:57 +00:00
LevelUniqueObjectState . Debris_object_count = 0 ;
LevelUniqueObjectState . num_objects = n_objs ;
assert ( LevelUniqueObjectState . num_objects > 0 ) ;
auto & Objects = LevelUniqueObjectState . get_objects ( ) ;
assert ( LevelUniqueObjectState . num_objects < Objects . size ( ) ) ;
2018-03-12 03:43:46 +00:00
Objects . set_count ( n_objs ) ;
2006-03-20 17:12:09 +00:00
2018-03-12 03:43:46 +00:00
for ( objnum_t i = n_objs ; i < MAX_OBJECTS ; + + i )
2015-12-03 03:26:49 +00:00
{
2018-12-30 00:43:57 +00:00
LevelUniqueObjectState . free_obj_list [ i ] = i ;
2018-03-12 03:43:46 +00:00
auto & obj = * Objects . vmptr ( i ) ;
2016-11-06 17:12:03 +00:00
DXX_POISON_VAR ( obj , 0xfd ) ;
obj . type = OBJ_NONE ;
2019-07-07 22:00:02 +00:00
obj . signature = object_signature_t { 0 } ;
2006-03-20 17:12:09 +00:00
}
}
//Tries to find a segment for an object, using find_point_seg()
2018-09-19 02:13:30 +00:00
imsegptridx_t find_object_seg ( const d_level_shared_segment_state & LevelSharedSegmentState , d_level_unique_segment_state & LevelUniqueSegmentState , const object_base & obj )
2006-03-20 17:12:09 +00:00
{
2018-09-19 02:13:30 +00:00
auto & Segments = LevelUniqueSegmentState . get_segments ( ) ;
return find_point_seg ( LevelSharedSegmentState , LevelUniqueSegmentState , obj . pos , Segments . vmptridx ( obj . segnum ) ) ;
2006-03-20 17:12:09 +00:00
}
//If an object is in a segment, set its segnum field and make sure it's
//properly linked. If not in any segment, returns 0, else 1.
//callers should generally use find_vector_intersection()
2018-10-08 03:58:48 +00:00
int update_object_seg ( fvmobjptr & vmobjptr , const d_level_shared_segment_state & LevelSharedSegmentState , d_level_unique_segment_state & LevelUniqueSegmentState , const vmobjptridx_t obj )
2006-03-20 17:12:09 +00:00
{
2018-09-19 02:13:30 +00:00
const auto & & newseg = find_object_seg ( LevelSharedSegmentState , LevelUniqueSegmentState , obj ) ;
2013-12-26 22:21:16 +00:00
if ( newseg = = segment_none )
2006-03-20 17:12:09 +00:00
return 0 ;
if ( newseg ! = obj - > segnum )
2018-10-08 03:58:48 +00:00
obj_relink ( vmobjptr , LevelUniqueSegmentState . get_segments ( ) . vmptr , obj , newseg ) ;
2006-03-20 17:12:09 +00:00
return 1 ;
}
2019-07-07 22:00:02 +00:00
unsigned laser_parent_is_player ( fvcobjptr & vcobjptr , const laser_parent & l , const object_base & o )
{
/* Player objects are never recycled, so skip the signature check.
*/
2019-07-18 02:06:45 +00:00
if ( l . parent_type ! = OBJ_PLAYER )
return 0 ;
/* As a special case, let the player be recognized even if he died
* before the weapon hit the target .
*/
if ( o . type ! = OBJ_PLAYER & & o . type ! = OBJ_GHOST )
2019-07-07 22:00:02 +00:00
return 0 ;
auto & parent_object = * vcobjptr ( l . parent_num ) ;
return ( & parent_object = = & o ) ;
}
unsigned laser_parent_is_object ( fvcobjptr & vcobjptr , const laser_parent & l , const object_base & o )
{
auto & parent_object = * vcobjptr ( l . parent_num ) ;
if ( & parent_object ! = & o )
return 0 ;
return laser_parent_is_matching_signature ( l , o ) ;
}
unsigned laser_parent_is_object ( const laser_parent & l , const vcobjptridx_t o )
{
if ( l . parent_num ! = o . get_unchecked_index ( ) )
return 0 ;
return laser_parent_is_matching_signature ( l , * o ) ;
}
unsigned laser_parent_object_exists ( fvcobjptr & vcobjptr , const laser_parent & l )
{
return laser_parent_is_matching_signature ( l , * vcobjptr ( l . parent_num ) ) ;
}
2018-10-08 03:58:48 +00:00
void set_powerup_id ( const d_powerup_info_array & Powerup_info , const d_vclip_array & Vclip , object_base & o , powerup_type_t id )
2015-03-28 17:18:02 +00:00
{
2015-09-15 02:48:04 +00:00
o . id = id ;
o . size = Powerup_info [ id ] . size ;
2015-03-28 17:18:02 +00:00
const auto vclip_num = Powerup_info [ id ] . vclip_num ;
2015-09-15 02:48:04 +00:00
o . rtype . vclip_info . vclip_num = vclip_num ;
o . rtype . vclip_info . frametime = Vclip [ vclip_num ] . frame_time ;
2015-03-28 17:18:02 +00:00
}
2006-03-20 17:12:09 +00:00
//go through all objects and make sure they have the correct segment numbers
2013-03-03 01:03:33 +00:00
void fix_object_segs ( )
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 ;
auto & vmobjptridx = Objects . vmptridx ;
2018-12-30 00:43:57 +00:00
auto & vcvertptr = Vertices . vcptr ;
2017-06-10 03:31:02 +00:00
range_for ( const auto & & o , vmobjptridx )
2014-12-23 04:20:27 +00:00
{
if ( o - > type ! = OBJ_NONE )
2018-10-08 03:58:48 +00:00
{
const auto oldsegnum = o - > segnum ;
if ( update_object_seg ( vmobjptr , LevelSharedSegmentState , LevelUniqueSegmentState , o ) = = 0 )
{
2015-01-11 05:08:30 +00:00
const auto pos = o - > pos ;
2018-10-08 03:58:48 +00:00
const auto segnum = o - > segnum ;
compute_segment_center ( vcvertptr , o - > pos , vcsegptr ( segnum ) ) ;
con_printf ( CON_URGENT , " Object %hu claims segment %hu, but has position {%i,%i,%i}; moving to %hu:{%i,%i,%i} " , o . get_unchecked_index ( ) , oldsegnum , pos . x , pos . y , pos . z , segnum , o - > pos . x , o - > pos . y , o - > pos . z ) ;
2006-03-20 17:12:09 +00:00
}
2018-10-08 03:58:48 +00:00
}
2014-12-23 04:20:27 +00:00
}
2006-03-20 17:12:09 +00:00
}
//--unused-- void object_use_new_object_list( object * new_list )
//--unused-- {
//--unused-- int i, segnum;
//--unused-- object *obj;
//--unused--
//--unused-- // First, unlink all the old objects for the segments array
//--unused-- for (segnum=0; segnum <= Highest_segment_index; segnum++) {
//--unused-- Segments[segnum].objects = -1;
//--unused-- }
//--unused-- // Then, erase all the objects
//--unused-- reset_objects(1);
//--unused--
//--unused-- // Fill in the object array
//--unused-- memcpy( Objects, new_list, sizeof(object)*MAX_OBJECTS );
//--unused--
//--unused-- Highest_object_index=-1;
//--unused--
//--unused-- // Relink 'em
//--unused-- for (i=0; i<MAX_OBJECTS; i++ ) {
//--unused-- obj = &Objects[i];
//--unused-- if ( obj->type != OBJ_NONE ) {
//--unused-- num_objects++;
//--unused-- Highest_object_index = i;
//--unused-- segnum = obj->segnum;
//--unused-- obj->next = obj->prev = obj->segnum = -1;
//--unused-- obj_link(i,segnum);
//--unused-- } else {
//--unused-- obj->next = obj->prev = obj->segnum = -1;
//--unused-- }
//--unused-- }
//--unused--
//--unused-- }
2022-04-17 22:27:19 +00:00
namespace {
2018-03-12 03:43:46 +00:00
# if defined(DXX_BUILD_DESCENT_I)
# define object_is_clearable_weapon(W,a,b) object_is_clearable_weapon(a,b)
# endif
static unsigned object_is_clearable_weapon ( const weapon_info_array & Weapon_info , const object_base obj , const unsigned clear_all )
2013-03-03 01:03:33 +00:00
{
2018-03-12 03:43:46 +00:00
if ( ! ( obj . type = = OBJ_WEAPON ) )
2013-03-03 01:03:33 +00:00
return 0 ;
2018-03-12 03:43:46 +00:00
const auto weapon_id = get_weapon_id ( obj ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2018-03-12 03:43:46 +00:00
if ( Weapon_info [ weapon_id ] . flags & WIF_PLACABLE )
2013-03-03 01:03:33 +00:00
return 0 ;
# endif
2018-03-12 03:43:46 +00:00
if ( clear_all )
return clear_all ;
2019-08-15 01:34:22 +00:00
return ! is_proximity_bomb_or_player_smart_mine ( weapon_id ) ;
2013-03-03 01:03:33 +00:00
}
2022-04-17 22:27:19 +00:00
}
2006-03-20 17:12:09 +00:00
//delete objects, such as weapons & explosions, that shouldn't stay between levels
// Changed by MK on 10/15/94, don't remove proximity bombs.
//if clear_all is set, clear even proximity bombs
void clear_transient_objects ( int clear_all )
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vmobjptridx = Objects . vmptridx ;
2017-06-10 03:31:02 +00:00
range_for ( const auto & & obj , vmobjptridx )
2014-10-12 23:05:46 +00:00
{
2018-03-12 03:43:46 +00:00
if ( object_is_clearable_weapon ( Weapon_info , obj , clear_all ) | |
2006-03-20 17:12:09 +00:00
obj - > type = = OBJ_FIREBALL | |
obj - > type = = OBJ_DEBRIS | |
( obj - > type ! = OBJ_NONE & & obj - > flags & OF_EXPLODING ) ) {
2018-12-30 00:43:57 +00:00
obj_delete ( LevelUniqueObjectState , Segments , obj ) ;
2006-03-20 17:12:09 +00:00
}
2014-10-12 23:05:46 +00:00
}
2006-03-20 17:12:09 +00:00
}
//attaches an object, such as a fireball, to another object, such as a robot
2018-03-12 03:43:46 +00:00
void obj_attach ( object_array & Objects , const vmobjptridx_t parent , const vmobjptridx_t sub )
2006-03-20 17:12:09 +00:00
{
Assert ( sub - > type = = OBJ_FIREBALL ) ;
2020-08-10 03:45:13 +00:00
assert ( sub - > control_source = = object : : control_type : : explosion ) ;
2011-03-28 22:39:31 +00:00
2013-12-26 22:21:16 +00:00
Assert ( sub - > ctype . expl_info . next_attach = = object_none ) ;
Assert ( sub - > ctype . expl_info . prev_attach = = object_none ) ;
2006-03-20 17:12:09 +00:00
2018-03-12 03:43:46 +00:00
assert ( parent - > attached_obj = = object_none | | Objects . vcptr ( parent - > attached_obj ) - > ctype . expl_info . prev_attach = = object_none ) ;
2006-03-20 17:12:09 +00:00
sub - > ctype . expl_info . next_attach = parent - > attached_obj ;
2013-12-26 22:21:16 +00:00
if ( sub - > ctype . expl_info . next_attach ! = object_none )
2018-03-12 03:43:46 +00:00
Objects . vmptr ( sub - > ctype . expl_info . next_attach ) - > ctype . expl_info . prev_attach = sub ;
2006-03-20 17:12:09 +00:00
2013-12-24 04:53:59 +00:00
parent - > attached_obj = sub ;
2006-03-20 17:12:09 +00:00
2013-12-24 04:53:59 +00:00
sub - > ctype . expl_info . attach_parent = parent ;
2006-03-20 17:12:09 +00:00
sub - > flags | = OF_ATTACHED ;
2013-12-24 04:53:59 +00:00
Assert ( sub - > ctype . expl_info . next_attach ! = sub ) ;
Assert ( sub - > ctype . expl_info . prev_attach ! = sub ) ;
2006-03-20 17:12:09 +00:00
}
2022-04-17 22:27:19 +00:00
namespace {
//detaches one object
2022-06-05 17:44:52 +00:00
static objnum_t obj_detach_one ( object_array & Objects , object & sub )
2006-03-20 17:12:09 +00:00
{
2016-04-23 17:59:47 +00:00
Assert ( sub . flags & OF_ATTACHED ) ;
Assert ( sub . ctype . expl_info . attach_parent ! = object_none ) ;
2006-03-20 17:12:09 +00:00
2022-06-05 17:44:52 +00:00
/* Check for the impossible case that the parent does not exist, or does
* not have any attached children .
*/
if ( const auto & parent_objp = * Objects . vcptr ( sub . ctype . expl_info . attach_parent ) ; parent_objp . type = = OBJ_NONE | | parent_objp . attached_obj = = object_none )
2006-03-20 17:12:09 +00:00
{
2016-04-23 17:59:47 +00:00
sub . flags & = ~ OF_ATTACHED ;
2022-06-05 17:44:52 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
}
2022-06-05 17:44:52 +00:00
/* If this is not the last sub-object in the list */
2016-04-23 17:59:47 +00:00
if ( sub . ctype . expl_info . next_attach ! = object_none )
{
2022-06-05 17:44:52 +00:00
/* Find the prev pointer of the next object */
2018-03-12 03:43:46 +00:00
auto & a = Objects . vmptr ( sub . ctype . expl_info . next_attach ) - > ctype . expl_info . prev_attach ;
2022-06-05 17:44:52 +00:00
/* Check that sub->next->prev is `sub` */
2018-03-12 03:43:46 +00:00
assert ( Objects . vcptr ( a ) = = & sub ) ;
2022-06-05 17:44:52 +00:00
/* Change the next object's prev pointer to point to `sub`'s
* predecessor . This is one half of the work required to remove ` sub `
* from the chain of sub - objects . The other half is handled below .
*/
2018-03-12 03:43:46 +00:00
a = sub . ctype . expl_info . prev_attach ;
2006-03-20 17:12:09 +00:00
}
2022-06-05 17:44:52 +00:00
/* If `sub` is the first sub-object in the chain, then `use_prev_attach` is
* false and the update should target the parent object .
*
* If ` sub ` is not the first sub - object in the chain , then
* ` use_prev_attach ` is true and the update should target the previous
* object in the chain .
*/
2017-08-02 02:49:12 +00:00
const auto use_prev_attach = ( sub . ctype . expl_info . prev_attach ! = object_none ) ;
2022-06-05 17:44:52 +00:00
/* If `use_prev_attach` is true, set `o` to the previous object and change
* ` sub - > prev ` to object_none so that a future call will set
* ` use_prev_attach ` to false .
*
* If ` use_prev_attach ` is false , set ` o ` to the parent object .
*/
2020-05-02 21:18:42 +00:00
auto & o = * Objects . vmptr ( use_prev_attach ? std : : exchange ( sub . ctype . expl_info . prev_attach , object_none ) : sub . ctype . expl_info . attach_parent ) ;
2022-06-05 17:44:52 +00:00
/* If `use_prev_attach` is true, then update the `next` reference in the
* previous object . This is the second half of removing ` sub ` from a chain
* of objects .
*
* If ` use_prev_attach ` is false , update ` attached_obj ` on the parent
* object , so that the parent has no attached objects . This assumes that
* ` use_prev_attach ` is false if and only if ` sub ` is the first child of
* the parent object . If ` sub ` is not the first child , then ` sub ` should
* have a back pointer ( and thus ` use_prev_attach ` would be true ) referring
* to a previous child , which may or may not be the first child .
*/
2017-08-02 02:49:12 +00:00
auto & update_attach = use_prev_attach ? o . ctype . expl_info . next_attach : o . attached_obj ;
2022-06-05 17:44:52 +00:00
/* Assert that the link that will be overwritten points to `sub`. If the
* link is from parent to child , then failure means that sub was not the
* first child of the parent . If the link is from previous to next , then
* failure means that sub pointed back to an element that did not point
* forward to sub .
*/
2018-03-12 03:43:46 +00:00
assert ( Objects . vcptr ( update_attach ) = = & sub ) ;
2016-04-23 17:59:47 +00:00
sub . flags & = ~ OF_ATTACHED ;
2022-06-05 17:44:52 +00:00
const auto next_attach = std : : exchange ( sub . ctype . expl_info . next_attach , object_none ) ;
update_attach = next_attach ;
return next_attach ;
2006-03-20 17:12:09 +00:00
}
2022-06-05 17:44:52 +00:00
//detaches all objects from this object
2018-03-12 03:43:46 +00:00
static void obj_detach_all ( object_array & Objects , object_base & parent )
2006-03-20 17:12:09 +00:00
{
2022-06-05 17:44:52 +00:00
for ( auto attached_obj = parent . attached_obj ; attached_obj ! = object_none ; )
attached_obj = obj_detach_one ( Objects , Objects . vmptr ( attached_obj ) ) ;
2006-03-20 17:12:09 +00:00
}
2022-04-17 22:27:19 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
//creates a marker object in the world. returns the object number
2020-07-22 03:11:18 +00:00
imobjptridx_t drop_marker_object ( const vms_vector & pos , const vmsegptridx_t segnum , const vms_matrix & orient , const game_marker_index marker_num )
2006-03-20 17:12:09 +00:00
{
2018-12-30 00:43:59 +00:00
auto & Polygon_models = LevelSharedPolygonModelState . Polygon_models ;
2020-08-24 01:31:28 +00:00
const auto Marker_model_num = LevelSharedPolygonModelState . Marker_model_num ;
if ( Marker_model_num > = Polygon_models . size ( ) )
{
con_printf ( CON_URGENT , " %s:%u: failed to drop marker object: invalid model number %u " , __FILE__ , __LINE__ , Marker_model_num ) ;
return object_none ;
}
2020-08-10 03:45:13 +00:00
const auto movement_type =
2019-10-02 02:06:00 +00:00
( ( Game_mode & GM_MULTI ) & & ! ( Game_mode & GM_MULTI_COOP ) & & Netgame . Allow_marker_view )
2020-08-10 03:45:13 +00:00
? object : : movement_type : : None
: object : : movement_type : : spinning ;
2022-07-02 18:10:45 +00:00
const auto & & obj = obj_create ( LevelUniqueObjectState , LevelSharedSegmentState , LevelUniqueSegmentState , OBJ_MARKER , underlying_value ( marker_num ) , segnum , pos , & orient , Polygon_models [ Marker_model_num ] . rad , object : : control_type : : None , movement_type , RT_POLYOBJ ) ;
2014-09-08 03:24:48 +00:00
if ( obj ! = object_none ) {
2018-09-06 02:03:29 +00:00
auto & o = * obj ;
o . rtype . pobj_info . model_num = Marker_model_num ;
2006-03-20 17:12:09 +00:00
2020-08-10 03:45:13 +00:00
if ( movement_type = = object : : movement_type : : spinning )
2019-10-02 02:06:00 +00:00
{
2018-10-08 03:58:48 +00:00
constexpr fix scale = F1_0 / 2 ;
const auto oi = obj . get_unchecked_index ( ) ;
auto & spin_vec = o . mtype . spin_rate ;
spin_vec = { } ;
if ( oi & 1 )
vm_vec_scale_add2 ( spin_vec , o . orient . fvec , ( oi & 8 ) ? scale : - scale ) ;
if ( oi & 2 )
vm_vec_scale_add2 ( spin_vec , o . orient . uvec , ( oi & 16 ) ? scale : - scale ) ;
if ( oi & 4 )
vm_vec_scale_add2 ( spin_vec , o . orient . rvec , ( oi & 32 ) ? scale : - scale ) ;
2019-10-02 02:06:00 +00:00
}
2006-03-20 17:12:09 +00:00
// MK, 10/16/95: Using lifeleft to make it flash, thus able to trim lightlevel from all objects.
2018-09-06 02:03:29 +00:00
o . lifeleft = IMMORTAL_TIME - 1 ;
2006-03-20 17:12:09 +00:00
}
2014-09-08 03:24:48 +00:00
return obj ;
2006-03-20 17:12:09 +00:00
}
// *viewer is a viewer, probably a missile.
// wake up all robots that were rendered last frame subject to some constraints.
2018-09-25 03:44:09 +00:00
void wake_up_rendered_objects ( const object & viewer , window_rendered_data & window )
2006-03-20 17:12:09 +00:00
{
2019-03-03 00:31:08 +00:00
auto & Objects = LevelUniqueObjectState . Objects ;
auto & vmobjptr = Objects . vmptr ;
2006-03-20 17:12:09 +00:00
// Make sure that we are processing current data.
2014-12-13 04:11:07 +00:00
if ( timer_query ( ) ! = window . time ) {
2006-03-20 17:12:09 +00:00
return ;
}
2018-09-25 03:44:09 +00:00
Ai_last_missile_camera = & viewer ;
2006-03-20 17:12:09 +00:00
2014-12-13 04:11:07 +00:00
range_for ( const auto objnum , window . rendered_robots )
2013-12-25 23:47:30 +00:00
{
2017-06-10 03:31:02 +00:00
const auto & & objp = vmobjptr ( objnum ) ;
2006-03-20 17:12:09 +00:00
if ( objp - > type = = OBJ_ROBOT ) {
2018-09-25 03:44:09 +00:00
if ( vm_vec_dist_quick ( viewer . pos , objp - > pos ) < F1_0 * 100 )
{
2013-12-25 03:16:41 +00:00
ai_local * ailp = & objp - > ctype . ai_info . ail ;
2021-08-26 03:13:45 +00:00
{
2006-03-20 17:12:09 +00:00
objp - > ctype . ai_info . SUB_FLAGS | = SUB_FLAGS_CAMERA_AWAKE ;
2015-04-02 02:36:53 +00:00
ailp - > player_awareness_type = player_awareness_type_t : : PA_WEAPON_ROBOT_COLLISION ;
2006-03-20 17:12:09 +00:00
ailp - > player_awareness_time = F1_0 * 3 ;
2019-06-20 04:02:27 +00:00
ailp - > previous_visibility = player_visibility_state : : visible_and_in_field_of_view ;
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
2010-12-22 00:17:59 +00:00
// Swap endianess of given object_rw if swap == 1
void object_rw_swap ( object_rw * obj , int swap )
2009-10-05 02:51:37 +00:00
{
if ( ! swap )
return ;
2013-02-21 00:20:26 +00:00
2009-10-05 02:51:37 +00:00
obj - > signature = SWAPINT ( obj - > signature ) ;
obj - > next = SWAPSHORT ( obj - > next ) ;
obj - > prev = SWAPSHORT ( obj - > prev ) ;
obj - > segnum = SWAPSHORT ( obj - > segnum ) ;
2010-12-22 00:17:59 +00:00
obj - > attached_obj = SWAPSHORT ( obj - > attached_obj ) ;
2009-10-05 02:51:37 +00:00
obj - > pos . x = SWAPINT ( obj - > pos . x ) ;
obj - > pos . y = SWAPINT ( obj - > pos . y ) ;
obj - > pos . z = SWAPINT ( obj - > pos . z ) ;
obj - > orient . rvec . x = SWAPINT ( obj - > orient . rvec . x ) ;
obj - > orient . rvec . y = SWAPINT ( obj - > orient . rvec . y ) ;
obj - > orient . rvec . z = SWAPINT ( obj - > orient . rvec . z ) ;
obj - > orient . fvec . x = SWAPINT ( obj - > orient . fvec . x ) ;
obj - > orient . fvec . y = SWAPINT ( obj - > orient . fvec . y ) ;
obj - > orient . fvec . z = SWAPINT ( obj - > orient . fvec . z ) ;
obj - > orient . uvec . x = SWAPINT ( obj - > orient . uvec . x ) ;
obj - > orient . uvec . y = SWAPINT ( obj - > orient . uvec . y ) ;
obj - > orient . uvec . z = SWAPINT ( obj - > orient . uvec . z ) ;
obj - > size = SWAPINT ( obj - > size ) ;
obj - > shields = SWAPINT ( obj - > shields ) ;
obj - > last_pos . x = SWAPINT ( obj - > last_pos . x ) ;
obj - > last_pos . y = SWAPINT ( obj - > last_pos . y ) ;
obj - > last_pos . z = SWAPINT ( obj - > last_pos . z ) ;
obj - > lifeleft = SWAPINT ( obj - > lifeleft ) ;
2020-08-10 03:45:13 +00:00
switch ( typename object : : movement_type { obj - > movement_source } )
2010-12-22 00:17:59 +00:00
{
2020-08-10 03:45:13 +00:00
case object : : movement_type : : None :
obj - > mtype = { } ;
break ;
case object : : movement_type : : physics :
2010-12-22 00:17:59 +00:00
obj - > mtype . phys_info . velocity . x = SWAPINT ( obj - > mtype . phys_info . velocity . x ) ;
obj - > mtype . phys_info . velocity . y = SWAPINT ( obj - > mtype . phys_info . velocity . y ) ;
obj - > mtype . phys_info . velocity . z = SWAPINT ( obj - > mtype . phys_info . velocity . z ) ;
obj - > mtype . phys_info . thrust . x = SWAPINT ( obj - > mtype . phys_info . thrust . x ) ;
obj - > mtype . phys_info . thrust . y = SWAPINT ( obj - > mtype . phys_info . thrust . y ) ;
obj - > mtype . phys_info . thrust . z = SWAPINT ( obj - > mtype . phys_info . thrust . z ) ;
obj - > mtype . phys_info . mass = SWAPINT ( obj - > mtype . phys_info . mass ) ;
obj - > mtype . phys_info . drag = SWAPINT ( obj - > mtype . phys_info . drag ) ;
obj - > mtype . phys_info . rotvel . x = SWAPINT ( obj - > mtype . phys_info . rotvel . x ) ;
obj - > mtype . phys_info . rotvel . y = SWAPINT ( obj - > mtype . phys_info . rotvel . y ) ;
obj - > mtype . phys_info . rotvel . z = SWAPINT ( obj - > mtype . phys_info . rotvel . z ) ;
2009-10-05 02:51:37 +00:00
obj - > mtype . phys_info . rotthrust . x = SWAPINT ( obj - > mtype . phys_info . rotthrust . x ) ;
obj - > mtype . phys_info . rotthrust . y = SWAPINT ( obj - > mtype . phys_info . rotthrust . y ) ;
obj - > mtype . phys_info . rotthrust . z = SWAPINT ( obj - > mtype . phys_info . rotthrust . z ) ;
2010-12-22 00:17:59 +00:00
obj - > mtype . phys_info . turnroll = SWAPINT ( obj - > mtype . phys_info . turnroll ) ;
obj - > mtype . phys_info . flags = SWAPSHORT ( obj - > mtype . phys_info . flags ) ;
2009-10-05 02:51:37 +00:00
break ;
2020-08-10 03:45:13 +00:00
case object : : movement_type : : spinning :
2009-10-05 02:51:37 +00:00
obj - > mtype . spin_rate . x = SWAPINT ( obj - > mtype . spin_rate . x ) ;
obj - > mtype . spin_rate . y = SWAPINT ( obj - > mtype . spin_rate . y ) ;
obj - > mtype . spin_rate . z = SWAPINT ( obj - > mtype . spin_rate . z ) ;
break ;
}
2020-08-10 03:45:13 +00:00
switch ( typename object : : control_type { obj - > control_source } )
2010-12-22 00:17:59 +00:00
{
2020-08-06 03:47:56 +00:00
case object : : control_type : : weapon :
2010-12-22 00:17:59 +00:00
obj - > ctype . laser_info . parent_type = SWAPSHORT ( obj - > ctype . laser_info . parent_type ) ;
obj - > ctype . laser_info . parent_num = SWAPSHORT ( obj - > ctype . laser_info . parent_num ) ;
obj - > ctype . laser_info . parent_signature = SWAPINT ( obj - > ctype . laser_info . parent_signature ) ;
obj - > ctype . laser_info . creation_time = SWAPINT ( obj - > ctype . laser_info . creation_time ) ;
obj - > ctype . laser_info . last_hitobj = SWAPSHORT ( obj - > ctype . laser_info . last_hitobj ) ;
obj - > ctype . laser_info . track_goal = SWAPSHORT ( obj - > ctype . laser_info . track_goal ) ;
obj - > ctype . laser_info . multiplier = SWAPINT ( obj - > ctype . laser_info . multiplier ) ;
2009-10-05 02:51:37 +00:00
break ;
2020-08-06 03:47:56 +00:00
case object : : control_type : : explosion :
2010-12-22 00:17:59 +00:00
obj - > ctype . expl_info . spawn_time = SWAPINT ( obj - > ctype . expl_info . spawn_time ) ;
obj - > ctype . expl_info . delete_time = SWAPINT ( obj - > ctype . expl_info . delete_time ) ;
obj - > ctype . expl_info . delete_objnum = SWAPSHORT ( obj - > ctype . expl_info . delete_objnum ) ;
obj - > ctype . expl_info . attach_parent = SWAPSHORT ( obj - > ctype . expl_info . attach_parent ) ;
obj - > ctype . expl_info . prev_attach = SWAPSHORT ( obj - > ctype . expl_info . prev_attach ) ;
obj - > ctype . expl_info . next_attach = SWAPSHORT ( obj - > ctype . expl_info . next_attach ) ;
2009-10-05 02:51:37 +00:00
break ;
2020-08-06 03:47:56 +00:00
case object : : control_type : : ai :
2010-12-22 00:17:59 +00:00
obj - > ctype . ai_info . hide_segment = SWAPSHORT ( obj - > ctype . ai_info . hide_segment ) ;
obj - > ctype . ai_info . hide_index = SWAPSHORT ( obj - > ctype . ai_info . hide_index ) ;
obj - > ctype . ai_info . path_length = SWAPSHORT ( obj - > ctype . ai_info . path_length ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
obj - > ctype . ai_info . cur_path_index = SWAPSHORT ( obj - > ctype . ai_info . cur_path_index ) ;
# elif defined(DXX_BUILD_DESCENT_II)
obj - > ctype . ai_info . dying_start_time = SWAPINT ( obj - > ctype . ai_info . dying_start_time ) ;
# endif
2010-12-22 00:17:59 +00:00
obj - > ctype . ai_info . danger_laser_num = SWAPSHORT ( obj - > ctype . ai_info . danger_laser_num ) ;
2009-10-05 02:51:37 +00:00
obj - > ctype . ai_info . danger_laser_signature = SWAPINT ( obj - > ctype . ai_info . danger_laser_signature ) ;
break ;
2020-08-06 03:47:56 +00:00
case object : : control_type : : light :
2009-10-05 02:51:37 +00:00
obj - > ctype . light_info . intensity = SWAPINT ( obj - > ctype . light_info . intensity ) ;
break ;
2020-08-06 03:47:56 +00:00
case object : : control_type : : powerup :
2010-12-22 00:17:59 +00:00
obj - > ctype . powerup_info . count = SWAPINT ( obj - > ctype . powerup_info . count ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2009-10-05 02:51:37 +00:00
obj - > ctype . powerup_info . creation_time = SWAPINT ( obj - > ctype . powerup_info . creation_time ) ;
2010-12-22 00:17:59 +00:00
obj - > ctype . powerup_info . flags = SWAPINT ( obj - > ctype . powerup_info . flags ) ;
2013-03-03 01:03:33 +00:00
# endif
2009-10-05 02:51:37 +00:00
break ;
2020-08-06 03:47:56 +00:00
case object : : control_type : : None :
case object : : control_type : : flying :
case object : : control_type : : slew :
case object : : control_type : : flythrough :
case object : : control_type : : repaircen :
case object : : control_type : : morph :
case object : : control_type : : debris :
case object : : control_type : : remote :
default :
break ;
2009-10-05 02:51:37 +00:00
}
2010-12-22 00:17:59 +00:00
switch ( obj - > render_type )
{
2009-10-05 02:51:37 +00:00
case RT_MORPH :
2010-12-22 00:17:59 +00:00
case RT_POLYOBJ :
case RT_NONE : // HACK below
{
if ( obj - > render_type = = RT_NONE & & obj - > type ! = OBJ_GHOST ) // HACK: when a player is dead or not connected yet, clients still expect to get polyobj data - even if render_type == RT_NONE at this time.
break ;
obj - > rtype . pobj_info . model_num = SWAPINT ( obj - > rtype . pobj_info . model_num ) ;
2014-09-20 23:47:27 +00:00
for ( uint_fast32_t i = 0 ; i < MAX_SUBMODELS ; i + + )
2010-12-22 00:17:59 +00:00
{
2009-10-05 02:51:37 +00:00
obj - > rtype . pobj_info . anim_angles [ i ] . p = SWAPINT ( obj - > rtype . pobj_info . anim_angles [ i ] . p ) ;
obj - > rtype . pobj_info . anim_angles [ i ] . b = SWAPINT ( obj - > rtype . pobj_info . anim_angles [ i ] . b ) ;
obj - > rtype . pobj_info . anim_angles [ i ] . h = SWAPINT ( obj - > rtype . pobj_info . anim_angles [ i ] . h ) ;
}
2010-12-22 00:17:59 +00:00
obj - > rtype . pobj_info . subobj_flags = SWAPINT ( obj - > rtype . pobj_info . subobj_flags ) ;
obj - > rtype . pobj_info . tmap_override = SWAPINT ( obj - > rtype . pobj_info . tmap_override ) ;
obj - > rtype . pobj_info . alt_textures = SWAPINT ( obj - > rtype . pobj_info . alt_textures ) ;
2009-10-05 02:51:37 +00:00
break ;
}
case RT_WEAPON_VCLIP :
case RT_HOSTAGE :
case RT_POWERUP :
case RT_FIREBALL :
obj - > rtype . vclip_info . vclip_num = SWAPINT ( obj - > rtype . vclip_info . vclip_num ) ;
obj - > rtype . vclip_info . frametime = SWAPINT ( obj - > rtype . vclip_info . frametime ) ;
break ;
case RT_LASER :
break ;
}
}
2015-11-26 02:56:56 +00:00
2016-04-06 03:34:13 +00:00
}
namespace dcx {
void ( check_warn_object_type ) ( const object_base & o , object_type_t t , const char * file , unsigned line )
2015-11-26 02:56:56 +00:00
{
if ( o . type ! = t )
con_printf ( CON_URGENT , " %s:%u: BUG: object %p has type %u, expected %u " , file , line , & o , o . type , t ) ;
}
2015-12-05 22:57:25 +00:00
}