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 the control center
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#if !defined(_WIN32) && !defined(macintosh)
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
#include "pstypes.h"
|
2012-07-07 18:35:06 +00:00
|
|
|
#include "dxxerror.h"
|
2006-03-20 17:12:09 +00:00
|
|
|
#include "inferno.h"
|
|
|
|
#include "cntrlcen.h"
|
|
|
|
#include "game.h"
|
|
|
|
#include "laser.h"
|
|
|
|
#include "gameseq.h"
|
|
|
|
#include "ai.h"
|
2015-07-25 23:10:45 +00:00
|
|
|
#include "player.h"
|
2006-03-20 17:12:09 +00:00
|
|
|
#include "multi.h"
|
2015-10-09 02:46:10 +00:00
|
|
|
#include "fwd-wall.h"
|
2014-12-22 04:35:49 +00:00
|
|
|
#include "segment.h"
|
2006-03-20 17:12:09 +00:00
|
|
|
#include "object.h"
|
|
|
|
#include "robot.h"
|
|
|
|
#include "vclip.h"
|
2014-01-18 18:02:02 +00:00
|
|
|
#include "physfs-serial.h"
|
2006-03-20 17:12:09 +00:00
|
|
|
#include "fireball.h"
|
|
|
|
#include "endlevel.h"
|
2007-05-21 11:24:27 +00:00
|
|
|
#include "state.h"
|
2013-03-03 19:41:09 +00:00
|
|
|
#include "args.h"
|
2006-03-20 17:12:09 +00:00
|
|
|
|
2014-10-12 23:05:46 +00:00
|
|
|
#include "compiler-range_for.h"
|
2015-02-28 19:36:01 +00:00
|
|
|
#include "partial_range.h"
|
2014-10-12 23:05:46 +00:00
|
|
|
|
2015-12-22 04:18:50 +00:00
|
|
|
namespace dsx {
|
2015-02-28 19:36:01 +00:00
|
|
|
array<reactor, MAX_REACTORS> Reactors;
|
2013-03-03 01:03:33 +00:00
|
|
|
#if defined(DXX_BUILD_DESCENT_II)
|
2015-02-28 19:36:01 +00:00
|
|
|
unsigned Num_reactors;
|
2013-03-03 01:03:33 +00:00
|
|
|
//how long to blow up on insane
|
|
|
|
int Base_control_center_explosion_time=DEFAULT_CONTROL_CENTER_EXPLOSION_TIME;
|
|
|
|
fix64 Last_time_cc_vis_check = 0;
|
|
|
|
int Reactor_strength=-1; //-1 mean not set by designer
|
|
|
|
#endif
|
2015-12-22 04:18:50 +00:00
|
|
|
}
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
control_center_triggers ControlCenterTriggers;
|
|
|
|
|
2015-12-22 04:18:50 +00:00
|
|
|
namespace dcx {
|
|
|
|
|
2006-03-20 17:12:09 +00:00
|
|
|
int Control_center_been_hit;
|
|
|
|
int Control_center_player_been_seen;
|
|
|
|
int Control_center_next_fire_time;
|
|
|
|
int Control_center_present;
|
|
|
|
|
2015-12-22 04:18:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace dsx {
|
|
|
|
|
2013-09-22 22:26:27 +00:00
|
|
|
static void do_countdown_frame();
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
//return the position & orientation of a gun on the control center object
|
2016-04-23 17:59:47 +00:00
|
|
|
static void calc_controlcen_gun_point(reactor &r, object &obj, const uint_fast32_t gun_num)
|
2006-03-20 17:12:09 +00:00
|
|
|
{
|
|
|
|
//instance gun position & orientation
|
|
|
|
|
2016-04-23 17:59:47 +00:00
|
|
|
auto &gun_point = obj.ctype.reactor_info.gun_pos[gun_num];
|
|
|
|
auto &gun_dir = obj.ctype.reactor_info.gun_dir[gun_num];
|
|
|
|
const auto &&m = vm_transposed_matrix(obj.orient);
|
2015-08-26 03:15:10 +00:00
|
|
|
vm_vec_rotate(gun_point, r.gun_points[gun_num], m);
|
2016-04-23 17:59:47 +00:00
|
|
|
vm_vec_add2(gun_point, obj.pos);
|
2015-08-26 03:15:10 +00:00
|
|
|
vm_vec_rotate(gun_dir, r.gun_dirs[gun_num], m);
|
|
|
|
}
|
|
|
|
|
2016-04-23 17:59:47 +00:00
|
|
|
void calc_controlcen_gun_point(object &obj)
|
2015-08-26 03:15:10 +00:00
|
|
|
{
|
2016-04-23 17:59:47 +00:00
|
|
|
assert(obj.type == OBJ_CNTRLCEN);
|
|
|
|
assert(obj.render_type == RT_POLYOBJ);
|
2015-08-26 03:15:10 +00:00
|
|
|
auto &reactor = get_reactor_definition(get_reactor_id(obj));
|
|
|
|
for (uint_fast32_t i = reactor.n_guns; i--;)
|
|
|
|
calc_controlcen_gun_point(reactor, obj, i);
|
2006-03-20 17:12:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// Look at control center guns, find best one to fire at *objp.
|
|
|
|
// Return best gun number (one whose direction dotted with vector to player is largest).
|
|
|
|
// If best gun has negative dot, return -1, meaning no gun is good.
|
2014-11-02 03:41:45 +00:00
|
|
|
static int calc_best_gun(int num_guns, const vcobjptr_t objreactor, const vms_vector &objpos)
|
2006-03-20 17:12:09 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
fix best_dot;
|
|
|
|
int best_gun;
|
2014-11-02 03:41:45 +00:00
|
|
|
auto &gun_pos = objreactor->ctype.reactor_info.gun_pos;
|
|
|
|
auto &gun_dir = objreactor->ctype.reactor_info.gun_dir;
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
best_dot = -F1_0*2;
|
|
|
|
best_gun = -1;
|
|
|
|
|
|
|
|
for (i=0; i<num_guns; i++) {
|
|
|
|
fix dot;
|
2014-11-02 03:41:45 +00:00
|
|
|
const auto gun_vec = vm_vec_normalized_quick(vm_vec_sub(objpos, gun_pos[i]));
|
|
|
|
dot = vm_vec_dot(gun_dir[i], gun_vec);
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
if (dot > best_dot) {
|
|
|
|
best_dot = dot;
|
|
|
|
best_gun = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Assert(best_gun != -1); // Contact Mike. This is impossible. Or maybe you're getting an unnormalized vector somewhere.
|
|
|
|
|
|
|
|
if (best_dot < 0)
|
|
|
|
return -1;
|
|
|
|
else
|
|
|
|
return best_gun;
|
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 dcx {
|
2013-12-29 04:28:07 +00:00
|
|
|
objnum_t Dead_controlcen_object_num=object_none;
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
int Control_center_destroyed = 0;
|
|
|
|
fix Countdown_timer=0;
|
|
|
|
int Countdown_seconds_left=0, Total_countdown_time=0; //in whole seconds
|
|
|
|
|
2016-07-16 16:52:04 +00:00
|
|
|
constexpr int D1_Alan_pavlish_reactor_times[NDL] = {50, 45, 40, 35, 30};
|
2015-12-22 04:18:50 +00:00
|
|
|
}
|
|
|
|
namespace dsx {
|
2013-12-17 05:07:35 +00:00
|
|
|
#if defined(DXX_BUILD_DESCENT_II)
|
2016-07-16 16:52:04 +00:00
|
|
|
constexpr int D2_Alan_pavlish_reactor_times[NDL] = {90, 60, 45, 35, 30};
|
2013-03-03 01:03:33 +00:00
|
|
|
#endif
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// Called every frame. If control center been destroyed, then actually do something.
|
2015-12-22 04:18:50 +00:00
|
|
|
void do_controlcen_dead_frame()
|
2006-03-20 17:12:09 +00:00
|
|
|
{
|
2015-07-25 23:10:46 +00:00
|
|
|
if ((Game_mode & GM_MULTI) && (get_local_player().connected != CONNECT_PLAYING)) // if out of level already there's no need for this
|
2012-05-25 10:14:28 +00:00
|
|
|
return;
|
|
|
|
|
2013-12-26 22:21:16 +00:00
|
|
|
if ((Dead_controlcen_object_num != object_none) && (Countdown_seconds_left > 0))
|
2006-03-20 17:12:09 +00:00
|
|
|
if (d_rand() < FrameTime*4)
|
2013-03-03 01:03:33 +00:00
|
|
|
#if defined(DXX_BUILD_DESCENT_I)
|
|
|
|
#define CC_FIREBALL_SCALE F1_0*3
|
|
|
|
#elif defined(DXX_BUILD_DESCENT_II)
|
|
|
|
#define CC_FIREBALL_SCALE F1_0
|
|
|
|
#endif
|
2014-08-16 23:18:17 +00:00
|
|
|
create_small_fireball_on_object(vobjptridx(Dead_controlcen_object_num), CC_FIREBALL_SCALE, 1);
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
if (Control_center_destroyed && !Endlevel_sequence)
|
|
|
|
do_countdown_frame();
|
|
|
|
}
|
|
|
|
|
|
|
|
#define COUNTDOWN_VOICE_TIME fl2f(12.75)
|
|
|
|
|
|
|
|
void do_countdown_frame()
|
|
|
|
{
|
|
|
|
fix old_time;
|
|
|
|
int fc, div_scale;
|
|
|
|
|
|
|
|
if (!Control_center_destroyed) return;
|
|
|
|
|
2013-03-03 01:03:33 +00:00
|
|
|
#if defined(DXX_BUILD_DESCENT_II)
|
2006-03-20 17:12:09 +00:00
|
|
|
if (!is_D2_OEM && !is_MAC_SHARE && !is_SHAREWARE) // get countdown in OEM and SHAREWARE only
|
|
|
|
{
|
|
|
|
// On last level, we don't want a countdown.
|
|
|
|
if (PLAYING_BUILTIN_MISSION && Current_level_num == Last_level)
|
|
|
|
{
|
|
|
|
if (!(Game_mode & GM_MULTI))
|
|
|
|
return;
|
|
|
|
if (Game_mode & GM_MULTI_ROBOTS)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2013-03-03 01:03:33 +00:00
|
|
|
#endif
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
// Control center destroyed, rock the player's ship.
|
|
|
|
fc = Countdown_seconds_left;
|
|
|
|
if (fc > 16)
|
|
|
|
fc = 16;
|
|
|
|
|
|
|
|
// At Trainee, decrease rocking of ship by 4x.
|
|
|
|
div_scale = 1;
|
|
|
|
if (Difficulty_level == 0)
|
|
|
|
div_scale = 4;
|
|
|
|
|
2012-05-14 17:06:28 +00:00
|
|
|
if (d_tick_step)
|
2008-09-30 00:07:07 +00:00
|
|
|
{
|
|
|
|
ConsoleObject->mtype.phys_info.rotvel.x += (fixmul(d_rand() - 16384, 3*F1_0/16 + (F1_0*(16-fc))/32))/div_scale;
|
|
|
|
ConsoleObject->mtype.phys_info.rotvel.z += (fixmul(d_rand() - 16384, 3*F1_0/16 + (F1_0*(16-fc))/32))/div_scale;
|
|
|
|
}
|
2006-03-20 17:12:09 +00:00
|
|
|
// Hook in the rumble sound effect here.
|
|
|
|
|
|
|
|
old_time = Countdown_timer;
|
2007-07-16 00:28:22 +00:00
|
|
|
Countdown_timer -= FrameTime;
|
2006-03-20 17:12:09 +00:00
|
|
|
Countdown_seconds_left = f2i(Countdown_timer + F1_0*7/8);
|
|
|
|
|
|
|
|
if ( (old_time > COUNTDOWN_VOICE_TIME ) && (Countdown_timer <= COUNTDOWN_VOICE_TIME) ) {
|
|
|
|
digi_play_sample( SOUND_COUNTDOWN_13_SECS, F3_0 );
|
|
|
|
}
|
|
|
|
if ( f2i(old_time + F1_0*7/8) != Countdown_seconds_left ) {
|
|
|
|
if ( (Countdown_seconds_left>=0) && (Countdown_seconds_left<10) )
|
|
|
|
digi_play_sample( SOUND_COUNTDOWN_0_SECS+Countdown_seconds_left, F3_0 );
|
|
|
|
if ( Countdown_seconds_left==Total_countdown_time-1)
|
|
|
|
digi_play_sample( SOUND_COUNTDOWN_29_SECS, F3_0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Countdown_timer > 0) {
|
|
|
|
fix size,old_size;
|
|
|
|
size = (i2f(Total_countdown_time)-Countdown_timer) / fl2f(0.65);
|
|
|
|
old_size = (i2f(Total_countdown_time)-old_time) / fl2f(0.65);
|
|
|
|
if (size != old_size && (Countdown_seconds_left < (Total_countdown_time-5) )) { // Every 2 seconds!
|
|
|
|
//@@if (Dead_controlcen_object_num != -1) {
|
|
|
|
//@@ vms_vector vp; //,v,c;
|
|
|
|
//@@ compute_segment_center(&vp, &Segments[Objects[Dead_controlcen_object_num].segnum]);
|
|
|
|
//@@ object_create_explosion( Objects[Dead_controlcen_object_num].segnum, &vp, size*10, VCLIP_SMALL_EXPLOSION);
|
|
|
|
//@@}
|
|
|
|
|
|
|
|
digi_play_sample( SOUND_CONTROL_CENTER_WARNING_SIREN, F3_0 );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int flash_value;
|
|
|
|
|
|
|
|
if (old_time > 0)
|
|
|
|
digi_play_sample( SOUND_MINE_BLEW_UP, F1_0 );
|
|
|
|
|
|
|
|
flash_value = f2i(-Countdown_timer * (64 / 4)); // 4 seconds to total whiteness
|
|
|
|
PALETTE_FLASH_SET(flash_value,flash_value,flash_value);
|
|
|
|
|
|
|
|
if (PaletteBlueAdd > 64 ) {
|
|
|
|
gr_set_current_canvas( NULL );
|
|
|
|
gr_clear_canvas(BM_XRGB(31,31,31)); //make screen all white to match palette effect
|
|
|
|
reset_palette_add(); //restore palette for death message
|
|
|
|
//controlcen->MaxCapacity = Fuelcen_max_amount;
|
|
|
|
//gauge_message( "Control Center Reset" );
|
|
|
|
DoPlayerDead(); //kill_player();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// Called when control center gets destroyed.
|
|
|
|
// This code is common to whether control center is implicitly imbedded in a boss,
|
|
|
|
// or is an object of its own.
|
|
|
|
// if objp == NULL that means the boss was the control center and don't set Dead_controlcen_object_num
|
2014-10-02 03:02:34 +00:00
|
|
|
void do_controlcen_destroyed_stuff(const objptridx_t objp)
|
2006-03-20 17:12:09 +00:00
|
|
|
{
|
2011-09-26 23:31:19 +00:00
|
|
|
int i;
|
2006-03-20 17:12:09 +00:00
|
|
|
|
2013-03-03 01:03:33 +00:00
|
|
|
#if defined(DXX_BUILD_DESCENT_II)
|
2011-09-26 23:31:19 +00:00
|
|
|
if ((Game_mode & GM_MULTI_ROBOTS) && Control_center_destroyed)
|
|
|
|
return; // Don't allow resetting if control center and boss on same level
|
2013-03-03 01:03:33 +00:00
|
|
|
#endif
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
// Must toggle walls whether it is a boss or control center.
|
|
|
|
for (i=0;i<ControlCenterTriggers.num_links;i++)
|
2016-01-09 16:38:14 +00:00
|
|
|
wall_toggle(vsegptridx(ControlCenterTriggers.seg[i]), ControlCenterTriggers.side[i]);
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
// And start the countdown stuff.
|
|
|
|
Control_center_destroyed = 1;
|
|
|
|
|
2013-03-03 01:03:33 +00:00
|
|
|
#if defined(DXX_BUILD_DESCENT_II)
|
2011-09-26 23:31:19 +00:00
|
|
|
// If a secret level, delete secret.sgc to indicate that we can't return to our secret level.
|
|
|
|
if (Current_level_num < 0)
|
|
|
|
PHYSFS_delete(SECRETC_FILENAME);
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
if (Base_control_center_explosion_time != DEFAULT_CONTROL_CENTER_EXPLOSION_TIME)
|
|
|
|
Total_countdown_time = Base_control_center_explosion_time + Base_control_center_explosion_time * (NDL-Difficulty_level-1)/2;
|
2013-12-17 05:07:35 +00:00
|
|
|
else if (!EMULATING_D1)
|
|
|
|
Total_countdown_time = D2_Alan_pavlish_reactor_times[Difficulty_level];
|
2006-03-20 17:12:09 +00:00
|
|
|
else
|
2013-03-03 01:03:33 +00:00
|
|
|
#endif
|
2013-12-17 05:07:35 +00:00
|
|
|
Total_countdown_time = D1_Alan_pavlish_reactor_times[Difficulty_level];
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
Countdown_timer = i2f(Total_countdown_time);
|
|
|
|
|
2014-01-11 22:52:00 +00:00
|
|
|
if (!Control_center_present || objp==object_none)
|
2006-03-20 17:12:09 +00:00
|
|
|
return;
|
|
|
|
|
2014-01-11 22:52:00 +00:00
|
|
|
Dead_controlcen_object_num = objp;
|
2006-03-20 17:12:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
//do whatever this thing does in a frame
|
2014-10-02 03:02:34 +00:00
|
|
|
void do_controlcen_frame(const vobjptridx_t obj)
|
2006-03-20 17:12:09 +00:00
|
|
|
{
|
|
|
|
int best_gun_num;
|
2008-04-13 13:52:58 +00:00
|
|
|
static fix controlcen_death_silence = 0;
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
// If a boss level, then Control_center_present will be 0.
|
|
|
|
if (!Control_center_present)
|
|
|
|
return;
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
2011-02-14 21:27:07 +00:00
|
|
|
if (cheats.robotfiringsuspended || (Game_suspended & SUSP_ROBOTS))
|
2006-03-20 17:12:09 +00:00
|
|
|
return;
|
|
|
|
#else
|
2011-02-14 21:27:07 +00:00
|
|
|
if (cheats.robotfiringsuspended)
|
2006-03-20 17:12:09 +00:00
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!(Control_center_been_hit || Control_center_player_been_seen)) {
|
2012-05-14 17:06:28 +00:00
|
|
|
if (!(d_tick_count % 8)) { // Do every so often...
|
2006-03-20 17:12:09 +00:00
|
|
|
// This is a hack. Since the control center is not processed by
|
|
|
|
// ai_do_frame, it doesn't know to deal with cloaked dudes. It
|
|
|
|
// seems to work in single-player mode because it is actually using
|
|
|
|
// the value of Believed_player_position that was set by the last
|
|
|
|
// person to go through ai_do_frame. But since a no-robots game
|
|
|
|
// never goes through ai_do_frame, I'm making it so the control
|
|
|
|
// center can spot cloaked dudes.
|
|
|
|
|
|
|
|
if (Game_mode & GM_MULTI)
|
2015-07-25 23:10:45 +00:00
|
|
|
Believed_player_pos = get_local_plrobj().pos;
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
// Hack for special control centers which are isolated and not reachable because the
|
|
|
|
// real control center is inside the boss.
|
2016-09-04 00:02:50 +00:00
|
|
|
auto &children = vcsegptr(obj->segnum)->children;
|
|
|
|
if (std::none_of(children.begin(), children.end(), IS_CHILD))
|
2006-03-20 17:12:09 +00:00
|
|
|
return;
|
|
|
|
|
2014-10-29 03:24:31 +00:00
|
|
|
auto vec_to_player = vm_vec_sub(ConsoleObject->pos, obj->pos);
|
2015-03-12 02:21:19 +00:00
|
|
|
auto dist_to_player = vm_vec_normalize_quick(vec_to_player);
|
2006-03-20 17:12:09 +00:00
|
|
|
if (dist_to_player < F1_0*200) {
|
2014-10-02 03:02:38 +00:00
|
|
|
Control_center_player_been_seen = player_is_visible_from_object(obj, obj->pos, 0, vec_to_player);
|
2006-03-20 17:12:09 +00:00
|
|
|
Control_center_next_fire_time = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-03-03 01:03:33 +00:00
|
|
|
#if defined(DXX_BUILD_DESCENT_II)
|
2006-03-20 17:12:09 +00:00
|
|
|
// Periodically, make the reactor fall asleep if player not visible.
|
|
|
|
if (Control_center_been_hit || Control_center_player_been_seen) {
|
2010-12-22 00:17:59 +00:00
|
|
|
if ((Last_time_cc_vis_check + F1_0*5 < GameTime64) || (Last_time_cc_vis_check > GameTime64)) {
|
2006-03-20 17:12:09 +00:00
|
|
|
fix dist_to_player;
|
|
|
|
|
2014-10-29 03:24:31 +00:00
|
|
|
auto vec_to_player = vm_vec_sub(ConsoleObject->pos, obj->pos);
|
2014-09-28 21:11:04 +00:00
|
|
|
dist_to_player = vm_vec_normalize_quick(vec_to_player);
|
2010-12-22 00:17:59 +00:00
|
|
|
Last_time_cc_vis_check = GameTime64;
|
2006-03-20 17:12:09 +00:00
|
|
|
if (dist_to_player < F1_0*120) {
|
2014-10-02 03:02:38 +00:00
|
|
|
Control_center_player_been_seen = player_is_visible_from_object(obj, obj->pos, 0, vec_to_player);
|
2006-03-20 17:12:09 +00:00
|
|
|
if (!Control_center_player_been_seen)
|
|
|
|
Control_center_been_hit = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2013-03-03 01:03:33 +00:00
|
|
|
#endif
|
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:52:58 +00:00
|
|
|
controlcen_death_silence += FrameTime;
|
|
|
|
else
|
|
|
|
controlcen_death_silence = 0;
|
|
|
|
|
|
|
|
if ((Control_center_next_fire_time < 0) && !(controlcen_death_silence > F1_0*2)) {
|
2016-10-02 00:34:40 +00:00
|
|
|
auto &player_info = get_local_plrobj().ctype.player_info;
|
|
|
|
const auto &player_pos = (player_info.powerup_flags & PLAYER_FLAGS_CLOAKED) ? Believed_player_pos : ConsoleObject->pos;
|
2015-08-26 03:15:10 +00:00
|
|
|
best_gun_num = calc_best_gun(
|
|
|
|
get_reactor_definition(get_reactor_id(obj)).n_guns,
|
|
|
|
obj,
|
|
|
|
player_pos
|
|
|
|
);
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
if (best_gun_num != -1) {
|
|
|
|
fix delta_fire_time;
|
|
|
|
|
2015-08-26 03:15:10 +00:00
|
|
|
auto vec_to_goal = vm_vec_sub(player_pos, obj->ctype.reactor_info.gun_pos[best_gun_num]);
|
2015-03-12 02:21:19 +00:00
|
|
|
auto dist_to_player = vm_vec_normalize_quick(vec_to_goal);
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
if (dist_to_player > F1_0*300)
|
|
|
|
{
|
|
|
|
Control_center_been_hit = 0;
|
|
|
|
Control_center_player_been_seen = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Game_mode & GM_MULTI)
|
2014-10-30 03:37:13 +00:00
|
|
|
multi_send_controlcen_fire(vec_to_goal, best_gun_num, obj);
|
2015-12-03 03:26:49 +00:00
|
|
|
Laser_create_new_easy( vec_to_goal, obj->ctype.reactor_info.gun_pos[best_gun_num], obj, weapon_id_type::CONTROLCEN_WEAPON_NUM, 1);
|
2006-03-20 17:12:09 +00:00
|
|
|
|
2013-03-03 01:03:33 +00:00
|
|
|
int count = 0;
|
|
|
|
#if defined(DXX_BUILD_DESCENT_I)
|
|
|
|
const unsigned scale_divisor = 4;
|
|
|
|
if (d_rand() < 32767/4)
|
|
|
|
#elif defined(DXX_BUILD_DESCENT_II)
|
|
|
|
const unsigned scale_divisor = 6;
|
|
|
|
int rand_prob;
|
2006-03-20 17:12:09 +00:00
|
|
|
// some of time, based on level, fire another thing, not directly at player, so it might hit him if he's constantly moving.
|
|
|
|
rand_prob = F1_0/(abs(Current_level_num)/4+2);
|
2013-03-03 01:03:33 +00:00
|
|
|
while ((d_rand() > rand_prob) && (count < 4))
|
|
|
|
#endif
|
|
|
|
{
|
2016-07-24 04:04:25 +00:00
|
|
|
vm_vec_scale_add2(vec_to_goal, make_random_vector(), F1_0/scale_divisor);
|
2014-09-28 21:11:04 +00:00
|
|
|
vm_vec_normalize_quick(vec_to_goal);
|
2006-03-20 17:12:09 +00:00
|
|
|
if (Game_mode & GM_MULTI)
|
2014-10-30 03:37:13 +00:00
|
|
|
multi_send_controlcen_fire(vec_to_goal, best_gun_num, obj);
|
2015-12-03 03:26:49 +00:00
|
|
|
Laser_create_new_easy( vec_to_goal, obj->ctype.reactor_info.gun_pos[best_gun_num], obj, weapon_id_type::CONTROLCEN_WEAPON_NUM, count == 0);
|
2006-03-20 17:12:09 +00:00
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
delta_fire_time = (NDL - Difficulty_level) * F1_0/4;
|
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)
|
|
|
|
delta_fire_time += F1_0/2;
|
2013-03-03 01:03:33 +00:00
|
|
|
#endif
|
2006-03-20 17:12:09 +00:00
|
|
|
|
|
|
|
if (Game_mode & GM_MULTI) // slow down rate of fire in multi player
|
|
|
|
delta_fire_time *= 2;
|
|
|
|
|
|
|
|
Control_center_next_fire_time = delta_fire_time;
|
|
|
|
|
|
|
|
}
|
|
|
|
} else
|
|
|
|
Control_center_next_fire_time -= FrameTime;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// This must be called at the start of each level.
|
|
|
|
// If this level contains a boss and mode != multiplayer, don't do control center stuff. (Ghost out control center object.)
|
|
|
|
// If this level contains a boss and mode == multiplayer, do control center stuff.
|
|
|
|
void init_controlcen_for_level(void)
|
|
|
|
{
|
2015-12-03 03:26:49 +00:00
|
|
|
objptr_t cntrlcen_objnum = nullptr, boss_objnum = nullptr;
|
2006-03-20 17:12:09 +00:00
|
|
|
|
2016-02-12 04:02:28 +00:00
|
|
|
range_for (const auto &&objp, vobjptridx)
|
2014-10-12 23:05:46 +00:00
|
|
|
{
|
2006-03-20 17:12:09 +00:00
|
|
|
if (objp->type == OBJ_CNTRLCEN)
|
|
|
|
{
|
2015-12-03 03:26:49 +00:00
|
|
|
if (cntrlcen_objnum == nullptr)
|
2015-12-22 04:18:51 +00:00
|
|
|
cntrlcen_objnum = objp;
|
2006-03-20 17:12:09 +00:00
|
|
|
}
|
2015-12-22 04:18:51 +00:00
|
|
|
else if (objp->type == OBJ_ROBOT && (Robot_info[get_robot_id(objp)].boss_flag))
|
|
|
|
{
|
2015-12-03 03:26:49 +00:00
|
|
|
if (boss_objnum == nullptr)
|
2015-12-22 04:18:51 +00:00
|
|
|
boss_objnum = objp;
|
2006-03-20 17:12:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
2015-12-03 03:26:49 +00:00
|
|
|
if (cntrlcen_objnum == nullptr)
|
|
|
|
{
|
2013-12-26 22:21:16 +00:00
|
|
|
Dead_controlcen_object_num = object_none;
|
2006-03-20 17:12:09 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-12-03 03:26:49 +00:00
|
|
|
if (boss_objnum != nullptr && !((Game_mode & GM_MULTI) && !(Game_mode & GM_MULTI_ROBOTS)))
|
|
|
|
{
|
|
|
|
if (cntrlcen_objnum != nullptr)
|
|
|
|
{
|
|
|
|
const vobjptr_t objp = cntrlcen_objnum;
|
|
|
|
objp->type = OBJ_GHOST;
|
|
|
|
objp->control_type = CT_NONE;
|
|
|
|
objp->render_type = RT_NONE;
|
2006-03-20 17:12:09 +00:00
|
|
|
Control_center_present = 0;
|
|
|
|
}
|
2015-12-03 03:26:49 +00:00
|
|
|
}
|
|
|
|
else if (cntrlcen_objnum != nullptr)
|
|
|
|
{
|
2006-03-20 17:12:09 +00:00
|
|
|
// Compute all gun positions.
|
2015-12-03 03:26:49 +00:00
|
|
|
const vobjptr_t objp = cntrlcen_objnum;
|
2015-08-26 03:15:10 +00:00
|
|
|
calc_controlcen_gun_point(objp);
|
2006-03-20 17:12:09 +00:00
|
|
|
Control_center_present = 1;
|
|
|
|
|
2013-03-03 01:03:33 +00:00
|
|
|
#if defined(DXX_BUILD_DESCENT_I)
|
|
|
|
const unsigned secret_level_shield_multiplier = 100;
|
|
|
|
#elif defined(DXX_BUILD_DESCENT_II)
|
|
|
|
const unsigned secret_level_shield_multiplier = 150;
|
|
|
|
if (Reactor_strength != -1)
|
|
|
|
objp->shields = i2f(Reactor_strength);
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{ //use old defaults
|
2006-03-20 17:12:09 +00:00
|
|
|
// Boost control center strength at higher levels.
|
|
|
|
if (Current_level_num >= 0)
|
|
|
|
objp->shields = F1_0*200 + (F1_0*200/4) * Current_level_num;
|
|
|
|
else
|
2013-03-03 01:03:33 +00:00
|
|
|
objp->shields = F1_0*200 - Current_level_num*F1_0*secret_level_shield_multiplier;
|
2006-03-20 17:12:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Say the control center has not yet been hit.
|
|
|
|
Control_center_been_hit = 0;
|
|
|
|
Control_center_player_been_seen = 0;
|
|
|
|
Control_center_next_fire_time = 0;
|
|
|
|
|
2013-12-26 22:21:16 +00:00
|
|
|
Dead_controlcen_object_num = object_none;
|
2006-03-20 17:12:09 +00:00
|
|
|
}
|
|
|
|
|
2013-03-03 01:03:33 +00:00
|
|
|
#if defined(DXX_BUILD_DESCENT_II)
|
2015-12-22 04:18:50 +00:00
|
|
|
void special_reactor_stuff()
|
2006-03-20 17:12:09 +00:00
|
|
|
{
|
|
|
|
if (Control_center_destroyed) {
|
|
|
|
Countdown_timer += i2f(Base_control_center_explosion_time + (NDL-1-Difficulty_level)*Base_control_center_explosion_time/(NDL-1));
|
|
|
|
Total_countdown_time = f2i(Countdown_timer)+2; // Will prevent "Self destruct sequence activated" message from replaying.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2016-01-09 16:38:14 +00:00
|
|
|
* reads n reactor structs from a PHYSFS_File
|
2006-03-20 17:12:09 +00:00
|
|
|
*/
|
2016-01-09 16:38:14 +00:00
|
|
|
void reactor_read_n(PHYSFS_File *fp, partial_range_t<reactor *> r)
|
2006-03-20 17:12:09 +00:00
|
|
|
{
|
2015-02-28 19:36:01 +00:00
|
|
|
range_for (auto &i, r)
|
|
|
|
{
|
|
|
|
i.model_num = PHYSFSX_readInt(fp);
|
|
|
|
i.n_guns = PHYSFSX_readInt(fp);
|
|
|
|
range_for (auto &j, i.gun_points)
|
|
|
|
PHYSFSX_readVector(fp, j);
|
|
|
|
range_for (auto &j, i.gun_dirs)
|
|
|
|
PHYSFSX_readVector(fp, j);
|
2006-03-20 17:12:09 +00:00
|
|
|
}
|
|
|
|
}
|
2013-03-03 01:03:33 +00:00
|
|
|
#endif
|
2015-12-22 04:18:50 +00:00
|
|
|
}
|
2006-03-20 17:12:09 +00:00
|
|
|
|
2014-01-18 18:02:02 +00:00
|
|
|
DEFINE_SERIAL_UDT_TO_MESSAGE(control_center_triggers, cct, (cct.num_links, cct.seg, cct.side));
|
|
|
|
ASSERT_SERIAL_UDT_MESSAGE_SIZE(control_center_triggers, 42);
|
|
|
|
|
2009-10-05 02:51:37 +00:00
|
|
|
/*
|
2016-01-09 16:38:14 +00:00
|
|
|
* reads n control_center_triggers 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 control_center_triggers_read(control_center_triggers *cct, PHYSFS_File *fp)
|
2009-10-05 02:51:37 +00:00
|
|
|
{
|
2014-01-18 18:02:02 +00:00
|
|
|
PHYSFSX_serialize_read(fp, *cct);
|
2009-10-05 02:51:37 +00:00
|
|
|
}
|
|
|
|
|
2016-01-09 16:38:14 +00:00
|
|
|
void control_center_triggers_write(const control_center_triggers *cct, PHYSFS_File *fp)
|
2006-03-20 17:12:09 +00:00
|
|
|
{
|
2014-01-18 18:02:02 +00:00
|
|
|
PHYSFSX_serialize_write(fp, *cct);
|
2006-03-20 17:12:09 +00:00
|
|
|
}
|