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 .
*/
/*
*
* Code for rendering & otherwise dealing with explosions
*
*/
2012-11-11 22:12:51 +00:00
# include <algorithm>
2016-10-02 00:34:41 +00:00
# include <numeric>
2006-03-20 17:12:09 +00:00
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
2012-07-07 18:35:06 +00:00
# include "dxxerror.h"
2012-07-01 02:54:33 +00:00
# include "maths.h"
2006-03-20 17:12:09 +00:00
# include "vecmat.h"
# include "gr.h"
# include "3d.h"
# include "inferno.h"
# include "object.h"
# include "vclip.h"
# include "game.h"
2015-04-02 02:36:57 +00:00
# include "robot.h"
2006-03-20 17:12:09 +00:00
# include "sounds.h"
# include "player.h"
# include "gauges.h"
# include "powerup.h"
# include "bm.h"
# include "ai.h"
# include "weapon.h"
# include "fireball.h"
# include "collide.h"
# include "newmenu.h"
# include "gameseq.h"
# include "physics.h"
# include "scores.h"
# include "laser.h"
# include "wall.h"
# include "multi.h"
# include "endlevel.h"
# include "timer.h"
# include "fuelcen.h"
2015-04-19 04:18:51 +00:00
# include "playsave.h"
2006-03-20 17:12:09 +00:00
# include "cntrlcen.h"
# include "gameseg.h"
# include "automap.h"
2014-07-03 01:47:29 +00:00
# include "byteutil.h"
2006-03-20 17:12:09 +00:00
2014-07-30 02:52:35 +00:00
# include "compiler-range_for.h"
2015-02-28 19:36:01 +00:00
# include "partial_range.h"
2014-07-30 02:52:35 +00:00
# include "segiter.h"
2012-11-11 22:12:51 +00:00
using std : : min ;
2006-03-20 17:12:09 +00:00
# define EXPLOSION_SCALE (F1_0*5 / 2) //explosion is the obj size times this
//--unused-- ubyte Frame_processed[MAX_OBJECTS];
2015-12-22 04:18:50 +00:00
namespace dsx {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2013-11-24 05:30:37 +00:00
fix Flash_effect = 0 ;
2016-07-16 16:52:04 +00:00
constexpr int PK1 = 1 , PK2 = 8 ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2017-02-11 21:42:43 +00:00
static bool can_collide ( const object * const weapon_object , const object_base & iter_object , const object * const parent_object )
2016-12-25 00:33:24 +00:00
{
# if defined(DXX_BUILD_DESCENT_I)
( void ) weapon_object ;
# elif defined(DXX_BUILD_DESCENT_II)
2017-02-11 21:42:43 +00:00
if ( weapon_object = = & iter_object )
2016-12-25 00:33:24 +00:00
return false ;
2016-12-25 00:33:24 +00:00
if ( iter_object . type = = OBJ_NONE )
return false ;
2016-12-25 00:33:24 +00:00
if ( iter_object . flags & OF_SHOULD_BE_DEAD )
return false ;
# endif
switch ( iter_object . type )
{
# if defined(DXX_BUILD_DESCENT_II)
case OBJ_WEAPON :
return is_proximity_bomb_or_smart_mine_or_placed_mine ( get_weapon_id ( iter_object ) ) ;
# endif
case OBJ_CNTRLCEN :
case OBJ_PLAYER :
return true ;
case OBJ_ROBOT :
if ( parent_object = = nullptr )
return false ;
if ( parent_object - > type ! = OBJ_ROBOT )
return true ;
return get_robot_id ( * parent_object ) ! = iter_object . id ;
default :
return false ;
}
}
2014-11-23 04:58:45 +00:00
static objptridx_t object_create_explosion_sub ( const objptridx_t objp , const vsegptridx_t segnum , const vms_vector & position , fix size , int vclip_type , fix maxdamage , fix maxdistance , fix maxforce , const cobjptridx_t parent )
2006-03-20 17:12:09 +00:00
{
2014-10-26 21:36:25 +00:00
auto obj = obj_create ( OBJ_FIREBALL , vclip_type , segnum , position , & vmd_identity_matrix , size ,
2006-03-20 17:12:09 +00:00
CT_EXPLOSION , MT_NONE , RT_FIREBALL ) ;
2016-10-29 23:16:15 +00:00
if ( obj = = object_none )
{
return object_none ;
2006-03-20 17:12:09 +00:00
}
//now set explosion-specific data
obj - > lifeleft = Vclip [ vclip_type ] . play_time ;
obj - > ctype . expl_info . spawn_time = - 1 ;
2013-12-26 22:21:16 +00:00
obj - > ctype . expl_info . delete_objnum = object_none ;
2006-03-20 17:12:09 +00:00
obj - > ctype . expl_info . delete_time = - 1 ;
if ( maxdamage > 0 ) {
2015-03-12 02:21:19 +00:00
fix force ;
2006-03-20 17:12:09 +00:00
vms_vector pos_hit , vforce ;
fix damage ;
// -- now legal for badass explosions on a wall. Assert(objp != NULL);
2016-02-12 04:02:28 +00:00
range_for ( const auto & & obj0p , vobjptridx )
2014-10-12 23:05:46 +00:00
{
2006-03-20 17:12:09 +00:00
// Weapons used to be affected by badass explosions, but this introduces serious problems.
// When a smart bomb blows up, if one of its children goes right towards a nearby wall, it will
// blow up, blowing up all the children. So I remove it. MK, 09/11/94
2016-12-25 00:33:24 +00:00
if ( can_collide ( objp , obj0p , parent ) )
2013-11-24 05:30:37 +00:00
{
2015-03-12 02:21:19 +00:00
const auto dist = vm_vec_dist_quick ( obj0p - > pos , obj - > pos ) ;
2006-03-20 17:12:09 +00:00
// Make damage be from 'maxdamage' to 0.0, where 0.0 is 'maxdistance' away;
if ( dist < maxdistance ) {
if ( object_to_object_visibility ( obj , obj0p , FQ_TRANSWALL ) ) {
damage = maxdamage - fixmuldiv ( dist , maxdamage , maxdistance ) ;
force = maxforce - fixmuldiv ( dist , maxforce , maxdistance ) ;
// Find the force vector on the object
2014-10-01 02:28:41 +00:00
vm_vec_normalized_dir_quick ( vforce , obj0p - > pos , obj - > pos ) ;
2014-09-28 21:11:05 +00:00
vm_vec_scale ( vforce , force ) ;
2006-03-20 17:12:09 +00:00
// Find where the point of impact is... ( pos_hit )
2014-09-28 21:11:45 +00:00
vm_vec_scale ( vm_vec_sub ( pos_hit , obj - > pos , obj0p - > pos ) , fixdiv ( obj0p - > size , obj0p - > size + dist ) ) ;
2006-03-20 17:12:09 +00:00
switch ( obj0p - > type ) {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
case OBJ_WEAPON :
2014-10-26 21:37:34 +00:00
phys_apply_force ( obj0p , vforce ) ;
2006-03-20 17:12:09 +00:00
2015-11-27 03:56:13 +00:00
if ( is_proximity_bomb_or_smart_mine ( get_weapon_id ( obj0p ) ) )
{ //prox bombs have chance of blowing up
2006-03-20 17:12:09 +00:00
if ( fixmul ( dist , force ) > i2f ( 8000 ) ) {
obj0p - > flags | = OF_SHOULD_BE_DEAD ;
2014-10-26 21:36:31 +00:00
explode_badass_weapon ( obj0p , obj0p - > pos ) ;
2006-03-20 17:12:09 +00:00
}
}
break ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
case OBJ_ROBOT :
{
2014-10-26 21:37:34 +00:00
phys_apply_force ( obj0p , vforce ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// If not a boss, stun for 2 seconds at 32 force, 1 second at 16 force
2017-01-28 18:12:21 +00:00
if ( objp ! = object_none & & objp - > type = = OBJ_WEAPON & & ! Robot_info [ get_robot_id ( obj0p ) ] . boss_flag & & Weapon_info [ get_weapon_id ( objp ) ] . flash )
{
2006-03-20 17:12:09 +00:00
ai_static * aip = & obj0p - > ctype . ai_info ;
2015-11-27 03:56:13 +00:00
int force_val = f2i ( fixdiv ( vm_vec_mag_quick ( vforce ) * Weapon_info [ get_weapon_id ( objp ) ] . flash , FrameTime ) / 128 ) + 2 ;
2006-03-20 17:12:09 +00:00
if ( obj - > ctype . ai_info . SKIP_AI_COUNT * FrameTime < F1_0 ) {
aip - > SKIP_AI_COUNT + = force_val ;
obj0p - > mtype . phys_info . rotthrust . x = ( ( d_rand ( ) - 16384 ) * force_val ) / 16 ;
obj0p - > mtype . phys_info . rotthrust . y = ( ( d_rand ( ) - 16384 ) * force_val ) / 16 ;
obj0p - > mtype . phys_info . rotthrust . z = ( ( d_rand ( ) - 16384 ) * force_val ) / 16 ;
obj0p - > mtype . phys_info . flags | = PF_USES_THRUST ;
//@@if (Robot_info[obj0p->id].companion)
//@@ buddy_message("Daisy, Daisy, Give me...");
} else
aip - > SKIP_AI_COUNT - - ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
// When a robot gets whacked by a badass force, he looks towards it because robots tend to get blasted from behind.
{
vms_vector neg_vforce ;
neg_vforce . x = vforce . x * - 2 * ( 7 - Difficulty_level ) / 8 ;
neg_vforce . y = vforce . y * - 2 * ( 7 - Difficulty_level ) / 8 ;
neg_vforce . z = vforce . z * - 2 * ( 7 - Difficulty_level ) / 8 ;
2014-10-26 21:37:34 +00:00
phys_apply_rot ( obj0p , neg_vforce ) ;
2006-03-20 17:12:09 +00:00
}
if ( obj0p - > shields > = 0 ) {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-11-27 03:56:13 +00:00
const auto & robot_info = Robot_info [ get_robot_id ( obj0p ) ] ;
if ( robot_info . boss_flag > = BOSS_D2 & & Boss_invulnerable_matter [ robot_info . boss_flag - BOSS_D2 ] )
2006-03-20 17:12:09 +00:00
damage / = 4 ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
if ( apply_damage_to_robot ( obj0p , damage , parent ) )
2015-07-25 23:10:46 +00:00
if ( ( objp ! = object_none ) & & ( parent = = get_local_player ( ) . objnum ) )
2016-10-15 00:53:19 +00:00
add_points_to_score ( ConsoleObject - > ctype . player_info , Robot_info [ get_robot_id ( obj0p ) ] . score_value ) ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-11-27 03:56:13 +00:00
if ( objp ! = object_none & & Robot_info [ get_robot_id ( obj0p ) ] . companion & & ! Weapon_info [ get_weapon_id ( objp ) ] . flash )
{
2012-12-02 22:28:51 +00:00
static const char ouch_str [ ] = " ouch! " " ouch! " " ouch! " " ouch! " ;
int count ;
2006-03-20 17:12:09 +00:00
count = f2i ( damage / 8 ) ;
if ( count > 4 )
count = 4 ;
else if ( count < = 0 )
count = 1 ;
2013-12-08 23:37:40 +00:00
buddy_message_str ( & ouch_str [ ( 4 - count ) * 6 ] ) ;
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
break ;
}
case OBJ_CNTRLCEN :
2015-05-09 19:55:58 +00:00
if ( parent ! = object_none & & obj0p - > shields > = 0 )
{
2006-03-20 17:12:09 +00:00
apply_damage_to_controlcen ( obj0p , damage , parent ) ;
}
break ;
case OBJ_PLAYER : {
2014-10-02 03:02:34 +00:00
cobjptridx_t killer = object_none ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// Hack! Warning! Test code!
2015-11-27 03:56:13 +00:00
if ( objp ! = object_none & & Weapon_info [ get_weapon_id ( objp ) ] . flash & & get_player_id ( obj0p ) = = Player_num )
{
2006-03-20 17:12:09 +00:00
int fe ;
2015-11-27 03:56:13 +00:00
fe = min ( F1_0 * 4 , force * Weapon_info [ get_weapon_id ( objp ) ] . flash / 32 ) ; // For four seconds or less
2006-03-20 17:12:09 +00:00
if ( objp - > ctype . laser_info . parent_signature = = ConsoleObject - > signature ) {
fe / = 2 ;
force / = 2 ;
}
if ( force > F1_0 ) {
Flash_effect = fe ;
PALETTE_FLASH_ADD ( PK1 + f2i ( PK2 * force ) , PK1 + f2i ( PK2 * force ) , PK1 + f2i ( PK2 * force ) ) ;
}
}
2013-03-03 01:03:33 +00:00
# endif
2014-01-11 23:38:37 +00:00
if ( ( objp ! = object_none ) & & ( Game_mode & GM_MULTI ) & & ( objp - > type = = OBJ_PLAYER ) ) {
2006-03-20 17:12:09 +00:00
killer = objp ;
}
2016-08-25 23:31:37 +00:00
auto vforce2 = vforce ;
2013-12-26 22:21:16 +00:00
if ( parent ! = object_none ) {
2014-07-05 22:21:47 +00:00
killer = parent ;
2006-03-20 17:12:09 +00:00
if ( killer ! = ConsoleObject ) // if someone else whacks you, cut force by 2x
2016-08-25 23:31:37 +00:00
{
2006-03-20 17:12:09 +00:00
vforce2 . x / = 2 ; vforce2 . y / = 2 ; vforce2 . z / = 2 ;
2016-08-25 23:31:37 +00:00
}
2006-03-20 17:12:09 +00:00
}
vforce2 . x / = 2 ; vforce2 . y / = 2 ; vforce2 . z / = 2 ;
2014-10-26 21:37:34 +00:00
phys_apply_force ( obj0p , vforce ) ;
phys_apply_rot ( obj0p , vforce2 ) ;
2016-08-25 23:31:37 +00:00
if ( obj0p - > shields > = 0 )
{
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
if ( Difficulty_level = = 0 )
damage / = 4 ;
2013-03-03 01:03:33 +00:00
# endif
2015-08-16 11:25:07 +00:00
apply_damage_to_player ( obj0p , killer , damage , 1 ) ;
2016-08-25 23:31:37 +00:00
}
2006-03-20 17:12:09 +00:00
}
break ;
default :
Int3 ( ) ; // Illegal object type
} // end switch
} else {
2008-04-06 20:23:28 +00:00
;
2006-03-20 17:12:09 +00:00
} // end if (object_to_object_visibility...
} // end if (dist < maxdistance)
}
} // end for
} // end if (maxdamage...
return obj ;
}
2014-11-23 04:58:45 +00:00
void object_create_muzzle_flash ( const vsegptridx_t segnum , const vms_vector & position , fix size , int vclip_type )
2006-03-20 17:12:09 +00:00
{
2014-01-11 23:38:37 +00:00
object_create_explosion_sub ( object_none , segnum , position , size , vclip_type , 0 , 0 , 0 , object_none ) ;
2006-03-20 17:12:09 +00:00
}
2014-11-23 04:58:45 +00:00
objptridx_t object_create_explosion ( const vsegptridx_t segnum , const vms_vector & position , fix size , int vclip_type )
2006-03-20 17:12:09 +00:00
{
2014-01-11 23:38:37 +00:00
return object_create_explosion_sub ( object_none , segnum , position , size , vclip_type , 0 , 0 , 0 , object_none ) ;
2006-03-20 17:12:09 +00:00
}
2014-11-23 04:58:45 +00:00
objptridx_t object_create_badass_explosion ( const objptridx_t objp , const vsegptridx_t segnum , const vms_vector & position , fix size , int vclip_type , fix maxdamage , fix maxdistance , fix maxforce , const cobjptridx_t parent )
2006-03-20 17:12:09 +00:00
{
2014-10-02 03:02:34 +00:00
const objptridx_t rval = object_create_explosion_sub ( objp , segnum , position , size , vclip_type , maxdamage , maxdistance , maxforce , parent ) ;
2006-03-20 17:12:09 +00:00
2014-01-11 23:34:11 +00:00
if ( ( objp ! = object_none ) & & ( objp - > type = = OBJ_WEAPON ) )
2015-05-09 17:38:58 +00:00
create_weapon_smart_children ( objp ) ;
2006-03-20 17:12:09 +00:00
return rval ;
}
//blows up a badass weapon, creating the badass explosion
//return the explosion object
2014-10-02 03:02:34 +00:00
void explode_badass_weapon ( const vobjptridx_t obj , const vms_vector & pos )
2006-03-20 17:12:09 +00:00
{
2015-11-27 03:56:13 +00:00
const auto weapon_id = get_weapon_id ( obj ) ;
const weapon_info * wi = & Weapon_info [ weapon_id ] ;
2006-03-20 17:12:09 +00:00
Assert ( wi - > damage_radius ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-12-03 03:26:49 +00:00
if ( weapon_id = = weapon_id_type : : EARTHSHAKER_ID | | weapon_id = = weapon_id_type : : ROBOT_EARTHSHAKER_ID )
2006-03-20 17:12:09 +00:00
smega_rock_stuff ( ) ;
2013-03-03 01:03:33 +00:00
# endif
2014-01-11 23:11:04 +00:00
digi_link_sound_to_object ( SOUND_BADASS_EXPLOSION , obj , 0 , F1_0 ) ;
2006-03-20 17:12:09 +00:00
2016-01-09 16:38:14 +00:00
object_create_badass_explosion ( obj , vsegptridx ( obj - > segnum ) , pos ,
2006-03-20 17:12:09 +00:00
wi - > impact_size ,
wi - > robot_hit_vclip ,
wi - > strength [ Difficulty_level ] ,
wi - > damage_radius , wi - > strength [ Difficulty_level ] ,
2014-12-23 04:20:27 +00:00
objptridx ( obj - > ctype . laser_info . parent_num ) ) ;
2006-03-20 17:12:09 +00:00
}
2014-10-02 03:02:34 +00:00
static void explode_badass_object ( const vobjptridx_t objp , fix damage , fix distance , fix force )
2006-03-20 17:12:09 +00:00
{
2016-01-09 16:38:14 +00:00
const auto & & rval = object_create_badass_explosion ( objp , vsegptridx ( objp - > segnum ) , objp - > pos , objp - > size ,
2015-11-15 22:30:41 +00:00
get_explosion_vclip ( objp , explosion_vclip_stage : : s0 ) ,
2006-03-20 17:12:09 +00:00
damage , distance , force ,
2014-01-05 05:17:34 +00:00
objp ) ;
2014-01-10 03:40:43 +00:00
if ( rval ! = object_none )
digi_link_sound_to_object ( SOUND_BADASS_EXPLOSION , rval , 0 , F1_0 ) ;
2006-03-20 17:12:09 +00:00
}
//blows up the player with a badass explosion
//return the explosion object
2014-10-02 03:02:34 +00:00
void explode_badass_player ( const vobjptridx_t objp )
2006-03-20 17:12:09 +00:00
{
2014-01-02 00:26:31 +00:00
explode_badass_object ( objp , F1_0 * 50 , F1_0 * 40 , F1_0 * 150 ) ;
2006-03-20 17:12:09 +00:00
}
2007-02-10 23:19:21 +00:00
# define DEBRIS_LIFE (f1_0 * (PERSISTENT_DEBRIS?60:2)) //lifespan in seconds
2006-03-20 17:12:09 +00:00
2016-04-23 17:59:47 +00:00
static void object_create_debris ( const object_base & parent , int subobj_num )
2006-03-20 17:12:09 +00:00
{
2016-04-23 17:59:47 +00:00
Assert ( parent . type = = OBJ_ROBOT | | parent . type = = OBJ_PLAYER ) ;
2006-03-20 17:12:09 +00:00
2016-04-23 17:59:47 +00:00
const auto & & obj = obj_create ( OBJ_DEBRIS , 0 , vsegptridx ( parent . segnum ) , parent . pos , & parent . orient , Polygon_models [ parent . rtype . pobj_info . model_num ] . submodel_rads [ subobj_num ] ,
2006-03-20 17:12:09 +00:00
CT_DEBRIS , MT_PHYSICS , RT_POLYOBJ ) ;
2014-01-10 04:02:53 +00:00
if ( ( obj = = object_none ) & & ( Highest_object_index > = MAX_OBJECTS - 1 ) ) {
2006-03-20 17:12:09 +00:00
// Int3(); // this happens often and is normal :-)
2014-01-02 00:26:58 +00:00
return ;
2006-03-20 17:12:09 +00:00
}
2014-01-10 04:02:53 +00:00
if ( obj = = object_none )
2014-01-02 00:26:58 +00:00
return ; // Not enough debris slots!
2006-03-20 17:12:09 +00:00
Assert ( subobj_num < 32 ) ;
//Set polygon-object-specific data
2016-04-23 17:59:47 +00:00
obj - > rtype . pobj_info . model_num = parent . rtype . pobj_info . model_num ;
2006-03-20 17:12:09 +00:00
obj - > rtype . pobj_info . subobj_flags = 1 < < subobj_num ;
2016-04-23 17:59:47 +00:00
obj - > rtype . pobj_info . tmap_override = parent . rtype . pobj_info . tmap_override ;
2006-03-20 17:12:09 +00:00
//Set physics data for this object
2007-02-10 23:19:21 +00:00
obj - > mtype . phys_info . velocity . x = D_RAND_MAX / 2 - d_rand ( ) ;
obj - > mtype . phys_info . velocity . y = D_RAND_MAX / 2 - d_rand ( ) ;
obj - > mtype . phys_info . velocity . z = D_RAND_MAX / 2 - d_rand ( ) ;
2014-09-28 21:11:04 +00:00
vm_vec_normalize_quick ( obj - > mtype . phys_info . velocity ) ;
2014-09-28 21:11:05 +00:00
vm_vec_scale ( obj - > mtype . phys_info . velocity , i2f ( 10 + ( 30 * d_rand ( ) / D_RAND_MAX ) ) ) ;
2006-03-20 17:12:09 +00:00
2016-04-23 17:59:47 +00:00
vm_vec_add2 ( obj - > mtype . phys_info . velocity , parent . mtype . phys_info . velocity ) ;
2006-03-20 17:12:09 +00:00
// -- used to be: Notice, not random! vm_vec_make(&obj->mtype.phys_info.rotvel,10*0x2000/3,10*0x4000/3,10*0x7000/3);
2015-01-12 00:26:02 +00:00
obj - > mtype . phys_info . rotvel = { d_rand ( ) + 0x1000 , d_rand ( ) * 2 + 0x4000 , d_rand ( ) * 3 + 0x2000 } ;
2014-09-28 21:11:04 +00:00
vm_vec_zero ( obj - > mtype . phys_info . rotthrust ) ;
2006-03-20 17:12:09 +00:00
obj - > lifeleft = 3 * DEBRIS_LIFE / 4 + fixmul ( d_rand ( ) , DEBRIS_LIFE ) ; // Some randomness, so they don't all go away at the same time.
2016-04-23 17:59:47 +00:00
obj - > mtype . phys_info . mass = fixmuldiv ( parent . mtype . phys_info . mass , obj - > size , parent . size ) ;
2006-03-20 17:12:09 +00:00
obj - > mtype . phys_info . drag = 0 ; //fl2f(0.2); //parent->mtype.phys_info.drag;
2011-01-24 20:51:08 +00:00
if ( PERSISTENT_DEBRIS )
{
obj - > mtype . phys_info . flags | = PF_BOUNCE ;
obj - > mtype . phys_info . drag = 128 ;
}
2006-03-20 17:12:09 +00:00
}
2017-03-11 19:56:25 +00:00
void draw_fireball ( grs_canvas & canvas , const vcobjptridx_t obj )
2006-03-20 17:12:09 +00:00
{
if ( obj - > lifeleft > 0 )
2017-03-11 19:56:25 +00:00
draw_vclip_object ( canvas , obj , obj - > lifeleft , 0 , Vclip [ get_fireball_id ( obj ) ] ) ;
2006-03-20 17:12:09 +00:00
}
// --------------------------------------------------------------------------------------------------------------------
// Return true if there is a door here and it is openable
// It is assumed that the player has all keys.
2014-10-02 03:02:34 +00:00
static int door_is_openable_by_player ( const vcsegptr_t segp , int sidenum )
2006-03-20 17:12:09 +00:00
{
2016-02-12 04:02:28 +00:00
const auto wall_num = segp - > sides [ sidenum ] . wall_num ;
2006-03-20 17:12:09 +00:00
2014-09-21 22:11:51 +00:00
if ( wall_num = = wall_none )
2006-03-20 17:12:09 +00:00
return 0 ; // no wall here.
2016-02-12 04:02:28 +00:00
auto & w = * vcwallptr ( wall_num ) ;
const auto wall_type = w . type ;
2006-03-20 17:12:09 +00:00
// Can't open locked doors.
2016-02-12 04:02:28 +00:00
if ( ( wall_type = = WALL_DOOR & & ( w . flags & WALL_DOOR_LOCKED ) ) | | wall_type = = WALL_CLOSED )
2006-03-20 17:12:09 +00:00
return 0 ;
return 1 ;
}
# define QUEUE_SIZE 64
// --------------------------------------------------------------------------------------------------------------------
// Return a segment %i segments away from initial segment.
// Returns -1 if can't find a segment that distance away.
2016-10-02 00:34:41 +00:00
segidx_t pick_connected_segment ( const vcsegidx_t start_seg , int max_depth )
2006-03-20 17:12:09 +00:00
{
2015-01-20 02:46:42 +00:00
using std : : swap ;
2006-03-20 17:12:09 +00:00
int i ;
int cur_depth ;
int head , tail ;
2015-07-12 01:04:19 +00:00
array < segnum_t , QUEUE_SIZE * 2 > seg_queue { } ;
2016-10-02 00:34:41 +00:00
array < uint8_t , MAX_SEGMENTS > depth { } ;
array < uint8_t , MAX_SIDES_PER_SEGMENT > side_rand ;
2006-03-20 17:12:09 +00:00
2013-12-18 23:55:14 +00:00
visited_segment_bitarray_t visited ;
2007-11-23 21:14:57 +00:00
2006-03-20 17:12:09 +00:00
head = 0 ;
tail = 0 ;
seg_queue [ head + + ] = start_seg ;
cur_depth = 0 ;
2016-10-02 00:34:41 +00:00
std : : iota ( side_rand . begin ( ) , side_rand . end ( ) , 0 ) ;
2006-03-20 17:12:09 +00:00
// Now, randomize a bit to start, so we don't always get started in the same direction.
for ( i = 0 ; i < 4 ; i + + ) {
2015-01-20 02:46:42 +00:00
int ind1 ;
2006-03-20 17:12:09 +00:00
ind1 = ( d_rand ( ) * MAX_SIDES_PER_SEGMENT ) > > 15 ;
2015-01-20 02:46:42 +00:00
swap ( side_rand [ ind1 ] , side_rand [ i ] ) ;
2006-03-20 17:12:09 +00:00
}
while ( tail ! = head ) {
2007-11-23 21:14:57 +00:00
int sidenum , count ;
2015-01-20 02:46:42 +00:00
int ind1 , ind2 ;
2006-03-20 17:12:09 +00:00
if ( cur_depth > = max_depth ) {
return seg_queue [ tail ] ;
}
2015-07-12 01:04:19 +00:00
const auto & & segp = vcsegptr ( seg_queue [ tail + + ] ) ;
2006-03-20 17:12:09 +00:00
tail & = QUEUE_SIZE - 1 ;
// to make random, switch a pair of entries in side_rand.
ind1 = ( d_rand ( ) * MAX_SIDES_PER_SEGMENT ) > > 15 ;
ind2 = ( d_rand ( ) * MAX_SIDES_PER_SEGMENT ) > > 15 ;
2015-01-20 02:46:42 +00:00
swap ( side_rand [ ind1 ] , side_rand [ ind2 ] ) ;
2007-11-23 21:14:57 +00:00
count = 0 ;
for ( sidenum = ind1 ; count < MAX_SIDES_PER_SEGMENT ; count + + ) {
2014-09-21 22:11:51 +00:00
int snrand ;
2007-11-23 21:14:57 +00:00
if ( sidenum = = MAX_SIDES_PER_SEGMENT )
sidenum = 0 ;
snrand = side_rand [ sidenum ] ;
2014-09-21 22:11:51 +00:00
auto wall_num = segp - > sides [ snrand ] . wall_num ;
2007-11-23 21:14:57 +00:00
sidenum + + ;
2006-03-20 17:12:09 +00:00
2015-05-28 03:08:39 +00:00
if ( ( wall_num = = wall_none | | door_is_openable_by_player ( segp , snrand ) ) & & IS_CHILD ( segp - > children [ snrand ] ) )
2007-11-23 21:14:57 +00:00
{
2013-12-18 23:55:14 +00:00
if ( ! visited [ segp - > children [ snrand ] ] ) {
2006-03-20 17:12:09 +00:00
seg_queue [ head + + ] = segp - > children [ snrand ] ;
2013-12-18 23:55:14 +00:00
visited [ segp - > children [ snrand ] ] = true ;
2006-03-20 17:12:09 +00:00
depth [ segp - > children [ snrand ] ] = cur_depth + 1 ;
head & = QUEUE_SIZE - 1 ;
if ( head > tail ) {
if ( head = = tail + QUEUE_SIZE - 1 )
Int3 ( ) ; // queue overflow. Make it bigger!
} else
if ( head + QUEUE_SIZE = = tail + QUEUE_SIZE - 1 )
Int3 ( ) ; // queue overflow. Make it bigger!
}
}
}
2007-11-23 21:14:57 +00:00
2015-07-12 01:04:19 +00:00
if ( seg_queue [ tail ] > Highest_segment_index )
{
2006-03-20 17:12:09 +00:00
// -- Int3(); // Something bad has happened. Queue is trashed. --MK, 12/13/94
2015-09-29 02:41:22 +00:00
return segment_none ;
2006-03-20 17:12:09 +00:00
}
cur_depth = depth [ seg_queue [ tail ] ] ;
}
2015-09-29 02:41:22 +00:00
return segment_none ;
2006-03-20 17:12:09 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
# define BASE_NET_DROP_DEPTH 10
# elif defined(DXX_BUILD_DESCENT_II)
2011-02-02 22:51:34 +00:00
# define BASE_NET_DROP_DEPTH 8
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
// ------------------------------------------------------------------------------------------------------
// Choose segment to drop a powerup in.
// For all active net players, try to create a N segment path from the player. If possible, return that
// segment. If not possible, try another player. After a few tries, use a random segment.
// Don't drop if control center in segment.
2016-02-25 13:11:08 +00:00
static vsegptridx_t choose_drop_segment ( playernum_t drop_pnum )
2006-03-20 17:12:09 +00:00
{
2014-09-21 22:10:12 +00:00
playernum_t pnum = 0 ;
2006-03-20 17:12:09 +00:00
int cur_drop_depth ;
int count ;
2014-10-30 03:32:51 +00:00
vms_vector * player_pos ;
2016-02-29 14:58:36 +00:00
auto & drop_playerobj = * vobjptr ( Players [ drop_pnum ] . objnum ) ;
2006-03-20 17:12:09 +00:00
2016-09-04 00:02:51 +00:00
d_srand ( static_cast < fix > ( timer_query ( ) ) ) ;
2006-03-20 17:12:09 +00:00
cur_drop_depth = BASE_NET_DROP_DEPTH + ( ( d_rand ( ) * BASE_NET_DROP_DEPTH * 2 ) > > 15 ) ;
2016-02-25 13:11:08 +00:00
player_pos = & drop_playerobj . pos ;
const auto player_seg = drop_playerobj . segnum ;
2006-03-20 17:12:09 +00:00
2013-12-29 04:28:07 +00:00
segnum_t segnum = segment_none ;
2013-12-26 22:21:16 +00:00
while ( ( segnum = = segment_none ) & & ( cur_drop_depth > BASE_NET_DROP_DEPTH / 2 ) ) {
2006-03-20 17:12:09 +00:00
pnum = ( d_rand ( ) * N_players ) > > 15 ;
count = 0 ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2016-02-25 13:11:08 +00:00
while ( ( count < N_players ) & & ( ( Players [ pnum ] . connected = = CONNECT_DISCONNECTED ) | | ( pnum = = drop_pnum ) ) )
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2016-02-25 13:11:08 +00:00
while ( ( count < N_players ) & & ( ( Players [ pnum ] . connected = = CONNECT_DISCONNECTED ) | | ( pnum = = drop_pnum ) | | ( ( Game_mode & ( GM_TEAM | GM_CAPTURE ) ) & & ( get_team ( pnum ) = = get_team ( drop_pnum ) ) ) ) )
2013-03-03 01:03:33 +00:00
# endif
2013-11-24 05:30:37 +00:00
{
2006-03-20 17:12:09 +00:00
pnum = ( pnum + 1 ) % N_players ;
count + + ;
}
if ( count = = N_players ) {
//if can't valid non-player person, use the player
2016-02-25 13:11:08 +00:00
pnum = drop_pnum ;
2006-03-20 17:12:09 +00:00
}
2016-10-02 00:34:41 +00:00
segnum = pick_connected_segment ( vcobjptr ( Players [ pnum ] . objnum ) - > segnum , cur_drop_depth ) ;
2013-12-26 22:21:16 +00:00
if ( segnum = = segment_none )
2006-03-20 17:12:09 +00:00
{
cur_drop_depth - - ;
continue ;
}
2013-11-24 05:30:37 +00:00
if ( Segments [ segnum ] . special = = SEGMENT_IS_CONTROLCEN )
2013-12-26 22:21:16 +00:00
{
segnum = segment_none ;
}
2006-03-20 17:12:09 +00:00
else { //don't drop in any children of control centers
2016-09-03 17:30:18 +00:00
range_for ( auto ch , vcsegptr ( segnum ) - > children )
{
if ( IS_CHILD ( ch ) & & vcsegptr ( ch ) - > special = = SEGMENT_IS_CONTROLCEN ) {
2013-12-26 22:21:16 +00:00
segnum = segment_none ;
2006-03-20 17:12:09 +00:00
break ;
}
}
}
//bail if not far enough from original position
2013-12-26 22:21:16 +00:00
if ( segnum ! = segment_none ) {
2016-01-09 16:38:14 +00:00
const auto & & segp = vcsegptridx ( segnum ) ;
const auto & & tempv = compute_segment_center ( segp ) ;
if ( find_connected_distance ( * player_pos , vcsegptridx ( player_seg ) , tempv , segp , - 1 , WID_FLY_FLAG ) < i2f ( 20 ) * cur_drop_depth )
{
2013-12-26 22:21:16 +00:00
segnum = segment_none ;
2006-03-20 17:12:09 +00:00
}
}
cur_drop_depth - - ;
}
2013-12-26 22:21:16 +00:00
if ( segnum = = segment_none ) {
2012-04-07 08:58:46 +00:00
cur_drop_depth = BASE_NET_DROP_DEPTH ;
2013-12-26 22:21:16 +00:00
while ( cur_drop_depth > 0 & & segnum = = segment_none ) // before dropping in random segment, try to find ANY segment which is connected to the player responsible for the drop so object will not spawn in inaccessible areas
2012-04-07 08:58:46 +00:00
{
2016-10-02 00:34:41 +00:00
segnum = pick_connected_segment ( vcobjptr ( Players [ drop_pnum ] . objnum ) - > segnum , - - cur_drop_depth ) ;
2015-09-29 02:41:22 +00:00
if ( segnum ! = segment_none & & vcsegptr ( segnum ) - > special = = SEGMENT_IS_CONTROLCEN )
2013-12-26 22:21:16 +00:00
segnum = segment_none ;
2012-04-07 08:58:46 +00:00
}
2016-01-09 16:38:14 +00:00
if ( segnum = = segment_none ) // basically it should be impossible segnum == -1 now... but oh well...
return vsegptridx ( static_cast < segnum_t > ( ( d_rand ( ) * Highest_segment_index ) > > 15 ) ) ;
}
return vsegptridx ( segnum ) ;
2006-03-20 17:12:09 +00:00
}
// ------------------------------------------------------------------------------------------------------
2016-02-25 13:11:08 +00:00
// (Re)spawns powerup if in a network game.
void maybe_drop_net_powerup ( powerup_type_t powerup_type , bool adjust_cap , bool random_player )
2006-03-20 17:12:09 +00:00
{
2016-02-25 13:11:08 +00:00
playernum_t pnum = Player_num ;
2006-03-20 17:12:09 +00:00
if ( ( Game_mode & GM_MULTI ) & & ! ( Game_mode & GM_MULTI_COOP ) ) {
2016-02-25 13:11:08 +00:00
if ( ( Game_mode & GM_NETWORK ) & & adjust_cap )
2011-01-14 13:29:08 +00:00
{
2016-07-14 01:59:03 +00:00
MultiLevelInv_Recount ( ) ; // recount current items
2016-02-25 13:11:08 +00:00
if ( ! MultiLevelInv_AllowSpawn ( powerup_type ) )
2011-01-14 13:29:08 +00:00
return ;
}
2006-03-20 17:12:09 +00:00
2015-02-22 16:31:06 +00:00
if ( Control_center_destroyed | | ( Network_status = = NETSTAT_ENDLEVEL ) )
2006-03-20 17:12:09 +00:00
return ;
2016-02-25 13:11:08 +00:00
if ( random_player )
{
2016-02-29 15:31:20 +00:00
uint_fast32_t failsafe_count = 0 ;
do {
2016-02-25 13:11:08 +00:00
pnum = d_rand ( ) % MAX_PLAYERS ;
2016-02-29 15:31:20 +00:00
if ( failsafe_count > MAX_PLAYERS * 4 ) // that was plenty of tries to find a good player...
{
pnum = Player_num ; // ... go with Player_num instead
break ;
}
failsafe_count + + ;
} while ( Players [ pnum ] . connected ! = CONNECT_PLAYING ) ;
2016-02-25 13:11:08 +00:00
}
2006-03-20 17:12:09 +00:00
//--old-- segnum = (d_rand() * Highest_segment_index) >> 15;
//--old-- Assert((segnum >= 0) && (segnum <= Highest_segment_index));
//--old-- if (segnum < 0)
//--old-- segnum = -segnum;
//--old-- while (segnum > Highest_segment_index)
//--old-- segnum /= 2;
Net_create_loc = 0 ;
2016-12-05 00:26:11 +00:00
const auto & & objnum = call_object_create_egg ( vobjptr ( Players [ pnum ] . objnum ) , 1 , powerup_type ) ;
2006-03-20 17:12:09 +00:00
2014-08-23 23:53:56 +00:00
if ( objnum = = object_none )
2006-03-20 17:12:09 +00:00
return ;
2016-02-25 13:11:08 +00:00
const auto & & segnum = choose_drop_segment ( pnum ) ;
2016-01-09 16:38:14 +00:00
const auto & & new_pos = pick_random_point_in_seg ( segnum ) ;
2014-10-30 03:32:38 +00:00
multi_send_create_powerup ( powerup_type , segnum , objnum , new_pos ) ;
2014-08-23 23:53:56 +00:00
objnum - > pos = new_pos ;
2014-09-28 21:11:04 +00:00
vm_vec_zero ( objnum - > mtype . phys_info . velocity ) ;
2006-03-20 17:12:09 +00:00
obj_relink ( objnum , segnum ) ;
2014-10-26 21:36:25 +00:00
object_create_explosion ( segnum , new_pos , i2f ( 5 ) , VCLIP_POWERUP_DISAPPEARANCE ) ;
2006-03-20 17:12:09 +00:00
}
}
// ------------------------------------------------------------------------------------------------------
// Return true if current segment contains some object.
2015-11-27 03:56:12 +00:00
static csegptr_t segment_contains_powerup ( const vcsegptr_t segnum , const powerup_type_t obj_id )
2006-03-20 17:12:09 +00:00
{
2015-02-05 03:03:49 +00:00
range_for ( const auto objp , objects_in ( segnum ) )
2015-11-27 03:56:12 +00:00
if ( objp - > type = = OBJ_POWERUP & & get_powerup_id ( objp ) = = obj_id )
2015-10-18 21:01:18 +00:00
return segnum ;
return segment_none ;
2006-03-20 17:12:09 +00:00
}
// ------------------------------------------------------------------------------------------------------
2015-11-27 03:56:12 +00:00
static csegptr_t powerup_nearby_aux ( const vcsegptr_t segnum , powerup_type_t object_id , uint_fast32_t depth )
2006-03-20 17:12:09 +00:00
{
2015-11-27 03:56:12 +00:00
if ( auto r = segment_contains_powerup ( segnum , object_id ) )
2015-10-18 21:01:18 +00:00
return r ;
2015-01-20 02:46:42 +00:00
if ( ! - - depth )
2015-10-18 21:01:18 +00:00
return segment_none ;
2015-02-05 03:03:49 +00:00
range_for ( const auto seg2 , segnum - > children )
2015-01-20 02:46:42 +00:00
{
2013-12-26 22:21:16 +00:00
if ( seg2 ! = segment_none )
2015-11-27 03:56:12 +00:00
if ( auto r = powerup_nearby_aux ( vcsegptr ( seg2 ) , object_id , depth ) )
2015-10-18 21:01:18 +00:00
return r ;
2006-03-20 17:12:09 +00:00
}
2015-10-18 21:01:18 +00:00
return segment_none ;
2006-03-20 17:12:09 +00:00
}
// ------------------------------------------------------------------------------------------------------
// Return true if some powerup is nearby (within 3 segments).
2016-04-23 17:59:47 +00:00
static csegptr_t weapon_nearby ( const object_base & objp , powerup_type_t weapon_id )
2006-03-20 17:12:09 +00:00
{
2016-04-23 17:59:47 +00:00
return powerup_nearby_aux ( vcsegptr ( objp . segnum ) , weapon_id , 2 ) ;
2006-03-20 17:12:09 +00:00
}
// ------------------------------------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
void maybe_replace_powerup_with_energy ( const vobjptr_t del_obj )
2006-03-20 17:12:09 +00:00
{
int weapon_index = - 1 ;
if ( del_obj - > contains_type ! = OBJ_POWERUP )
return ;
if ( del_obj - > contains_id = = POW_CLOAK ) {
2015-11-27 03:56:12 +00:00
if ( weapon_nearby ( del_obj , static_cast < powerup_type_t > ( del_obj - > contains_id ) ) ! = nullptr )
2015-10-18 21:01:18 +00:00
{
2006-03-20 17:12:09 +00:00
del_obj - > contains_count = 0 ;
}
return ;
}
switch ( del_obj - > contains_id ) {
2015-10-18 21:01:18 +00:00
case POW_VULCAN_WEAPON :
weapon_index = primary_weapon_index_t : : VULCAN_INDEX ;
break ;
case POW_SPREADFIRE_WEAPON :
weapon_index = primary_weapon_index_t : : SPREADFIRE_INDEX ;
break ;
case POW_PLASMA_WEAPON :
weapon_index = primary_weapon_index_t : : PLASMA_INDEX ;
break ;
case POW_FUSION_WEAPON :
weapon_index = primary_weapon_index_t : : FUSION_INDEX ;
break ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-10-18 21:01:18 +00:00
case POW_GAUSS_WEAPON :
weapon_index = primary_weapon_index_t : : GAUSS_INDEX ;
break ;
case POW_HELIX_WEAPON :
weapon_index = primary_weapon_index_t : : HELIX_INDEX ;
break ;
case POW_PHOENIX_WEAPON :
weapon_index = primary_weapon_index_t : : PHOENIX_INDEX ;
break ;
case POW_OMEGA_WEAPON :
weapon_index = primary_weapon_index_t : : OMEGA_INDEX ;
break ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
// Don't drop vulcan ammo if player maxed out.
2016-10-02 00:34:40 +00:00
auto & player_info = get_local_plrobj ( ) . ctype . player_info ;
if ( ( weapon_index_uses_vulcan_ammo ( weapon_index ) | | del_obj - > contains_id = = POW_VULCAN_AMMO ) & &
player_info . vulcan_ammo > = VULCAN_AMMO_MAX )
2006-03-20 17:12:09 +00:00
del_obj - > contains_count = 0 ;
else if ( weapon_index ! = - 1 ) {
2016-10-02 00:34:43 +00:00
if ( player_has_primary_weapon ( player_info , weapon_index ) . has_weapon ( ) | | weapon_nearby ( del_obj , static_cast < powerup_type_t > ( del_obj - > contains_id ) ) ! = nullptr )
2015-10-18 21:01:18 +00:00
{
2006-03-20 17:12:09 +00:00
if ( d_rand ( ) > 16384 ) {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
del_obj - > contains_count = 1 ;
# endif
2006-03-20 17:12:09 +00:00
del_obj - > contains_type = OBJ_POWERUP ;
2013-09-20 23:12:51 +00:00
if ( weapon_index_uses_vulcan_ammo ( weapon_index ) ) {
2006-03-20 17:12:09 +00:00
del_obj - > contains_id = POW_VULCAN_AMMO ;
2013-11-24 05:30:37 +00:00
}
else {
2006-03-20 17:12:09 +00:00
del_obj - > contains_id = POW_ENERGY ;
}
} else {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
del_obj - > contains_count = 0 ;
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
del_obj - > contains_type = OBJ_POWERUP ;
del_obj - > contains_id = POW_SHIELD_BOOST ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
}
} else if ( del_obj - > contains_id = = POW_QUAD_FIRE )
2016-10-02 00:34:40 +00:00
{
if ( ( player_info . powerup_flags & PLAYER_FLAGS_QUAD_LASERS ) | | weapon_nearby ( del_obj , static_cast < powerup_type_t > ( del_obj - > contains_id ) ) ! = nullptr )
2015-10-18 21:01:18 +00:00
{
2006-03-20 17:12:09 +00:00
if ( d_rand ( ) > 16384 ) {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
del_obj - > contains_count = 1 ;
# endif
2006-03-20 17:12:09 +00:00
del_obj - > contains_type = OBJ_POWERUP ;
del_obj - > contains_id = POW_ENERGY ;
} else {
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
del_obj - > contains_count = 0 ;
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
del_obj - > contains_type = OBJ_POWERUP ;
del_obj - > contains_id = POW_SHIELD_BOOST ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
}
2016-10-02 00:34:40 +00:00
}
2006-03-20 17:12:09 +00:00
// If this robot was gated in by the boss and it now contains energy, make it contain nothing,
// else the room gets full of energy.
if ( ( del_obj - > matcen_creator = = BOSS_GATE_MATCEN_NUM ) & & ( del_obj - > contains_id = = POW_ENERGY ) & & ( del_obj - > contains_type = = OBJ_POWERUP ) ) {
del_obj - > contains_count = 0 ;
}
// Change multiplayer extra-lives into invulnerability
if ( ( Game_mode & GM_MULTI ) & & ( del_obj - > contains_id = = POW_EXTRA_LIFE ) )
{
del_obj - > contains_id = POW_INVULNERABILITY ;
}
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2016-02-06 22:12:55 +00:00
# define drop_powerup(type, id, num, init_vel, pos, segnum, player) (drop_powerup)(type, id, num, init_vel, pos, segnum)
2013-03-03 01:03:33 +00:00
static
# endif
2016-02-06 22:12:55 +00:00
objptridx_t drop_powerup ( int type , int id , int num , const vms_vector & init_vel , const vms_vector & pos , const vsegptridx_t segnum , bool player )
2006-03-20 17:12:09 +00:00
{
2014-08-23 23:53:56 +00:00
objptridx_t objnum = object_none ;
2015-11-26 02:56:54 +00:00
vms_vector new_pos ;
2006-03-20 17:12:09 +00:00
int count ;
switch ( type ) {
case OBJ_POWERUP :
for ( count = 0 ; count < num ; count + + ) {
int rand_scale ;
2015-11-26 02:56:54 +00:00
auto new_velocity = init_vel ;
2015-03-12 02:21:19 +00:00
const auto old_mag = vm_vec_mag_quick ( init_vel ) ;
2006-03-20 17:12:09 +00:00
// We want powerups to move more in network mode.
if ( ( Game_mode & GM_MULTI ) & & ! ( Game_mode & GM_MULTI_ROBOTS ) ) {
rand_scale = 4 ;
// extra life powerups are converted to invulnerability in multiplayer, for what is an extra life, anyway?
if ( id = = POW_EXTRA_LIFE )
id = POW_INVULNERABILITY ;
} else
rand_scale = 2 ;
new_velocity . x + = fixmul ( old_mag + F1_0 * 32 , d_rand ( ) * rand_scale - 16384 * rand_scale ) ;
new_velocity . y + = fixmul ( old_mag + F1_0 * 32 , d_rand ( ) * rand_scale - 16384 * rand_scale ) ;
new_velocity . z + = fixmul ( old_mag + F1_0 * 32 , d_rand ( ) * rand_scale - 16384 * rand_scale ) ;
// Give keys zero velocity so they can be tracked better in multi
if ( ( Game_mode & GM_MULTI ) & & ( id > = POW_KEY_BLUE ) & & ( id < = POW_KEY_GOLD ) )
2014-09-28 21:11:04 +00:00
vm_vec_zero ( new_velocity ) ;
2006-03-20 17:12:09 +00:00
2014-10-28 01:46:07 +00:00
new_pos = pos ;
2006-03-20 17:12:09 +00:00
// new_pos.x += (d_rand()-16384)*8;
// new_pos.y += (d_rand()-16384)*8;
// new_pos.z += (d_rand()-16384)*8;
if ( Game_mode & GM_MULTI )
{
if ( Net_create_loc > = MAX_NET_CREATE_OBJECTS )
{
2013-12-26 22:21:16 +00:00
return object_none ;
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
if ( ( Game_mode & GM_NETWORK ) & & Network_status = = NETSTAT_ENDLEVEL )
2013-12-26 22:21:16 +00:00
return object_none ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
2014-10-26 21:35:16 +00:00
auto obj = obj_create ( OBJ_POWERUP , id , segnum , new_pos , & vmd_identity_matrix , Powerup_info [ id ] . size , CT_POWERUP , MT_PHYSICS , RT_POWERUP ) ;
2013-12-29 04:28:07 +00:00
objnum = obj ;
2006-03-20 17:12:09 +00:00
2016-10-29 23:16:15 +00:00
if ( objnum = = object_none )
{
2006-03-20 17:12:09 +00:00
Int3 ( ) ;
2016-10-29 23:16:15 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
}
2016-12-04 20:39:03 +00:00
# if defined(DXX_BUILD_DESCENT_II)
if ( player )
obj - > flags | = OF_PLAYER_DROPPED ;
# endif
2006-03-20 17:12:09 +00:00
if ( Game_mode & GM_MULTI )
{
Net_create_objnums [ Net_create_loc + + ] = objnum ;
}
obj - > mtype . phys_info . velocity = new_velocity ;
obj - > mtype . phys_info . drag = 512 ; //1024;
obj - > mtype . phys_info . mass = F1_0 ;
obj - > mtype . phys_info . flags = PF_BOUNCE ;
2013-11-24 05:30:37 +00:00
obj - > rtype . vclip_info . vclip_num = Powerup_info [ get_powerup_id ( obj ) ] . vclip_num ;
2006-03-20 17:12:09 +00:00
obj - > rtype . vclip_info . frametime = Vclip [ obj - > rtype . vclip_info . vclip_num ] . frame_time ;
obj - > rtype . vclip_info . framenum = 0 ;
2013-11-24 05:30:37 +00:00
switch ( get_powerup_id ( obj ) ) {
2006-03-20 17:12:09 +00:00
case POW_MISSILE_1 :
case POW_MISSILE_4 :
case POW_SHIELD_BOOST :
case POW_ENERGY :
obj - > lifeleft = ( d_rand ( ) + F1_0 * 3 ) * 64 ; // Lives for 3 to 3.5 binary minutes (a binary minute is 64 seconds)
if ( Game_mode & GM_MULTI )
obj - > lifeleft / = 2 ;
break ;
default :
// if (Game_mode & GM_MULTI)
// obj->lifeleft = (d_rand() + F1_0*3) * 64; // Lives for 5 to 5.5 binary minutes (a binary minute is 64 seconds)
break ;
}
}
break ;
case OBJ_ROBOT :
for ( count = 0 ; count < num ; count + + ) {
int rand_scale ;
2014-10-29 03:24:31 +00:00
auto new_velocity = vm_vec_normalized_quick ( init_vel ) ;
2015-03-12 02:21:19 +00:00
const auto old_mag = vm_vec_mag_quick ( init_vel ) ;
2006-03-20 17:12:09 +00:00
// We want powerups to move more in network mode.
// if (Game_mode & GM_MULTI)
// rand_scale = 4;
// else
rand_scale = 2 ;
new_velocity . x + = ( d_rand ( ) - 16384 ) * 2 ;
new_velocity . y + = ( d_rand ( ) - 16384 ) * 2 ;
new_velocity . z + = ( d_rand ( ) - 16384 ) * 2 ;
2014-09-28 21:11:04 +00:00
vm_vec_normalize_quick ( new_velocity ) ;
2014-09-28 21:11:05 +00:00
vm_vec_scale ( new_velocity , ( F1_0 * 32 + old_mag ) * rand_scale ) ;
2014-10-28 01:46:07 +00:00
new_pos = pos ;
2006-03-20 17:12:09 +00:00
// This is dangerous, could be outside mine.
// new_pos.x += (d_rand()-16384)*8;
// new_pos.y += (d_rand()-16384)*7;
// new_pos.z += (d_rand()-16384)*6;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
2016-02-06 22:12:55 +00:00
const auto robot_id = ObjId [ type ] ;
2013-03-03 01:03:33 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2016-02-06 22:12:55 +00:00
const auto robot_id = id ;
2013-03-03 01:03:33 +00:00
# endif
2017-01-22 09:55:42 +00:00
const auto & & obj = robot_create ( id , segnum , new_pos , & vmd_identity_matrix , Polygon_models [ Robot_info [ robot_id ] . model_num ] . rad , ai_behavior : : AIB_NORMAL ) ;
2006-03-20 17:12:09 +00:00
2013-12-29 04:28:07 +00:00
objnum = obj ;
2016-10-29 23:16:15 +00:00
if ( objnum = = object_none )
{
2006-03-20 17:12:09 +00:00
Int3 ( ) ;
2016-10-29 23:16:15 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
}
2016-02-06 22:12:55 +00:00
# if defined(DXX_BUILD_DESCENT_II)
if ( player )
obj - > flags | = OF_PLAYER_DROPPED ;
# endif
2006-03-20 17:12:09 +00:00
if ( Game_mode & GM_MULTI )
{
Net_create_objnums [ Net_create_loc + + ] = objnum ;
}
//Set polygon-object-specific data
2013-10-07 23:52:33 +00:00
obj - > rtype . pobj_info . model_num = Robot_info [ get_robot_id ( obj ) ] . model_num ;
2006-03-20 17:12:09 +00:00
obj - > rtype . pobj_info . subobj_flags = 0 ;
//set Physics info
obj - > mtype . phys_info . velocity = new_velocity ;
2013-10-07 23:52:33 +00:00
obj - > mtype . phys_info . mass = Robot_info [ get_robot_id ( obj ) ] . mass ;
obj - > mtype . phys_info . drag = Robot_info [ get_robot_id ( obj ) ] . drag ;
2006-03-20 17:12:09 +00:00
obj - > mtype . phys_info . flags | = ( PF_LEVELLING ) ;
2013-10-07 23:52:33 +00:00
obj - > shields = Robot_info [ get_robot_id ( obj ) ] . strength ;
2006-03-20 17:12:09 +00:00
2013-12-25 03:16:41 +00:00
ai_local * ailp = & obj - > ctype . ai_info . ail ;
2015-04-02 02:36:53 +00:00
ailp - > player_awareness_type = player_awareness_type_t : : PA_WEAPON_ROBOT_COLLISION ;
2013-12-25 03:16:41 +00:00
ailp - > player_awareness_time = F1_0 * 3 ;
2006-03-20 17:12:09 +00:00
obj - > ctype . ai_info . CURRENT_STATE = AIS_LOCK ;
obj - > ctype . ai_info . GOAL_STATE = AIS_LOCK ;
obj - > ctype . ai_info . REMOTE_OWNER = - 1 ;
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
// At JasenW's request, robots which contain robots
// sometimes drop shields.
if ( d_rand ( ) > 16384 )
2016-02-06 22:12:55 +00:00
drop_powerup ( OBJ_POWERUP , POW_SHIELD_BOOST , 1 , init_vel , pos , segnum , false ) ;
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
break ;
default :
Error ( " Error: Illegal type (%i) in object spawning. \n " , type ) ;
}
return objnum ;
}
2015-05-09 17:39:00 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2016-10-02 00:34:42 +00:00
static bool skip_create_egg_powerup ( const object & player , powerup_type_t powerup )
2015-05-09 17:39:00 +00:00
{
2015-10-30 02:52:55 +00:00
fix current ;
2016-10-02 00:34:42 +00:00
auto & player_info = player . ctype . player_info ;
2015-05-09 17:39:00 +00:00
if ( powerup = = POW_SHIELD_BOOST )
2016-10-02 00:34:42 +00:00
current = player . shields ;
2015-05-09 17:39:00 +00:00
else if ( powerup = = POW_ENERGY )
2016-09-11 18:49:13 +00:00
current = player_info . energy ;
2015-05-09 17:39:00 +00:00
else
return false ;
int limit ;
if ( current > = i2f ( 150 ) )
limit = 8192 ;
else if ( current > = i2f ( 100 ) )
limit = 16384 ;
else
return false ;
return d_rand ( ) > limit ;
}
# endif
2006-03-20 17:12:09 +00:00
// ----------------------------------------------------------------------------
// Returns created object number.
// If object dropped by player, set flag.
2015-05-09 17:39:01 +00:00
static objptridx_t object_create_player_egg ( int type , int id , int num , const vms_vector & init_vel , const vms_vector & pos , const vsegptridx_t segnum )
2006-03-20 17:12:09 +00:00
{
2016-02-06 22:12:55 +00:00
return drop_powerup ( type , id , num , init_vel , pos , segnum , true ) ;
2015-05-09 17:39:01 +00:00
}
objptridx_t object_create_robot_egg ( int type , int id , int num , const vms_vector & init_vel , const vms_vector & pos , const vsegptridx_t segnum )
{
# if defined(DXX_BUILD_DESCENT_II)
if ( ! ( Game_mode & GM_MULTI ) )
2006-03-20 17:12:09 +00:00
{
2015-05-09 17:39:01 +00:00
if ( type = = OBJ_POWERUP )
2006-03-20 17:12:09 +00:00
{
2016-10-02 00:34:42 +00:00
if ( skip_create_egg_powerup ( get_local_plrobj ( ) , static_cast < powerup_type_t > ( id ) ) )
2015-05-09 17:39:00 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
}
}
2013-03-03 01:03:33 +00:00
# endif
2016-02-06 22:12:55 +00:00
const auto rval = drop_powerup ( type , id , num , init_vel , pos , segnum , false ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2013-12-26 22:21:16 +00:00
if ( rval ! = object_none )
2006-03-20 17:12:09 +00:00
{
2015-05-09 17:39:01 +00:00
if ( type = = OBJ_POWERUP )
2006-03-20 17:12:09 +00:00
{
2015-05-09 17:39:01 +00:00
if ( id = = POW_VULCAN_WEAPON | | id = = POW_GAUSS_WEAPON )
2014-08-23 23:53:56 +00:00
rval - > ctype . powerup_info . count = VULCAN_WEAPON_AMMO_AMOUNT ;
2015-05-09 17:39:01 +00:00
else if ( id = = POW_OMEGA_WEAPON )
2014-08-23 23:53:56 +00:00
rval - > ctype . powerup_info . count = MAX_OMEGA_CHARGE ;
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
return rval ;
}
2015-05-09 17:39:01 +00:00
objptridx_t object_create_robot_egg ( const vobjptr_t objp )
{
2016-01-09 16:38:14 +00:00
return object_create_robot_egg ( objp - > contains_type , objp - > contains_id , objp - > contains_count , objp - > mtype . phys_info . velocity , objp - > pos , vsegptridx ( objp - > segnum ) ) ;
2015-05-09 17:39:01 +00:00
}
2006-03-20 17:12:09 +00:00
// -- extern int Items_destroyed;
// -------------------------------------------------------------------------------------------------------
// Put count objects of type type (eg, powerup), id = id (eg, energy) into *objp, then drop them! Yippee!
// Returns created object number.
2016-12-05 00:26:11 +00:00
objptridx_t call_object_create_egg ( const object_base & objp , const unsigned count , const int id )
2006-03-20 17:12:09 +00:00
{
if ( count > 0 ) {
2016-12-05 00:26:11 +00:00
return object_create_player_egg ( OBJ_POWERUP , id , count , objp . mtype . phys_info . velocity , objp . pos , vsegptridx ( objp . segnum ) ) ;
2006-03-20 17:12:09 +00:00
}
2013-12-26 22:21:16 +00:00
return object_none ;
2006-03-20 17:12:09 +00:00
}
//what vclip does this explode with?
2015-11-15 22:30:41 +00:00
int get_explosion_vclip ( const vcobjptr_t obj , explosion_vclip_stage stage )
2006-03-20 17:12:09 +00:00
{
if ( obj - > type = = OBJ_ROBOT ) {
2015-11-15 22:30:41 +00:00
const auto vclip_ptr = stage = = explosion_vclip_stage : : s0
? & robot_info : : exp1_vclip_num
: & robot_info : : exp2_vclip_num ;
const auto vclip_num = Robot_info [ get_robot_id ( obj ) ] . * vclip_ptr ;
if ( vclip_num > - 1 )
return vclip_num ;
2006-03-20 17:12:09 +00:00
}
else if ( obj - > type = = OBJ_PLAYER & & Player_ship - > expl_vclip_num > - 1 )
return Player_ship - > expl_vclip_num ;
return VCLIP_SMALL_EXPLOSION ; //default
}
//blow up a polygon model
2016-04-23 17:59:47 +00:00
static void explode_model ( object_base & obj )
2006-03-20 17:12:09 +00:00
{
2016-04-23 17:59:47 +00:00
Assert ( obj . render_type = = RT_POLYOBJ ) ;
2006-03-20 17:12:09 +00:00
2016-04-23 17:59:47 +00:00
const auto poly_model_num = obj . rtype . pobj_info . model_num ;
2015-11-15 22:30:41 +00:00
const auto dying_model_num = Dying_modelnums [ poly_model_num ] ;
const auto model_num = ( dying_model_num ! = - 1 )
2016-04-23 17:59:47 +00:00
? ( obj . rtype . pobj_info . model_num = dying_model_num )
2015-11-15 22:30:41 +00:00
: poly_model_num ;
const auto n_models = Polygon_models [ model_num ] . n_models ;
if ( n_models > 1 ) {
for ( unsigned i = 1 ; i < n_models ; + + i )
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2016-04-23 17:59:47 +00:00
if ( ! ( i = = 5 & & obj . type = = OBJ_ROBOT & & get_robot_id ( obj ) = = 44 ) ) //energy sucker energy part
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
object_create_debris ( obj , i ) ;
//make parent object only draw center part
2016-04-23 17:59:47 +00:00
obj . rtype . pobj_info . subobj_flags = 1 ;
2006-03-20 17:12:09 +00:00
}
}
//if the object has a destroyed model, switch to it. Otherwise, delete it.
2016-04-23 17:59:47 +00:00
static void maybe_delete_object ( object_base & del_obj )
2006-03-20 17:12:09 +00:00
{
2016-04-23 17:59:47 +00:00
const auto dead_modelnum = Dead_modelnums [ del_obj . rtype . pobj_info . model_num ] ;
if ( dead_modelnum ! = - 1 )
{
del_obj . rtype . pobj_info . model_num = dead_modelnum ;
del_obj . flags | = OF_DESTROYED ;
2006-03-20 17:12:09 +00:00
}
else { //normal, multi-stage explosion
2016-04-23 17:59:47 +00:00
if ( del_obj . type = = OBJ_PLAYER )
del_obj . render_type = RT_NONE ;
2006-03-20 17:12:09 +00:00
else
2016-04-23 17:59:47 +00:00
del_obj . flags | = OF_SHOULD_BE_DEAD ;
2006-03-20 17:12:09 +00:00
}
}
// -------------------------------------------------------------------------------------------------------
//blow up an object. Takes the object to destroy, and the point of impact
2014-10-02 03:02:34 +00:00
void explode_object ( const vobjptridx_t hitobj , fix delay_time )
2006-03-20 17:12:09 +00:00
{
if ( hitobj - > flags & OF_EXPLODING ) return ;
if ( delay_time ) { //wait a little while before creating explosion
//create a placeholder object to do the delay, with id==-1
2016-01-09 16:38:14 +00:00
auto obj = obj_create ( OBJ_FIREBALL , - 1 , vsegptridx ( hitobj - > segnum ) , hitobj - > pos , & vmd_identity_matrix , 0 ,
2006-03-20 17:12:09 +00:00
CT_EXPLOSION , MT_NONE , RT_NONE ) ;
2014-09-08 03:24:48 +00:00
if ( obj = = object_none ) {
2006-03-20 17:12:09 +00:00
maybe_delete_object ( hitobj ) ; //no explosion, die instantly
Int3 ( ) ;
return ;
}
//now set explosion-specific data
obj - > lifeleft = delay_time ;
2014-01-11 23:55:24 +00:00
obj - > ctype . expl_info . delete_objnum = hitobj ;
2006-03-20 17:12:09 +00:00
obj - > ctype . expl_info . delete_time = - 1 ;
obj - > ctype . expl_info . spawn_time = 0 ;
}
else {
int vclip_num ;
2015-11-15 22:30:41 +00:00
vclip_num = get_explosion_vclip ( hitobj , explosion_vclip_stage : : s0 ) ;
2006-03-20 17:12:09 +00:00
2016-01-09 16:38:14 +00:00
auto expl_obj = object_create_explosion ( vsegptridx ( hitobj - > segnum ) , hitobj - > pos , fixmul ( hitobj - > size , EXPLOSION_SCALE ) , vclip_num ) ;
2006-03-20 17:12:09 +00:00
if ( ! expl_obj ) {
maybe_delete_object ( hitobj ) ; //no explosion, die instantly
return ;
}
//don't make debris explosions have physics, because they often
//happen when the debris has hit the wall, so the fireball is trying
//to move into the wall, which shows off FVI problems.
if ( hitobj - > type ! = OBJ_DEBRIS & & hitobj - > movement_type = = MT_PHYSICS ) {
expl_obj - > movement_type = MT_PHYSICS ;
expl_obj - > mtype . phys_info = hitobj - > mtype . phys_info ;
}
if ( hitobj - > render_type = = RT_POLYOBJ & & hitobj - > type ! = OBJ_DEBRIS )
explode_model ( hitobj ) ;
maybe_delete_object ( hitobj ) ;
}
hitobj - > flags | = OF_EXPLODING ; //say that this is blowing up
hitobj - > control_type = CT_NONE ; //become inert while exploding
}
//do whatever needs to be done for this piece of debris for this frame
2014-10-02 03:02:34 +00:00
void do_debris_frame ( const vobjptridx_t obj )
2006-03-20 17:12:09 +00:00
{
Assert ( obj - > control_type = = CT_DEBRIS ) ;
if ( obj - > lifeleft < 0 )
explode_object ( obj , 0 ) ;
}
//do whatever needs to be done for this explosion for this frame
2014-10-02 03:02:34 +00:00
void do_explosion_sequence ( const vobjptr_t obj )
2006-03-20 17:12:09 +00:00
{
Assert ( obj - > control_type = = CT_EXPLOSION ) ;
//See if we should die of old age
if ( obj - > lifeleft < = 0 ) { // We died of old age
obj - > flags | = OF_SHOULD_BE_DEAD ;
obj - > lifeleft = 0 ;
}
//See if we should create a secondary explosion
if ( obj - > lifeleft < = obj - > ctype . expl_info . spawn_time ) {
2014-08-16 23:18:17 +00:00
auto del_obj = vobjptridx ( obj - > ctype . expl_info . delete_objnum ) ;
2014-10-26 21:33:50 +00:00
auto & spawn_pos = del_obj - > pos ;
2013-11-24 05:30:37 +00:00
Assert ( del_obj - > type = = OBJ_ROBOT | | del_obj - > type = = OBJ_CLUTTER | | del_obj - > type = = OBJ_CNTRLCEN | | del_obj - > type = = OBJ_PLAYER ) ;
2013-12-26 22:21:16 +00:00
Assert ( del_obj - > segnum ! = segment_none ) ;
2006-03-20 17:12:09 +00:00
2015-08-17 02:44:56 +00:00
const auto & & expl_obj = [ & ] {
2015-11-15 22:30:41 +00:00
const auto vclip_num = get_explosion_vclip ( del_obj , explosion_vclip_stage : : s1 ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2015-08-17 02:44:56 +00:00
if ( del_obj - > type = = OBJ_ROBOT )
{
const auto & ri = Robot_info [ get_robot_id ( del_obj ) ] ;
if ( ri . badass )
2016-01-09 16:38:14 +00:00
return object_create_badass_explosion ( object_none , vsegptridx ( del_obj - > segnum ) , spawn_pos , fixmul ( del_obj - > size , EXPLOSION_SCALE ) , vclip_num , F1_0 * ri . badass , i2f ( 4 ) * ri . badass , i2f ( 35 ) * ri . badass , object_none ) ;
2015-08-17 02:44:56 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2016-01-09 16:38:14 +00:00
return object_create_explosion ( vsegptridx ( del_obj - > segnum ) , spawn_pos , fixmul ( del_obj - > size , EXPLOSION_SCALE ) , vclip_num ) ;
2015-08-17 02:44:56 +00:00
} ( ) ;
2006-03-20 17:12:09 +00:00
if ( ( del_obj - > contains_count > 0 ) & & ! ( Game_mode & GM_MULTI ) ) { // Multiplayer handled outside of this code!!
// If dropping a weapon that the player has, drop energy instead, unless it's vulcan, in which case drop vulcan ammo.
if ( del_obj - > contains_type = = OBJ_POWERUP )
maybe_replace_powerup_with_energy ( del_obj ) ;
2015-05-09 17:39:01 +00:00
object_create_robot_egg ( del_obj ) ;
2006-03-20 17:12:09 +00:00
} else if ( ( del_obj - > type = = OBJ_ROBOT ) & & ! ( Game_mode & GM_MULTI ) ) { // Multiplayer handled outside this code!!
2013-11-24 05:30:37 +00:00
robot_info * robptr = & Robot_info [ get_robot_id ( del_obj ) ] ;
2006-03-20 17:12:09 +00:00
if ( robptr - > contains_count ) {
if ( ( ( d_rand ( ) * 16 ) > > 15 ) < robptr - > contains_prob ) {
del_obj - > contains_count = ( ( d_rand ( ) * robptr - > contains_count ) > > 15 ) + 1 ;
del_obj - > contains_type = robptr - > contains_type ;
del_obj - > contains_id = robptr - > contains_id ;
maybe_replace_powerup_with_energy ( del_obj ) ;
2015-05-09 17:39:01 +00:00
object_create_robot_egg ( del_obj ) ;
2006-03-20 17:12:09 +00:00
}
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2013-11-03 22:27:28 +00:00
if ( robot_is_thief ( robptr ) )
2006-03-20 17:12:09 +00:00
drop_stolen_items ( del_obj ) ;
2013-11-24 05:30:37 +00:00
if ( robot_is_companion ( robptr ) ) {
2006-03-20 17:12:09 +00:00
DropBuddyMarker ( del_obj ) ;
}
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
}
2013-11-24 05:30:37 +00:00
const robot_info * robptr = & Robot_info [ get_robot_id ( del_obj ) ] ;
if ( robptr - > exp2_sound_num > - 1 )
2016-01-09 16:38:14 +00:00
digi_link_sound_to_pos ( robptr - > exp2_sound_num , vsegptridx ( del_obj - > segnum ) , 0 , spawn_pos , 0 , F1_0 ) ;
2006-03-20 17:12:09 +00:00
//PLAY_SOUND_3D( Robot_info[del_obj->id].exp2_sound_num, spawn_pos, del_obj->segnum );
obj - > ctype . expl_info . spawn_time = - 1 ;
//make debris
if ( del_obj - > render_type = = RT_POLYOBJ )
explode_model ( del_obj ) ; //explode a polygon model
//set some parm in explosion
2009-02-22 10:53:10 +00:00
//If num_objects < MAX_USED_OBJECTS, expl_obj could be set to dead before this setting causing the delete_obj not to be removed. If so, directly delete del_obj
if ( expl_obj & & ! ( expl_obj - > flags & OF_SHOULD_BE_DEAD ) )
{
2006-03-20 17:12:09 +00:00
if ( del_obj - > movement_type = = MT_PHYSICS ) {
expl_obj - > movement_type = MT_PHYSICS ;
expl_obj - > mtype . phys_info = del_obj - > mtype . phys_info ;
}
expl_obj - > ctype . expl_info . delete_time = expl_obj - > lifeleft / 2 ;
2013-12-24 04:53:59 +00:00
expl_obj - > ctype . expl_info . delete_objnum = del_obj ;
2006-03-20 17:12:09 +00:00
}
else {
maybe_delete_object ( del_obj ) ;
}
}
//See if we should delete an object
if ( obj - > lifeleft < = obj - > ctype . expl_info . delete_time ) {
2015-07-12 01:04:19 +00:00
const auto & & del_obj = vobjptr ( obj - > ctype . expl_info . delete_objnum ) ;
2006-03-20 17:12:09 +00:00
obj - > ctype . expl_info . delete_time = - 1 ;
maybe_delete_object ( del_obj ) ;
}
}
# define EXPL_WALL_TIME (f1_0)
# define EXPL_WALL_TOTAL_FIREBALLS 32
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
# define EXPL_WALL_FIREBALL_SIZE 0x48000 //smallest size
# elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
# define EXPL_WALL_FIREBALL_SIZE (0x48000*6 / 10) //smallest size
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
2015-12-22 04:18:50 +00:00
}
namespace dcx {
2015-02-28 19:36:01 +00:00
array < expl_wall , MAX_EXPLODING_WALLS > expl_wall_list ;
2006-03-20 17:12:09 +00:00
void init_exploding_walls ( )
{
2015-02-28 19:36:01 +00:00
range_for ( auto & i , expl_wall_list )
2017-01-08 22:32:00 +00:00
{
expl_wall * const p = & i ;
DXX_POISON_MEMORY ( reinterpret_cast < uint8_t * > ( p ) , sizeof ( * p ) , 0xfd ) ;
2015-02-28 19:36:01 +00:00
i . segnum = segment_none ;
2017-01-08 22:32:00 +00:00
}
2006-03-20 17:12:09 +00:00
}
2015-12-22 04:18:50 +00:00
}
namespace dsx {
2006-03-20 17:12:09 +00:00
//explode the given wall
2014-11-23 04:58:45 +00:00
void explode_wall ( const vsegptridx_t segnum , int sidenum )
2006-03-20 17:12:09 +00:00
{
//find a free slot
2015-02-28 19:36:01 +00:00
const auto e = end ( expl_wall_list ) ;
2015-11-26 02:56:54 +00:00
const auto predicate = [ ] ( expl_wall & w ) {
return w . segnum = = segment_none ;
2015-02-28 19:36:01 +00:00
} ;
const auto i = std : : find_if ( begin ( expl_wall_list ) , e , predicate ) ;
if ( i = = e )
{ //didn't find slot.
2006-03-20 17:12:09 +00:00
Int3 ( ) ;
return ;
}
2015-02-28 19:36:01 +00:00
auto & w = * i ;
w . segnum = segnum ;
w . sidenum = sidenum ;
w . time = 0 ;
2006-03-20 17:12:09 +00:00
//play one long sound for whole door wall explosion
2014-11-23 04:58:45 +00:00
const auto pos = compute_center_point_on_side ( segnum , sidenum ) ;
2014-10-26 21:33:50 +00:00
digi_link_sound_to_pos ( SOUND_EXPLODING_WALL , segnum , sidenum , pos , 0 , F1_0 ) ;
2006-03-20 17:12:09 +00:00
}
//handle walls for this frame
//note: this wall code assumes the wall is not triangulated
void do_exploding_wall_frame ( )
{
2015-02-28 19:36:01 +00:00
range_for ( auto & i , expl_wall_list )
{
auto segnum = i . segnum ;
2006-03-20 17:12:09 +00:00
2013-12-26 22:21:16 +00:00
if ( segnum ! = segment_none ) {
2017-02-19 19:33:38 +00:00
unsigned sidenum = i . sidenum ;
2006-03-20 17:12:09 +00:00
fix oldfrac , newfrac ;
int old_count , new_count , e ; //n,
2015-02-28 19:36:01 +00:00
oldfrac = fixdiv ( i . time , EXPL_WALL_TIME ) ;
2006-03-20 17:12:09 +00:00
2015-02-28 19:36:01 +00:00
i . time + = FrameTime ;
if ( i . time > EXPL_WALL_TIME )
i . time = EXPL_WALL_TIME ;
2006-03-20 17:12:09 +00:00
2014-11-23 04:58:45 +00:00
const auto seg = vsegptridx ( segnum ) ;
2015-02-28 19:36:01 +00:00
if ( i . time > ( EXPL_WALL_TIME * 3 ) / 4 ) {
2016-02-12 04:02:28 +00:00
auto & w1 = * vwallptr ( seg - > sides [ sidenum ] . wall_num ) ;
const auto a = w1 . clip_num ;
const auto n = WallAnims [ a ] . num_frames ;
2006-03-20 17:12:09 +00:00
2015-12-22 04:18:51 +00:00
const auto & & csegp = seg . absolute_sibling ( seg - > children [ sidenum ] ) ;
2014-09-06 04:06:18 +00:00
auto cside = find_connect_side ( seg , csegp ) ;
2006-03-20 17:12:09 +00:00
wall_set_tmap_num ( seg , sidenum , csegp , cside , a , n - 1 ) ;
2016-02-12 04:02:28 +00:00
w1 . flags | = WALL_BLASTED ;
vwallptr ( csegp - > sides [ cside ] . wall_num ) - > flags | = WALL_BLASTED ;
2006-03-20 17:12:09 +00:00
}
2015-02-28 19:36:01 +00:00
newfrac = fixdiv ( i . time , EXPL_WALL_TIME ) ;
2006-03-20 17:12:09 +00:00
old_count = f2i ( EXPL_WALL_TOTAL_FIREBALLS * fixmul ( oldfrac , oldfrac ) ) ;
new_count = f2i ( EXPL_WALL_TOTAL_FIREBALLS * fixmul ( newfrac , newfrac ) ) ;
//n = new_count - old_count;
//now create all the next explosions
for ( e = old_count ; e < new_count ; e + + ) {
fix size ;
//calc expl position
2014-12-14 05:23:00 +00:00
const auto vertnum_list = get_side_verts ( seg , sidenum ) ;
2006-03-20 17:12:09 +00:00
2014-11-02 03:42:34 +00:00
const auto & v0 = Vertices [ vertnum_list [ 0 ] ] ;
const auto & v1 = Vertices [ vertnum_list [ 1 ] ] ;
const auto & v2 = Vertices [ vertnum_list [ 2 ] ] ;
2006-03-20 17:12:09 +00:00
2014-11-02 03:42:34 +00:00
const auto vv0 = vm_vec_sub ( v0 , v1 ) ;
const auto vv1 = vm_vec_sub ( v2 , v1 ) ;
2006-03-20 17:12:09 +00:00
2014-11-02 03:43:57 +00:00
auto pos = vm_vec_scale_add ( v1 , vv0 , d_rand ( ) * 2 ) ;
2014-09-28 21:43:14 +00:00
vm_vec_scale_add2 ( pos , vv1 , d_rand ( ) * 2 ) ;
2006-03-20 17:12:09 +00:00
size = EXPL_WALL_FIREBALL_SIZE + ( 2 * EXPL_WALL_FIREBALL_SIZE * e / EXPL_WALL_TOTAL_FIREBALLS ) ;
//fireballs start away from door, with subsequent ones getting closer
2015-12-22 04:18:50 +00:00
vm_vec_scale_add2 ( pos , vcsegptr ( segnum ) - > sides [ sidenum ] . normals [ 0 ] , size * ( EXPL_WALL_TOTAL_FIREBALLS - e ) / EXPL_WALL_TOTAL_FIREBALLS ) ;
2006-03-20 17:12:09 +00:00
2016-01-09 16:38:14 +00:00
const auto & & is = vsegptridx ( i . segnum ) ;
2006-03-20 17:12:09 +00:00
if ( e & 3 ) //3 of 4 are normal
2016-01-09 16:38:14 +00:00
object_create_explosion ( is , pos , size , VCLIP_SMALL_EXPLOSION ) ;
2006-03-20 17:12:09 +00:00
else
2016-01-09 16:38:14 +00:00
object_create_badass_explosion ( object_none , is , pos ,
2006-03-20 17:12:09 +00:00
size ,
VCLIP_SMALL_EXPLOSION ,
i2f ( 4 ) , // damage strength
i2f ( 20 ) , // damage radius
i2f ( 50 ) , // damage force
2014-01-10 04:02:53 +00:00
object_none // parent id
2006-03-20 17:12:09 +00:00
) ;
}
2015-02-28 19:36:01 +00:00
if ( i . time > = EXPL_WALL_TIME )
2017-01-08 22:32:00 +00:00
{
expl_wall * const p = & i ;
DXX_POISON_MEMORY ( reinterpret_cast < uint8_t * > ( p ) , sizeof ( * p ) , 0xfd ) ;
2015-02-28 19:36:01 +00:00
i . segnum = segment_none ; //flag this slot as free
2017-01-08 22:32:00 +00:00
}
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
//creates afterburner blobs behind the specified object
2014-10-02 03:02:34 +00:00
void drop_afterburner_blobs ( const vobjptr_t obj , int count , fix size_scale , fix lifetime )
2006-03-20 17:12:09 +00:00
{
2014-11-02 03:43:57 +00:00
auto pos_left = vm_vec_scale_add ( obj - > pos , obj - > orient . fvec , - obj - > size ) ;
2014-09-28 21:43:14 +00:00
vm_vec_scale_add2 ( pos_left , obj - > orient . rvec , - obj - > size / 4 ) ;
2014-11-02 03:43:57 +00:00
const auto pos_right = vm_vec_scale_add ( pos_left , obj - > orient . rvec , obj - > size / 2 ) ;
2006-03-20 17:12:09 +00:00
if ( count = = 1 )
2014-09-28 21:43:01 +00:00
vm_vec_avg ( pos_left , pos_left , pos_right ) ;
2006-03-20 17:12:09 +00:00
2016-01-09 16:38:13 +00:00
{
const auto & & segnum = find_point_seg ( pos_left , vsegptridx ( obj - > segnum ) ) ;
2013-12-26 22:21:16 +00:00
if ( segnum ! = segment_none )
2014-10-26 21:36:25 +00:00
object_create_explosion ( segnum , pos_left , size_scale , VCLIP_AFTERBURNER_BLOB ) ;
2016-01-09 16:38:13 +00:00
}
2006-03-20 17:12:09 +00:00
if ( count > 1 ) {
2016-01-09 16:38:13 +00:00
const auto & & segnum = find_point_seg ( pos_right , vsegptridx ( obj - > segnum ) ) ;
2013-12-26 22:21:16 +00:00
if ( segnum ! = segment_none ) {
2014-10-26 21:36:25 +00:00
auto blob_obj = object_create_explosion ( segnum , pos_right , size_scale , VCLIP_AFTERBURNER_BLOB ) ;
2014-09-13 22:01:17 +00:00
if ( lifetime ! = - 1 & & blob_obj ! = object_none )
2006-03-20 17:12:09 +00:00
blob_obj - > lifeleft = lifetime ;
}
}
}
2009-10-05 02:51:37 +00:00
/*
2016-01-09 16:38:14 +00:00
* reads n expl_wall structs from a PHYSFS_File and swaps if specified
2009-10-05 02:51:37 +00:00
*/
2016-01-09 16:38:14 +00:00
void expl_wall_read_n_swap ( PHYSFS_File * fp , int swap , partial_range_t < expl_wall * > r )
2009-10-05 02:51:37 +00:00
{
2015-02-28 19:36:01 +00:00
range_for ( auto & e , r )
{
disk_expl_wall d ;
PHYSFS_read ( fp , & d , sizeof ( d ) , 1 ) ;
if ( swap )
{
d . segnum = SWAPINT ( d . segnum ) ;
d . sidenum = SWAPINT ( d . sidenum ) ;
d . time = SWAPINT ( d . time ) ;
}
e . segnum = d . segnum ;
e . sidenum = d . sidenum ;
e . time = d . time ;
}
2009-10-05 02:51:37 +00:00
}
2013-03-03 01:03:33 +00:00
# endif
2015-12-22 04:18:50 +00:00
}