dxx-rebirth/similar/main/object.cpp

2349 lines
70 KiB
C++
Raw Normal View History

2006-03-20 17:12:09 +00:00
/*
2014-06-01 17:55:23 +00:00
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING.txt.
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below.
* See COPYING.txt for license details.
2006-03-20 17:12:09 +00:00
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
*/
/*
*
* object rendering
*
*/
#include <algorithm>
2015-09-09 03:27:52 +00:00
#include <cstdlib>
2006-03-20 17:12:09 +00:00
#include <stdio.h>
#include "inferno.h"
#include "game.h"
#include "gr.h"
#include "bm.h"
#include "3d.h"
#include "segment.h"
#include "texmap.h"
#include "laser.h"
#include "key.h"
#include "gameseg.h"
#include "textures.h"
#include "object.h"
2013-09-24 01:59:09 +00:00
#include "controls.h"
2006-03-20 17:12:09 +00:00
#include "physics.h"
#include "slew.h"
2006-03-20 17:12:09 +00:00
#include "render.h"
#include "wall.h"
#include "vclip.h"
#include "robot.h"
#include "interp.h"
2006-03-20 17:12:09 +00:00
#include "fireball.h"
#include "laser.h"
#include "dxxerror.h"
2006-03-20 17:12:09 +00:00
#include "ai.h"
#include "hostage.h"
#include "morph.h"
#include "cntrlcen.h"
#include "powerup.h"
#include "fuelcen.h"
#include "endlevel.h"
2015-04-19 04:18:51 +00:00
#include "hudmsg.h"
2006-03-20 17:12:09 +00:00
#include "sounds.h"
#include "collide.h"
#include "lighting.h"
#include "newdemo.h"
#include "player.h"
#include "weapon.h"
#include "newmenu.h"
#include "gauges.h"
#include "multi.h"
#include "menu.h"
#include "args.h"
#include "text.h"
#include "piggy.h"
#include "switch.h"
#include "gameseq.h"
#include "playsave.h"
#include "timer.h"
#if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
#include "editor/editor.h"
#endif
2014-10-12 23:10:05 +00:00
#include "compiler-exchange.h"
#include "compiler-range_for.h"
#include "partial_range.h"
using std::min;
using std::max;
namespace dsx {
2016-04-23 17:59:47 +00:00
static void obj_detach_all(object_base &parent);
2016-04-23 17:59:47 +00:00
static void obj_detach_one(object &sub);
2006-03-20 17:12:09 +00:00
/*
* Global variables
*/
object *ConsoleObject; //the object that is the player
}
2006-03-20 17:12:09 +00:00
namespace dcx {
static array<objnum_t, MAX_OBJECTS> free_obj_list;
2006-03-20 17:12:09 +00:00
//Data for objects
// -- Object stuff
//info on the various types of objects
int num_objects=0;
}
2006-03-20 17:12:09 +00:00
namespace dsx {
2006-03-20 17:12:09 +00:00
#ifndef RELEASE
//set viewer object to next object in array
void object_goto_next_viewer()
{
objnum_t start_obj;
2016-04-06 03:34:13 +00:00
start_obj = vcobjptridx(Viewer); //get viewer object number
2006-03-20 17:12:09 +00:00
2016-02-12 04:02:28 +00:00
range_for (const auto &&i, vcobjptr)
2014-10-12 23:05:46 +00:00
{
(void)i;
2006-03-20 17:12:09 +00:00
start_obj++;
if (start_obj > Highest_object_index ) start_obj = 0;
const auto &&objp = vobjptr(start_obj);
if (objp->type != OBJ_NONE)
{
Viewer = objp;
2006-03-20 17:12:09 +00:00
return;
}
}
Error( "Could not find a viewer object!" );
2006-03-20 17:12:09 +00:00
}
#endif
2006-03-20 17:12:09 +00:00
2014-08-23 23:53:56 +00:00
objptridx_t obj_find_first_of_type(int type)
{
2016-02-12 04:02:28 +00:00
range_for (const auto &&i, vobjptridx)
2014-08-23 23:53:56 +00:00
{
if (i->type==type)
return i;
}
return object_none;
}
2006-03-20 17:12:09 +00:00
2016-04-06 03:34:14 +00:00
}
namespace dcx {
2006-03-20 17:12:09 +00:00
//draw an object that has one bitmap & doesn't rotate
2017-03-11 19:56:24 +00:00
void draw_object_blob(grs_canvas &canvas, const object_base &obj, const bitmap_index bmi)
2006-03-20 17:12:09 +00:00
{
auto &bm = GameBitmaps[bmi.index];
2006-03-20 17:12:09 +00:00
PIGGY_PAGE_IN( bmi );
2016-04-06 03:34:14 +00:00
const auto osize = obj.size;
// draw these with slight offset to viewer preventing too much ugly clipping
2016-04-06 03:34:14 +00:00
auto pos = obj.pos;
if (obj.type == OBJ_FIREBALL && get_fireball_id(obj) == VCLIP_VOLATILE_WALL_HIT)
{
vms_vector offs_vec;
2016-04-06 03:34:14 +00:00
vm_vec_normalized_dir_quick(offs_vec, Viewer->pos, pos);
2014-09-28 21:43:14 +00:00
vm_vec_scale_add2(pos,offs_vec,F1_0);
}
using wh = std::pair<fix, fix>;
const auto bm_w = bm.bm_w;
const auto bm_h = bm.bm_h;
const auto p = (bm_w > bm_h)
? wh(osize, fixmuldiv(osize, bm_h, bm_w))
: wh(fixmuldiv(osize, bm_w, bm_h), osize);
2017-03-11 19:56:24 +00:00
g3_draw_bitmap(canvas, pos, p.first, p.second, bm);
2006-03-20 17:12:09 +00:00
}
2016-04-06 03:34:14 +00:00
}
namespace dsx {
2006-03-20 17:12:09 +00:00
//draw an object that is a texture-mapped rod
void draw_object_tmap_rod(grs_canvas &canvas, const vcobjptridx_t obj, const bitmap_index bitmapi, int lighted)
2006-03-20 17:12:09 +00:00
{
g3s_lrgb light;
2006-03-20 17:12:09 +00:00
PIGGY_PAGE_IN(bitmapi);
2014-11-13 03:34:42 +00:00
auto &bitmap = GameBitmaps[bitmapi.index];
2006-03-20 17:12:09 +00:00
const auto delta = vm_vec_copy_scale(obj->orient.uvec,obj->size);
2006-03-20 17:12:09 +00:00
2014-10-30 03:11:06 +00:00
const auto top_v = vm_vec_add(obj->pos,delta);
2014-10-29 03:24:31 +00:00
const auto bot_v = vm_vec_sub(obj->pos,delta);
2006-03-20 17:12:09 +00:00
const auto top_p = g3_rotate_point(top_v);
const auto bot_p = g3_rotate_point(bot_v);
2006-03-20 17:12:09 +00:00
if (lighted)
{
light = compute_object_light(obj);
}
2006-03-20 17:12:09 +00:00
else
{
light.r = light.g = light.b = f1_0;
}
2017-02-11 21:42:40 +00:00
g3_draw_rod_tmap(canvas, bitmap, bot_p, obj->size, top_p, obj->size, light);
2006-03-20 17:12:09 +00:00
}
//used for robot engine glow
#define MAX_VELOCITY i2f(50)
//what darkening level to use when cloaked
#define CLOAKED_FADE_LEVEL 28
#define CLOAK_FADEIN_DURATION_PLAYER F2_0
#define CLOAK_FADEOUT_DURATION_PLAYER F2_0
#define CLOAK_FADEIN_DURATION_ROBOT F1_0
#define CLOAK_FADEOUT_DURATION_ROBOT F1_0
//do special cloaked render
2017-03-11 19:56:27 +00:00
static void draw_cloaked_object(grs_canvas &canvas, const vcobjptr_t obj, const g3s_lrgb light, glow_values_t glow, const fix64 cloak_start_time, const fix total_cloaked_time, const fix Cloak_fadein_duration, const fix Cloak_fadeout_duration)
2006-03-20 17:12:09 +00:00
{
2015-11-19 03:23:34 +00:00
fix cloak_delta_time;
2006-03-20 17:12:09 +00:00
fix light_scale=F1_0;
int cloak_value=0;
int fading=0; //if true, fading, else cloaking
cloak_delta_time = GameTime64 - cloak_start_time;
2006-03-20 17:12:09 +00:00
if (cloak_delta_time < Cloak_fadein_duration/2) {
#if defined(DXX_BUILD_DESCENT_I)
light_scale = Cloak_fadein_duration/2 - cloak_delta_time;
#elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
light_scale = fixdiv(Cloak_fadein_duration/2 - cloak_delta_time,Cloak_fadein_duration/2);
#endif
2006-03-20 17:12:09 +00:00
fading = 1;
}
else if (cloak_delta_time < Cloak_fadein_duration) {
#if defined(DXX_BUILD_DESCENT_I)
cloak_value = f2i((cloak_delta_time - Cloak_fadein_duration/2) * CLOAKED_FADE_LEVEL);
#elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
cloak_value = f2i(fixdiv(cloak_delta_time - Cloak_fadein_duration/2,Cloak_fadein_duration/2) * CLOAKED_FADE_LEVEL);
#endif
2006-03-20 17:12:09 +00:00
2015-11-19 03:23:34 +00:00
} else if (GameTime64 < (cloak_start_time + total_cloaked_time) -Cloak_fadeout_duration) {
2006-03-20 17:12:09 +00:00
static int cloak_delta=0,cloak_dir=1;
static fix cloak_timer=0;
//note, if more than one cloaked object is visible at once, the
//pulse rate will change!
cloak_timer -= FrameTime;
while (cloak_timer < 0) {
cloak_timer += Cloak_fadeout_duration/12;
cloak_delta += cloak_dir;
if (cloak_delta==0 || cloak_delta==4)
cloak_dir = -cloak_dir;
}
cloak_value = CLOAKED_FADE_LEVEL - cloak_delta;
2015-11-19 03:23:34 +00:00
} else if (GameTime64 < (cloak_start_time + total_cloaked_time) -Cloak_fadeout_duration/2) {
2006-03-20 17:12:09 +00:00
#if defined(DXX_BUILD_DESCENT_I)
cloak_value = f2i((total_cloaked_time - Cloak_fadeout_duration/2 - cloak_delta_time) * CLOAKED_FADE_LEVEL);
#elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
cloak_value = f2i(fixdiv(total_cloaked_time - Cloak_fadeout_duration/2 - cloak_delta_time,Cloak_fadeout_duration/2) * CLOAKED_FADE_LEVEL);
#endif
2006-03-20 17:12:09 +00:00
} else {
#if defined(DXX_BUILD_DESCENT_I)
light_scale = Cloak_fadeout_duration/2 - (total_cloaked_time - cloak_delta_time);
#elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
light_scale = fixdiv(Cloak_fadeout_duration/2 - (total_cloaked_time - cloak_delta_time),Cloak_fadeout_duration/2);
#endif
2006-03-20 17:12:09 +00:00
fading = 1;
}
2015-02-14 22:48:29 +00:00
alternate_textures alt_textures;
#if defined(DXX_BUILD_DESCENT_II)
if (fading)
#endif
{
2015-10-09 02:46:09 +00:00
const unsigned ati = static_cast<unsigned>(obj->rtype.pobj_info.alt_textures) - 1;
if (ati < multi_player_textures.size())
{
2015-10-09 02:46:09 +00:00
alt_textures = multi_player_textures[ati];
}
}
2006-03-20 17:12:09 +00:00
if (fading) {
g3s_lrgb new_light;
2006-03-20 17:12:09 +00:00
new_light.r = fixmul(light.r,light_scale);
new_light.g = fixmul(light.g,light_scale);
new_light.b = fixmul(light.b,light_scale);
2006-03-20 17:12:09 +00:00
glow[0] = fixmul(glow[0],light_scale);
2017-03-11 19:56:27 +00:00
draw_polygon_model(canvas, obj->pos,
2006-03-20 17:12:09 +00:00
&obj->orient,
obj->rtype.pobj_info.anim_angles,
2006-03-20 17:12:09 +00:00
obj->rtype.pobj_info.model_num,obj->rtype.pobj_info.subobj_flags,
new_light,
&glow,
2006-03-20 17:12:09 +00:00
alt_textures );
}
else {
2017-03-11 19:56:27 +00:00
gr_settransblend(canvas, cloak_value, GR_BLEND_NORMAL);
g3_set_special_render(draw_tmap_flat); //use special flat drawer
2017-03-11 19:56:27 +00:00
draw_polygon_model(canvas, obj->pos,
2006-03-20 17:12:09 +00:00
&obj->orient,
obj->rtype.pobj_info.anim_angles,
2006-03-20 17:12:09 +00:00
obj->rtype.pobj_info.model_num,obj->rtype.pobj_info.subobj_flags,
light,
&glow,
alt_textures );
g3_set_special_render(draw_tmap);
2017-03-11 19:56:27 +00:00
gr_settransblend(canvas, GR_FADE_OFF, GR_BLEND_NORMAL);
2006-03-20 17:12:09 +00:00
}
}
//draw an object which renders as a polygon model
2014-10-02 03:02:34 +00:00
static void draw_polygon_object(const vobjptridx_t obj)
2006-03-20 17:12:09 +00:00
{
g3s_lrgb light;
glow_values_t engine_glow_value;
engine_glow_value[0] = 0;
#if defined(DXX_BUILD_DESCENT_II)
engine_glow_value[1] = -1; //element 0 is for engine glow, 1 for headlight
#endif
2006-03-20 17:12:09 +00:00
// If option set for bright players in netgame, brighten them!
light = unlikely(Netgame.BrightPlayers && (Game_mode & GM_MULTI) && obj->type == OBJ_PLAYER)
? g3s_lrgb{F1_0 * 2, F1_0 * 2, F1_0 * 2}
: compute_object_light(obj);
2006-03-20 17:12:09 +00:00
#if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
//make robots brighter according to robot glow field
if (obj->type == OBJ_ROBOT)
{
const auto glow = Robot_info[get_robot_id(obj)].glow<<12;
light.r += glow; //convert 4:4 to 16:16
light.g += glow; //convert 4:4 to 16:16
light.b += glow; //convert 4:4 to 16:16
}
2006-03-20 17:12:09 +00:00
if ((obj->type == OBJ_WEAPON &&
2015-12-03 03:26:49 +00:00
get_weapon_id(obj) == weapon_id_type::FLARE_ID) ||
obj->type == OBJ_MARKER
)
{
light.r += F1_0*2;
light.g += F1_0*2;
light.b += F1_0*2;
}
#endif
2006-03-20 17:12:09 +00:00
2015-12-04 03:36:31 +00:00
push_interpolation_method imsave(1, true);
2006-03-20 17:12:09 +00:00
//set engine glow value
engine_glow_value[0] = f1_0/5;
if (obj->movement_type == MT_PHYSICS) {
if (obj->mtype.phys_info.flags & PF_USES_THRUST && obj->type==OBJ_PLAYER && get_player_id(obj)==Player_num) {
2014-09-28 21:11:03 +00:00
fix thrust_mag = vm_vec_mag_quick(obj->mtype.phys_info.thrust);
2006-03-20 17:12:09 +00:00
engine_glow_value[0] += (fixdiv(thrust_mag,Player_ship->max_thrust)*4)/5;
}
else {
2014-09-28 21:11:03 +00:00
fix speed = vm_vec_mag_quick(obj->mtype.phys_info.velocity);
#if defined(DXX_BUILD_DESCENT_I)
engine_glow_value[0] += (fixdiv(speed,MAX_VELOCITY)*4)/5;
#elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
engine_glow_value[0] += (fixdiv(speed,MAX_VELOCITY)*3)/5;
#endif
2006-03-20 17:12:09 +00:00
}
}
#if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
//set value for player headlight
if (obj->type == OBJ_PLAYER) {
auto &player_flags = obj->ctype.player_info.powerup_flags;
if (player_flags & PLAYER_FLAGS_HEADLIGHT && !Endlevel_sequence)
if (player_flags & PLAYER_FLAGS_HEADLIGHT_ON)
2006-03-20 17:12:09 +00:00
engine_glow_value[1] = -2; //draw white!
else
engine_glow_value[1] = -1; //draw normal color (grey)
else
engine_glow_value[1] = -3; //don't draw
}
#endif
2006-03-20 17:12:09 +00:00
if (obj->rtype.pobj_info.tmap_override != -1) {
#ifndef NDEBUG
polymodel *pm = &Polygon_models[obj->rtype.pobj_info.model_num];
#endif
2015-02-14 22:48:29 +00:00
array<bitmap_index, 12> bm_ptrs;
2006-03-20 17:12:09 +00:00
Assert(pm->n_textures<=12);
2015-02-14 22:48:29 +00:00
//fill whole array, in case simple model needs more
bm_ptrs.fill(Textures[obj->rtype.pobj_info.tmap_override]);
2017-02-19 19:33:43 +00:00
draw_polygon_model(*grd_curcanv, obj->pos,
2006-03-20 17:12:09 +00:00
&obj->orient,
obj->rtype.pobj_info.anim_angles,
2006-03-20 17:12:09 +00:00
obj->rtype.pobj_info.model_num,
obj->rtype.pobj_info.subobj_flags,
light,
&engine_glow_value,
2006-03-20 17:12:09 +00:00
bm_ptrs);
}
else {
2015-11-19 03:23:34 +00:00
std::pair<fix64, fix> cloak_duration;
std::pair<fix, fix> cloak_fade;
if (obj->type==OBJ_PLAYER && (obj->ctype.player_info.powerup_flags & PLAYER_FLAGS_CLOAKED))
{
auto &cloak_time = obj->ctype.player_info.cloak_time;
2015-11-19 03:23:34 +00:00
cloak_duration = {cloak_time, CLOAK_TIME_MAX};
cloak_fade = {CLOAK_FADEIN_DURATION_PLAYER, CLOAK_FADEOUT_DURATION_PLAYER};
}
2006-03-20 17:12:09 +00:00
else if ((obj->type == OBJ_ROBOT) && (obj->ctype.ai_info.CLOAKED)) {
if (Robot_info[get_robot_id(obj)].boss_flag)
2015-11-19 03:23:34 +00:00
cloak_duration = {Boss_cloak_start_time, Boss_cloak_duration};
2006-03-20 17:12:09 +00:00
else
2015-11-19 03:23:34 +00:00
cloak_duration = {GameTime64-F1_0*10, F1_0 * 20};
cloak_fade = {CLOAK_FADEIN_DURATION_ROBOT, CLOAK_FADEOUT_DURATION_ROBOT};
2006-03-20 17:12:09 +00:00
} else {
2015-02-14 22:48:29 +00:00
alternate_textures alt_textures;
2015-10-09 02:46:09 +00:00
const unsigned ati = static_cast<unsigned>(obj->rtype.pobj_info.alt_textures) - 1;
if (ati < multi_player_textures.size())
alt_textures = multi_player_textures[ati];
2006-03-20 17:12:09 +00:00
#if defined(DXX_BUILD_DESCENT_II)
if (obj->type == OBJ_ROBOT)
{
2006-03-20 17:12:09 +00:00
// Snipers get bright when they fire.
ai_local *ailp = &obj->ctype.ai_info.ail;
if (ailp->next_fire < F1_0/8) {
2015-04-02 02:36:57 +00:00
if (obj->ctype.ai_info.behavior == ai_behavior::AIB_SNIPE)
{
light.r = 2*light.r + F1_0;
light.g = 2*light.g + F1_0;
light.b = 2*light.b + F1_0;
}
2006-03-20 17:12:09 +00:00
}
}
#endif
const auto is_weapon_with_inner_model = (obj->type == OBJ_WEAPON && Weapon_info[get_weapon_id(obj)].model_num_inner > -1);
bool draw_simple_model;
if (is_weapon_with_inner_model)
{
2017-01-01 00:45:44 +00:00
gr_settransblend(*grd_curcanv, GR_FADE_OFF, GR_BLEND_ADDITIVE_A);
draw_simple_model = static_cast<fix>(vm_vec_dist_quick(Viewer->pos, obj->pos)) < Simple_model_threshhold_scale * F1_0*2;
if (draw_simple_model)
2017-02-19 19:33:43 +00:00
draw_polygon_model(*grd_curcanv, obj->pos,
2006-03-20 17:12:09 +00:00
&obj->orient,
obj->rtype.pobj_info.anim_angles,
Weapon_info[get_weapon_id(obj)].model_num_inner,
2006-03-20 17:12:09 +00:00
obj->rtype.pobj_info.subobj_flags,
light,
&engine_glow_value,
2006-03-20 17:12:09 +00:00
alt_textures);
}
2017-02-19 19:33:43 +00:00
draw_polygon_model(*grd_curcanv, obj->pos,
&obj->orient,
obj->rtype.pobj_info.anim_angles,obj->rtype.pobj_info.model_num,
obj->rtype.pobj_info.subobj_flags,
light,
&engine_glow_value,
alt_textures);
if (is_weapon_with_inner_model)
{
#if !DXX_USE_OGL // in software rendering must draw inner model last
2017-01-01 00:45:44 +00:00
gr_settransblend(*grd_curcanv, GR_FADE_OFF, GR_BLEND_ADDITIVE_A);
if (draw_simple_model)
2017-02-19 19:33:43 +00:00
draw_polygon_model(*grd_curcanv, obj->pos,
&obj->orient,
obj->rtype.pobj_info.anim_angles,
Weapon_info[obj->id].model_num_inner,
obj->rtype.pobj_info.subobj_flags,
light,
&engine_glow_value,
alt_textures);
#endif
2017-01-01 00:45:44 +00:00
gr_settransblend(*grd_curcanv, GR_FADE_OFF, GR_BLEND_NORMAL);
}
return;
2006-03-20 17:12:09 +00:00
}
2017-03-11 19:56:27 +00:00
draw_cloaked_object(*grd_curcanv, obj, light, engine_glow_value, cloak_duration.first, cloak_duration.second, cloak_fade.first, cloak_fade.second);
2006-03-20 17:12:09 +00:00
}
}
}
2006-03-20 17:12:09 +00:00
//------------------------------------------------------------------------------
// These variables are used to keep a list of the 3 closest robots to the viewer.
// The code works like this: Every time render object is called with a polygon model,
// it finds the distance of that robot to the viewer. If this distance if within 10
// segments of the viewer, it does the following: If there aren't already 3 robots in
// the closet-robots list, it just sticks that object into the list along with its distance.
// If the list already contains 3 robots, then it finds the robot in that list that is
// farthest from the viewer. If that object is farther than the object currently being
// rendered, then the new object takes over that far object's slot. *Then* after all
// objects are rendered, object_render_targets is called an it draws a target on top
// of all the objects.
//091494: #define MAX_CLOSE_ROBOTS 3
//--unused-- static int Object_draw_lock_boxes = 0;
//091494: static int Object_num_close = 0;
//091494: static object * Object_close_ones[MAX_CLOSE_ROBOTS];
//091494: static fix Object_close_distance[MAX_CLOSE_ROBOTS];
//091494: set_close_objects(object *obj)
//091494: {
//091494: fix dist;
//091494:
//091494: if ( (obj->type != OBJ_ROBOT) || (Object_draw_lock_boxes==0) )
//091494: return;
//091494:
//091494: // The following code keeps a list of the 10 closest robots to the
//091494: // viewer. See comments in front of this function for how this works.
//091494: dist = vm_vec_dist( &obj->pos, &Viewer->pos );
//091494: if ( dist < i2f(20*10) ) {
//091494: if ( Object_num_close < MAX_CLOSE_ROBOTS ) {
//091494: Object_close_ones[Object_num_close] = obj;
//091494: Object_close_distance[Object_num_close] = dist;
//091494: Object_num_close++;
//091494: } else {
//091494: int i, farthest_robot;
//091494: fix farthest_distance;
//091494: // Find the farthest robot in the list
//091494: farthest_robot = 0;
//091494: farthest_distance = Object_close_distance[0];
//091494: for (i=1; i<Object_num_close; i++ ) {
//091494: if ( Object_close_distance[i] > farthest_distance ) {
//091494: farthest_distance = Object_close_distance[i];
//091494: farthest_robot = i;
//091494: }
//091494: }
//091494: // If this object is closer to the viewer than
//091494: // the farthest in the list, replace the farthest with this object.
//091494: if ( farthest_distance > dist ) {
//091494: Object_close_ones[farthest_robot] = obj;
//091494: Object_close_distance[farthest_robot] = dist;
//091494: }
//091494: }
//091494: }
//091494: }
namespace dcx {
objnum_t Player_fired_laser_this_frame=object_none;
}
2006-03-20 17:12:09 +00:00
namespace dsx {
2006-03-20 17:12:09 +00:00
// -----------------------------------------------------------------------------
//this routine checks to see if an robot rendered near the middle of
//the screen, and if so and the player had fired, "warns" the robot
static void set_robot_location_info(object &objp)
2006-03-20 17:12:09 +00:00
{
if (Player_fired_laser_this_frame != object_none) {
const auto &&temp = g3_rotate_point(objp.pos);
2006-03-20 17:12:09 +00:00
if (temp.p3_codes & CC_BEHIND) //robot behind the screen
return;
//the code below to check for object near the center of the screen
//completely ignores z, which may not be good
if ((abs(temp.p3_x) < F1_0*4) && (abs(temp.p3_y) < F1_0*4)) {
objp.ctype.ai_info.danger_laser_num = Player_fired_laser_this_frame;
objp.ctype.ai_info.danger_laser_signature = vcobjptr(Player_fired_laser_this_frame)->signature;
2006-03-20 17:12:09 +00:00
}
}
}
// ------------------------------------------------------------------------------------------------------------------
2014-10-02 03:02:34 +00:00
void create_small_fireball_on_object(const vobjptridx_t objp, fix size_scale, int sound_flag)
2006-03-20 17:12:09 +00:00
{
fix size;
vms_vector pos;
2006-03-20 17:12:09 +00:00
pos = objp->pos;
auto rand_vec = make_random_vector();
2006-03-20 17:12:09 +00:00
2014-09-28 21:11:05 +00:00
vm_vec_scale(rand_vec, objp->size/2);
2006-03-20 17:12:09 +00:00
2014-09-28 21:43:00 +00:00
vm_vec_add2(pos, rand_vec);
2006-03-20 17:12:09 +00:00
#if defined(DXX_BUILD_DESCENT_I)
size = fixmul(size_scale, F1_0 + d_rand()*4);
#elif defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
size = fixmul(size_scale, F1_0/2 + d_rand()*4/2);
#endif
2006-03-20 17:12:09 +00:00
const auto &&segnum = find_point_seg(pos, vsegptridx(objp->segnum));
if (segnum != segment_none) {
auto expl_obj = object_create_explosion(segnum, pos, size, VCLIP_SMALL_EXPLOSION);
2006-03-20 17:12:09 +00:00
if (!expl_obj)
return;
obj_attach(objp,expl_obj);
if (d_rand() < 8192) {
fix vol = F1_0/2;
if (objp->type == OBJ_ROBOT)
vol *= 2;
if (sound_flag)
digi_link_sound_to_object(SOUND_EXPLODING_WALL, objp, 0, vol);
2006-03-20 17:12:09 +00:00
}
}
}
// -- mk, 02/05/95 -- #define VCLIP_INVULNERABILITY_EFFECT VCLIP_SMALL_EXPLOSION
// -- mk, 02/05/95 --
// -- mk, 02/05/95 -- // -----------------------------------------------------------------------------
// -- mk, 02/05/95 -- void do_player_invulnerability_effect(object *objp)
// -- mk, 02/05/95 -- {
// -- mk, 02/05/95 -- if (d_rand() < FrameTime*8) {
// -- mk, 02/05/95 -- create_vclip_on_object(objp, F1_0, VCLIP_INVULNERABILITY_EFFECT);
// -- mk, 02/05/95 -- }
// -- mk, 02/05/95 -- }
// -----------------------------------------------------------------------------
// Render an object. Calls one of several routines based on type
2014-10-02 03:02:34 +00:00
void render_object(const vobjptridx_t obj)
2006-03-20 17:12:09 +00:00
{
if (unlikely(obj == Viewer))
return;
if (unlikely(obj->type==OBJ_NONE))
{
2006-03-20 17:12:09 +00:00
Int3();
return;
}
#if !DXX_USE_OGL
2015-04-22 02:44:29 +00:00
const auto mld_save = exchange(Max_linear_depth, Max_linear_depth_objects);
#endif
2006-03-20 17:12:09 +00:00
bool alpha = false;
switch (obj->render_type)
{
case RT_NONE:
break; //doesn't render, like the player
2006-03-20 17:12:09 +00:00
case RT_POLYOBJ:
#if defined(DXX_BUILD_DESCENT_II)
if ( PlayerCfg.AlphaBlendMarkers && obj->type == OBJ_MARKER ) // set nice transparency/blending for certrain objects
{
alpha = true;
2017-01-01 00:45:44 +00:00
gr_settransblend(*grd_curcanv, 10, GR_BLEND_ADDITIVE_A);
}
#endif
2006-03-20 17:12:09 +00:00
draw_polygon_object(obj);
if (obj->type == OBJ_ROBOT) //"warn" robot if being shot at
2006-03-20 17:12:09 +00:00
set_robot_location_info(obj);
break;
2006-03-20 17:12:09 +00:00
case RT_MORPH:
2017-03-11 19:56:27 +00:00
draw_morph_object(*grd_curcanv, obj);
break;
2006-03-20 17:12:09 +00:00
case RT_FIREBALL:
if (PlayerCfg.AlphaBlendFireballs) // set nice transparency/blending for certrain objects
{
alpha = true;
2017-01-01 00:45:44 +00:00
gr_settransblend(*grd_curcanv, GR_FADE_OFF, GR_BLEND_ADDITIVE_C);
}
2006-03-20 17:12:09 +00:00
2017-03-11 19:56:25 +00:00
draw_fireball(*grd_curcanv, obj);
2006-03-20 17:12:09 +00:00
break;
case RT_WEAPON_VCLIP:
if (PlayerCfg.AlphaBlendWeapons && !is_proximity_bomb_or_smart_mine(get_weapon_id(obj))) // set nice transparency/blending for certain objects
{
alpha = true;
2017-01-01 00:45:44 +00:00
gr_settransblend(*grd_curcanv, 7, GR_BLEND_ADDITIVE_A);
}
2006-03-20 17:12:09 +00:00
2017-03-11 19:56:25 +00:00
draw_weapon_vclip(*grd_curcanv, obj);
break;
2006-03-20 17:12:09 +00:00
case RT_HOSTAGE:
2017-03-11 19:56:26 +00:00
draw_hostage(*grd_curcanv, obj);
break;
2006-03-20 17:12:09 +00:00
case RT_POWERUP:
if (PlayerCfg.AlphaBlendPowerups) // set nice transparency/blending for certrain objects
switch ( get_powerup_id(obj) )
{
case POW_EXTRA_LIFE:
case POW_ENERGY:
case POW_SHIELD_BOOST:
case POW_CLOAK:
case POW_INVULNERABILITY:
#if defined(DXX_BUILD_DESCENT_II)
case POW_HOARD_ORB:
#endif
alpha = true;
2017-01-01 00:45:44 +00:00
gr_settransblend(*grd_curcanv, 7, GR_BLEND_ADDITIVE_A);
break;
case POW_LASER:
case POW_KEY_BLUE:
case POW_KEY_RED:
case POW_KEY_GOLD:
case POW_MISSILE_1:
case POW_MISSILE_4:
case POW_QUAD_FIRE:
case POW_VULCAN_WEAPON:
case POW_SPREADFIRE_WEAPON:
case POW_PLASMA_WEAPON:
case POW_FUSION_WEAPON:
case POW_PROXIMITY_WEAPON:
case POW_HOMING_AMMO_1:
case POW_HOMING_AMMO_4:
case POW_SMARTBOMB_WEAPON:
case POW_MEGA_WEAPON:
case POW_VULCAN_AMMO:
case POW_TURBO:
case POW_MEGAWOW:
#if defined(DXX_BUILD_DESCENT_II)
case POW_FULL_MAP:
case POW_HEADLIGHT:
case POW_GAUSS_WEAPON:
case POW_HELIX_WEAPON:
case POW_PHOENIX_WEAPON:
case POW_OMEGA_WEAPON:
case POW_SUPER_LASER:
case POW_CONVERTER:
case POW_AMMO_RACK:
case POW_AFTERBURNER:
case POW_SMISSILE1_1:
case POW_SMISSILE1_4:
case POW_GUIDED_MISSILE_1:
case POW_GUIDED_MISSILE_4:
case POW_SMART_MINE:
case POW_MERCURY_MISSILE_1:
case POW_MERCURY_MISSILE_4:
case POW_EARTHSHAKER_MISSILE:
case POW_FLAG_BLUE:
case POW_FLAG_RED:
#endif
break;
}
2017-03-11 19:56:26 +00:00
draw_powerup(*grd_curcanv, obj);
break;
case RT_LASER:
if (PlayerCfg.AlphaBlendLasers) // set nice transparency/blending for certrain objects
{
alpha = true;
2017-01-01 00:45:44 +00:00
gr_settransblend(*grd_curcanv, 7, GR_BLEND_ADDITIVE_A);
}
2006-03-20 17:12:09 +00:00
2017-03-11 19:56:26 +00:00
Laser_render(*grd_curcanv, obj);
break;
2006-03-20 17:12:09 +00:00
default:
Error("Unknown render_type <%d>",obj->render_type);
}
2006-03-20 17:12:09 +00:00
if (alpha)
2017-01-01 00:45:44 +00:00
gr_settransblend(*grd_curcanv, GR_FADE_OFF, GR_BLEND_NORMAL); // revert any transparency/blending setting back to normal
2006-03-20 17:12:09 +00:00
if ( obj->render_type != RT_NONE && Newdemo_state == ND_STATE_RECORDING )
newdemo_record_render_object(obj);
#if !DXX_USE_OGL
2006-03-20 17:12:09 +00:00
Max_linear_depth = mld_save;
2015-04-22 02:44:29 +00:00
#endif
2006-03-20 17:12:09 +00:00
}
void reset_player_object()
{
//Init physics
2014-09-28 21:11:04 +00:00
vm_vec_zero(ConsoleObject->mtype.phys_info.velocity);
vm_vec_zero(ConsoleObject->mtype.phys_info.thrust);
vm_vec_zero(ConsoleObject->mtype.phys_info.rotvel);
vm_vec_zero(ConsoleObject->mtype.phys_info.rotthrust);
2013-12-26 02:47:34 +00:00
ConsoleObject->mtype.phys_info.turnroll = 0;
2006-03-20 17:12:09 +00:00
ConsoleObject->mtype.phys_info.mass = Player_ship->mass;
ConsoleObject->mtype.phys_info.drag = Player_ship->drag;
ConsoleObject->mtype.phys_info.flags = PF_TURNROLL | PF_LEVELLING | PF_WIGGLE | PF_USES_THRUST;
2006-03-20 17:12:09 +00:00
//Init render info
ConsoleObject->render_type = RT_POLYOBJ;
ConsoleObject->rtype.pobj_info.model_num = Player_ship->model_num; //what model is this?
ConsoleObject->rtype.pobj_info.subobj_flags = 0; //zero the flags
ConsoleObject->rtype.pobj_info.tmap_override = -1; //no tmap override!
ConsoleObject->rtype.pobj_info.anim_angles = {};
2006-03-20 17:12:09 +00:00
// Clear misc
ConsoleObject->flags = 0;
ConsoleObject->matcen_creator = 0;
2006-03-20 17:12:09 +00:00
}
//make object0 the player, setting all relevant fields
void init_player_object()
{
2015-07-12 01:04:21 +00:00
const auto &&console = vobjptr(ConsoleObject);
console->type = OBJ_PLAYER;
set_player_id(console, 0); //no sub-types for player
console->signature = object_signature_t{0}; //player has zero, others start at 1
console->size = Polygon_models[Player_ship->model_num].rad;
console->control_type = CT_SLEW; //default is player slewing
console->movement_type = MT_PHYSICS; //change this sometime
console->lifeleft = IMMORTAL_TIME;
console->attached_obj = object_none;
2006-03-20 17:12:09 +00:00
reset_player_object();
}
//sets up the free list & init player & whatever else
void init_objects()
{
for (objnum_t i = 0; i< MAX_OBJECTS; ++i)
{
2006-03-20 17:12:09 +00:00
free_obj_list[i] = i;
2016-11-06 17:12:03 +00:00
auto &obj = *vobjptr(i);
DXX_POISON_VAR(obj, 0xfd);
obj.type = OBJ_NONE;
2006-03-20 17:12:09 +00:00
}
range_for (auto &j, Segments)
j.objects = object_none;
2006-03-20 17:12:09 +00:00
ConsoleObject = Viewer = &Objects.front();
2006-03-20 17:12:09 +00:00
init_player_object();
obj_link_unchecked(vobjptridx(ConsoleObject), vsegptridx(segment_first)); //put in the world in segment 0
2006-03-20 17:12:09 +00:00
num_objects = 1; //just the player
Objects.set_count(1);
2006-03-20 17:12:09 +00:00
}
//after calling init_object(), the network code has grabbed specific
//object slots without allocating them. Go though the objects & build
//the free list, then set the apporpriate globals
void special_reset_objects(void)
{
num_objects=MAX_OBJECTS;
Objects.set_count(1);
assert(Objects.front().type != OBJ_NONE); //0 should be used
2006-03-20 17:12:09 +00:00
2016-11-06 17:12:03 +00:00
DXX_POISON_VAR(free_obj_list, 0xfd);
for (objnum_t i = MAX_OBJECTS; i--;)
if (vcobjptr(i)->type == OBJ_NONE)
2006-03-20 17:12:09 +00:00
free_obj_list[--num_objects] = i;
else
if (i > Highest_object_index)
Objects.set_count(i + 1);
2006-03-20 17:12:09 +00:00
}
//link the object into the list for its segment
2014-10-02 03:02:34 +00:00
void obj_link(const vobjptridx_t obj,const vsegptridx_t segnum)
2006-03-20 17:12:09 +00:00
{
assert(obj->segnum == segment_none);
assert(obj->next == object_none);
assert(obj->prev == object_none);
obj_link_unchecked(obj, segnum);
}
void obj_link_unchecked(const vobjptridx_t obj,const vsegptridx_t segnum)
{
2006-03-20 17:12:09 +00:00
obj->segnum = segnum;
2014-10-02 03:02:34 +00:00
obj->next = segnum->objects;
obj->prev = object_none;
2006-03-20 17:12:09 +00:00
2014-10-02 03:02:34 +00:00
segnum->objects = obj;
2006-03-20 17:12:09 +00:00
if (obj->next != object_none)
vobjptr(obj->next)->prev = obj;
2006-03-20 17:12:09 +00:00
}
2016-07-23 04:10:43 +00:00
void obj_unlink(object_base &obj)
2006-03-20 17:12:09 +00:00
{
2016-07-23 04:10:43 +00:00
const auto next = obj.next;
2016-11-06 17:12:03 +00:00
/* It is a bug elsewhere if vsegptr ever fails here. However, it is
* expensive to check, so only force verification in debug builds.
*
* In debug builds, always compute it, for the side effect of
* validating the segment number.
*
* In release builds, compute it when it is needed.
*/
#ifndef NDEBUG
const auto &&segp = vsegptr(obj.segnum);
#endif
2016-07-23 04:10:43 +00:00
((obj.prev == object_none)
2016-11-06 17:12:03 +00:00
? (
#ifdef NDEBUG
vsegptr(obj.segnum)
#else
segp
#endif
)->objects
2016-07-23 04:10:43 +00:00
: vobjptr(obj.prev)->next) = next;
2006-03-20 17:12:09 +00:00
2016-07-23 04:10:43 +00:00
obj.segnum = segment_none;
2006-03-20 17:12:09 +00:00
2016-07-23 04:10:43 +00:00
if (next != object_none)
vobjptr(next)->prev = obj.prev;
2016-11-06 17:12:03 +00:00
DXX_POISON_VAR(obj.next, 0xfa);
DXX_POISON_VAR(obj.prev, 0xfa);
2006-03-20 17:12:09 +00:00
}
// Returns a new, unique signature for a new object
2015-03-22 18:49:21 +00:00
object_signature_t obj_get_signature()
{
static short sig = 0; // Yes! Short! a) We do not need higher values b) the demo system only stores shorts
2015-01-28 03:42:52 +00:00
uint_fast32_t lsig = sig;
2016-11-10 04:22:19 +00:00
for (const auto &&b = vcobjptr.begin(), &&e = vcobjptr.end();;)
{
2015-01-28 03:42:52 +00:00
if (unlikely(lsig == std::numeric_limits<decltype(sig)>::max()))
lsig = 0;
++ lsig;
const auto predicate = [lsig](const object_base &o) {
if (o.type == OBJ_NONE)
return false;
return o.signature.get() == lsig;
2015-01-28 03:42:52 +00:00
};
if (std::any_of(b, e, predicate))
2015-01-28 03:42:52 +00:00
continue;
sig = static_cast<int16_t>(lsig);
2015-03-22 18:49:21 +00:00
return object_signature_t{static_cast<uint16_t>(lsig)};
}
}
2006-03-20 17:12:09 +00:00
}
namespace dcx {
2015-01-28 03:42:52 +00:00
static unsigned Debris_object_count;
2006-03-20 17:12:09 +00:00
}
namespace dsx {
2006-03-20 17:12:09 +00:00
//returns the number of a free object, updating Highest_object_index.
//Generally, obj_create() should be called to get an object, since it
//fills in important fields and does the linking.
//returns -1 if no free objects
2014-01-12 19:14:16 +00:00
objptridx_t obj_allocate()
2006-03-20 17:12:09 +00:00
{
if ( num_objects >= MAX_OBJECTS ) {
return object_none;
2006-03-20 17:12:09 +00:00
}
2014-11-20 03:00:41 +00:00
auto objnum = free_obj_list[num_objects++];
2006-03-20 17:12:09 +00:00
if (objnum > Highest_object_index) {
Objects.set_count(objnum + 1);
2006-03-20 17:12:09 +00:00
}
const auto &&r = vobjptridx(objnum);
assert(r->type == OBJ_NONE);
return r;
2006-03-20 17:12:09 +00:00
}
//frees up an object. Generally, obj_delete() should be called to get
//rid of an object. This function deallocates the object entry after
//the object has been unlinked
static void obj_free(objnum_t objnum)
2006-03-20 17:12:09 +00:00
{
free_obj_list[--num_objects] = objnum;
Assert(num_objects >= 0);
if (objnum == Highest_object_index)
{
objnum_t o = Highest_object_index;
for (;;)
{
--o;
if (vcobjptr(o)->type != OBJ_NONE)
break;
if (o == 0)
break;
}
Objects.set_count(o + 1);
}
2006-03-20 17:12:09 +00:00
}
//-----------------------------------------------------------------------------
// Scan the object list, freeing down to num_used objects
// Returns number of slots freed.
2014-09-20 23:47:27 +00:00
static void free_object_slots(uint_fast32_t num_used)
2006-03-20 17:12:09 +00:00
{
array<object *, MAX_OBJECTS> obj_list;
unsigned num_already_free, num_to_free, olind = 0;
2006-03-20 17:12:09 +00:00
num_already_free = MAX_OBJECTS - Highest_object_index - 1;
if (MAX_OBJECTS - num_already_free < num_used)
return;
2006-03-20 17:12:09 +00:00
2016-02-12 04:02:28 +00:00
range_for (const auto &&objp, vobjptr)
2014-10-12 23:05:46 +00:00
{
2015-06-13 22:42:18 +00:00
if (objp->flags & OF_SHOULD_BE_DEAD)
{
2006-03-20 17:12:09 +00:00
num_already_free++;
if (MAX_OBJECTS - num_already_free < num_used)
return;
2006-03-20 17:12:09 +00:00
} else
2015-06-13 22:42:18 +00:00
switch (objp->type)
{
2006-03-20 17:12:09 +00:00
case OBJ_NONE:
num_already_free++;
if (MAX_OBJECTS - num_already_free < num_used)
return;
2006-03-20 17:12:09 +00:00
break;
case OBJ_WALL:
Int3(); // This is curious. What is an object that is a wall?
break;
case OBJ_FIREBALL:
case OBJ_WEAPON:
case OBJ_DEBRIS:
2015-06-13 22:42:18 +00:00
obj_list[olind++] = objp;
2006-03-20 17:12:09 +00:00
break;
case OBJ_ROBOT:
case OBJ_HOSTAGE:
case OBJ_PLAYER:
case OBJ_CNTRLCEN:
case OBJ_CLUTTER:
case OBJ_GHOST:
case OBJ_LIGHT:
case OBJ_CAMERA:
case OBJ_POWERUP:
2016-11-20 23:12:00 +00:00
case OBJ_COOP:
case OBJ_MARKER:
2006-03-20 17:12:09 +00:00
break;
}
}
num_to_free = MAX_OBJECTS - num_used - num_already_free;
if (num_to_free > olind) {
num_to_free = olind;
}
// Capture before num_to_free modified
2016-02-12 04:02:28 +00:00
const auto &&r = partial_const_range(obj_list, num_to_free);
2014-10-02 03:02:34 +00:00
auto l = [&r, &num_to_free](bool (*predicate)(const vcobjptr_t)) -> bool {
2015-07-12 01:04:21 +00:00
range_for (const auto i, r)
{
const auto &&o = vobjptr(i);
if (predicate(o))
{
o->flags |= OF_SHOULD_BE_DEAD;
if (!-- num_to_free)
return true;
}
2015-07-12 01:04:21 +00:00
}
return false;
};
2006-03-20 17:12:09 +00:00
2014-10-02 03:02:34 +00:00
auto predicate_debris = [](const vcobjptr_t o) { return o->type == OBJ_DEBRIS; };
if (l(predicate_debris))
return;
2006-03-20 17:12:09 +00:00
2014-10-02 03:02:34 +00:00
auto predicate_fireball = [](const vcobjptr_t o) { return o->type == OBJ_FIREBALL && o->ctype.expl_info.delete_objnum == object_none; };
if (l(predicate_fireball))
return;
2006-03-20 17:12:09 +00:00
2015-12-03 03:26:49 +00:00
auto predicate_flare = [](const vcobjptr_t o) { return (o->type == OBJ_WEAPON) && (get_weapon_id(o) == weapon_id_type::FLARE_ID); };
if (l(predicate_flare))
return;
2006-03-20 17:12:09 +00:00
2015-12-03 03:26:49 +00:00
auto predicate_nonflare_weapon = [](const vcobjptr_t o) { return (o->type == OBJ_WEAPON) && (get_weapon_id(o) != weapon_id_type::FLARE_ID); };
if (l(predicate_nonflare_weapon))
return;
2006-03-20 17:12:09 +00:00
}
//-----------------------------------------------------------------------------
//initialize a new object. adds to the list for the given segment
//note that segnum is really just a suggestion, since this routine actually
//searches for the correct segment
//returns the object number
2014-10-02 03:02:34 +00:00
objptridx_t obj_create(object_type_t type, ubyte id,vsegptridx_t segnum,const vms_vector &pos,
const vms_matrix *orient,fix size,ubyte ctype,ubyte mtype,ubyte rtype)
2006-03-20 17:12:09 +00:00
{
// Some consistency checking. FIXME: Add more debug output here to probably trace all possible occurances back.
2006-03-20 17:12:09 +00:00
Assert(ctype <= CT_CNTRLCEN);
if (type==OBJ_DEBRIS && Debris_object_count>=Max_debris_objects && !PERSISTENT_DEBRIS)
return object_none;
2006-03-20 17:12:09 +00:00
if (get_seg_masks(pos, segnum, 0).centermask != 0)
2014-10-02 03:02:34 +00:00
{
auto p = find_point_seg(pos,segnum);
if (p == segment_none) {
return object_none; //don't create this object
2006-03-20 17:12:09 +00:00
}
2014-10-02 03:02:34 +00:00
segnum = p;
}
2006-03-20 17:12:09 +00:00
// Find next free object
2016-10-29 23:16:18 +00:00
const auto &&obj = obj_allocate();
2006-03-20 17:12:09 +00:00
2014-01-10 04:00:34 +00:00
if (obj == object_none) //no free objects
return object_none;
2006-03-20 17:12:09 +00:00
2014-01-05 05:17:34 +00:00
Assert(obj->type == OBJ_NONE); //make sure unused
2015-01-28 03:42:52 +00:00
auto signature = obj_get_signature();
2006-03-20 17:12:09 +00:00
// Zero out object structure to keep weird bugs from happening
// in uninitialized fields.
2014-07-04 04:15:04 +00:00
*obj = {};
2015-01-03 23:44:32 +00:00
// Tell Valgrind to warn on any uninitialized fields.
2016-11-06 17:12:03 +00:00
DXX_POISON_VAR(*obj, 0xfd);
2006-03-20 17:12:09 +00:00
2015-01-28 03:42:52 +00:00
obj->signature = signature;
obj->type = type;
obj->id = id;
obj->last_pos = pos;
obj->pos = pos;
obj->size = size;
obj->flags = 0;
2006-03-20 17:12:09 +00:00
//@@if (orient != NULL)
//@@ obj->orient = *orient;
obj->orient = orient?*orient:vmd_identity_matrix;
obj->control_type = ctype;
2016-11-20 23:12:00 +00:00
set_object_movement_type(*obj, mtype);
2006-03-20 17:12:09 +00:00
obj->render_type = rtype;
obj->contains_type = -1;
obj->contains_id = -1;
obj->contains_count = 0;
obj->matcen_creator = 0;
2006-03-20 17:12:09 +00:00
obj->lifeleft = IMMORTAL_TIME; //assume immortal
obj->attached_obj = object_none;
2006-03-20 17:12:09 +00:00
if (obj->control_type == CT_POWERUP)
{
2006-03-20 17:12:09 +00:00
obj->ctype.powerup_info.count = 1;
obj->ctype.powerup_info.flags = 0;
obj->ctype.powerup_info.creation_time = GameTime64;
}
2006-03-20 17:12:09 +00:00
// Init physics info for this object
if (obj->movement_type == MT_PHYSICS) {
2015-12-22 04:18:51 +00:00
obj->mtype.phys_info = {};
2006-03-20 17:12:09 +00:00
}
if (obj->render_type == RT_POLYOBJ)
{
obj->rtype.pobj_info.subobj_flags = 0;
2006-03-20 17:12:09 +00:00
obj->rtype.pobj_info.tmap_override = -1;
obj->rtype.pobj_info.alt_textures = 0;
}
2006-03-20 17:12:09 +00:00
obj->shields = 20*F1_0;
{
auto p = find_point_seg(pos,segnum); //find correct segment
// Previously this was only an assert check. Now it is also
// checked at runtime.
segnum = p;
}
2006-03-20 17:12:09 +00:00
obj_link_unchecked(obj, segnum);
2006-03-20 17:12:09 +00:00
// Set (or not) persistent bit in phys_info.
if (obj->type == OBJ_WEAPON) {
Assert(obj->control_type == CT_WEAPON);
obj->mtype.phys_info.flags |= (Weapon_info[get_weapon_id(obj)].persistent*PF_PERSISTENT);
obj->ctype.laser_info.creation_time = GameTime64;
2017-01-15 00:03:13 +00:00
obj->ctype.laser_info.clear_hitobj();
2006-03-20 17:12:09 +00:00
obj->ctype.laser_info.multiplier = F1_0;
#if defined(DXX_BUILD_DESCENT_II)
obj->ctype.laser_info.last_afterburner_time = 0;
#endif
2006-03-20 17:12:09 +00:00
}
if (obj->control_type == CT_EXPLOSION)
obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = object_none;
2006-03-20 17:12:09 +00:00
if (obj->type == OBJ_DEBRIS)
Debris_object_count++;
2014-01-10 04:00:34 +00:00
return obj;
2006-03-20 17:12:09 +00:00
}
#if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
//create a copy of an object. returns new object number
2016-10-29 23:16:18 +00:00
objptridx_t obj_create_copy(const object &srcobj, const vms_vector &new_pos, const vsegptridx_t newsegnum)
2006-03-20 17:12:09 +00:00
{
// Find next free object
2016-10-29 23:16:18 +00:00
const auto &&obj = obj_allocate();
2006-03-20 17:12:09 +00:00
if (obj == object_none)
return object_none;
2006-03-20 17:12:09 +00:00
2016-10-29 23:16:18 +00:00
*obj = srcobj;
2006-03-20 17:12:09 +00:00
2014-11-01 03:06:04 +00:00
obj->pos = obj->last_pos = new_pos;
2006-03-20 17:12:09 +00:00
obj_link_unchecked(obj,newsegnum);
2006-03-20 17:12:09 +00:00
obj->signature = obj_get_signature();
2006-03-20 17:12:09 +00:00
//we probably should initialize sub-structures here
return obj;
2006-03-20 17:12:09 +00:00
}
#endif
//remove object from the world
2014-10-02 03:02:34 +00:00
void obj_delete(const vobjptridx_t obj)
2006-03-20 17:12:09 +00:00
{
Assert(obj->type != OBJ_NONE);
Assert(obj != ConsoleObject);
#if defined(DXX_BUILD_DESCENT_II)
2015-12-03 03:26:49 +00:00
if (obj->type==OBJ_WEAPON && get_weapon_id(obj)==weapon_id_type::GUIDEDMISS_ID && obj->ctype.laser_info.parent_type==OBJ_PLAYER)
{
2015-09-15 02:48:04 +00:00
const auto pnum = get_player_id(vcobjptr(obj->ctype.laser_info.parent_num));
2006-03-20 17:12:09 +00:00
if (pnum!=Player_num) {
Guided_missile[pnum]=NULL;
}
else if (Newdemo_state==ND_STATE_RECORDING)
newdemo_record_guided_end();
}
#endif
2006-03-20 17:12:09 +00:00
if (obj == Viewer) //deleting the viewer?
Viewer = ConsoleObject; //..make the player the viewer
if (obj->flags & OF_ATTACHED) //detach this from object
obj_detach_one(obj);
if (obj->attached_obj != object_none) //detach all objects from this
2006-03-20 17:12:09 +00:00
obj_detach_all(obj);
if (obj->type == OBJ_DEBRIS)
Debris_object_count--;
2014-01-11 17:14:20 +00:00
obj_unlink(obj);
2016-11-06 17:12:03 +00:00
DXX_POISON_VAR(*obj, 0xfa);
2006-03-20 17:12:09 +00:00
obj->type = OBJ_NONE; //unused!
2014-01-11 17:14:20 +00:00
obj_free(obj);
2006-03-20 17:12:09 +00:00
}
#define DEATH_SEQUENCE_LENGTH (F1_0*5)
#define DEATH_SEQUENCE_EXPLODE_TIME (F1_0*2)
object *Dead_player_camera = NULL; // Object index of object watching deader.
static object *Viewer_save;
}
namespace dcx {
player_dead_state Player_dead_state = player_dead_state::no; // If !0, then player is dead, but game continues so he can watch.
static int Player_flags_save;
2006-03-20 17:12:09 +00:00
int Death_sequence_aborted=0;
static fix Camera_to_player_dist_goal = F1_0*4;
static uint8_t Control_type_save, Render_type_save;
}
2006-03-20 17:12:09 +00:00
namespace dsx {
2006-03-20 17:12:09 +00:00
// ------------------------------------------------------------------------------------------------------------------
void dead_player_end(void)
{
if (Player_dead_state == player_dead_state::no)
2006-03-20 17:12:09 +00:00
return;
if (Newdemo_state == ND_STATE_RECORDING)
newdemo_record_restore_cockpit();
Player_dead_state = player_dead_state::no;
obj_delete(vobjptridx(Dead_player_camera));
2006-03-20 17:12:09 +00:00
Dead_player_camera = NULL;
select_cockpit(PlayerCfg.CockpitMode[0]);
2006-03-20 17:12:09 +00:00
Viewer = Viewer_save;
ConsoleObject->type = OBJ_PLAYER;
ConsoleObject->flags = Player_flags_save;
Assert((Control_type_save == CT_FLYING) || (Control_type_save == CT_SLEW));
ConsoleObject->control_type = Control_type_save;
ConsoleObject->render_type = Render_type_save;
auto &player_info = ConsoleObject->ctype.player_info;
player_info.powerup_flags &= ~PLAYER_FLAGS_INVULNERABLE;
player_info.Player_eggs_dropped = false;
2006-03-20 17:12:09 +00:00
}
// ------------------------------------------------------------------------------------------------------------------
// Camera is less than size of player away from
2015-07-12 01:04:21 +00:00
static void set_camera_pos(vms_vector &camera_pos, const vcobjptridx_t objp)
2006-03-20 17:12:09 +00:00
{
int count = 0;
fix camera_player_dist;
fix far_scale;
2014-11-01 14:57:37 +00:00
camera_player_dist = vm_vec_dist_quick(camera_pos, objp->pos);
2006-03-20 17:12:09 +00:00
if (camera_player_dist < Camera_to_player_dist_goal) {
2006-03-20 17:12:09 +00:00
// Camera is too close to player object, so move it away.
fvi_query fq;
fvi_info hit_data;
2014-11-01 14:57:37 +00:00
auto player_camera_vec = vm_vec_sub(camera_pos, objp->pos);
2006-03-20 17:12:09 +00:00
if ((player_camera_vec.x == 0) && (player_camera_vec.y == 0) && (player_camera_vec.z == 0))
player_camera_vec.x += F1_0/16;
hit_data.hit_type = HIT_WALL;
far_scale = F1_0;
while ((hit_data.hit_type != HIT_NONE) && (count++ < 6)) {
2014-09-28 21:11:04 +00:00
vm_vec_normalize_quick(player_camera_vec);
2014-09-28 21:11:05 +00:00
vm_vec_scale(player_camera_vec, Camera_to_player_dist_goal);
2006-03-20 17:12:09 +00:00
fq.p0 = &objp->pos;
2014-10-30 03:11:06 +00:00
const auto closer_p1 = vm_vec_add(objp->pos, player_camera_vec); // This is the actual point we want to put the camera at.
2014-09-28 21:11:05 +00:00
vm_vec_scale(player_camera_vec, far_scale); // ...but find a point 50% further away...
2014-10-30 03:11:06 +00:00
const auto local_p1 = vm_vec_add(objp->pos, player_camera_vec); // ...so we won't have to do as many cuts.
2006-03-20 17:12:09 +00:00
fq.p1 = &local_p1;
fq.startseg = objp->segnum;
fq.rad = 0;
2014-01-11 17:14:20 +00:00
fq.thisobjnum = objp;
2015-02-05 03:03:51 +00:00
fq.ignore_obj_list.first = nullptr;
2006-03-20 17:12:09 +00:00
fq.flags = 0;
2015-01-20 02:46:42 +00:00
find_vector_intersection(fq, hit_data);
2006-03-20 17:12:09 +00:00
if (hit_data.hit_type == HIT_NONE) {
2014-11-01 14:57:37 +00:00
camera_pos = closer_p1;
2006-03-20 17:12:09 +00:00
} else {
2014-10-02 03:02:38 +00:00
make_random_vector(player_camera_vec);
2006-03-20 17:12:09 +00:00
far_scale = 3*F1_0/2;
}
}
}
}
// ------------------------------------------------------------------------------------------------------------------
window_event_result dead_player_frame()
2006-03-20 17:12:09 +00:00
{
static fix time_dead = 0;
2006-03-20 17:12:09 +00:00
if (Player_dead_state != player_dead_state::no)
{
time_dead += FrameTime;
2006-03-20 17:12:09 +00:00
// If unable to create camera at time of death, create now.
if (Dead_player_camera == Viewer_save) {
const auto &player = get_local_plrobj();
const auto &&objnum = obj_create(OBJ_CAMERA, 0, vsegptridx(player.segnum), player.pos, &player.orient, 0, CT_NONE, MT_NONE, RT_NONE);
2006-03-20 17:12:09 +00:00
if (objnum != object_none)
2014-09-08 03:24:48 +00:00
Viewer = Dead_player_camera = objnum;
2006-03-20 17:12:09 +00:00
else {
Int3();
}
}
ConsoleObject->mtype.phys_info.rotvel.x = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/4;
ConsoleObject->mtype.phys_info.rotvel.y = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/2;
ConsoleObject->mtype.phys_info.rotvel.z = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/3;
Camera_to_player_dist_goal = min(time_dead*8, F1_0*20) + ConsoleObject->size;
2015-07-12 01:04:21 +00:00
set_camera_pos(Dead_player_camera->pos, vcobjptridx(ConsoleObject));
2006-03-20 17:12:09 +00:00
// the following line uncommented by WraithX, 4-12-00
if (time_dead < DEATH_SEQUENCE_EXPLODE_TIME + F1_0 * 2)
{
2014-10-29 03:24:31 +00:00
const auto fvec = vm_vec_sub(ConsoleObject->pos, Dead_player_camera->pos);
2014-10-01 02:28:42 +00:00
vm_vector_2_matrix(Dead_player_camera->orient, fvec, nullptr, nullptr);
2006-03-20 17:12:09 +00:00
Dead_player_camera->mtype.phys_info = ConsoleObject->mtype.phys_info;
// the following "if" added by WraithX to get rid of camera "wiggle"
2015-11-06 03:51:10 +00:00
Dead_player_camera->mtype.phys_info.flags &= ~PF_WIGGLE;
// end "if" added by WraithX, 4/13/00
2006-03-20 17:12:09 +00:00
// the following line uncommented by WraithX, 4-12-00
}
else
{
// the following line uncommented by WraithX, 4-11-00
Dead_player_camera->movement_type = MT_PHYSICS;
//Dead_player_camera->mtype.phys_info.rotvel.y = F1_0/8;
// the following line uncommented by WraithX, 4-12-00
}
// end addition by WX
if (time_dead > DEATH_SEQUENCE_EXPLODE_TIME) {
2016-01-09 16:38:10 +00:00
if (Player_dead_state != player_dead_state::exploded)
{
auto &player_info = get_local_plrobj().ctype.player_info;
const auto hostages_lost = exchange(player_info.mission.hostages_on_board, 0);
2006-03-20 17:12:09 +00:00
2015-11-06 03:51:10 +00:00
if (hostages_lost > 1)
HUD_init_message(HM_DEFAULT, TXT_SHIP_DESTROYED_2, hostages_lost);
else
2015-11-06 03:51:10 +00:00
HUD_init_message_literal(HM_DEFAULT, hostages_lost == 1 ? TXT_SHIP_DESTROYED_1 : TXT_SHIP_DESTROYED_0);
2006-03-20 17:12:09 +00:00
Player_dead_state = player_dead_state::exploded;
2006-03-20 17:12:09 +00:00
2014-12-23 04:20:27 +00:00
const auto cobjp = vobjptridx(ConsoleObject);
drop_player_eggs(cobjp);
player_info.Player_eggs_dropped = true;
2006-03-20 17:12:09 +00:00
if (Game_mode & GM_MULTI)
{
multi_send_player_deres(deres_explode);
2006-03-20 17:12:09 +00:00
}
2014-12-23 04:20:27 +00:00
explode_badass_player(cobjp);
2006-03-20 17:12:09 +00:00
//is this next line needed, given the badass call above?
2014-12-23 04:20:27 +00:00
explode_object(cobjp,0);
2006-03-20 17:12:09 +00:00
ConsoleObject->flags &= ~OF_SHOULD_BE_DEAD; //don't really kill player
ConsoleObject->render_type = RT_NONE; //..just make him disappear
ConsoleObject->type = OBJ_GHOST; //..and kill intersections
#if defined(DXX_BUILD_DESCENT_II)
player_info.powerup_flags &= ~PLAYER_FLAGS_HEADLIGHT_ON;
#endif
2006-03-20 17:12:09 +00:00
}
} else {
if (d_rand() < FrameTime*4) {
if (Game_mode & GM_MULTI)
multi_send_create_explosion(Player_num);
2014-12-23 04:20:27 +00:00
create_small_fireball_on_object(vobjptridx(ConsoleObject), F1_0, 1);
2006-03-20 17:12:09 +00:00
}
}
if (Death_sequence_aborted)
{
auto &player_info = get_local_plrobj().ctype.player_info;
if (!player_info.Player_eggs_dropped) {
player_info.Player_eggs_dropped = true;
2014-12-23 04:20:27 +00:00
drop_player_eggs(vobjptridx(ConsoleObject));
2006-03-20 17:12:09 +00:00
if (Game_mode & GM_MULTI)
{
multi_send_player_deres(deres_explode);
2006-03-20 17:12:09 +00:00
}
}
return DoPlayerDead(); //kill_player();
2006-03-20 17:12:09 +00:00
}
}
else
time_dead = 0;
return window_event_result::handled;
2006-03-20 17:12:09 +00:00
}
// ------------------------------------------------------------------------------------------------------------------
static void start_player_death_sequence(object &player)
2006-03-20 17:12:09 +00:00
{
assert(&player == ConsoleObject);
if (Player_dead_state != player_dead_state::no ||
Dead_player_camera != NULL ||
((Game_mode & GM_MULTI) && (get_local_player().connected != CONNECT_PLAYING)))
2006-03-20 17:12:09 +00:00
return;
//Assert(Dead_player_camera == NULL);
reset_rear_view();
if (!(Game_mode & GM_MULTI))
HUD_clear_messages();
Death_sequence_aborted = 0;
if (Game_mode & GM_MULTI)
{
#if defined(DXX_BUILD_DESCENT_II)
// If Hoard, increase number of orbs by 1. Only if you haven't killed yourself. This prevents cheating
if (game_mode_hoard())
{
auto &player_info = player.ctype.player_info;
auto &proximity = player_info.secondary_ammo[PROXIMITY_INDEX];
if (proximity < 12)
2015-07-25 23:10:46 +00:00
{
const auto is_bad_kill = []{
auto &lplr = get_local_player();
auto &lplrobj = get_local_plrobj();
const auto killer_objnum = lplrobj.ctype.player_info.killer_objnum;
if (killer_objnum == lplr.objnum)
2015-07-25 23:10:46 +00:00
/* Self kill */
return true;
if (killer_objnum == object_none)
/* Non-player kill */
return true;
const auto &&killer_objp = vobjptr(killer_objnum);
if (killer_objp->type != OBJ_PLAYER)
return true;
2015-07-25 23:10:46 +00:00
if (!(Game_mode & GM_TEAM))
return false;
return get_team(Player_num) == get_team(get_player_id(killer_objp));
2015-07-25 23:10:46 +00:00
};
if (!is_bad_kill())
++ proximity;
2015-07-25 23:10:46 +00:00
}
}
#endif
multi_send_kill(vobjptridx(get_local_player().objnum));
2006-03-20 17:12:09 +00:00
}
PaletteRedAdd = 40;
Player_dead_state = player_dead_state::yes;
2006-03-20 17:12:09 +00:00
vm_vec_zero(player.mtype.phys_info.rotthrust);
vm_vec_zero(player.mtype.phys_info.thrust);
2006-03-20 17:12:09 +00:00
const auto &&objnum = obj_create(OBJ_CAMERA, 0, vsegptridx(player.segnum), player.pos, &player.orient, 0, CT_NONE, MT_NONE, RT_NONE);
2006-03-20 17:12:09 +00:00
Viewer_save = Viewer;
if (objnum != object_none)
2014-09-08 03:24:48 +00:00
Viewer = Dead_player_camera = objnum;
2006-03-20 17:12:09 +00:00
else {
Int3();
Dead_player_camera = Viewer;
}
select_cockpit(CM_LETTERBOX);
if (Newdemo_state == ND_STATE_RECORDING)
newdemo_record_letterbox();
Player_flags_save = player.flags;
Control_type_save = player.control_type;
Render_type_save = player.render_type;
2006-03-20 17:12:09 +00:00
player.flags &= ~OF_SHOULD_BE_DEAD;
2006-03-20 17:12:09 +00:00
// Players[Player_num].flags |= PLAYER_FLAGS_INVULNERABLE;
player.control_type = CT_NONE;
2006-03-20 17:12:09 +00:00
PALETTE_FLASH_SET(0,0,0);
}
// ------------------------------------------------------------------------------------------------------------------
static void obj_delete_all_that_should_be_dead()
2006-03-20 17:12:09 +00:00
{
objnum_t local_dead_player_object=object_none;
2006-03-20 17:12:09 +00:00
// Move all objects
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_NONE) && (objp->flags&OF_SHOULD_BE_DEAD) ) {
Assert(!(objp->type==OBJ_FIREBALL && objp->ctype.expl_info.delete_time!=-1));
if (objp->type==OBJ_PLAYER) {
if ( get_player_id(objp) == Player_num ) {
if (local_dead_player_object == object_none) {
2006-03-20 17:12:09 +00:00
start_player_death_sequence(objp);
local_dead_player_object = objp;
2006-03-20 17:12:09 +00:00
} else
Int3(); // Contact Mike: Illegal, killed player twice in this frame!
// Ok to continue, won't start death sequence again!
// kill_player();
}
} else {
2014-11-26 03:39:21 +00:00
obj_delete(objp);
2006-03-20 17:12:09 +00:00
}
}
}
}
//when an object has moved into a new segment, this function unlinks it
//from its old segment, and links it into the new segment
2014-10-02 03:02:34 +00:00
void obj_relink(const vobjptridx_t objnum,const vsegptridx_t newsegnum)
2006-03-20 17:12:09 +00:00
{
obj_unlink(objnum);
obj_link_unchecked(objnum,newsegnum);
2006-03-20 17:12:09 +00:00
}
// for getting out of messed up linking situations (i.e. caused by demo playback)
void obj_relink_all(void)
{
2016-02-12 04:02:28 +00:00
range_for (const auto &&segp, vsegptr)
2015-06-13 22:42:18 +00:00
{
segp->objects = object_none;
}
2016-02-12 04:02:28 +00:00
range_for (const auto &&obj, vobjptridx)
2014-10-12 23:05:46 +00:00
{
if (obj->type != OBJ_NONE)
{
auto segnum = obj->segnum;
if (segnum > Highest_segment_index)
segnum = segment_first;
obj_link_unchecked(obj, vsegptridx(segnum));
}
2014-10-12 23:05:46 +00:00
}
}
2006-03-20 17:12:09 +00:00
//process a continuously-spinning object
2016-04-23 17:59:47 +00:00
static void spin_object(object_base &obj)
2006-03-20 17:12:09 +00:00
{
vms_angvec rotangs;
2016-04-23 17:59:47 +00:00
assert(obj.movement_type == MT_SPINNING);
2006-03-20 17:12:09 +00:00
2016-04-23 17:59:47 +00:00
const fix frametime = FrameTime;
rotangs.p = fixmul(obj.mtype.spin_rate.x, frametime);
rotangs.h = fixmul(obj.mtype.spin_rate.y, frametime);
rotangs.b = fixmul(obj.mtype.spin_rate.z, frametime);
2006-03-20 17:12:09 +00:00
2015-02-05 03:03:51 +00:00
const auto &&rotmat = vm_angles_2_matrix(rotangs);
2016-04-23 17:59:47 +00:00
obj.orient = vm_matrix_x_matrix(obj.orient, rotmat);
check_and_fix_matrix(obj.orient);
2006-03-20 17:12:09 +00:00
}
#if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
int Drop_afterburner_blob_flag; //ugly hack
//see if wall is volatile, and if so, cause damage to player
//returns true if player is in lava
#endif
2006-03-20 17:12:09 +00:00
//--------------------------------------------------------------------
//move an object for the current frame
static window_event_result object_move_one(const vobjptridx_t obj)
2006-03-20 17:12:09 +00:00
{
2015-07-12 01:04:21 +00:00
const auto previous_segment = obj->segnum;
auto result = window_event_result::handled;
2006-03-20 17:12:09 +00:00
obj->last_pos = obj->pos; // Save the current position
if ((obj->type==OBJ_PLAYER) && (Player_num==get_player_id(obj))) {
const auto &&segp = vsegptr(obj->segnum);
#if defined(DXX_BUILD_DESCENT_II)
if (game_mode_capture_flag())
fuelcen_check_for_goal(segp);
else if (game_mode_hoard())
fuelcen_check_for_hoard_goal(segp);
2006-03-20 17:12:09 +00:00
#endif
auto &player_info = get_local_plrobj().ctype.player_info;
auto &energy = player_info.energy;
2016-07-03 00:54:16 +00:00
const fix fuel = fuelcen_give_fuel(segp, INITIAL_ENERGY - energy);
2006-03-20 17:12:09 +00:00
if (fuel > 0 ) {
2016-07-03 00:54:16 +00:00
energy += fuel;
2006-03-20 17:12:09 +00:00
}
#if defined(DXX_BUILD_DESCENT_II)
auto &pl_shields = get_local_plrobj().shields;
const fix shields = repaircen_give_shields(segp, INITIAL_SHIELDS - pl_shields);
2006-03-20 17:12:09 +00:00
if (shields > 0) {
pl_shields += shields;
2006-03-20 17:12:09 +00:00
}
#endif
2006-03-20 17:12:09 +00:00
}
if (obj->lifeleft != IMMORTAL_TIME) //if not immortal...
obj->lifeleft -= FrameTime; //...inevitable countdown towards death
2006-03-20 17:12:09 +00:00
#if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
Drop_afterburner_blob_flag = 0;
#endif
2006-03-20 17:12:09 +00:00
switch (obj->control_type) {
case CT_NONE: break;
case CT_FLYING:
read_flying_controls( obj );
break;
case CT_REPAIRCEN: Int3(); // -- hey! these are no longer supported!! -- do_repair_sequence(obj); break;
case CT_POWERUP: do_powerup_frame(obj); break;
case CT_MORPH: //morph implies AI
do_morph_frame(obj);
//NOTE: FALLS INTO AI HERE!!!!
case CT_AI:
//NOTE LINK TO CT_MORPH ABOVE!!!
if (Game_suspended & SUSP_ROBOTS) return window_event_result::ignored;
2006-03-20 17:12:09 +00:00
do_ai_frame(obj);
break;
case CT_WEAPON: Laser_do_weapon_sequence(obj); break;
case CT_EXPLOSION: do_explosion_sequence(obj); break;
#ifndef RELEASE
case CT_SLEW:
if ( keyd_pressed[KEY_PAD5] ) slew_stop();
2006-03-20 17:12:09 +00:00
if ( keyd_pressed[KEY_NUMLOCK] ) {
slew_reset_orient();
2006-03-20 17:12:09 +00:00
}
slew_frame(0 ); // Does velocity addition for us.
break;
#endif
// case CT_FLYTHROUGH:
// do_flythrough(obj,0); // HACK:do_flythrough should operate on an object!!!!
// //check_object_seg(obj);
// return; // DON'T DO THE REST OF OBJECT STUFF SINCE THIS IS A SPECIAL CASE!!!
// break;
case CT_DEBRIS: do_debris_frame(obj); break;
case CT_LIGHT: break; //doesn't do anything
case CT_REMOTE: break; //doesn't do anything
2006-03-20 17:12:09 +00:00
case CT_CNTRLCEN: do_controlcen_frame(obj); break;
default:
Error("Unknown control type %d in object %hu, sig/type/id = %i/%i/%i",obj->control_type, static_cast<objnum_t>(obj), obj->signature.get(), obj->type, obj->id);
2006-03-20 17:12:09 +00:00
break;
}
if (obj->lifeleft < 0 ) { // We died of old age
obj->flags |= OF_SHOULD_BE_DEAD;
if ( obj->type==OBJ_WEAPON && Weapon_info[get_weapon_id(obj)].damage_radius )
explode_badass_weapon(obj, obj->pos);
#if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
else if ( obj->type==OBJ_ROBOT) //make robots explode
explode_object(obj,0);
#endif
2006-03-20 17:12:09 +00:00
}
if (obj->type == OBJ_NONE || obj->flags&OF_SHOULD_BE_DEAD)
return window_event_result::ignored; // object has been deleted
2006-03-20 17:12:09 +00:00
switch (obj->movement_type) {
case MT_NONE: break; //this doesn't move
2006-03-20 17:12:09 +00:00
case MT_PHYSICS: result = do_physics_sim(obj); break; //move by physics
2006-03-20 17:12:09 +00:00
case MT_SPINNING: spin_object(obj); break;
}
// If player and moved to another segment, see if hit any triggers.
// also check in player under a lavafall
if (obj->type == OBJ_PLAYER && obj->movement_type==MT_PHYSICS) {
if (previous_segment != obj->segnum && n_phys_segs > 1)
{
auto seg0 = vsegptridx(phys_seglist[0]);
#if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
int old_level = Current_level_num;
#endif
2016-02-12 04:02:28 +00:00
range_for (const auto i, partial_const_range(phys_seglist, 1u, n_phys_segs))
{
const auto &&seg1 = seg0.absolute_sibling(i);
const auto connect_side = find_connect_side(seg1, seg0);
if (connect_side != side_none)
{
result = check_trigger(seg0, connect_side, get_local_plrobj(), obj, 0);
#if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
//maybe we've gone on to the next level. if so, bail!
if (Current_level_num != old_level)
return window_event_result::ignored;
2006-03-20 17:12:09 +00:00
#endif
}
seg0 = seg1;
2006-03-20 17:12:09 +00:00
}
}
#if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
{
2016-01-29 04:05:47 +00:00
bool under_lavafall = false;
2006-03-20 17:12:09 +00:00
auto &playing = obj->ctype.player_info.lavafall_hiss_playing;
2016-01-29 04:05:47 +00:00
const auto &&segp = vcsegptr(obj->segnum);
if (const auto sidemask = get_seg_masks(obj->pos, segp, obj->size).sidemask)
{
for (unsigned sidenum = 0; sidenum != MAX_SIDES_PER_SEGMENT; ++sidenum)
{
if (!(sidemask & (1 << sidenum)))
continue;
const auto wall_num = segp->sides[sidenum].wall_num;
2016-02-12 04:02:28 +00:00
if (wall_num != wall_none && vcwallptr(wall_num)->type == WALL_ILLUSION)
2016-01-29 04:05:47 +00:00
{
const auto type = check_volatile_wall(obj, segp, sidenum);
if (type != volatile_wall_result::none)
{
2006-03-20 17:12:09 +00:00
under_lavafall = 1;
2016-01-29 04:05:47 +00:00
if (!playing)
2015-11-27 03:56:13 +00:00
{
2016-01-29 04:05:47 +00:00
playing = 1;
const auto sound = (type == volatile_wall_result::lava) ? SOUND_LAVAFALL_HISS : SOUND_SHIP_IN_WATERFALL;
digi_link_sound_to_object3( sound, obj, 1, F1_0, vm_distance{i2f(256)}, -1, -1);
2016-01-29 04:05:47 +00:00
break;
2006-03-20 17:12:09 +00:00
}
}
}
2016-01-29 04:05:47 +00:00
}
2006-03-20 17:12:09 +00:00
}
2016-01-29 04:05:47 +00:00
if (!under_lavafall && playing)
{
playing = 0;
digi_kill_sound_linked_to_object( obj);
2006-03-20 17:12:09 +00:00
}
}
#endif
2006-03-20 17:12:09 +00:00
}
#if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
//see if guided missile has flown through exit trigger
if (obj==Guided_missile[Player_num] && obj->signature==Guided_missile_sig[Player_num]) {
if (previous_segment != obj->segnum) {
2015-07-12 01:04:21 +00:00
const auto &&psegp = vcsegptr(previous_segment);
const auto &&connect_side = find_connect_side(vcsegptridx(obj->segnum), psegp);
if (connect_side != side_none)
{
2015-07-12 01:04:21 +00:00
const auto wall_num = psegp->sides[connect_side].wall_num;
2014-09-21 22:11:51 +00:00
if ( wall_num != wall_none ) {
2016-02-12 04:02:28 +00:00
auto trigger_num = vcwallptr(wall_num)->trigger;
2015-04-26 20:15:50 +00:00
if (trigger_num != trigger_none)
{
const auto &&t = vctrgptr(trigger_num);
if (t->type == TT_EXIT)
2006-03-20 17:12:09 +00:00
Guided_missile[Player_num]->lifeleft = 0;
}
2006-03-20 17:12:09 +00:00
}
}
}
}
if (Drop_afterburner_blob_flag) {
Assert(obj==ConsoleObject);
drop_afterburner_blobs(obj, 2, i2f(5)/2, -1); // -1 means use default lifetime
if (Game_mode & GM_MULTI)
multi_send_drop_blobs(Player_num);
Drop_afterburner_blob_flag = 0;
}
if ((obj->type == OBJ_WEAPON) && (Weapon_info[get_weapon_id(obj)].afterburner_size)) {
2014-09-28 21:11:03 +00:00
fix vel = vm_vec_mag_quick(obj->mtype.phys_info.velocity);
2006-03-20 17:12:09 +00:00
fix delay, lifetime;
if (vel > F1_0*200)
delay = F1_0/16;
else if (vel > F1_0*40)
delay = fixdiv(F1_0*13,vel);
else
delay = F1_0/4;
lifetime = (delay * 3)/2;
if (!(Game_mode & GM_MULTI)) {
delay /= 2;
lifetime *= 2;
}
assert(obj->control_type == CT_WEAPON);
if ((obj->ctype.laser_info.last_afterburner_time + delay < GameTime64) || (obj->ctype.laser_info.last_afterburner_time > GameTime64)) {
drop_afterburner_blobs(obj, 1, i2f(Weapon_info[get_weapon_id(obj)].afterburner_size)/16, lifetime);
obj->ctype.laser_info.last_afterburner_time = GameTime64;
2006-03-20 17:12:09 +00:00
}
}
#endif
return result;
2006-03-20 17:12:09 +00:00
}
//--------------------------------------------------------------------
//move all objects for the current frame
window_event_result object_move_all()
2006-03-20 17:12:09 +00:00
{
auto result = window_event_result::ignored;
if (Highest_object_index > MAX_USED_OBJECTS)
free_object_slots(MAX_USED_OBJECTS); // Free all possible object slots.
2006-03-20 17:12:09 +00:00
obj_delete_all_that_should_be_dead();
if (PlayerCfg.AutoLeveling)
2006-03-20 17:12:09 +00:00
ConsoleObject->mtype.phys_info.flags |= PF_LEVELLING;
else
ConsoleObject->mtype.phys_info.flags &= ~PF_LEVELLING;
// Move all objects
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_NONE) && (!(objp->flags&OF_SHOULD_BE_DEAD)) ) {
result = std::max(object_move_one( objp ), result);
2006-03-20 17:12:09 +00:00
}
}
// check_duplicate_objects();
// remove_incorrect_objects();
return result;
2006-03-20 17:12:09 +00:00
}
//--unused-- // -----------------------------------------------------------
//--unused-- // Moved here from eobject.c on 02/09/94 by MK.
//--unused-- int find_last_obj(int i)
//--unused-- {
//--unused-- for (i=MAX_OBJECTS;--i>=0;)
//--unused-- if (Objects[i].type != OBJ_NONE) break;
//--unused--
//--unused-- return i;
//--unused--
//--unused-- }
//make object array non-sparse
void compress_objects(void)
{
//last_i = find_last_obj(MAX_OBJECTS);
// Note: It's proper to do < (rather than <=) Highest_object_index here because we
// are just removing gaps, and the last object can't be a gap.
2014-11-26 03:39:21 +00:00
for (objnum_t start_i=0;start_i<Highest_object_index;start_i++)
{
const auto &&start_objp = vobjptridx(start_i);
if (start_objp->type == OBJ_NONE) {
auto highest = Highest_object_index;
const auto &&h = vobjptr(static_cast<objnum_t>(highest));
2014-11-26 03:39:21 +00:00
auto segnum_copy = h->segnum;
2006-03-20 17:12:09 +00:00
2014-11-26 03:39:21 +00:00
obj_unlink(h);
2006-03-20 17:12:09 +00:00
*start_objp = *h;
2006-03-20 17:12:09 +00:00
#if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
if (Cur_object_index == Highest_object_index)
Cur_object_index = start_i;
#endif
2014-11-26 03:39:21 +00:00
h->type = OBJ_NONE;
2006-03-20 17:12:09 +00:00
obj_link(start_objp, vsegptridx(segnum_copy));
2006-03-20 17:12:09 +00:00
while (vobjptr(static_cast<objnum_t>(--highest))->type == OBJ_NONE)
{
}
Objects.set_count(highest + 1);
2006-03-20 17:12:09 +00:00
//last_i = find_last_obj(last_i);
}
}
2006-03-20 17:12:09 +00:00
reset_objects(num_objects);
}
//called after load. Takes number of objects, and objects should be
//compressed. resets free list, marks unused objects as unused
void reset_objects(int n_objs)
{
num_objects = n_objs;
Assert(num_objects>0);
for (objnum_t i = num_objects; i < MAX_OBJECTS; ++i)
{
2006-03-20 17:12:09 +00:00
free_obj_list[i] = i;
2016-11-06 17:12:03 +00:00
auto &obj = *vobjptr(i);
DXX_POISON_VAR(obj, 0xfd);
obj.type = OBJ_NONE;
2006-03-20 17:12:09 +00:00
}
Objects.set_count(num_objects);
2006-03-20 17:12:09 +00:00
Debris_object_count = 0;
}
//Tries to find a segment for an object, using find_point_seg()
segptridx_t find_object_seg(const vobjptr_t obj)
2006-03-20 17:12:09 +00:00
{
return find_point_seg(obj->pos, vsegptridx(obj->segnum));
2006-03-20 17:12:09 +00:00
}
//If an object is in a segment, set its segnum field and make sure it's
//properly linked. If not in any segment, returns 0, else 1.
//callers should generally use find_vector_intersection()
2014-10-02 03:02:34 +00:00
int update_object_seg(const vobjptridx_t obj)
2006-03-20 17:12:09 +00:00
{
2014-11-20 03:00:36 +00:00
auto newseg = find_object_seg(obj);
if (newseg == segment_none)
2006-03-20 17:12:09 +00:00
return 0;
if ( newseg != obj->segnum )
obj_relink(obj, newseg);
2006-03-20 17:12:09 +00:00
return 1;
}
void set_powerup_id(object_base &o, powerup_type_t id)
{
2015-09-15 02:48:04 +00:00
o.id = id;
o.size = Powerup_info[id].size;
const auto vclip_num = Powerup_info[id].vclip_num;
2015-09-15 02:48:04 +00:00
o.rtype.vclip_info.vclip_num = vclip_num;
o.rtype.vclip_info.frametime = Vclip[vclip_num].frame_time;
}
2006-03-20 17:12:09 +00:00
//go through all objects and make sure they have the correct segment numbers
void fix_object_segs()
2006-03-20 17:12:09 +00:00
{
2016-02-12 04:02:28 +00:00
range_for (const auto &&o, vobjptridx)
2014-12-23 04:20:27 +00:00
{
if (o->type != OBJ_NONE)
if (update_object_seg(o) == 0) {
const auto pos = o->pos;
2015-07-12 01:04:21 +00:00
compute_segment_center(o->pos, vcsegptr(o->segnum));
con_printf(CON_URGENT, "Object %hu claims segment %u, but has position {%i,%i,%i}; moving to {%i,%i,%i}", static_cast<objnum_t>(o), o->segnum, pos.x, pos.y, pos.z, o->pos.x, o->pos.y, o->pos.z);
2006-03-20 17:12:09 +00:00
}
2014-12-23 04:20:27 +00:00
}
2006-03-20 17:12:09 +00:00
}
//--unused-- void object_use_new_object_list( object * new_list )
//--unused-- {
//--unused-- int i, segnum;
//--unused-- object *obj;
//--unused--
//--unused-- // First, unlink all the old objects for the segments array
//--unused-- for (segnum=0; segnum <= Highest_segment_index; segnum++) {
//--unused-- Segments[segnum].objects = -1;
//--unused-- }
//--unused-- // Then, erase all the objects
//--unused-- reset_objects(1);
//--unused--
//--unused-- // Fill in the object array
//--unused-- memcpy( Objects, new_list, sizeof(object)*MAX_OBJECTS );
//--unused--
//--unused-- Highest_object_index=-1;
//--unused--
//--unused-- // Relink 'em
//--unused-- for (i=0; i<MAX_OBJECTS; i++ ) {
//--unused-- obj = &Objects[i];
//--unused-- if ( obj->type != OBJ_NONE ) {
//--unused-- num_objects++;
//--unused-- Highest_object_index = i;
//--unused-- segnum = obj->segnum;
//--unused-- obj->next = obj->prev = obj->segnum = -1;
//--unused-- obj_link(i,segnum);
//--unused-- } else {
//--unused-- obj->next = obj->prev = obj->segnum = -1;
//--unused-- }
//--unused-- }
//--unused--
//--unused-- }
2014-10-02 03:02:34 +00:00
static int object_is_clearable_weapon(const vcobjptr_t obj, int clear_all)
{
if (!(obj->type == OBJ_WEAPON))
return 0;
#if defined(DXX_BUILD_DESCENT_II)
if (Weapon_info[get_weapon_id(obj)].flags&WIF_PLACABLE)
return 0;
#endif
return (clear_all || !is_proximity_bomb_or_smart_mine(get_weapon_id(obj)));
}
2006-03-20 17:12:09 +00:00
//delete objects, such as weapons & explosions, that shouldn't stay between levels
// Changed by MK on 10/15/94, don't remove proximity bombs.
//if clear_all is set, clear even proximity bombs
void clear_transient_objects(int clear_all)
{
2016-02-12 04:02:28 +00:00
range_for (const auto &&obj, vobjptridx)
2014-10-12 23:05:46 +00:00
{
if (object_is_clearable_weapon(obj, clear_all) ||
2006-03-20 17:12:09 +00:00
obj->type == OBJ_FIREBALL ||
obj->type == OBJ_DEBRIS ||
obj->type == OBJ_DEBRIS ||
(obj->type!=OBJ_NONE && obj->flags & OF_EXPLODING)) {
2014-11-26 03:39:21 +00:00
obj_delete(obj);
2006-03-20 17:12:09 +00:00
}
2014-10-12 23:05:46 +00:00
}
2006-03-20 17:12:09 +00:00
}
//attaches an object, such as a fireball, to another object, such as a robot
2014-10-02 03:02:34 +00:00
void obj_attach(const vobjptridx_t parent,const vobjptridx_t sub)
2006-03-20 17:12:09 +00:00
{
Assert(sub->type == OBJ_FIREBALL);
Assert(sub->control_type == CT_EXPLOSION);
Assert(sub->ctype.expl_info.next_attach==object_none);
Assert(sub->ctype.expl_info.prev_attach==object_none);
2006-03-20 17:12:09 +00:00
Assert(parent->attached_obj==object_none || vcobjptr(parent->attached_obj)->ctype.expl_info.prev_attach==object_none);
2006-03-20 17:12:09 +00:00
sub->ctype.expl_info.next_attach = parent->attached_obj;
if (sub->ctype.expl_info.next_attach != object_none)
vobjptr(sub->ctype.expl_info.next_attach)->ctype.expl_info.prev_attach = sub;
2006-03-20 17:12:09 +00:00
parent->attached_obj = sub;
2006-03-20 17:12:09 +00:00
sub->ctype.expl_info.attach_parent = parent;
2006-03-20 17:12:09 +00:00
sub->flags |= OF_ATTACHED;
Assert(sub->ctype.expl_info.next_attach != sub);
Assert(sub->ctype.expl_info.prev_attach != sub);
2006-03-20 17:12:09 +00:00
}
//dettaches one object
2016-04-23 17:59:47 +00:00
void obj_detach_one(object &sub)
2006-03-20 17:12:09 +00:00
{
2016-04-23 17:59:47 +00:00
Assert(sub.flags & OF_ATTACHED);
Assert(sub.ctype.expl_info.attach_parent != object_none);
2006-03-20 17:12:09 +00:00
2016-04-23 17:59:47 +00:00
const auto &&parent_objp = vcobjptr(sub.ctype.expl_info.attach_parent);
if (parent_objp->type == OBJ_NONE || parent_objp->attached_obj == object_none)
2006-03-20 17:12:09 +00:00
{
2016-04-23 17:59:47 +00:00
sub.flags &= ~OF_ATTACHED;
2006-03-20 17:12:09 +00:00
return;
}
2016-04-23 17:59:47 +00:00
if (sub.ctype.expl_info.next_attach != object_none)
{
const auto &&o = vobjptr(sub.ctype.expl_info.next_attach);
assert(vobjptr(o->ctype.expl_info.prev_attach) == &sub);
o->ctype.expl_info.prev_attach = sub.ctype.expl_info.prev_attach;
2006-03-20 17:12:09 +00:00
}
2016-04-23 17:59:47 +00:00
if (sub.ctype.expl_info.prev_attach != object_none)
{
const auto &&o = vobjptr(sub.ctype.expl_info.prev_attach);
assert(vobjptr(o->ctype.expl_info.next_attach) == &sub);
o->ctype.expl_info.next_attach = sub.ctype.expl_info.next_attach;
2006-03-20 17:12:09 +00:00
}
else {
2016-04-23 17:59:47 +00:00
const auto &&o = vobjptr(sub.ctype.expl_info.attach_parent);
assert(vobjptr(o->attached_obj) == &sub);
o->attached_obj = sub.ctype.expl_info.next_attach;
2006-03-20 17:12:09 +00:00
}
2016-04-23 17:59:47 +00:00
sub.ctype.expl_info.next_attach = sub.ctype.expl_info.prev_attach = object_none;
sub.flags &= ~OF_ATTACHED;
2006-03-20 17:12:09 +00:00
}
//dettaches all objects from this object
2016-04-23 17:59:47 +00:00
static void obj_detach_all(object_base &parent)
2006-03-20 17:12:09 +00:00
{
2016-04-23 17:59:47 +00:00
while (parent.attached_obj != object_none)
obj_detach_one(vobjptr(parent.attached_obj));
2006-03-20 17:12:09 +00:00
}
#if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
//creates a marker object in the world. returns the object number
objptridx_t drop_marker_object(const vms_vector &pos, const vsegptridx_t segnum, const vms_matrix &orient, int marker_num)
2006-03-20 17:12:09 +00:00
{
Assert(Marker_model_num != -1);
2014-10-30 03:19:32 +00:00
auto obj = obj_create(OBJ_MARKER, marker_num, segnum, pos, &orient, Polygon_models[Marker_model_num].rad, CT_NONE, MT_NONE, RT_POLYOBJ);
2014-09-08 03:24:48 +00:00
if (obj != object_none) {
2006-03-20 17:12:09 +00:00
obj->rtype.pobj_info.model_num = Marker_model_num;
2014-09-28 21:43:14 +00:00
vm_vec_copy_scale(obj->mtype.spin_rate,obj->orient.uvec,F1_0/2);
2006-03-20 17:12:09 +00:00
// MK, 10/16/95: Using lifeleft to make it flash, thus able to trim lightlevel from all objects.
obj->lifeleft = IMMORTAL_TIME - 1;
}
2014-09-08 03:24:48 +00:00
return obj;
2006-03-20 17:12:09 +00:00
}
// *viewer is a viewer, probably a missile.
// wake up all robots that were rendered last frame subject to some constraints.
2015-05-13 03:20:28 +00:00
void wake_up_rendered_objects(const vobjptr_t viewer, window_rendered_data &window)
2006-03-20 17:12:09 +00:00
{
// Make sure that we are processing current data.
2014-12-13 04:11:07 +00:00
if (timer_query() != window.time) {
2006-03-20 17:12:09 +00:00
return;
}
Ai_last_missile_camera = viewer;
2006-03-20 17:12:09 +00:00
2014-12-13 04:11:07 +00:00
range_for (const auto objnum, window.rendered_robots)
{
int fcval = d_tick_count & 3;
2006-03-20 17:12:09 +00:00
if ((objnum & 3) == fcval) {
const auto &&objp = vobjptr(objnum);
2006-03-20 17:12:09 +00:00
if (objp->type == OBJ_ROBOT) {
2014-10-01 02:28:41 +00:00
if (vm_vec_dist_quick(viewer->pos, objp->pos) < F1_0*100) {
ai_local *ailp = &objp->ctype.ai_info.ail;
if (ailp->player_awareness_type == player_awareness_type_t::PA_NONE) {
2006-03-20 17:12:09 +00:00
objp->ctype.ai_info.SUB_FLAGS |= SUB_FLAGS_CAMERA_AWAKE;
ailp->player_awareness_type = player_awareness_type_t::PA_WEAPON_ROBOT_COLLISION;
2006-03-20 17:12:09 +00:00
ailp->player_awareness_time = F1_0*3;
ailp->previous_visibility = 2;
}
}
}
}
}
}
#endif
2006-03-20 17:12:09 +00:00
// Swap endianess of given object_rw if swap == 1
void object_rw_swap(object_rw *obj, int swap)
{
if (!swap)
return;
obj->signature = SWAPINT(obj->signature);
obj->next = SWAPSHORT(obj->next);
obj->prev = SWAPSHORT(obj->prev);
obj->segnum = SWAPSHORT(obj->segnum);
obj->attached_obj = SWAPSHORT(obj->attached_obj);
obj->pos.x = SWAPINT(obj->pos.x);
obj->pos.y = SWAPINT(obj->pos.y);
obj->pos.z = SWAPINT(obj->pos.z);
obj->orient.rvec.x = SWAPINT(obj->orient.rvec.x);
obj->orient.rvec.y = SWAPINT(obj->orient.rvec.y);
obj->orient.rvec.z = SWAPINT(obj->orient.rvec.z);
obj->orient.fvec.x = SWAPINT(obj->orient.fvec.x);
obj->orient.fvec.y = SWAPINT(obj->orient.fvec.y);
obj->orient.fvec.z = SWAPINT(obj->orient.fvec.z);
obj->orient.uvec.x = SWAPINT(obj->orient.uvec.x);
obj->orient.uvec.y = SWAPINT(obj->orient.uvec.y);
obj->orient.uvec.z = SWAPINT(obj->orient.uvec.z);
obj->size = SWAPINT(obj->size);
obj->shields = SWAPINT(obj->shields);
obj->last_pos.x = SWAPINT(obj->last_pos.x);
obj->last_pos.y = SWAPINT(obj->last_pos.y);
obj->last_pos.z = SWAPINT(obj->last_pos.z);
obj->lifeleft = SWAPINT(obj->lifeleft);
switch (obj->movement_type)
{
case MT_PHYSICS:
obj->mtype.phys_info.velocity.x = SWAPINT(obj->mtype.phys_info.velocity.x);
obj->mtype.phys_info.velocity.y = SWAPINT(obj->mtype.phys_info.velocity.y);
obj->mtype.phys_info.velocity.z = SWAPINT(obj->mtype.phys_info.velocity.z);
obj->mtype.phys_info.thrust.x = SWAPINT(obj->mtype.phys_info.thrust.x);
obj->mtype.phys_info.thrust.y = SWAPINT(obj->mtype.phys_info.thrust.y);
obj->mtype.phys_info.thrust.z = SWAPINT(obj->mtype.phys_info.thrust.z);
obj->mtype.phys_info.mass = SWAPINT(obj->mtype.phys_info.mass);
obj->mtype.phys_info.drag = SWAPINT(obj->mtype.phys_info.drag);
obj->mtype.phys_info.rotvel.x = SWAPINT(obj->mtype.phys_info.rotvel.x);
obj->mtype.phys_info.rotvel.y = SWAPINT(obj->mtype.phys_info.rotvel.y);
obj->mtype.phys_info.rotvel.z = SWAPINT(obj->mtype.phys_info.rotvel.z);
obj->mtype.phys_info.rotthrust.x = SWAPINT(obj->mtype.phys_info.rotthrust.x);
obj->mtype.phys_info.rotthrust.y = SWAPINT(obj->mtype.phys_info.rotthrust.y);
obj->mtype.phys_info.rotthrust.z = SWAPINT(obj->mtype.phys_info.rotthrust.z);
obj->mtype.phys_info.turnroll = SWAPINT(obj->mtype.phys_info.turnroll);
obj->mtype.phys_info.flags = SWAPSHORT(obj->mtype.phys_info.flags);
break;
case MT_SPINNING:
obj->mtype.spin_rate.x = SWAPINT(obj->mtype.spin_rate.x);
obj->mtype.spin_rate.y = SWAPINT(obj->mtype.spin_rate.y);
obj->mtype.spin_rate.z = SWAPINT(obj->mtype.spin_rate.z);
break;
}
switch (obj->control_type)
{
case CT_WEAPON:
obj->ctype.laser_info.parent_type = SWAPSHORT(obj->ctype.laser_info.parent_type);
obj->ctype.laser_info.parent_num = SWAPSHORT(obj->ctype.laser_info.parent_num);
obj->ctype.laser_info.parent_signature = SWAPINT(obj->ctype.laser_info.parent_signature);
obj->ctype.laser_info.creation_time = SWAPINT(obj->ctype.laser_info.creation_time);
obj->ctype.laser_info.last_hitobj = SWAPSHORT(obj->ctype.laser_info.last_hitobj);
obj->ctype.laser_info.track_goal = SWAPSHORT(obj->ctype.laser_info.track_goal);
obj->ctype.laser_info.multiplier = SWAPINT(obj->ctype.laser_info.multiplier);
break;
case CT_EXPLOSION:
obj->ctype.expl_info.spawn_time = SWAPINT(obj->ctype.expl_info.spawn_time);
obj->ctype.expl_info.delete_time = SWAPINT(obj->ctype.expl_info.delete_time);
obj->ctype.expl_info.delete_objnum = SWAPSHORT(obj->ctype.expl_info.delete_objnum);
obj->ctype.expl_info.attach_parent = SWAPSHORT(obj->ctype.expl_info.attach_parent);
obj->ctype.expl_info.prev_attach = SWAPSHORT(obj->ctype.expl_info.prev_attach);
obj->ctype.expl_info.next_attach = SWAPSHORT(obj->ctype.expl_info.next_attach);
break;
case CT_AI:
obj->ctype.ai_info.hide_segment = SWAPSHORT(obj->ctype.ai_info.hide_segment);
obj->ctype.ai_info.hide_index = SWAPSHORT(obj->ctype.ai_info.hide_index);
obj->ctype.ai_info.path_length = SWAPSHORT(obj->ctype.ai_info.path_length);
#if defined(DXX_BUILD_DESCENT_I)
obj->ctype.ai_info.cur_path_index = SWAPSHORT(obj->ctype.ai_info.cur_path_index);
#elif defined(DXX_BUILD_DESCENT_II)
obj->ctype.ai_info.dying_start_time = SWAPINT(obj->ctype.ai_info.dying_start_time);
#endif
obj->ctype.ai_info.danger_laser_num = SWAPSHORT(obj->ctype.ai_info.danger_laser_num);
obj->ctype.ai_info.danger_laser_signature = SWAPINT(obj->ctype.ai_info.danger_laser_signature);
break;
case CT_LIGHT:
obj->ctype.light_info.intensity = SWAPINT(obj->ctype.light_info.intensity);
break;
case CT_POWERUP:
obj->ctype.powerup_info.count = SWAPINT(obj->ctype.powerup_info.count);
#if defined(DXX_BUILD_DESCENT_II)
obj->ctype.powerup_info.creation_time = SWAPINT(obj->ctype.powerup_info.creation_time);
obj->ctype.powerup_info.flags = SWAPINT(obj->ctype.powerup_info.flags);
#endif
break;
}
switch (obj->render_type)
{
case RT_MORPH:
case RT_POLYOBJ:
case RT_NONE: // HACK below
{
if (obj->render_type == RT_NONE && obj->type != OBJ_GHOST) // HACK: when a player is dead or not connected yet, clients still expect to get polyobj data - even if render_type == RT_NONE at this time.
break;
obj->rtype.pobj_info.model_num = SWAPINT(obj->rtype.pobj_info.model_num);
2014-09-20 23:47:27 +00:00
for (uint_fast32_t i=0;i<MAX_SUBMODELS;i++)
{
obj->rtype.pobj_info.anim_angles[i].p = SWAPINT(obj->rtype.pobj_info.anim_angles[i].p);
obj->rtype.pobj_info.anim_angles[i].b = SWAPINT(obj->rtype.pobj_info.anim_angles[i].b);
obj->rtype.pobj_info.anim_angles[i].h = SWAPINT(obj->rtype.pobj_info.anim_angles[i].h);
}
obj->rtype.pobj_info.subobj_flags = SWAPINT(obj->rtype.pobj_info.subobj_flags);
obj->rtype.pobj_info.tmap_override = SWAPINT(obj->rtype.pobj_info.tmap_override);
obj->rtype.pobj_info.alt_textures = SWAPINT(obj->rtype.pobj_info.alt_textures);
break;
}
case RT_WEAPON_VCLIP:
case RT_HOSTAGE:
case RT_POWERUP:
case RT_FIREBALL:
obj->rtype.vclip_info.vclip_num = SWAPINT(obj->rtype.vclip_info.vclip_num);
obj->rtype.vclip_info.frametime = SWAPINT(obj->rtype.vclip_info.frametime);
break;
case RT_LASER:
break;
}
}
2015-11-26 02:56:56 +00:00
}
namespace dcx {
void (check_warn_object_type)(const object_base &o, object_type_t t, const char *file, unsigned line)
2015-11-26 02:56:56 +00:00
{
if (o.type != t)
con_printf(CON_URGENT, "%s:%u: BUG: object %p has type %u, expected %u", file, line, &o, o.type, t);
}
2015-12-05 22:57:25 +00:00
}