1 /* $Id: object.c,v 1.14 2004-05-22 02:05:51 btb Exp $ */
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
20 * Revision 1.5 1995/10/26 14:08:03 allender
21 * optimization to do physics on objects that didn't render last
22 * frame only every so often
24 * Revision 1.4 1995/10/20 00:50:57 allender
25 * make alt texture for player ship work correctly when cloaked
27 * Revision 1.3 1995/09/14 14:11:32 allender
28 * fix_object_segs returns void
30 * Revision 1.2 1995/08/12 11:31:01 allender
31 * removed #ifdef NEWDEMO -- always in
33 * Revision 1.1 1995/05/16 15:29:23 allender
36 * Revision 2.3 1995/06/15 12:30:51 john
37 * Fixed bug with multiplayer ships cloaking out wrongly.
39 * Revision 2.2 1995/05/15 11:34:53 john
40 * Fixed bug as Matt directed that fixed problems with the exit
41 * triggers being missed on slow frame rate computer.
43 * Revision 2.1 1995/03/21 14:38:51 john
44 * Ifdef'd out the NETWORK code.
46 * Revision 2.0 1995/02/27 11:28:14 john
47 * New version 2.0, which has no anonymous unions, builds with
48 * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
50 * Revision 1.335 1995/02/22 12:57:30 allender
51 * remove anonymous unions from object structure
53 * Revision 1.334 1995/02/09 22:04:40 mike
54 * fix lifeleft on badass weapons.
56 * Revision 1.333 1995/02/08 12:54:00 matt
57 * Fixed object freeing code which was deleting some explosions it shouldn't
59 * Revision 1.332 1995/02/08 11:37:26 mike
60 * Check for failures in call to obj_create.
62 * Revision 1.331 1995/02/05 17:48:52 rob
63 * Changed assert in obj_relink, more robust.
65 * Revision 1.330 1995/02/05 13:39:48 mike
66 * remove invulnerability effect code (actually, comment out).
68 * Revision 1.329 1995/02/04 12:29:52 rob
69 * Get rid of potential assert error for explosion detachment.
71 * Revision 1.328 1995/02/01 18:15:57 rob
72 * Removed debugging output from engine glow change.
74 * Revision 1.327 1995/02/01 16:20:12 matt
75 * Made engine glow vary over a wider range, and made the glow be based
76 * on thrust/speed, without regard to direction.
78 * Revision 1.326 1995/01/29 14:46:24 rob
79 * Fixed invul. vclip to only appear on player who is invul.
81 * Revision 1.325 1995/01/29 13:48:16 mike
82 * Add invulnerability graphical effect viewable by other players.
84 * Revision 1.324 1995/01/29 11:39:25 mike
85 * Add invulnerability effect.
87 * Revision 1.323 1995/01/27 17:02:41 mike
88 * add more information to an Error call.
90 * Revision 1.322 1995/01/26 22:11:30 mike
91 * Purple chromo-blaster (ie, fusion cannon) spruce up (chromification)
93 * Revision 1.321 1995/01/25 20:04:10 matt
94 * Moved matrix check to avoid orthogonalizing an uninitialize matrix
96 * Revision 1.320 1995/01/25 12:11:35 matt
97 * Make sure orient matrix is orthogonal when resetting player object
99 * Revision 1.319 1995/01/21 21:46:22 mike
100 * Optimize code in Assert (and prevent warning message).
102 * Revision 1.318 1995/01/21 21:22:16 rob
103 * Removed HUD clear messages.
104 * Added more Asserts to try and find illegal control type bug.
106 * Revision 1.317 1995/01/15 15:34:30 matt
107 * When freeing object slots, don't free fireballs that will be deleting
110 * Revision 1.316 1995/01/14 19:16:48 john
111 * First version of new bitmap paging code.
113 * Revision 1.315 1995/01/12 18:53:37 john
114 * Fixed parameter passing error.
116 * Revision 1.314 1995/01/12 12:09:47 yuan
117 * Added coop object capability.
119 * Revision 1.313 1994/12/15 16:45:44 matt
120 * Took out slew stuff for release version
122 * Revision 1.312 1994/12/15 13:04:25 mike
123 * Replace Players[Player_num].time_total references with GameTime.
125 * Revision 1.311 1994/12/15 11:01:04 mike
126 * add Object_minus_one for debugging.
128 * Revision 1.310 1994/12/15 03:03:33 matt
129 * Added error checking for NULL return from object_create_explosion()
131 * Revision 1.309 1994/12/14 17:25:31 matt
132 * Made next viewer func based on release not ndebug
134 * Revision 1.308 1994/12/13 12:55:42 mike
135 * hostages on board messages for when you die.
137 * Revision 1.307 1994/12/12 17:18:11 mike
138 * make boss cloak/teleport when get hit, make quad laser 3/4 as powerful.
140 * Revision 1.306 1994/12/12 00:27:11 matt
141 * Added support for no-levelling option
143 * Revision 1.305 1994/12/11 22:41:14 matt
144 * Added command-line option, -nolevel, which turns off player ship levelling
146 * Revision 1.304 1994/12/11 22:03:23 mike
147 * free up object slots as necessary.
149 * Revision 1.303 1994/12/11 14:09:31 mike
150 * make boss explosion sounds softer.
152 * Revision 1.302 1994/12/11 13:25:11 matt
153 * Restored calls to fix_object_segs() when debugging is turned off, since
154 * it's not a big routine, and could fix some possibly bad problems.
156 * Revision 1.301 1994/12/11 12:38:25 mike
157 * make boss explosion sounds louder in create_small_fireball.
159 * Revision 1.300 1994/12/10 15:28:37 matt
160 * Added asserts for debugging
162 * Revision 1.299 1994/12/09 16:18:51 matt
163 * Fixed init_player_object, for editor
165 * Revision 1.298 1994/12/09 15:03:10 matt
166 * Two changes for Mike:
167 * 1. Do better placement of camera during death sequence (prevents hang)
168 * 2. Only record dodging information if the player fired in a frame
170 * Revision 1.297 1994/12/09 14:59:12 matt
171 * Added system to attach a fireball to another object for rendering purposes,
172 * so the fireball always renders on top of (after) the object.
174 * Revision 1.296 1994/12/08 20:05:07 matt
175 * Removed unneeded debug message
177 * Revision 1.295 1994/12/08 12:36:02 matt
178 * Added new object allocation & deallocation functions so other code
179 * could stop messing around with internal object data structures.
181 * Revision 1.294 1994/12/07 20:13:37 matt
182 * Added debris object limiter
184 * Revision 1.293 1994/12/06 16:58:38 matt
187 * Revision 1.292 1994/12/05 22:34:35 matt
188 * Make tmap_override objects use override texture as alt texture. This
189 * should have the effect of making simpler models use the override texture.
191 * Revision 1.291 1994/12/05 12:23:53 mike
192 * make camera start closer, but move away from player in death sequence.
194 * Revision 1.290 1994/12/02 11:11:18 mike
195 * hook sound effect to player small explosions (ctrlcen, too).
197 * Revision 1.289 1994/11/28 21:50:52 mike
200 * Revision 1.288 1994/11/27 23:12:28 matt
201 * Made changes for new mprintf calling convention
203 * Revision 1.287 1994/11/27 20:35:50 matt
206 * Revision 1.286 1994/11/27 20:30:52 matt
209 * Revision 1.285 1994/11/21 11:43:21 mike
212 * Revision 1.284 1994/11/19 15:19:37 mike
213 * rip out unused code and data.
215 * Revision 1.283 1994/11/18 23:41:59 john
216 * Changed some shorts to ints.
218 * Revision 1.282 1994/11/18 16:16:17 mike
219 * Separate depth on objects vs. walls.
221 * Revision 1.281 1994/11/18 12:05:35 rob
222 * Removed unnecessary invulnerability flag set in player death.
223 * (I hope its unnecessary.. its commented out if it proves crucial)
224 * fixes powerup dropping bug for net play.
226 * Revision 1.280 1994/11/16 20:36:34 rob
227 * Changed player explosion (small) code.
229 * Revision 1.279 1994/11/16 18:26:04 matt
230 * Clear tmap override on player, to fix "rock ship" bug
232 * Revision 1.278 1994/11/16 14:54:12 rob
233 * Moved hook for network explosions.
235 * Revision 1.277 1994/11/14 11:40:42 mike
236 * plot inner polygon on laser based on detail level.
238 * Revision 1.276 1994/11/10 14:02:59 matt
239 * Hacked in support for player ships with different textures
241 * Revision 1.275 1994/11/08 12:19:08 mike
242 * Make a generally useful function for putting small explosions on any object.
244 * Revision 1.274 1994/11/04 19:55:54 rob
245 * Changed calls to player_explode to accomodate new parameter.
247 * Revision 1.273 1994/11/02 21:54:27 matt
248 * Delete the camera when the death sequence is done
250 * Revision 1.272 1994/11/02 11:36:35 rob
251 * Added player-in-process-of-dying explosions to network play.
253 * Revision 1.271 1994/10/31 17:25:33 matt
256 * Revision 1.270 1994/10/31 16:11:19 allender
257 * on demo recording, store letterbox mode in demo.
259 * Revision 1.269 1994/10/31 10:36:18 mike
260 * Make cloak effect fadein/fadeout different for robots versus player.
262 * Revision 1.268 1994/10/30 14:11:44 mike
263 * rip out repair center stuff.
265 * Revision 1.267 1994/10/28 19:43:52 mike
266 * Boss cloaking effect.
268 * Revision 1.266 1994/10/27 11:33:42 mike
269 * Add Highest_ever_object_index -- high water mark in object creation.
271 * Revision 1.265 1994/10/25 10:51:12 matt
272 * Vulcan cannon powerups now contain ammo count
274 * Revision 1.264 1994/10/24 20:49:24 matt
275 * Made cloaked objects pulse
277 * Revision 1.263 1994/10/21 12:19:45 matt
278 * Clear transient objects when saving (& loading) games
280 * Revision 1.262 1994/10/21 11:25:23 mike
281 * Use new constant IMMORTAL_TIME.
283 * Revision 1.261 1994/10/19 16:50:35 matt
284 * If out of segment, put player in center of segment when checking objects
287 * Revision 1.260 1994/10/17 23:21:55 mike
288 * Clean up robot cloaking, move more to ai.c
290 * Revision 1.259 1994/10/17 21:34:49 matt
291 * Added support for new Control Center/Main Reactor
293 * Revision 1.258 1994/10/17 21:18:04 mike
296 * Revision 1.257 1994/10/17 14:12:23 matt
297 * Cleaned up problems with player dying from mine explosion
299 * Revision 1.256 1994/10/15 19:04:31 mike
300 * Don't remove proximity bombs after you die.
302 * Revision 1.255 1994/10/14 15:57:00 mike
303 * Don't show ids in network mode.
304 * Fix, I hope, but in death sequence.
306 * Revision 1.254 1994/10/12 08:04:29 mike
307 * Don't decloak player on death.
309 * Revision 1.253 1994/10/11 20:36:16 matt
310 * Clear "transient" objects (weapons,explosions,etc.) when starting a level
312 * Revision 1.252 1994/10/11 12:24:09 matt
313 * Cleaned up/change badass explosion calls
315 * Revision 1.251 1994/10/08 19:30:20 matt
316 * Fixed (I hope) a bug in cloaking of multiplayer objects
318 * Revision 1.250 1994/10/08 14:03:15 rob
319 * Changed cloaking routine.
321 * Revision 1.249 1994/10/07 22:17:27 mike
322 * Asserts on valid segnum.
324 * Revision 1.248 1994/10/07 19:11:14 matt
325 * Added cool cloak transition effect
337 #include <string.h> // for memset
353 #include "textures.h"
362 #include "fireball.h"
365 #include "pa_enabl.h"
369 #include "cntrlcen.h"
372 #include "endlevel.h"
377 #include "lighting.h"
399 #include "editor/editor.h"
403 #include "3dfx_des.h"
406 void obj_detach_all(object *parent);
407 void obj_detach_one(object *sub);
408 int free_object_slots(int num_used);
414 extern sbyte WasRecorded[MAX_OBJECTS];
416 ubyte CollisionResult[MAX_OBJECT_TYPES][MAX_OBJECT_TYPES];
418 object *ConsoleObject; //the object that is the player
420 static short free_obj_list[MAX_OBJECTS];
426 //info on the various types of objects
428 object Object_minus_one;
431 object Objects[MAX_OBJECTS];
433 int Highest_object_index=0;
434 int Highest_ever_object_index=0;
436 // grs_bitmap *robot_bms[MAX_ROBOT_BITMAPS]; //all bitmaps for all robots
438 // int robot_bm_nums[MAX_ROBOT_TYPES]; //starting bitmap num for each robot
439 // int robot_n_bitmaps[MAX_ROBOT_TYPES]; //how many bitmaps for each robot
441 // char *robot_names[MAX_ROBOT_TYPES]; //name of each robot
443 //--unused-- int Num_robot_types=0;
445 int print_object_info = 0;
446 //@@int Object_viewer = 0;
448 //object * Slew_object = NULL; // Object containing slew object info.
450 //--unused-- int Player_controller_type = 0;
452 window_rendered_data Window_rendered_data[MAX_RENDERED_WINDOWS];
455 char Object_type_names[MAX_OBJECT_TYPES][9] = {
476 //set viewer object to next object in array
477 void object_goto_next_viewer()
479 int i, start_obj = 0;
481 start_obj = Viewer - Objects; //get viewer object number
483 for (i=0;i<=Highest_object_index;i++) {
486 if (start_obj > Highest_object_index ) start_obj = 0;
488 if (Objects[start_obj].type != OBJ_NONE ) {
489 Viewer = &Objects[start_obj];
494 Error( "Couldn't find a viewer object!" );
498 //set viewer object to next object in array
499 void object_goto_prev_viewer()
501 int i, start_obj = 0;
503 start_obj = Viewer - Objects; //get viewer object number
505 for (i=0; i<=Highest_object_index; i++) {
508 if (start_obj < 0 ) start_obj = Highest_object_index;
510 if (Objects[start_obj].type != OBJ_NONE ) {
511 Viewer = &Objects[start_obj];
516 Error( "Couldn't find a viewer object!" );
521 object *obj_find_first_of_type (int type)
525 for (i=0;i<=Highest_object_index;i++)
526 if (Objects[i].type==type)
527 return (&Objects[i]);
528 return ((object *)NULL);
531 int obj_return_num_of_type (int type)
535 for (i=0;i<=Highest_object_index;i++)
536 if (Objects[i].type==type)
540 int obj_return_num_of_typeid (int type,int id)
544 for (i=0;i<=Highest_object_index;i++)
545 if (Objects[i].type==type && Objects[i].id==id)
550 int global_orientation = 0;
552 //draw an object that has one bitmap & doesn't rotate
553 void draw_object_blob(object *obj,bitmap_index bmi)
556 grs_bitmap * bm = &GameBitmaps[bmi.index];
559 if (obj->type == OBJ_FIREBALL)
560 orientation = (obj-Objects) & 7;
562 orientation = global_orientation;
565 //@@ if (obj->type == OBJ_POWERUP) {
566 //@@ if ( GameBitmaps[(bmi).index].bm_flags & BM_FLAG_PAGED_OUT)
567 //@@ piggy_bitmap_page_in_w( bmi,1 );
569 //@@ if (bm->bm_handle) {
570 //@@ DDGRUNLOCK(dd_grd_curcanv);
573 PIGGY_PAGE_IN( bmi );
576 if (bm->bm_w > bm->bm_h)
578 g3_draw_bitmap(&obj->pos,obj->size,fixmuldiv(obj->size,bm->bm_h,bm->bm_w),bm, orientation);
582 g3_draw_bitmap(&obj->pos,fixmuldiv(obj->size,bm->bm_w,bm->bm_h),obj->size,bm, orientation);
585 //@@ if (bm->bm_handle) {
586 //@@ DDGRLOCK(dd_grd_curcanv);
591 //draw an object that is a texture-mapped rod
592 void draw_object_tmap_rod(object *obj,bitmap_index bitmapi,int lighted)
594 grs_bitmap * bitmap = &GameBitmaps[bitmapi.index];
597 vms_vector delta,top_v,bot_v;
598 g3s_point top_p,bot_p;
600 PIGGY_PAGE_IN(bitmapi);
602 bitmap->bm_handle = bitmapi.index;
604 vm_vec_copy_scale(&delta,&obj->orient.uvec,obj->size);
606 vm_vec_add(&top_v,&obj->pos,&delta);
607 vm_vec_sub(&bot_v,&obj->pos,&delta);
609 g3_rotate_point(&top_p,&top_v);
610 g3_rotate_point(&bot_p,&bot_v);
613 light = compute_object_light(obj,&top_p.p3_vec);
618 _3dfx_rendering_poly_obj = 1;
621 #ifdef PA_3DFX_VOODOO
625 g3_draw_rod_tmap(bitmap,&bot_p,obj->size,&top_p,obj->size,light);
628 _3dfx_rendering_poly_obj = 0;
633 int Linear_tmap_polygon_objects = 1;
635 extern fix Max_thrust;
637 //used for robot engine glow
638 #define MAX_VELOCITY i2f(50)
640 //function that takes the same parms as draw_tmap, but renders as flat poly
641 //we need this to do the cloaked effect
642 extern void draw_tmap_flat();
644 //what darkening level to use when cloaked
645 #define CLOAKED_FADE_LEVEL 28
647 #define CLOAK_FADEIN_DURATION_PLAYER F2_0
648 #define CLOAK_FADEOUT_DURATION_PLAYER F2_0
650 #define CLOAK_FADEIN_DURATION_ROBOT F1_0
651 #define CLOAK_FADEOUT_DURATION_ROBOT F1_0
653 //do special cloaked render
654 void draw_cloaked_object(object *obj,fix light,fix *glow,fix cloak_start_time,fix cloak_end_time)
656 fix cloak_delta_time,total_cloaked_time;
657 fix light_scale=F1_0;
659 int fading=0; //if true, fading, else cloaking
660 fix Cloak_fadein_duration=F1_0;
661 fix Cloak_fadeout_duration=F1_0;
664 total_cloaked_time = cloak_end_time-cloak_start_time;
668 Cloak_fadein_duration = CLOAK_FADEIN_DURATION_PLAYER;
669 Cloak_fadeout_duration = CLOAK_FADEOUT_DURATION_PLAYER;
672 Cloak_fadein_duration = CLOAK_FADEIN_DURATION_ROBOT;
673 Cloak_fadeout_duration = CLOAK_FADEOUT_DURATION_ROBOT;
676 Int3(); // Contact Mike: Unexpected object type in draw_cloaked_object.
679 cloak_delta_time = GameTime - cloak_start_time;
681 if (cloak_delta_time < Cloak_fadein_duration/2) {
683 light_scale = fixdiv(Cloak_fadein_duration/2 - cloak_delta_time,Cloak_fadein_duration/2);
687 else if (cloak_delta_time < Cloak_fadein_duration) {
689 cloak_value = f2i(fixdiv(cloak_delta_time - Cloak_fadein_duration/2,Cloak_fadein_duration/2) * CLOAKED_FADE_LEVEL);
691 } else if (GameTime < cloak_end_time-Cloak_fadeout_duration) {
692 static int cloak_delta=0,cloak_dir=1;
693 static fix cloak_timer=0;
695 //note, if more than one cloaked object is visible at once, the
696 //pulse rate will change!
698 cloak_timer -= FrameTime;
699 while (cloak_timer < 0) {
701 cloak_timer += Cloak_fadeout_duration/12;
703 cloak_delta += cloak_dir;
705 if (cloak_delta==0 || cloak_delta==4)
706 cloak_dir = -cloak_dir;
709 cloak_value = CLOAKED_FADE_LEVEL - cloak_delta;
711 } else if (GameTime < cloak_end_time-Cloak_fadeout_duration/2) {
713 cloak_value = f2i(fixdiv(total_cloaked_time - Cloak_fadeout_duration/2 - cloak_delta_time,Cloak_fadeout_duration/2) * CLOAKED_FADE_LEVEL);
717 light_scale = fixdiv(Cloak_fadeout_duration/2 - (total_cloaked_time - cloak_delta_time),Cloak_fadeout_duration/2);
723 fix new_light,save_glow;
724 bitmap_index * alt_textures = NULL;
727 if ( obj->rtype.pobj_info.alt_textures > 0 )
728 alt_textures = multi_player_textures[obj->rtype.pobj_info.alt_textures-1];
731 new_light = fixmul(light,light_scale);
733 glow[0] = fixmul(glow[0],light_scale);
734 draw_polygon_model(&obj->pos,
736 (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
737 obj->rtype.pobj_info.model_num,obj->rtype.pobj_info.subobj_flags,
744 Gr_scanline_darkening_level = cloak_value;
745 gr_setcolor(BM_XRGB(0,0,0)); //set to black (matters for s3)
746 g3_set_special_render(draw_tmap_flat,NULL,NULL); //use special flat drawer
747 draw_polygon_model(&obj->pos,
749 (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
750 obj->rtype.pobj_info.model_num,obj->rtype.pobj_info.subobj_flags,
754 g3_set_special_render(NULL,NULL,NULL);
755 Gr_scanline_darkening_level = GR_FADE_LEVELS;
760 //draw an object which renders as a polygon model
761 void draw_polygon_object(object *obj)
765 fix engine_glow_value[2]; //element 0 is for engine glow, 1 for headlight
767 light = compute_object_light(obj,NULL);
769 // If option set for bright players in netgame, brighten them!
771 if (Game_mode & GM_MULTI)
772 if (Netgame.BrightPlayers)
776 //make robots brighter according to robot glow field
777 if (obj->type == OBJ_ROBOT)
778 light += (Robot_info[obj->id].glow<<12); //convert 4:4 to 16:16
780 if (obj->type == OBJ_WEAPON)
781 if (obj->id == FLARE_ID)
784 if (obj->type == OBJ_MARKER)
788 imsave = Interpolation_method;
789 if (Linear_tmap_polygon_objects)
790 Interpolation_method = 1;
792 //set engine glow value
793 engine_glow_value[0] = f1_0/5;
794 if (obj->movement_type == MT_PHYSICS) {
796 if (obj->mtype.phys_info.flags & PF_USES_THRUST && obj->type==OBJ_PLAYER && obj->id==Player_num) {
797 fix thrust_mag = vm_vec_mag_quick(&obj->mtype.phys_info.thrust);
798 engine_glow_value[0] += (fixdiv(thrust_mag,Player_ship->max_thrust)*4)/5;
801 fix speed = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
802 engine_glow_value[0] += (fixdiv(speed,MAX_VELOCITY)*3)/5;
806 //set value for player headlight
807 if (obj->type == OBJ_PLAYER) {
808 if (Players[obj->id].flags & PLAYER_FLAGS_HEADLIGHT && !Endlevel_sequence)
809 if (Players[obj->id].flags & PLAYER_FLAGS_HEADLIGHT_ON)
810 engine_glow_value[1] = -2; //draw white!
812 engine_glow_value[1] = -1; //draw normal color (grey)
814 engine_glow_value[1] = -3; //don't draw
817 if (obj->rtype.pobj_info.tmap_override != -1) {
819 polymodel *pm = &Polygon_models[obj->rtype.pobj_info.model_num];
821 bitmap_index bm_ptrs[12];
825 Assert(pm->n_textures<=12);
827 for (i=0;i<12;i++) //fill whole array, in case simple model needs more
828 bm_ptrs[i] = Textures[obj->rtype.pobj_info.tmap_override];
830 draw_polygon_model(&obj->pos,
832 (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
833 obj->rtype.pobj_info.model_num,
834 obj->rtype.pobj_info.subobj_flags,
841 if (obj->type==OBJ_PLAYER && (Players[obj->id].flags&PLAYER_FLAGS_CLOAKED))
842 draw_cloaked_object(obj,light,engine_glow_value,Players[obj->id].cloak_time,Players[obj->id].cloak_time+CLOAK_TIME_MAX);
843 else if ((obj->type == OBJ_ROBOT) && (obj->ctype.ai_info.CLOAKED)) {
844 if (Robot_info[obj->id].boss_flag)
845 draw_cloaked_object(obj,light,engine_glow_value, Boss_cloak_start_time, Boss_cloak_end_time);
847 draw_cloaked_object(obj,light,engine_glow_value, GameTime-F1_0*10, GameTime+F1_0*10);
849 bitmap_index * alt_textures = NULL;
852 if ( obj->rtype.pobj_info.alt_textures > 0 )
853 alt_textures = multi_player_textures[obj->rtype.pobj_info.alt_textures-1];
856 // Snipers get bright when they fire.
857 if (Ai_local_info[obj-Objects].next_fire < F1_0/8) {
858 if (obj->ctype.ai_info.behavior == AIB_SNIPE)
859 light = 2*light + F1_0;
862 draw_polygon_model(&obj->pos,
864 (vms_angvec *)&obj->rtype.pobj_info.anim_angles,obj->rtype.pobj_info.model_num,
865 obj->rtype.pobj_info.subobj_flags,
869 if (obj->type == OBJ_WEAPON && (Weapon_info[obj->id].model_num_inner > -1 )) {
870 fix dist_to_eye = vm_vec_dist_quick(&Viewer->pos, &obj->pos);
871 if (dist_to_eye < Simple_model_threshhold_scale * F1_0*2)
872 draw_polygon_model(&obj->pos,
874 (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
875 Weapon_info[obj->id].model_num_inner,
876 obj->rtype.pobj_info.subobj_flags,
884 Interpolation_method = imsave;
888 //------------------------------------------------------------------------------
889 // These variables are used to keep a list of the 3 closest robots to the viewer.
890 // The code works like this: Every time render object is called with a polygon model,
891 // it finds the distance of that robot to the viewer. If this distance if within 10
892 // segments of the viewer, it does the following: If there aren't already 3 robots in
893 // the closet-robots list, it just sticks that object into the list along with its distance.
894 // If the list already contains 3 robots, then it finds the robot in that list that is
895 // farthest from the viewer. If that object is farther than the object currently being
896 // rendered, then the new object takes over that far object's slot. *Then* after all
897 // objects are rendered, object_render_targets is called an it draws a target on top
898 // of all the objects.
900 //091494: #define MAX_CLOSE_ROBOTS 3
901 //--unused-- static int Object_draw_lock_boxes = 0;
902 //091494: static int Object_num_close = 0;
903 //091494: static object * Object_close_ones[MAX_CLOSE_ROBOTS];
904 //091494: static fix Object_close_distance[MAX_CLOSE_ROBOTS];
906 //091494: set_close_objects(object *obj)
910 //091494: if ( (obj->type != OBJ_ROBOT) || (Object_draw_lock_boxes==0) )
913 //091494: // The following code keeps a list of the 10 closest robots to the
914 //091494: // viewer. See comments in front of this function for how this works.
915 //091494: dist = vm_vec_dist( &obj->pos, &Viewer->pos );
916 //091494: if ( dist < i2f(20*10) ) {
917 //091494: if ( Object_num_close < MAX_CLOSE_ROBOTS ) {
918 //091494: Object_close_ones[Object_num_close] = obj;
919 //091494: Object_close_distance[Object_num_close] = dist;
920 //091494: Object_num_close++;
922 //091494: int i, farthest_robot;
923 //091494: fix farthest_distance;
924 //091494: // Find the farthest robot in the list
925 //091494: farthest_robot = 0;
926 //091494: farthest_distance = Object_close_distance[0];
927 //091494: for (i=1; i<Object_num_close; i++ ) {
928 //091494: if ( Object_close_distance[i] > farthest_distance ) {
929 //091494: farthest_distance = Object_close_distance[i];
930 //091494: farthest_robot = i;
933 //091494: // If this object is closer to the viewer than
934 //091494: // the farthest in the list, replace the farthest with this object.
935 //091494: if ( farthest_distance > dist ) {
936 //091494: Object_close_ones[farthest_robot] = obj;
937 //091494: Object_close_distance[farthest_robot] = dist;
943 int Player_fired_laser_this_frame=-1;
947 // -----------------------------------------------------------------------------
948 //this routine checks to see if an robot rendered near the middle of
949 //the screen, and if so and the player had fired, "warns" the robot
950 void set_robot_location_info(object *objp)
952 if (Player_fired_laser_this_frame != -1) {
955 g3_rotate_point(&temp,&objp->pos);
957 if (temp.p3_codes & CC_BEHIND) //robot behind the screen
960 //the code below to check for object near the center of the screen
961 //completely ignores z, which may not be good
963 if ((abs(temp.p3_x) < F1_0*4) && (abs(temp.p3_y) < F1_0*4)) {
964 objp->ctype.ai_info.danger_laser_num = Player_fired_laser_this_frame;
965 objp->ctype.ai_info.danger_laser_signature = Objects[Player_fired_laser_this_frame].signature;
972 // ------------------------------------------------------------------------------------------------------------------
973 void create_small_fireball_on_object(object *objp, fix size_scale, int sound_flag)
976 vms_vector pos, rand_vec;
980 make_random_vector(&rand_vec);
982 vm_vec_scale(&rand_vec, objp->size/2);
984 vm_vec_add2(&pos, &rand_vec);
986 size = fixmul(size_scale, F1_0/2 + d_rand()*4/2);
988 segnum = find_point_seg(&pos, objp->segnum);
991 expl_obj = object_create_explosion(segnum, &pos, size, VCLIP_SMALL_EXPLOSION);
994 obj_attach(objp,expl_obj);
995 if (d_rand() < 8192) {
997 if (objp->type == OBJ_ROBOT)
1000 digi_link_sound_to_object(SOUND_EXPLODING_WALL, objp-Objects, 0, vol);
1005 // ------------------------------------------------------------------------------------------------------------------
1006 void create_vclip_on_object(object *objp, fix size_scale, int vclip_num)
1009 vms_vector pos, rand_vec;
1013 make_random_vector(&rand_vec);
1015 vm_vec_scale(&rand_vec, objp->size/2);
1017 vm_vec_add2(&pos, &rand_vec);
1019 size = fixmul(size_scale, F1_0 + d_rand()*4);
1021 segnum = find_point_seg(&pos, objp->segnum);
1024 expl_obj = object_create_explosion(segnum, &pos, size, vclip_num);
1028 expl_obj->movement_type = MT_PHYSICS;
1029 expl_obj->mtype.phys_info.velocity.x = objp->mtype.phys_info.velocity.x/2;
1030 expl_obj->mtype.phys_info.velocity.y = objp->mtype.phys_info.velocity.y/2;
1031 expl_obj->mtype.phys_info.velocity.z = objp->mtype.phys_info.velocity.z/2;
1035 // -- mk, 02/05/95 -- #define VCLIP_INVULNERABILITY_EFFECT VCLIP_SMALL_EXPLOSION
1036 // -- mk, 02/05/95 --
1037 // -- mk, 02/05/95 -- // -----------------------------------------------------------------------------
1038 // -- mk, 02/05/95 -- void do_player_invulnerability_effect(object *objp)
1039 // -- mk, 02/05/95 -- {
1040 // -- mk, 02/05/95 -- if (d_rand() < FrameTime*8) {
1041 // -- mk, 02/05/95 -- create_vclip_on_object(objp, F1_0, VCLIP_INVULNERABILITY_EFFECT);
1042 // -- mk, 02/05/95 -- }
1043 // -- mk, 02/05/95 -- }
1045 // -----------------------------------------------------------------------------
1046 // Render an object. Calls one of several routines based on type
1047 void render_object(object *obj)
1051 if ( obj == Viewer ) return;
1053 if ( obj->type==OBJ_NONE ) {
1055 mprintf( (1, "ERROR!!!! Bogus obj %d in seg %d is rendering!\n", obj-Objects, obj->segnum ));
1061 mld_save = Max_linear_depth;
1062 Max_linear_depth = Max_linear_depth_objects;
1064 switch (obj->render_type) {
1066 case RT_NONE: break; //doesn't render, like the player
1070 draw_polygon_object(obj);
1072 //"warn" robot if being shot at
1073 if (obj->type == OBJ_ROBOT)
1074 set_robot_location_info(obj);
1076 //JOHN SAID TO: if ( (obj->type==OBJ_PLAYER) && ((keyd_pressed[KEY_W]) || (keyd_pressed[KEY_I])))
1077 //JOHN SAID TO: object_render_id(obj);
1079 // -- mk, 02/05/95 -- if (obj->type == OBJ_PLAYER)
1080 // -- mk, 02/05/95 -- if (Players[obj->id].flags & PLAYER_FLAGS_INVULNERABLE)
1081 // -- mk, 02/05/95 -- do_player_invulnerability_effect(obj);
1085 case RT_MORPH: draw_morph_object(obj); break;
1087 case RT_FIREBALL: draw_fireball(obj); break;
1089 case RT_WEAPON_VCLIP: draw_weapon_vclip(obj); break;
1091 case RT_HOSTAGE: draw_hostage(obj); break;
1093 case RT_POWERUP: draw_powerup(obj); break;
1095 case RT_LASER: Laser_render(obj); break;
1097 default: Error("Unknown render_type <%d>",obj->render_type);
1101 if ( obj->render_type != RT_NONE )
1102 if ( Newdemo_state == ND_STATE_RECORDING ) {
1103 if (!WasRecorded[obj-Objects]) {
1104 newdemo_record_render_object(obj);
1105 WasRecorded[obj-Objects]=1;
1110 Max_linear_depth = mld_save;
1114 //--unused-- void object_toggle_lock_targets() {
1115 //--unused-- Object_draw_lock_boxes ^= 1;
1118 //091494: //draw target boxes for nearby robots
1119 //091494: void object_render_targets()
1121 //091494: g3s_point pt;
1122 //091494: ubyte codes;
1124 //091494: int radius,x,y;
1126 //091494: if (Object_draw_lock_boxes==0)
1129 //091494: for (i=0; i<Object_num_close; i++ ) {
1131 //091494: codes = g3_rotate_point(&pt, &Object_close_ones[i]->pos );
1132 //091494: if ( !(codes & CC_BEHIND) ) {
1133 //091494: g3_project_point(&pt);
1134 //091494: if (pt.p3_flags & PF_PROJECTED) {
1135 //091494: x = f2i(pt.p3_sx);
1136 //091494: y = f2i(pt.p3_sy);
1137 //091494: radius = f2i(fixdiv((grd_curcanv->cv_bitmap.bm_w*Object_close_ones[i]->size)/8,pt.z));
1138 //091494: gr_setcolor( BM_XRGB(0,31,0) );
1139 //091494: gr_box(x-radius,y-radius,x+radius,y+radius);
1143 //091494: Object_num_close=0;
1145 //--unused-- //draw target boxes for nearby robots
1146 //--unused-- void object_render_id(object * obj)
1148 //--unused-- g3s_point pt;
1149 //--unused-- ubyte codes;
1150 //--unused-- int x,y;
1151 //--unused-- int w, h, aw;
1152 //--unused-- char s[20], *s1;
1154 //--unused-- s1 = network_get_player_name( obj-Objects );
1156 //--unused-- if (s1)
1157 //--unused-- sprintf( s, "%s", s1 );
1159 //--unused-- sprintf( s, "<%d>", obj->id );
1161 //--unused-- codes = g3_rotate_point(&pt, &obj->pos );
1162 //--unused-- if ( !(codes & CC_BEHIND) ) {
1163 //--unused-- g3_project_point(&pt);
1164 //--unused-- if (pt.p3_flags & PF_PROJECTED) {
1165 //--unused-- gr_get_string_size( s, &w, &h, &aw );
1166 //--unused-- x = f2i(pt.p3_sx) - w/2;
1167 //--unused-- y = f2i(pt.p3_sy) - h/2;
1168 //--unused-- if ( x>= 0 && y>=0 && (x+w)<=grd_curcanv->cv_bitmap.bm_w && (y+h)<grd_curcanv->cv_bitmap.bm_h ) {
1169 //--unused-- gr_set_fontcolor( BM_XRGB(0,31,0), -1 );
1170 //--unused-- gr_string( x, y, s );
1176 void check_and_fix_matrix(vms_matrix *m);
1178 #define vm_angvec_zero(v) (v)->p=(v)->b=(v)->h=0
1180 void reset_player_object()
1186 vm_vec_zero(&ConsoleObject->mtype.phys_info.velocity);
1187 vm_vec_zero(&ConsoleObject->mtype.phys_info.thrust);
1188 vm_vec_zero(&ConsoleObject->mtype.phys_info.rotvel);
1189 vm_vec_zero(&ConsoleObject->mtype.phys_info.rotthrust);
1190 ConsoleObject->mtype.phys_info.brakes = ConsoleObject->mtype.phys_info.turnroll = 0;
1191 ConsoleObject->mtype.phys_info.mass = Player_ship->mass;
1192 ConsoleObject->mtype.phys_info.drag = Player_ship->drag;
1193 ConsoleObject->mtype.phys_info.flags |= PF_TURNROLL | PF_LEVELLING | PF_WIGGLE | PF_USES_THRUST;
1197 ConsoleObject->render_type = RT_POLYOBJ;
1198 ConsoleObject->rtype.pobj_info.model_num = Player_ship->model_num; //what model is this?
1199 ConsoleObject->rtype.pobj_info.subobj_flags = 0; //zero the flags
1200 ConsoleObject->rtype.pobj_info.tmap_override = -1; //no tmap override!
1202 for (i=0;i<MAX_SUBMODELS;i++)
1203 vm_angvec_zero(&ConsoleObject->rtype.pobj_info.anim_angles[i]);
1207 ConsoleObject->flags = 0;
1212 //make object0 the player, setting all relevant fields
1213 void init_player_object()
1215 ConsoleObject->type = OBJ_PLAYER;
1216 ConsoleObject->id = 0; //no sub-types for player
1218 ConsoleObject->signature = 0; //player has zero, others start at 1
1220 ConsoleObject->size = Polygon_models[Player_ship->model_num].rad;
1222 ConsoleObject->control_type = CT_SLEW; //default is player slewing
1223 ConsoleObject->movement_type = MT_PHYSICS; //change this sometime
1225 ConsoleObject->lifeleft = IMMORTAL_TIME;
1227 ConsoleObject->attached_obj = -1;
1229 reset_player_object();
1233 //sets up the free list & init player & whatever else
1240 for (i=0;i<MAX_OBJECTS;i++) {
1241 free_obj_list[i] = i;
1242 Objects[i].type = OBJ_NONE;
1243 Objects[i].segnum = -1;
1246 for (i=0;i<MAX_SEGMENTS;i++)
1247 Segments[i].objects = -1;
1249 ConsoleObject = Viewer = &Objects[0];
1251 init_player_object();
1252 obj_link(ConsoleObject-Objects,0); //put in the world in segment 0
1254 num_objects = 1; //just the player
1255 Highest_object_index = 0;
1260 //after calling init_object(), the network code has grabbed specific
1261 //object slots without allocating them. Go though the objects & build
1262 //the free list, then set the apporpriate globals
1263 void special_reset_objects(void)
1267 num_objects=MAX_OBJECTS;
1269 Highest_object_index = 0;
1270 Assert(Objects[0].type != OBJ_NONE); //0 should be used
1272 for (i=MAX_OBJECTS;i--;)
1273 if (Objects[i].type == OBJ_NONE)
1274 free_obj_list[--num_objects] = i;
1276 if (i > Highest_object_index)
1277 Highest_object_index = i;
1281 int is_object_in_seg( int segnum, int objn )
1283 int objnum, count = 0;
1285 for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next) {
1286 if ( count > MAX_OBJECTS ) {
1290 if ( objnum==objn ) count++;
1295 int search_all_segments_for_object( int objnum )
1300 for (i=0; i<=Highest_segment_index; i++) {
1301 count += is_object_in_seg( i, objnum );
1306 void johns_obj_unlink(int segnum, int objnum)
1308 object *obj = &Objects[objnum];
1309 segment *seg = &Segments[segnum];
1311 Assert(objnum != -1);
1313 if (obj->prev == -1)
1314 seg->objects = obj->next;
1316 Objects[obj->prev].next = obj->next;
1318 if (obj->next != -1) Objects[obj->next].prev = obj->prev;
1321 void remove_incorrect_objects()
1323 int segnum, objnum, count;
1325 for (segnum=0; segnum <= Highest_segment_index; segnum++) {
1327 for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next) {
1330 if ( count > MAX_OBJECTS ) {
1331 mprintf((1, "Object list in segment %d is circular.\n", segnum ));
1335 if (Objects[objnum].segnum != segnum ) {
1337 mprintf((0, "Removing object %d from segment %d.\n", objnum, segnum ));
1340 johns_obj_unlink(segnum,objnum);
1346 void remove_all_objects_but( int segnum, int objnum )
1350 for (i=0; i<=Highest_segment_index; i++) {
1352 if (is_object_in_seg( i, objnum )) {
1353 johns_obj_unlink( i, objnum );
1359 int check_duplicate_objects()
1363 for (i=0;i<=Highest_object_index;i++) {
1364 if ( Objects[i].type != OBJ_NONE ) {
1365 count = search_all_segments_for_object( i );
1368 mprintf((1, "Object %d is in %d segments!\n", i, count ));
1371 remove_all_objects_but( Objects[i].segnum, i );
1379 void list_seg_objects( int segnum )
1381 int objnum, count = 0;
1383 for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next) {
1385 if ( count > MAX_OBJECTS ) {
1395 //link the object into the list for its segment
1396 void obj_link(int objnum,int segnum)
1398 object *obj = &Objects[objnum];
1400 Assert(objnum != -1);
1402 Assert(obj->segnum == -1);
1404 Assert(segnum>=0 && segnum<=Highest_segment_index);
1406 obj->segnum = segnum;
1408 obj->next = Segments[segnum].objects;
1411 Segments[segnum].objects = objnum;
1413 if (obj->next != -1) Objects[obj->next].prev = objnum;
1415 //list_seg_objects( segnum );
1416 //check_duplicate_objects();
1418 Assert(Objects[0].next != 0);
1419 if (Objects[0].next == 0)
1420 Objects[0].next = -1;
1422 Assert(Objects[0].prev != 0);
1423 if (Objects[0].prev == 0)
1424 Objects[0].prev = -1;
1427 void obj_unlink(int objnum)
1429 object *obj = &Objects[objnum];
1430 segment *seg = &Segments[obj->segnum];
1432 Assert(objnum != -1);
1434 if (obj->prev == -1)
1435 seg->objects = obj->next;
1437 Objects[obj->prev].next = obj->next;
1439 if (obj->next != -1) Objects[obj->next].prev = obj->prev;
1443 Assert(Objects[0].next != 0);
1444 Assert(Objects[0].prev != 0);
1447 int Object_next_signature = 1; //player gets 0, others start at 1
1449 int Debris_object_count=0;
1451 int Unused_object_slots;
1453 //returns the number of a free object, updating Highest_object_index.
1454 //Generally, obj_create() should be called to get an object, since it
1455 //fills in important fields and does the linking.
1456 //returns -1 if no free objects
1457 int obj_allocate(void)
1461 if ( num_objects >= MAX_OBJECTS-2 ) {
1464 num_freed = free_object_slots(MAX_OBJECTS-10);
1465 mprintf((0, " *** Freed %i objects in frame %i\n", num_freed, FrameCount));
1468 if ( num_objects >= MAX_OBJECTS ) {
1470 mprintf((1, "Object creation failed - too many objects!\n" ));
1475 objnum = free_obj_list[num_objects++];
1477 if (objnum > Highest_object_index) {
1478 Highest_object_index = objnum;
1479 if (Highest_object_index > Highest_ever_object_index)
1480 Highest_ever_object_index = Highest_object_index;
1485 Unused_object_slots=0;
1486 for (i=0; i<=Highest_object_index; i++)
1487 if (Objects[i].type == OBJ_NONE)
1488 Unused_object_slots++;
1493 //frees up an object. Generally, obj_delete() should be called to get
1494 //rid of an object. This function deallocates the object entry after
1495 //the object has been unlinked
1496 void obj_free(int objnum)
1498 free_obj_list[--num_objects] = objnum;
1499 Assert(num_objects >= 0);
1501 if (objnum == Highest_object_index)
1502 while (Objects[--Highest_object_index].type == OBJ_NONE);
1505 //-----------------------------------------------------------------------------
1506 // Scan the object list, freeing down to num_used objects
1507 // Returns number of slots freed.
1508 int free_object_slots(int num_used)
1511 int obj_list[MAX_OBJECTS];
1512 int num_already_free, num_to_free, original_num_to_free;
1515 num_already_free = MAX_OBJECTS - Highest_object_index - 1;
1517 if (MAX_OBJECTS - num_already_free < num_used)
1520 for (i=0; i<=Highest_object_index; i++) {
1521 if (Objects[i].flags & OF_SHOULD_BE_DEAD) {
1523 if (MAX_OBJECTS - num_already_free < num_used)
1524 return num_already_free;
1526 switch (Objects[i].type) {
1529 if (MAX_OBJECTS - num_already_free < num_used)
1534 Int3(); // This is curious. What is an object that is a wall?
1539 obj_list[olind++] = i;
1555 num_to_free = MAX_OBJECTS - num_used - num_already_free;
1556 original_num_to_free = num_to_free;
1558 if (num_to_free > olind) {
1559 mprintf((1, "Warning: Asked to free %i objects, but can only free %i.\n", num_to_free, olind));
1560 num_to_free = olind;
1563 for (i=0; i<num_to_free; i++)
1564 if (Objects[obj_list[i]].type == OBJ_DEBRIS) {
1566 mprintf((0, "Freeing DEBRIS object %3i\n", obj_list[i]));
1567 Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1571 return original_num_to_free;
1573 for (i=0; i<num_to_free; i++)
1574 if (Objects[obj_list[i]].type == OBJ_FIREBALL && Objects[obj_list[i]].ctype.expl_info.delete_objnum==-1) {
1576 mprintf((0, "Freeing FIREBALL object %3i\n", obj_list[i]));
1577 Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1581 return original_num_to_free;
1583 for (i=0; i<num_to_free; i++)
1584 if ((Objects[obj_list[i]].type == OBJ_WEAPON) && (Objects[obj_list[i]].id == FLARE_ID)) {
1586 Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1590 return original_num_to_free;
1592 for (i=0; i<num_to_free; i++)
1593 if ((Objects[obj_list[i]].type == OBJ_WEAPON) && (Objects[obj_list[i]].id != FLARE_ID)) {
1595 mprintf((0, "Freeing WEAPON object %3i\n", obj_list[i]));
1596 Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1599 return original_num_to_free - num_to_free;
1602 //-----------------------------------------------------------------------------
1603 //initialize a new object. adds to the list for the given segment
1604 //note that segnum is really just a suggestion, since this routine actually
1605 //searches for the correct segment
1606 //returns the object number
1607 int obj_create(ubyte type,ubyte id,int segnum,vms_vector *pos,
1608 vms_matrix *orient,fix size,ubyte ctype,ubyte mtype,ubyte rtype)
1613 Assert(segnum <= Highest_segment_index);
1614 Assert (segnum >= 0);
1615 Assert(ctype <= CT_CNTRLCEN);
1617 if (type==OBJ_DEBRIS && Debris_object_count>=Max_debris_objects)
1620 if (get_seg_masks(pos, segnum, 0, __FILE__, __LINE__).centermask != 0)
1621 if ((segnum=find_point_seg(pos,segnum))==-1) {
1623 mprintf((0,"Bad segnum in obj_create (type=%d)\n",type));
1625 return -1; //don't create this object
1628 // Find next free object
1629 objnum = obj_allocate();
1631 if (objnum == -1) //no free objects
1634 Assert(Objects[objnum].type == OBJ_NONE); //make sure unused
1636 obj = &Objects[objnum];
1638 Assert(obj->segnum == -1);
1640 // Zero out object structure to keep weird bugs from happening
1641 // in uninitialized fields.
1642 memset( obj, 0, sizeof(object) );
1644 obj->signature = Object_next_signature++;
1647 obj->last_pos = *pos;
1651 //@@if (orient != NULL)
1652 //@@ obj->orient = *orient;
1654 obj->orient = orient?*orient:vmd_identity_matrix;
1656 obj->control_type = ctype;
1657 obj->movement_type = mtype;
1658 obj->render_type = rtype;
1659 obj->contains_type = -1;
1661 obj->lifeleft = IMMORTAL_TIME; //assume immortal
1662 obj->attached_obj = -1;
1664 if (obj->control_type == CT_POWERUP)
1665 obj->ctype.powerup_info.count = 1;
1667 // Init physics info for this object
1668 if (obj->movement_type == MT_PHYSICS) {
1670 vm_vec_zero(&obj->mtype.phys_info.velocity);
1671 vm_vec_zero(&obj->mtype.phys_info.thrust);
1672 vm_vec_zero(&obj->mtype.phys_info.rotvel);
1673 vm_vec_zero(&obj->mtype.phys_info.rotthrust);
1675 obj->mtype.phys_info.mass = 0;
1676 obj->mtype.phys_info.drag = 0;
1677 obj->mtype.phys_info.brakes = 0;
1678 obj->mtype.phys_info.turnroll = 0;
1679 obj->mtype.phys_info.flags = 0;
1682 if (obj->render_type == RT_POLYOBJ)
1683 obj->rtype.pobj_info.tmap_override = -1;
1685 obj->shields = 20*F1_0;
1687 segnum = find_point_seg(pos,segnum); //find correct segment
1691 obj->segnum = -1; //set to zero by memset, above
1692 obj_link(objnum,segnum);
1694 // Set (or not) persistent bit in phys_info.
1695 if (obj->type == OBJ_WEAPON) {
1696 Assert(obj->control_type == CT_WEAPON);
1697 obj->mtype.phys_info.flags |= (Weapon_info[obj->id].persistent*PF_PERSISTENT);
1698 obj->ctype.laser_info.creation_time = GameTime;
1699 obj->ctype.laser_info.last_hitobj = -1;
1700 obj->ctype.laser_info.multiplier = F1_0;
1703 if (obj->control_type == CT_POWERUP)
1704 obj->ctype.powerup_info.creation_time = GameTime;
1706 if (obj->control_type == CT_EXPLOSION)
1707 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
1710 if (print_object_info)
1711 mprintf( (0, "Created object %d of type %d\n", objnum, obj->type ));
1714 if (obj->type == OBJ_DEBRIS)
1715 Debris_object_count++;
1721 //create a copy of an object. returns new object number
1722 int obj_create_copy(int objnum, vms_vector *new_pos, int newsegnum)
1727 // Find next free object
1728 newobjnum = obj_allocate();
1730 if (newobjnum == -1)
1733 obj = &Objects[newobjnum];
1735 *obj = Objects[objnum];
1737 obj->pos = obj->last_pos = *new_pos;
1739 obj->next = obj->prev = obj->segnum = -1;
1741 obj_link(newobjnum,newsegnum);
1743 obj->signature = Object_next_signature++;
1745 //we probably should initialize sub-structures here
1752 extern void newdemo_record_guided_end();
1754 //remove object from the world
1755 void obj_delete(int objnum)
1758 object *obj = &Objects[objnum];
1760 Assert(objnum != -1);
1761 Assert(objnum != 0 );
1762 Assert(obj->type != OBJ_NONE);
1763 Assert(obj != ConsoleObject);
1765 if (obj->type==OBJ_WEAPON && obj->id==GUIDEDMISS_ID) {
1766 pnum=Objects[obj->ctype.laser_info.parent_num].id;
1767 mprintf ((0,"Deleting a guided missile! Player %d\n\n",pnum));
1769 if (pnum!=Player_num) {
1770 mprintf ((0,"deleting missile that belongs to %d (%s)!\n",pnum,Players[pnum].callsign));
1771 Guided_missile[pnum]=NULL;
1773 else if (Newdemo_state==ND_STATE_RECORDING)
1774 newdemo_record_guided_end();
1778 if (obj == Viewer) //deleting the viewer?
1779 Viewer = ConsoleObject; //..make the player the viewer
1781 if (obj->flags & OF_ATTACHED) //detach this from object
1782 obj_detach_one(obj);
1784 if (obj->attached_obj != -1) //detach all objects from this
1785 obj_detach_all(obj);
1787 #if !defined(NDEBUG) && !defined(NMONO)
1788 if (print_object_info) mprintf( (0, "Deleting object %d of type %d\n", objnum, Objects[objnum].type ));
1791 if (obj->type == OBJ_DEBRIS)
1792 Debris_object_count--;
1796 Assert(Objects[0].next != 0);
1798 obj->type = OBJ_NONE; //unused!
1799 obj->signature = -1;
1800 obj->segnum=-1; // zero it!
1805 #define DEATH_SEQUENCE_LENGTH (F1_0*5)
1806 #define DEATH_SEQUENCE_EXPLODE_TIME (F1_0*2)
1808 int Player_is_dead = 0; // If !0, then player is dead, but game continues so he can watch.
1809 object *Dead_player_camera = NULL; // Object index of object watching deader.
1810 fix Player_time_of_death; // Time at which player died.
1811 object *Viewer_save;
1812 int Player_flags_save;
1813 int Player_exploded = 0;
1814 int Death_sequence_aborted=0;
1815 int Player_eggs_dropped=0;
1816 fix Camera_to_player_dist_goal=F1_0*4;
1818 ubyte Control_type_save, Render_type_save;
1819 extern int Cockpit_mode_save; //set while in letterbox or rear view, or -1
1821 // ------------------------------------------------------------------------------------------------------------------
1822 void dead_player_end(void)
1824 if (!Player_is_dead)
1827 if (Newdemo_state == ND_STATE_RECORDING)
1828 newdemo_record_restore_cockpit();
1831 Player_exploded = 0;
1832 obj_delete(Dead_player_camera-Objects);
1833 Dead_player_camera = NULL;
1834 select_cockpit(Cockpit_mode_save);
1835 Cockpit_mode_save = -1;
1836 Viewer = Viewer_save;
1837 ConsoleObject->type = OBJ_PLAYER;
1838 ConsoleObject->flags = Player_flags_save;
1840 Assert((Control_type_save == CT_FLYING) || (Control_type_save == CT_SLEW));
1842 ConsoleObject->control_type = Control_type_save;
1843 ConsoleObject->render_type = Render_type_save;
1844 Players[Player_num].flags &= ~PLAYER_FLAGS_INVULNERABLE;
1845 Player_eggs_dropped = 0;
1849 // ------------------------------------------------------------------------------------------------------------------
1850 // Camera is less than size of player away from
1851 void set_camera_pos(vms_vector *camera_pos, object *objp)
1854 fix camera_player_dist;
1857 camera_player_dist = vm_vec_dist_quick(camera_pos, &objp->pos);
1859 if (camera_player_dist < Camera_to_player_dist_goal) { //2*objp->size) {
1860 // Camera is too close to player object, so move it away.
1861 vms_vector player_camera_vec;
1864 vms_vector local_p1;
1866 vm_vec_sub(&player_camera_vec, camera_pos, &objp->pos);
1867 if ((player_camera_vec.x == 0) && (player_camera_vec.y == 0) && (player_camera_vec.z == 0))
1868 player_camera_vec.x += F1_0/16;
1870 hit_data.hit_type = HIT_WALL;
1873 while ((hit_data.hit_type != HIT_NONE) && (count++ < 6)) {
1874 vms_vector closer_p1;
1875 vm_vec_normalize_quick(&player_camera_vec);
1876 vm_vec_scale(&player_camera_vec, Camera_to_player_dist_goal);
1879 vm_vec_add(&closer_p1, &objp->pos, &player_camera_vec); // This is the actual point we want to put the camera at.
1880 vm_vec_scale(&player_camera_vec, far_scale); // ...but find a point 50% further away...
1881 vm_vec_add(&local_p1, &objp->pos, &player_camera_vec); // ...so we won't have to do as many cuts.
1884 fq.startseg = objp->segnum;
1886 fq.thisobjnum = objp-Objects;
1887 fq.ignore_obj_list = NULL;
1889 find_vector_intersection( &fq, &hit_data);
1891 if (hit_data.hit_type == HIT_NONE) {
1892 *camera_pos = closer_p1;
1894 make_random_vector(&player_camera_vec);
1895 far_scale = 3*F1_0/2;
1901 extern void drop_player_eggs(object *objp);
1902 extern int get_explosion_vclip(object *obj,int stage);
1903 extern void multi_cap_objects();
1904 extern int Proximity_dropped,Smartmines_dropped;
1906 // ------------------------------------------------------------------------------------------------------------------
1907 void dead_player_frame(void)
1912 if (Player_is_dead) {
1913 time_dead = GameTime - Player_time_of_death;
1915 // If unable to create camera at time of death, create now.
1916 if (Dead_player_camera == Viewer_save) {
1918 object *player = &Objects[Players[Player_num].objnum];
1920 // this next line was changed by WraithX, instead of CT_FLYING, it was CT_NONE: instead of MT_PHYSICS, it was MT_NONE.
1921 objnum = obj_create(OBJ_CAMERA, 0, player->segnum, &player->pos, &player->orient, 0, CT_FLYING, MT_PHYSICS, RT_NONE);
1923 mprintf((0, "Creating new dead player camera.\n"));
1925 Viewer = Dead_player_camera = &Objects[objnum];
1927 mprintf((1, "Can't create dead player camera.\n"));
1932 ConsoleObject->mtype.phys_info.rotvel.x = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/4;
1933 ConsoleObject->mtype.phys_info.rotvel.y = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/2;
1934 ConsoleObject->mtype.phys_info.rotvel.z = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/3;
1936 Camera_to_player_dist_goal = min(time_dead*8, F1_0*20) + ConsoleObject->size;
1938 set_camera_pos(&Dead_player_camera->pos, ConsoleObject);
1940 // the following line uncommented by WraithX, 4-12-00
1941 if (time_dead < DEATH_SEQUENCE_EXPLODE_TIME + F1_0 * 2)
1943 vm_vec_sub(&fvec, &ConsoleObject->pos, &Dead_player_camera->pos);
1944 vm_vector_2_matrix(&Dead_player_camera->orient, &fvec, NULL, NULL);
1945 Dead_player_camera->mtype.phys_info = ConsoleObject->mtype.phys_info;
1947 // the following "if" added by WraithX to get rid of camera "wiggle"
1948 if (Dead_player_camera->mtype.phys_info.flags & PF_WIGGLE)
1950 Dead_player_camera->mtype.phys_info.flags = (Dead_player_camera->mtype.phys_info.flags & ~PF_WIGGLE);
1951 }// end "if" added by WraithX, 4/13/00
1953 // the following line uncommented by WraithX, 4-12-00
1957 // the following line uncommented by WraithX, 4-11-00
1958 Dead_player_camera->movement_type = MT_PHYSICS;
1959 //Dead_player_camera->mtype.phys_info.rotvel.y = F1_0/8;
1960 // the following line uncommented by WraithX, 4-12-00
1962 // end addition by WX
1964 if (time_dead > DEATH_SEQUENCE_EXPLODE_TIME) {
1965 if (!Player_exploded) {
1967 if (Players[Player_num].hostages_on_board > 1)
1968 HUD_init_message(TXT_SHIP_DESTROYED_2, Players[Player_num].hostages_on_board);
1969 else if (Players[Player_num].hostages_on_board == 1)
1970 HUD_init_message(TXT_SHIP_DESTROYED_1);
1972 HUD_init_message(TXT_SHIP_DESTROYED_0);
1980 Player_exploded = 1;
1982 if (Game_mode & GM_NETWORK)
1985 multi_cap_objects();
1989 drop_player_eggs(ConsoleObject);
1990 Player_eggs_dropped = 1;
1992 if (Game_mode & GM_MULTI)
1994 //multi_send_position(Players[Player_num].objnum);
1995 multi_send_player_explode(MULTI_PLAYER_EXPLODE);
1999 explode_badass_player(ConsoleObject);
2001 //is this next line needed, given the badass call above?
2002 explode_object(ConsoleObject,0);
2003 ConsoleObject->flags &= ~OF_SHOULD_BE_DEAD; //don't really kill player
2004 ConsoleObject->render_type = RT_NONE; //..just make him disappear
2005 ConsoleObject->type = OBJ_GHOST; //..and kill intersections
2006 Players[Player_num].flags &= ~PLAYER_FLAGS_HEADLIGHT_ON;
2009 if (d_rand() < FrameTime*4) {
2011 if (Game_mode & GM_MULTI)
2012 multi_send_create_explosion(Player_num);
2014 create_small_fireball_on_object(ConsoleObject, F1_0, 1);
2019 if (Death_sequence_aborted) { //time_dead > DEATH_SEQUENCE_LENGTH) {
2020 if (!Player_eggs_dropped) {
2023 if (Game_mode & GM_NETWORK)
2026 multi_cap_objects();
2030 drop_player_eggs(ConsoleObject);
2031 Player_eggs_dropped = 1;
2033 if (Game_mode & GM_MULTI)
2035 //multi_send_position(Players[Player_num].objnum);
2036 multi_send_player_explode(MULTI_PLAYER_EXPLODE);
2041 DoPlayerDead(); //kill_player();
2047 void AdjustMineSpawn()
2049 if (!(Game_mode & GM_NETWORK))
2050 return; // No need for this function in any other mode
2052 if (!(Game_mode & GM_HOARD))
2053 Players[Player_num].secondary_ammo[PROXIMITY_INDEX]+=Proximity_dropped;
2054 Players[Player_num].secondary_ammo[SMART_MINE_INDEX]+=Smartmines_dropped;
2055 Proximity_dropped=0;
2056 Smartmines_dropped=0;
2061 int Killed_in_frame = -1;
2062 short Killed_objnum = -1;
2063 extern char Multi_killed_yourself;
2065 // ------------------------------------------------------------------------------------------------------------------
2066 void start_player_death_sequence(object *player)
2070 Assert(player == ConsoleObject);
2071 if ((Player_is_dead != 0) || (Dead_player_camera != NULL))
2074 //Assert(Player_is_dead == 0);
2075 //Assert(Dead_player_camera == NULL);
2079 if (!(Game_mode & GM_MULTI))
2080 HUD_clear_messages();
2082 Killed_in_frame = FrameCount;
2083 Killed_objnum = player-Objects;
2084 Death_sequence_aborted = 0;
2087 if (Game_mode & GM_MULTI)
2089 multi_send_kill(Players[Player_num].objnum);
2091 // If Hoard, increase number of orbs by 1
2092 // Only if you haven't killed yourself
2093 // This prevents cheating
2095 if (Game_mode & GM_HOARD)
2096 if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]<12)
2097 if (!Multi_killed_yourself)
2098 Players[Player_num].secondary_ammo[PROXIMITY_INDEX]++;
2110 //Players[Player_num].flags &= ~(PLAYER_FLAGS_AFTERBURNER);
2112 vm_vec_zero(&player->mtype.phys_info.rotthrust);
2113 vm_vec_zero(&player->mtype.phys_info.thrust);
2115 Player_time_of_death = GameTime;
2117 // this next line was changed by WraithX, instead of CT_FLYING, it was CT_NONE: instead of MT_PHYSICS, it was MT_NONE.
2118 objnum = obj_create(OBJ_CAMERA, 0, player->segnum, &player->pos, &player->orient, 0, CT_FLYING, MT_PHYSICS, RT_NONE);
2119 Viewer_save = Viewer;
2121 Viewer = Dead_player_camera = &Objects[objnum];
2123 mprintf((1, "Can't create dead player camera.\n"));
2125 Dead_player_camera = Viewer;
2128 if (Cockpit_mode_save == -1) //if not already saved
2129 Cockpit_mode_save = Cockpit_mode;
2130 select_cockpit(CM_LETTERBOX);
2131 if (Newdemo_state == ND_STATE_RECORDING)
2132 newdemo_record_letterbox();
2134 Player_flags_save = player->flags;
2135 Control_type_save = player->control_type;
2136 Render_type_save = player->render_type;
2138 player->flags &= ~OF_SHOULD_BE_DEAD;
2139 // Players[Player_num].flags |= PLAYER_FLAGS_INVULNERABLE;
2140 player->control_type = CT_FLYING; // change from CT_NONE to CT_FLYING by WraithX
2141 player->shields = F1_0*1000;
2143 PALETTE_FLASH_SET(0,0,0);
2146 // ------------------------------------------------------------------------------------------------------------------
2147 void obj_delete_all_that_should_be_dead()
2151 int local_dead_player_object=-1;
2156 for (i=0;i<=Highest_object_index;i++) {
2157 if ((objp->type!=OBJ_NONE) && (objp->flags&OF_SHOULD_BE_DEAD) ) {
2158 Assert(!(objp->type==OBJ_FIREBALL && objp->ctype.expl_info.delete_time!=-1));
2159 if (objp->type==OBJ_PLAYER) {
2160 if ( objp->id == Player_num ) {
2161 if (local_dead_player_object == -1) {
2162 start_player_death_sequence(objp);
2163 local_dead_player_object = objp-Objects;
2165 Int3(); // Contact Mike: Illegal, killed player twice in this frame!
2166 // Ok to continue, won't start death sequence again!
2177 //when an object has moved into a new segment, this function unlinks it
2178 //from its old segment, and links it into the new segment
2179 void obj_relink(int objnum,int newsegnum)
2182 Assert((objnum >= 0) && (objnum <= Highest_object_index));
2183 Assert((newsegnum <= Highest_segment_index) && (newsegnum >= 0));
2187 obj_link(objnum,newsegnum);
2190 if (get_seg_masks(&Objects[objnum].pos, Objects[objnum].segnum, 0, __FILE__, __LINE__).centermask != 0)
2191 mprintf((1, "obj_relink violates seg masks.\n"));
2195 //process a continuously-spinning object
2197 spin_object(object *obj)
2200 vms_matrix rotmat, new_pm;
2202 Assert(obj->movement_type == MT_SPINNING);
2204 rotangs.p = fixmul(obj->mtype.spin_rate.x,FrameTime);
2205 rotangs.h = fixmul(obj->mtype.spin_rate.y,FrameTime);
2206 rotangs.b = fixmul(obj->mtype.spin_rate.z,FrameTime);
2208 vm_angles_2_matrix(&rotmat,&rotangs);
2210 vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat);
2211 obj->orient = new_pm;
2213 check_and_fix_matrix(&obj->orient);
2216 int Drop_afterburner_blob_flag; //ugly hack
2217 extern void multi_send_drop_blobs(char);
2218 extern void fuelcen_check_for_goal (segment *);
2220 //see if wall is volatile, and if so, cause damage to player
2221 //returns true if player is in lava
2222 int check_volatile_wall(object *obj,int segnum,int sidenum,vms_vector *hitpt);
2224 // Time at which this object last created afterburner blobs.
2225 fix Last_afterburner_time[MAX_OBJECTS];
2227 //--------------------------------------------------------------------
2228 //move an object for the current frame
2229 void object_move_one( object * obj )
2234 int previous_segment = obj->segnum;
2236 obj->last_pos = obj->pos; // Save the current position
2238 if ((obj->type==OBJ_PLAYER) && (Player_num==obj->id)) {
2242 if (Game_mode & GM_CAPTURE)
2243 fuelcen_check_for_goal (&Segments[obj->segnum]);
2244 if (Game_mode & GM_HOARD)
2245 fuelcen_check_for_hoard_goal (&Segments[obj->segnum]);
2248 fuel=fuelcen_give_fuel( &Segments[obj->segnum], INITIAL_ENERGY-Players[Player_num].energy );
2250 Players[Player_num].energy += fuel;
2253 shields = repaircen_give_shields( &Segments[obj->segnum], INITIAL_ENERGY-Players[Player_num].energy );
2255 Players[Player_num].shields += shields;
2259 if (obj->lifeleft != IMMORTAL_TIME) { //if not immortal...
2260 // Ok, this is a big hack by MK.
2261 // If you want an object to last for exactly one frame, then give it a lifeleft of ONE_FRAME_TIME.
2262 if (obj->lifeleft != ONE_FRAME_TIME)
2263 obj->lifeleft -= FrameTime; //...inevitable countdown towards death
2266 Drop_afterburner_blob_flag = 0;
2268 switch (obj->control_type) {
2270 case CT_NONE: break;
2274 #if !defined(NDEBUG) && !defined(NMONO)
2275 if (print_object_info>1) mprintf( (0, "Moving player object #%d\n", obj-Objects ));
2278 read_flying_controls( obj );
2282 case CT_REPAIRCEN: Int3(); // -- hey! these are no longer supported!! -- do_repair_sequence(obj); break;
2284 case CT_POWERUP: do_powerup_frame(obj); break;
2286 case CT_MORPH: //morph implies AI
2287 do_morph_frame(obj);
2288 //NOTE: FALLS INTO AI HERE!!!!
2291 //NOTE LINK TO CT_MORPH ABOVE!!!
2292 if (Game_suspended & SUSP_ROBOTS) return;
2293 #if !defined(NDEBUG) && !defined(NMONO)
2294 if (print_object_info>1)
2295 mprintf( (0, "AI: Moving robot object #%d\n",obj-Objects ));
2300 case CT_WEAPON: Laser_do_weapon_sequence(obj); break;
2301 case CT_EXPLOSION: do_explosion_sequence(obj); break;
2305 if ( keyd_pressed[KEY_PAD5] ) slew_stop( obj );
2306 if ( keyd_pressed[KEY_NUMLOCK] ) {
2307 slew_reset_orient( obj );
2308 * (ubyte *) 0x417 &= ~0x20; //kill numlock
2310 slew_frame(0 ); // Does velocity addition for us.
2315 // case CT_FLYTHROUGH:
2316 // do_flythrough(obj,0); // HACK:do_flythrough should operate on an object!!!!
2317 // //check_object_seg(obj);
2318 // return; // DON'T DO THE REST OF OBJECT STUFF SINCE THIS IS A SPECIAL CASE!!!
2321 case CT_DEBRIS: do_debris_frame(obj); break;
2323 case CT_LIGHT: break; //doesn't do anything
2325 case CT_REMOTE: break; //movement is handled in com_process_input
2327 case CT_CNTRLCEN: do_controlcen_frame(obj); break;
2332 Error("Unknown control type %d in object %li, sig/type/id = %i/%i/%i",obj->control_type, obj-Objects, obj->signature, obj->type, obj->id);
2334 Error("Unknown control type %d in object %i, sig/type/id = %i/%i/%i",obj->control_type, obj-Objects, obj->signature, obj->type, obj->id);
2341 if (obj->lifeleft < 0 ) { // We died of old age
2342 obj->flags |= OF_SHOULD_BE_DEAD;
2343 if ( obj->type==OBJ_WEAPON && Weapon_info[obj->id].damage_radius )
2344 explode_badass_weapon(obj,&obj->pos);
2345 else if ( obj->type==OBJ_ROBOT) //make robots explode
2346 explode_object(obj,0);
2349 // the following ( if !dead) statement added by WraithX
2350 if (!Player_is_dead)
2352 if (obj->type == OBJ_NONE || obj->flags&OF_SHOULD_BE_DEAD)
2353 return; //object has been deleted
2354 }// end addition by WraithX
2356 switch (obj->movement_type) {
2358 case MT_NONE: break; //this doesn't move
2360 case MT_PHYSICS: do_physics_sim(obj); break; //move by physics
2362 case MT_SPINNING: spin_object(obj); break;
2366 // If player and moved to another segment, see if hit any triggers.
2367 // also check in player under a lavafall
2368 if (obj->type == OBJ_PLAYER && obj->movement_type==MT_PHYSICS) {
2370 if (previous_segment != obj->segnum) {
2373 int old_level = Current_level_num;
2375 for (i=0;i<n_phys_segs-1;i++) {
2376 connect_side = find_connect_side(&Segments[phys_seglist[i+1]], &Segments[phys_seglist[i]]);
2377 if (connect_side != -1)
2378 check_trigger(&Segments[phys_seglist[i]], connect_side, obj-Objects,0);
2380 else { // segments are not directly connected, so do binary subdivision until you find connected segments.
2381 mprintf((1, "UNCONNECTED SEGMENTS %d,%d\n",phys_seglist[i+1],phys_seglist[i]));
2382 // -- Unnecessary, MK, 09/04/95 -- Int3();
2386 //maybe we've gone on to the next level. if so, bail!
2388 if (Current_level_num != old_level)
2395 int sidemask,under_lavafall=0;
2396 static int lavafall_hiss_playing[MAX_PLAYERS]={0};
2398 sidemask = get_seg_masks(&obj->pos, obj->segnum, obj->size, __FILE__, __LINE__).sidemask;
2400 int sidenum,bit,wall_num;
2402 for (sidenum=0,bit=1;sidenum<6;bit<<=1,sidenum++)
2403 if ((sidemask & bit) && ((wall_num=Segments[obj->segnum].sides[sidenum].wall_num)!=-1) && Walls[wall_num].type==WALL_ILLUSION) {
2405 if ((type=check_volatile_wall(obj,obj->segnum,sidenum,&obj->pos))!=0) {
2406 int sound = (type==1)?SOUND_LAVAFALL_HISS:SOUND_SHIP_IN_WATERFALL;
2408 if (!lavafall_hiss_playing[obj->id]) {
2409 digi_link_sound_to_object3( sound, obj-Objects, 1, F1_0, i2f(256), -1, -1);
2410 lavafall_hiss_playing[obj->id] = 1;
2416 if (!under_lavafall && lavafall_hiss_playing[obj->id]) {
2417 digi_kill_sound_linked_to_object( obj-Objects);
2418 lavafall_hiss_playing[obj->id] = 0;
2423 //see if guided missile has flown through exit trigger
2424 if (obj==Guided_missile[Player_num] && obj->signature==Guided_missile_sig[Player_num]) {
2425 if (previous_segment != obj->segnum) {
2427 connect_side = find_connect_side(&Segments[obj->segnum], &Segments[previous_segment]);
2428 if (connect_side != -1) {
2429 int wall_num,trigger_num;
2430 wall_num = Segments[previous_segment].sides[connect_side].wall_num;
2431 if ( wall_num != -1 ) {
2432 trigger_num = Walls[wall_num].trigger;
2433 if (trigger_num != -1)
2434 if (Triggers[trigger_num].type == TT_EXIT)
2435 Guided_missile[Player_num]->lifeleft = 0;
2441 if (Drop_afterburner_blob_flag) {
2442 Assert(obj==ConsoleObject);
2443 drop_afterburner_blobs(obj, 2, i2f(5)/2, -1); // -1 means use default lifetime
2445 if (Game_mode & GM_MULTI)
2446 multi_send_drop_blobs(Player_num);
2448 Drop_afterburner_blob_flag = 0;
2451 if ((obj->type == OBJ_WEAPON) && (Weapon_info[obj->id].afterburner_size)) {
2452 int objnum = obj-Objects;
2453 fix vel = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
2454 fix delay, lifetime;
2458 else if (vel > F1_0*40)
2459 delay = fixdiv(F1_0*13,vel);
2463 lifetime = (delay * 3)/2;
2464 if (!(Game_mode & GM_MULTI)) {
2469 if ((Last_afterburner_time[objnum] + delay < GameTime) || (Last_afterburner_time[objnum] > GameTime)) {
2470 drop_afterburner_blobs(obj, 1, i2f(Weapon_info[obj->id].afterburner_size)/16, lifetime);
2471 Last_afterburner_time[objnum] = GameTime;
2476 obj++; //kill warning
2480 int Max_used_objects = MAX_OBJECTS - 20;
2482 //--------------------------------------------------------------------
2483 //move all objects for the current frame
2484 void object_move_all()
2489 // -- mprintf((0, "Frame %i: %i/%i objects used.\n", FrameCount, num_objects, MAX_OBJECTS));
2491 // check_duplicate_objects();
2492 // remove_incorrect_objects();
2494 if (Highest_object_index > Max_used_objects)
2495 free_object_slots(Max_used_objects); // Free all possible object slots.
2497 obj_delete_all_that_should_be_dead();
2499 if (Auto_leveling_on)
2500 ConsoleObject->mtype.phys_info.flags |= PF_LEVELLING;
2502 ConsoleObject->mtype.phys_info.flags &= ~PF_LEVELLING;
2508 for (i=0;i<=Highest_object_index;i++) {
2509 if ( (objp->type != OBJ_NONE) && (!(objp->flags&OF_SHOULD_BE_DEAD)) ) {
2510 object_move_one( objp );
2518 // check_duplicate_objects();
2519 // remove_incorrect_objects();
2524 //--unused-- // -----------------------------------------------------------
2525 //--unused-- // Moved here from eobject.c on 02/09/94 by MK.
2526 //--unused-- int find_last_obj(int i)
2528 //--unused-- for (i=MAX_OBJECTS;--i>=0;)
2529 //--unused-- if (Objects[i].type != OBJ_NONE) break;
2531 //--unused-- return i;
2536 //make object array non-sparse
2537 void compress_objects(void)
2539 int start_i; //,last_i;
2541 //last_i = find_last_obj(MAX_OBJECTS);
2543 // Note: It's proper to do < (rather than <=) Highest_object_index here because we
2544 // are just removing gaps, and the last object can't be a gap.
2545 for (start_i=0;start_i<Highest_object_index;start_i++)
2547 if (Objects[start_i].type == OBJ_NONE) {
2551 segnum_copy = Objects[Highest_object_index].segnum;
2553 obj_unlink(Highest_object_index);
2555 Objects[start_i] = Objects[Highest_object_index];
2558 if (Cur_object_index == Highest_object_index)
2559 Cur_object_index = start_i;
2562 Objects[Highest_object_index].type = OBJ_NONE;
2564 obj_link(start_i,segnum_copy);
2566 while (Objects[--Highest_object_index].type == OBJ_NONE);
2568 //last_i = find_last_obj(last_i);
2572 reset_objects(num_objects);
2576 //called after load. Takes number of objects, and objects should be
2577 //compressed. resets free list, marks unused objects as unused
2578 void reset_objects(int n_objs)
2582 num_objects = n_objs;
2584 Assert(num_objects>0);
2586 for (i=num_objects;i<MAX_OBJECTS;i++) {
2587 free_obj_list[i] = i;
2588 Objects[i].type = OBJ_NONE;
2589 Objects[i].segnum = -1;
2592 Highest_object_index = num_objects-1;
2594 Debris_object_count = 0;
2597 //Tries to find a segment for an object, using find_point_seg()
2598 int find_object_seg(object * obj )
2600 return find_point_seg(&obj->pos,obj->segnum);
2604 //If an object is in a segment, set its segnum field and make sure it's
2605 //properly linked. If not in any segment, returns 0, else 1.
2606 //callers should generally use find_vector_intersection()
2607 int update_object_seg(object * obj )
2611 newseg = find_object_seg(obj);
2616 if ( newseg != obj->segnum )
2617 obj_relink(obj-Objects, newseg );
2623 //go through all objects and make sure they have the correct segment numbers
2629 for (i=0;i<=Highest_object_index;i++)
2630 if (Objects[i].type != OBJ_NONE)
2631 if (update_object_seg(&Objects[i]) == 0) {
2632 mprintf((1,"Cannot find segment for object %d in fix_object_segs()\n"));
2634 compute_segment_center(&Objects[i].pos,&Segments[Objects[i].segnum]);
2639 //--unused-- void object_use_new_object_list( object * new_list )
2641 //--unused-- int i, segnum;
2642 //--unused-- object *obj;
2644 //--unused-- // First, unlink all the old objects for the segments array
2645 //--unused-- for (segnum=0; segnum <= Highest_segment_index; segnum++) {
2646 //--unused-- Segments[segnum].objects = -1;
2648 //--unused-- // Then, erase all the objects
2649 //--unused-- reset_objects(1);
2651 //--unused-- // Fill in the object array
2652 //--unused-- memcpy( Objects, new_list, sizeof(object)*MAX_OBJECTS );
2654 //--unused-- Highest_object_index=-1;
2656 //--unused-- // Relink 'em
2657 //--unused-- for (i=0; i<MAX_OBJECTS; i++ ) {
2658 //--unused-- obj = &Objects[i];
2659 //--unused-- if ( obj->type != OBJ_NONE ) {
2660 //--unused-- num_objects++;
2661 //--unused-- Highest_object_index = i;
2662 //--unused-- segnum = obj->segnum;
2663 //--unused-- obj->next = obj->prev = obj->segnum = -1;
2664 //--unused-- obj_link(i,segnum);
2665 //--unused-- } else {
2666 //--unused-- obj->next = obj->prev = obj->segnum = -1;
2672 //delete objects, such as weapons & explosions, that shouldn't stay between levels
2673 // Changed by MK on 10/15/94, don't remove proximity bombs.
2674 //if clear_all is set, clear even proximity bombs
2675 void clear_transient_objects(int clear_all)
2680 for (objnum=0,obj=&Objects[0];objnum<=Highest_object_index;objnum++,obj++)
2681 if (((obj->type == OBJ_WEAPON) && !(Weapon_info[obj->id].flags&WIF_PLACABLE) && (clear_all || ((obj->id != PROXIMITY_ID) && (obj->id != SUPERPROX_ID)))) ||
2682 obj->type == OBJ_FIREBALL ||
2683 obj->type == OBJ_DEBRIS ||
2684 obj->type == OBJ_DEBRIS ||
2685 (obj->type!=OBJ_NONE && obj->flags & OF_EXPLODING)) {
2688 if (Objects[objnum].lifeleft > i2f(2))
2689 mprintf((0,"Note: Clearing object %d (type=%d, id=%d) with lifeleft=%x\n",objnum,Objects[objnum].type,Objects[objnum].id,Objects[objnum].lifeleft));
2694 else if (Objects[objnum].type!=OBJ_NONE && Objects[objnum].lifeleft < i2f(2))
2695 mprintf((0,"Note: NOT clearing object %d (type=%d, id=%d) with lifeleft=%x\n",objnum,Objects[objnum].type,Objects[objnum].id,Objects[objnum].lifeleft));
2699 //attaches an object, such as a fireball, to another object, such as a robot
2700 void obj_attach(object *parent,object *sub)
2702 Assert(sub->type == OBJ_FIREBALL);
2703 Assert(sub->control_type == CT_EXPLOSION);
2705 Assert(sub->ctype.expl_info.next_attach==-1);
2706 Assert(sub->ctype.expl_info.prev_attach==-1);
2708 Assert(parent->attached_obj==-1 || Objects[parent->attached_obj].ctype.expl_info.prev_attach==-1);
2710 sub->ctype.expl_info.next_attach = parent->attached_obj;
2712 if (sub->ctype.expl_info.next_attach != -1)
2713 Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach = sub-Objects;
2715 parent->attached_obj = sub-Objects;
2717 sub->ctype.expl_info.attach_parent = parent-Objects;
2718 sub->flags |= OF_ATTACHED;
2720 Assert(sub->ctype.expl_info.next_attach != sub-Objects);
2721 Assert(sub->ctype.expl_info.prev_attach != sub-Objects);
2724 //dettaches one object
2725 void obj_detach_one(object *sub)
2727 Assert(sub->flags & OF_ATTACHED);
2728 Assert(sub->ctype.expl_info.attach_parent != -1);
2730 if ((Objects[sub->ctype.expl_info.attach_parent].type == OBJ_NONE) || (Objects[sub->ctype.expl_info.attach_parent].attached_obj == -1))
2732 sub->flags &= ~OF_ATTACHED;
2736 if (sub->ctype.expl_info.next_attach != -1) {
2737 Assert(Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach=sub-Objects);
2738 Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach = sub->ctype.expl_info.prev_attach;
2741 if (sub->ctype.expl_info.prev_attach != -1) {
2742 Assert(Objects[sub->ctype.expl_info.prev_attach].ctype.expl_info.next_attach=sub-Objects);
2743 Objects[sub->ctype.expl_info.prev_attach].ctype.expl_info.next_attach = sub->ctype.expl_info.next_attach;
2746 Assert(Objects[sub->ctype.expl_info.attach_parent].attached_obj=sub-Objects);
2747 Objects[sub->ctype.expl_info.attach_parent].attached_obj = sub->ctype.expl_info.next_attach;
2750 sub->ctype.expl_info.next_attach = sub->ctype.expl_info.prev_attach = -1;
2751 sub->flags &= ~OF_ATTACHED;
2755 //dettaches all objects from this object
2756 void obj_detach_all(object *parent)
2758 while (parent->attached_obj != -1)
2759 obj_detach_one(&Objects[parent->attached_obj]);
2762 //creates a marker object in the world. returns the object number
2763 int drop_marker_object(vms_vector *pos,int segnum,vms_matrix *orient, int marker_num)
2767 Assert(Marker_model_num != -1);
2769 objnum = obj_create(OBJ_MARKER, marker_num, segnum, pos, orient, Polygon_models[Marker_model_num].rad, CT_NONE, MT_NONE, RT_POLYOBJ);
2772 object *obj = &Objects[objnum];
2774 obj->rtype.pobj_info.model_num = Marker_model_num;
2776 vm_vec_copy_scale(&obj->mtype.spin_rate,&obj->orient.uvec,F1_0/2);
2778 // MK, 10/16/95: Using lifeleft to make it flash, thus able to trim lightlevel from all objects.
2779 obj->lifeleft = IMMORTAL_TIME - 1;
2785 extern int Ai_last_missile_camera;
2787 // *viewer is a viewer, probably a missile.
2788 // wake up all robots that were rendered last frame subject to some constraints.
2789 void wake_up_rendered_objects(object *viewer, int window_num)
2793 // Make sure that we are processing current data.
2794 if (FrameCount != Window_rendered_data[window_num].frame) {
2795 mprintf((1, "Warning: Called wake_up_rendered_objects with a bogus window.\n"));
2799 Ai_last_missile_camera = viewer-Objects;
2801 for (i=0; i<Window_rendered_data[window_num].num_objects; i++) {
2804 int fcval = FrameCount & 3;
2806 objnum = Window_rendered_data[window_num].rendered_objects[i];
2807 if ((objnum & 3) == fcval) {
2808 objp = &Objects[objnum];
2810 if (objp->type == OBJ_ROBOT) {
2811 if (vm_vec_dist_quick(&viewer->pos, &objp->pos) < F1_0*100) {
2812 ai_local *ailp = &Ai_local_info[objnum];
2813 if (ailp->player_awareness_type == 0) {
2814 objp->ctype.ai_info.SUB_FLAGS |= SUB_FLAGS_CAMERA_AWAKE;
2815 ailp->player_awareness_type = PA_WEAPON_ROBOT_COLLISION;
2816 ailp->player_awareness_time = F1_0*3;
2817 ailp->previous_visibility = 2;