]> icculus.org git repositories - btb/d2x.git/blob - main/object.c
make next axis config key skip currently allocated axes (d1x r1.11)
[btb/d2x.git] / main / object.c
1 /* $Id: object.c,v 1.14 2004-05-22 02:05:51 btb Exp $ */
2 /*
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.
13 */
14
15 /*
16  *
17  * object rendering
18  *
19  * Old Log:
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
23  *
24  * Revision 1.4  1995/10/20  00:50:57  allender
25  * make alt texture for player ship work correctly when cloaked
26  *
27  * Revision 1.3  1995/09/14  14:11:32  allender
28  * fix_object_segs returns void
29  *
30  * Revision 1.2  1995/08/12  11:31:01  allender
31  * removed #ifdef NEWDEMO -- always in
32  *
33  * Revision 1.1  1995/05/16  15:29:23  allender
34  * Initial revision
35  *
36  * Revision 2.3  1995/06/15  12:30:51  john
37  * Fixed bug with multiplayer ships cloaking out wrongly.
38  *
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.
42  *
43  * Revision 2.1  1995/03/21  14:38:51  john
44  * Ifdef'd out the NETWORK code.
45  *
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.
49  *
50  * Revision 1.335  1995/02/22  12:57:30  allender
51  * remove anonymous unions from object structure
52  *
53  * Revision 1.334  1995/02/09  22:04:40  mike
54  * fix lifeleft on badass weapons.
55  *
56  * Revision 1.333  1995/02/08  12:54:00  matt
57  * Fixed object freeing code which was deleting some explosions it shouldn't
58  *
59  * Revision 1.332  1995/02/08  11:37:26  mike
60  * Check for failures in call to obj_create.
61  *
62  * Revision 1.331  1995/02/05  17:48:52  rob
63  * Changed assert in obj_relink, more robust.
64  *
65  * Revision 1.330  1995/02/05  13:39:48  mike
66  * remove invulnerability effect code (actually, comment out).
67  *
68  * Revision 1.329  1995/02/04  12:29:52  rob
69  * Get rid of potential assert error for explosion detachment.
70  *
71  * Revision 1.328  1995/02/01  18:15:57  rob
72  * Removed debugging output from engine glow change.
73  *
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.
77  *
78  * Revision 1.326  1995/01/29  14:46:24  rob
79  * Fixed invul. vclip to only appear on player who is invul.
80  *
81  * Revision 1.325  1995/01/29  13:48:16  mike
82  * Add invulnerability graphical effect viewable by other players.
83  *
84  * Revision 1.324  1995/01/29  11:39:25  mike
85  * Add invulnerability effect.
86  *
87  * Revision 1.323  1995/01/27  17:02:41  mike
88  * add more information to an Error call.
89  *
90  * Revision 1.322  1995/01/26  22:11:30  mike
91  * Purple chromo-blaster (ie, fusion cannon) spruce up (chromification)
92  *
93  * Revision 1.321  1995/01/25  20:04:10  matt
94  * Moved matrix check to avoid orthogonalizing an uninitialize matrix
95  *
96  * Revision 1.320  1995/01/25  12:11:35  matt
97  * Make sure orient matrix is orthogonal when resetting player object
98  *
99  * Revision 1.319  1995/01/21  21:46:22  mike
100  * Optimize code in Assert (and prevent warning message).
101  *
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.
105  *
106  * Revision 1.317  1995/01/15  15:34:30  matt
107  * When freeing object slots, don't free fireballs that will be deleting
108  * other objects.
109  *
110  * Revision 1.316  1995/01/14  19:16:48  john
111  * First version of new bitmap paging code.
112  *
113  * Revision 1.315  1995/01/12  18:53:37  john
114  * Fixed parameter passing error.
115  *
116  * Revision 1.314  1995/01/12  12:09:47  yuan
117  * Added coop object capability.
118  *
119  * Revision 1.313  1994/12/15  16:45:44  matt
120  * Took out slew stuff for release version
121  *
122  * Revision 1.312  1994/12/15  13:04:25  mike
123  * Replace Players[Player_num].time_total references with GameTime.
124  *
125  * Revision 1.311  1994/12/15  11:01:04  mike
126  * add Object_minus_one for debugging.
127  *
128  * Revision 1.310  1994/12/15  03:03:33  matt
129  * Added error checking for NULL return from object_create_explosion()
130  *
131  * Revision 1.309  1994/12/14  17:25:31  matt
132  * Made next viewer func based on release not ndebug
133  *
134  * Revision 1.308  1994/12/13  12:55:42  mike
135  * hostages on board messages for when you die.
136  *
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.
139  *
140  * Revision 1.306  1994/12/12  00:27:11  matt
141  * Added support for no-levelling option
142  *
143  * Revision 1.305  1994/12/11  22:41:14  matt
144  * Added command-line option, -nolevel, which turns off player ship levelling
145  *
146  * Revision 1.304  1994/12/11  22:03:23  mike
147  * free up object slots as necessary.
148  *
149  * Revision 1.303  1994/12/11  14:09:31  mike
150  * make boss explosion sounds softer.
151  *
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.
155  *
156  * Revision 1.301  1994/12/11  12:38:25  mike
157  * make boss explosion sounds louder in create_small_fireball.
158  *
159  * Revision 1.300  1994/12/10  15:28:37  matt
160  * Added asserts for debugging
161  *
162  * Revision 1.299  1994/12/09  16:18:51  matt
163  * Fixed init_player_object, for editor
164  *
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
169  *
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.
173  *
174  * Revision 1.296  1994/12/08  20:05:07  matt
175  * Removed unneeded debug message
176  *
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.
180  *
181  * Revision 1.294  1994/12/07  20:13:37  matt
182  * Added debris object limiter
183  *
184  * Revision 1.293  1994/12/06  16:58:38  matt
185  * Killed warnings
186  *
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.
190  *
191  * Revision 1.291  1994/12/05  12:23:53  mike
192  * make camera start closer, but move away from player in death sequence.
193  *
194  * Revision 1.290  1994/12/02  11:11:18  mike
195  * hook sound effect to player small explosions (ctrlcen, too).
196  *
197  * Revision 1.289  1994/11/28  21:50:52  mike
198  * optimizations.
199  *
200  * Revision 1.288  1994/11/27  23:12:28  matt
201  * Made changes for new mprintf calling convention
202  *
203  * Revision 1.287  1994/11/27  20:35:50  matt
204  * Fixed dumb mistake
205  *
206  * Revision 1.286  1994/11/27  20:30:52  matt
207  * Got rid of warning
208  *
209  * Revision 1.285  1994/11/21  11:43:21  mike
210  * ndebug stuff.
211  *
212  * Revision 1.284  1994/11/19  15:19:37  mike
213  * rip out unused code and data.
214  *
215  * Revision 1.283  1994/11/18  23:41:59  john
216  * Changed some shorts to ints.
217  *
218  * Revision 1.282  1994/11/18  16:16:17  mike
219  * Separate depth on objects vs. walls.
220  *
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.
225  *
226  * Revision 1.280  1994/11/16  20:36:34  rob
227  * Changed player explosion (small) code.
228  *
229  * Revision 1.279  1994/11/16  18:26:04  matt
230  * Clear tmap override on player, to fix "rock ship" bug
231  *
232  * Revision 1.278  1994/11/16  14:54:12  rob
233  * Moved hook for network explosions.
234  *
235  * Revision 1.277  1994/11/14  11:40:42  mike
236  * plot inner polygon on laser based on detail level.
237  *
238  * Revision 1.276  1994/11/10  14:02:59  matt
239  * Hacked in support for player ships with different textures
240  *
241  * Revision 1.275  1994/11/08  12:19:08  mike
242  * Make a generally useful function for putting small explosions on any object.
243  *
244  * Revision 1.274  1994/11/04  19:55:54  rob
245  * Changed calls to player_explode to accomodate new parameter.
246  *
247  * Revision 1.273  1994/11/02  21:54:27  matt
248  * Delete the camera when the death sequence is done
249  *
250  * Revision 1.272  1994/11/02  11:36:35  rob
251  * Added player-in-process-of-dying explosions to network play.
252  *
253  * Revision 1.271  1994/10/31  17:25:33  matt
254  * Fixed cloaked bug
255  *
256  * Revision 1.270  1994/10/31  16:11:19  allender
257  * on demo recording, store letterbox mode in demo.
258  *
259  * Revision 1.269  1994/10/31  10:36:18  mike
260  * Make cloak effect fadein/fadeout different for robots versus player.
261  *
262  * Revision 1.268  1994/10/30  14:11:44  mike
263  * rip out repair center stuff.
264  *
265  * Revision 1.267  1994/10/28  19:43:52  mike
266  * Boss cloaking effect.
267  *
268  * Revision 1.266  1994/10/27  11:33:42  mike
269  * Add Highest_ever_object_index -- high water mark in object creation.
270  *
271  * Revision 1.265  1994/10/25  10:51:12  matt
272  * Vulcan cannon powerups now contain ammo count
273  *
274  * Revision 1.264  1994/10/24  20:49:24  matt
275  * Made cloaked objects pulse
276  *
277  * Revision 1.263  1994/10/21  12:19:45  matt
278  * Clear transient objects when saving (& loading) games
279  *
280  * Revision 1.262  1994/10/21  11:25:23  mike
281  * Use new constant IMMORTAL_TIME.
282  *
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
285  *
286  *
287  * Revision 1.260  1994/10/17  23:21:55  mike
288  * Clean up robot cloaking, move more to ai.c
289  *
290  * Revision 1.259  1994/10/17  21:34:49  matt
291  * Added support for new Control Center/Main Reactor
292  *
293  * Revision 1.258  1994/10/17  21:18:04  mike
294  * robot cloaking.
295  *
296  * Revision 1.257  1994/10/17  14:12:23  matt
297  * Cleaned up problems with player dying from mine explosion
298  *
299  * Revision 1.256  1994/10/15  19:04:31  mike
300  * Don't remove proximity bombs after you die.
301  *
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.
305  *
306  * Revision 1.254  1994/10/12  08:04:29  mike
307  * Don't decloak player on death.
308  *
309  * Revision 1.253  1994/10/11  20:36:16  matt
310  * Clear "transient" objects (weapons,explosions,etc.) when starting a level
311  *
312  * Revision 1.252  1994/10/11  12:24:09  matt
313  * Cleaned up/change badass explosion calls
314  *
315  * Revision 1.251  1994/10/08  19:30:20  matt
316  * Fixed (I hope) a bug in cloaking of multiplayer objects
317  *
318  * Revision 1.250  1994/10/08  14:03:15  rob
319  * Changed cloaking routine.
320  *
321  * Revision 1.249  1994/10/07  22:17:27  mike
322  * Asserts on valid segnum.
323  *
324  * Revision 1.248  1994/10/07  19:11:14  matt
325  * Added cool cloak transition effect
326  *
327  */
328
329 #ifdef HAVE_CONFIG_H
330 #include <conf.h>
331 #endif
332
333 #ifdef WINDOWS
334 #include "desw.h"
335 #endif
336
337 #include <string.h>     // for memset
338 #include <stdio.h>
339
340 #include "inferno.h"
341 #include "game.h"
342 #include "gr.h"
343 #include "stdlib.h"
344 #include "bm.h"
345 //#include "error.h"
346 #include "mono.h"
347 #include "3d.h"
348 #include "segment.h"
349 #include "texmap.h"
350 #include "laser.h"
351 #include "key.h"
352 #include "gameseg.h"
353 #include "textures.h"
354
355 #include "object.h"
356 #include "physics.h"
357 #include "slew.h"               
358 #include "render.h"
359 #include "wall.h"
360 #include "vclip.h"
361 #include "polyobj.h"
362 #include "fireball.h"
363 #include "laser.h"
364 #include "error.h"
365 #include "pa_enabl.h"
366 #include "ai.h"
367 #include "hostage.h"
368 #include "morph.h"
369 #include "cntrlcen.h"
370 #include "powerup.h"
371 #include "fuelcen.h"
372 #include "endlevel.h"
373
374 #include "sounds.h"
375 #include "collide.h"
376
377 #include "lighting.h"
378 #include "newdemo.h"
379 #include "player.h"
380 #include "weapon.h"
381 #ifdef NETWORK
382 #include "network.h"
383 #endif
384 #include "newmenu.h"
385 #include "gauges.h"
386 #include "multi.h"
387 #include "menu.h"
388 #include "args.h"
389 #include "text.h"
390 #include "piggy.h"
391 #include "switch.h"
392 #include "gameseq.h"
393
394 #ifdef TACTILE
395 #include "tactile.h"
396 #endif
397
398 #ifdef EDITOR
399 #include "editor/editor.h"
400 #endif
401
402 #ifdef _3DFX
403 #include "3dfx_des.h"
404 #endif
405
406 void obj_detach_all(object *parent);
407 void obj_detach_one(object *sub);
408 int free_object_slots(int num_used);
409
410 /*
411  *  Global variables
412  */
413
414 extern sbyte WasRecorded[MAX_OBJECTS];
415
416 ubyte CollisionResult[MAX_OBJECT_TYPES][MAX_OBJECT_TYPES];
417
418 object *ConsoleObject;                                  //the object that is the player
419
420 static short free_obj_list[MAX_OBJECTS];
421
422 //Data for objects
423
424 // -- Object stuff
425
426 //info on the various types of objects
427 #ifndef NDEBUG
428 object  Object_minus_one;
429 #endif
430
431 object Objects[MAX_OBJECTS];
432 int num_objects=0;
433 int Highest_object_index=0;
434 int Highest_ever_object_index=0;
435
436 // grs_bitmap *robot_bms[MAX_ROBOT_BITMAPS];    //all bitmaps for all robots
437
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
440
441 // char *robot_names[MAX_ROBOT_TYPES];          //name of each robot
442
443 //--unused-- int Num_robot_types=0;
444
445 int print_object_info = 0;
446 //@@int Object_viewer = 0;
447
448 //object * Slew_object = NULL;  // Object containing slew object info.
449
450 //--unused-- int Player_controller_type = 0;
451
452 window_rendered_data Window_rendered_data[MAX_RENDERED_WINDOWS];
453
454 #ifndef NDEBUG
455 char    Object_type_names[MAX_OBJECT_TYPES][9] = {
456         "WALL    ",
457         "FIREBALL",
458         "ROBOT   ",
459         "HOSTAGE ",
460         "PLAYER  ",
461         "WEAPON  ",
462         "CAMERA  ",
463         "POWERUP ",
464         "DEBRIS  ",
465         "CNTRLCEN",
466         "FLARE   ",
467         "CLUTTER ",
468         "GHOST   ",
469         "LIGHT   ",
470         "COOP    ",
471         "MARKER  ",
472 };
473 #endif
474
475 #ifndef RELEASE
476 //set viewer object to next object in array
477 void object_goto_next_viewer()
478 {
479         int i, start_obj = 0;
480
481         start_obj = Viewer - Objects;           //get viewer object number
482         
483         for (i=0;i<=Highest_object_index;i++) {
484
485                 start_obj++;
486                 if (start_obj > Highest_object_index ) start_obj = 0;
487
488                 if (Objects[start_obj].type != OBJ_NONE )       {
489                         Viewer = &Objects[start_obj];
490                         return;
491                 }
492         }
493
494         Error( "Couldn't find a viewer object!" );
495
496 }
497
498 //set viewer object to next object in array
499 void object_goto_prev_viewer()
500 {
501         int i, start_obj = 0;
502
503         start_obj = Viewer - Objects;           //get viewer object number
504         
505         for (i=0; i<=Highest_object_index; i++) {
506
507                 start_obj--;
508                 if (start_obj < 0 ) start_obj = Highest_object_index;
509
510                 if (Objects[start_obj].type != OBJ_NONE )       {
511                         Viewer = &Objects[start_obj];
512                         return;
513                 }
514         }
515
516         Error( "Couldn't find a viewer object!" );
517
518 }
519 #endif
520
521 object *obj_find_first_of_type (int type)
522  {
523   int i;
524
525   for (i=0;i<=Highest_object_index;i++)
526         if (Objects[i].type==type)
527          return (&Objects[i]);
528   return ((object *)NULL);
529  }
530
531 int obj_return_num_of_type (int type)
532  {
533   int i,count=0;
534
535   for (i=0;i<=Highest_object_index;i++)
536         if (Objects[i].type==type)
537          count++;
538   return (count);
539  }
540 int obj_return_num_of_typeid (int type,int id)
541  {
542   int i,count=0;
543
544   for (i=0;i<=Highest_object_index;i++)
545         if (Objects[i].type==type && Objects[i].id==id)
546          count++;
547   return (count);
548  }
549
550 int global_orientation = 0;
551
552 //draw an object that has one bitmap & doesn't rotate
553 void draw_object_blob(object *obj,bitmap_index bmi)
554 {
555         int     orientation=0;
556         grs_bitmap * bm = &GameBitmaps[bmi.index];
557
558
559         if (obj->type == OBJ_FIREBALL)
560                 orientation = (obj-Objects) & 7;
561
562         orientation = global_orientation;
563
564 //@@#ifdef WINDOWS
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 );
568 //@@    }
569 //@@    if (bm->bm_handle) {
570 //@@            DDGRUNLOCK(dd_grd_curcanv);
571 //@@    }
572 //@@#else
573         PIGGY_PAGE_IN( bmi );
574 //@@#endif      
575
576         if (bm->bm_w > bm->bm_h)
577
578                 g3_draw_bitmap(&obj->pos,obj->size,fixmuldiv(obj->size,bm->bm_h,bm->bm_w),bm, orientation);
579
580         else
581
582                 g3_draw_bitmap(&obj->pos,fixmuldiv(obj->size,bm->bm_w,bm->bm_h),obj->size,bm, orientation);
583
584 //@@#ifdef WINDOWS
585 //@@    if (bm->bm_handle) {
586 //@@            DDGRLOCK(dd_grd_curcanv);
587 //@@    }
588 //@@#endif
589 }
590
591 //draw an object that is a texture-mapped rod
592 void draw_object_tmap_rod(object *obj,bitmap_index bitmapi,int lighted)
593 {
594         grs_bitmap * bitmap = &GameBitmaps[bitmapi.index];
595         fix light;
596
597         vms_vector delta,top_v,bot_v;
598         g3s_point top_p,bot_p;
599
600         PIGGY_PAGE_IN(bitmapi);
601
602    bitmap->bm_handle = bitmapi.index;
603
604         vm_vec_copy_scale(&delta,&obj->orient.uvec,obj->size);
605
606         vm_vec_add(&top_v,&obj->pos,&delta);
607         vm_vec_sub(&bot_v,&obj->pos,&delta);
608
609         g3_rotate_point(&top_p,&top_v);
610         g3_rotate_point(&bot_p,&bot_v);
611
612         if (lighted)
613                 light = compute_object_light(obj,&top_p.p3_vec);
614         else
615                 light = f1_0;
616
617    #ifdef _3DFX
618    _3dfx_rendering_poly_obj = 1;
619    #endif
620
621         #ifdef PA_3DFX_VOODOO
622         light = f1_0;
623         #endif
624
625         g3_draw_rod_tmap(bitmap,&bot_p,obj->size,&top_p,obj->size,light);
626
627    #ifdef _3DFX
628    _3dfx_rendering_poly_obj = 0;
629    #endif
630
631 }
632
633 int     Linear_tmap_polygon_objects = 1;
634
635 extern fix Max_thrust;
636
637 //used for robot engine glow
638 #define MAX_VELOCITY i2f(50)
639
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();
643
644 //what darkening level to use when cloaked
645 #define CLOAKED_FADE_LEVEL              28
646
647 #define CLOAK_FADEIN_DURATION_PLAYER    F2_0
648 #define CLOAK_FADEOUT_DURATION_PLAYER   F2_0
649
650 #define CLOAK_FADEIN_DURATION_ROBOT     F1_0
651 #define CLOAK_FADEOUT_DURATION_ROBOT    F1_0
652
653 //do special cloaked render
654 void draw_cloaked_object(object *obj,fix light,fix *glow,fix cloak_start_time,fix cloak_end_time)
655 {
656         fix cloak_delta_time,total_cloaked_time;
657         fix light_scale=F1_0;
658         int cloak_value=0;
659         int fading=0;           //if true, fading, else cloaking
660         fix     Cloak_fadein_duration=F1_0;
661         fix     Cloak_fadeout_duration=F1_0;
662
663
664         total_cloaked_time = cloak_end_time-cloak_start_time;
665
666         switch (obj->type) {
667                 case OBJ_PLAYER:
668                         Cloak_fadein_duration = CLOAK_FADEIN_DURATION_PLAYER;
669                         Cloak_fadeout_duration = CLOAK_FADEOUT_DURATION_PLAYER;
670                         break;
671                 case OBJ_ROBOT:
672                         Cloak_fadein_duration = CLOAK_FADEIN_DURATION_ROBOT;
673                         Cloak_fadeout_duration = CLOAK_FADEOUT_DURATION_ROBOT;
674                         break;
675                 default:
676                         Int3();         //      Contact Mike: Unexpected object type in draw_cloaked_object.
677         }
678
679         cloak_delta_time = GameTime - cloak_start_time;
680
681         if (cloak_delta_time < Cloak_fadein_duration/2) {
682
683                 light_scale = fixdiv(Cloak_fadein_duration/2 - cloak_delta_time,Cloak_fadein_duration/2);
684                 fading = 1;
685
686         }
687         else if (cloak_delta_time < Cloak_fadein_duration) {
688
689                 cloak_value = f2i(fixdiv(cloak_delta_time - Cloak_fadein_duration/2,Cloak_fadein_duration/2) * CLOAKED_FADE_LEVEL);
690
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;
694
695                 //note, if more than one cloaked object is visible at once, the
696                 //pulse rate will change!
697
698                 cloak_timer -= FrameTime;
699                 while (cloak_timer < 0) {
700
701                         cloak_timer += Cloak_fadeout_duration/12;
702
703                         cloak_delta += cloak_dir;
704
705                         if (cloak_delta==0 || cloak_delta==4)
706                                 cloak_dir = -cloak_dir;
707                 }
708
709                 cloak_value = CLOAKED_FADE_LEVEL - cloak_delta;
710         
711         } else if (GameTime < cloak_end_time-Cloak_fadeout_duration/2) {
712
713                 cloak_value = f2i(fixdiv(total_cloaked_time - Cloak_fadeout_duration/2 - cloak_delta_time,Cloak_fadeout_duration/2) * CLOAKED_FADE_LEVEL);
714
715         } else {
716
717                 light_scale = fixdiv(Cloak_fadeout_duration/2 - (total_cloaked_time - cloak_delta_time),Cloak_fadeout_duration/2);
718                 fading = 1;
719         }
720
721
722         if (fading) {
723                 fix new_light,save_glow;
724                 bitmap_index * alt_textures = NULL;
725
726 #ifdef NETWORK
727                 if ( obj->rtype.pobj_info.alt_textures > 0 )
728                         alt_textures = multi_player_textures[obj->rtype.pobj_info.alt_textures-1];
729 #endif
730
731                 new_light = fixmul(light,light_scale);
732                 save_glow = glow[0];
733                 glow[0] = fixmul(glow[0],light_scale);
734                 draw_polygon_model(&obj->pos,
735                                    &obj->orient,
736                                    (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
737                                    obj->rtype.pobj_info.model_num,obj->rtype.pobj_info.subobj_flags,
738                                    new_light,
739                                    glow,
740                                    alt_textures );
741                 glow[0] = save_glow;
742         }
743         else {
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,
748                                    &obj->orient,
749                                    (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
750                                    obj->rtype.pobj_info.model_num,obj->rtype.pobj_info.subobj_flags,
751                                    light,
752                                    glow,
753                                    NULL );
754                 g3_set_special_render(NULL,NULL,NULL);
755                 Gr_scanline_darkening_level = GR_FADE_LEVELS;
756         }
757
758 }
759
760 //draw an object which renders as a polygon model
761 void draw_polygon_object(object *obj)
762 {
763         fix light;
764         int     imsave;
765         fix engine_glow_value[2];               //element 0 is for engine glow, 1 for headlight
766
767         light = compute_object_light(obj,NULL);
768
769         //      If option set for bright players in netgame, brighten them!
770 #ifdef NETWORK
771         if (Game_mode & GM_MULTI)
772                 if (Netgame.BrightPlayers)
773                         light = F1_0;
774 #endif
775
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
779
780         if (obj->type == OBJ_WEAPON)
781                 if (obj->id == FLARE_ID)
782                         light += F1_0*2;
783
784         if (obj->type == OBJ_MARKER)
785                 light += F1_0*2;
786
787
788         imsave = Interpolation_method;
789         if (Linear_tmap_polygon_objects)
790                 Interpolation_method = 1;
791
792         //set engine glow value
793         engine_glow_value[0] = f1_0/5;
794         if (obj->movement_type == MT_PHYSICS) {
795
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;
799                 }
800                 else {
801                         fix speed = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
802                         engine_glow_value[0] += (fixdiv(speed,MAX_VELOCITY)*3)/5;
803                 }
804         }
805
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!
811                         else
812                                 engine_glow_value[1] = -1;              //draw normal color (grey)
813                 else
814                         engine_glow_value[1] = -3;                      //don't draw
815         }
816
817         if (obj->rtype.pobj_info.tmap_override != -1) {
818 #ifndef NDEBUG
819                 polymodel *pm = &Polygon_models[obj->rtype.pobj_info.model_num];
820 #endif
821                 bitmap_index bm_ptrs[12];
822
823                 int i;
824
825                 Assert(pm->n_textures<=12);
826
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];
829
830                 draw_polygon_model(&obj->pos,
831                                    &obj->orient,
832                                    (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
833                                    obj->rtype.pobj_info.model_num,
834                                    obj->rtype.pobj_info.subobj_flags,
835                                    light,
836                                    engine_glow_value,
837                                    bm_ptrs);
838         }
839         else {
840
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);
846                         else
847                                 draw_cloaked_object(obj,light,engine_glow_value, GameTime-F1_0*10, GameTime+F1_0*10);
848                 } else {
849                         bitmap_index * alt_textures = NULL;
850         
851                         #ifdef NETWORK
852                         if ( obj->rtype.pobj_info.alt_textures > 0 )
853                                 alt_textures = multi_player_textures[obj->rtype.pobj_info.alt_textures-1];
854                         #endif
855
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;
860                         }
861
862                         draw_polygon_model(&obj->pos,
863                                            &obj->orient,
864                                            (vms_angvec *)&obj->rtype.pobj_info.anim_angles,obj->rtype.pobj_info.model_num,
865                                            obj->rtype.pobj_info.subobj_flags,
866                                            light,
867                                            engine_glow_value,
868                                            alt_textures);
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,
873                                                            &obj->orient,
874                                                            (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
875                                                            Weapon_info[obj->id].model_num_inner,
876                                                            obj->rtype.pobj_info.subobj_flags,
877                                                            light,
878                                                            engine_glow_value,
879                                                            alt_textures);
880                         }
881                 }
882         }
883
884         Interpolation_method = imsave;
885
886 }
887
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.
899
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];
905
906 //091494: set_close_objects(object *obj)
907 //091494: {
908 //091494:       fix dist;
909 //091494:
910 //091494:       if ( (obj->type != OBJ_ROBOT) || (Object_draw_lock_boxes==0) )  
911 //091494:               return;
912 //091494:
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++;
921 //091494:               } else {
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;
931 //091494:                               }
932 //091494:                       }
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;
938 //091494:                       }
939 //091494:               }
940 //091494:       }
941 //091494: }
942
943 int     Player_fired_laser_this_frame=-1;
944
945
946
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)
951 {
952         if (Player_fired_laser_this_frame != -1) {
953                 g3s_point temp;
954
955                 g3_rotate_point(&temp,&objp->pos);
956
957                 if (temp.p3_codes & CC_BEHIND)          //robot behind the screen
958                         return;
959
960                 //the code below to check for object near the center of the screen
961                 //completely ignores z, which may not be good
962
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;
966                 }
967         }
968
969
970 }
971
972 //      ------------------------------------------------------------------------------------------------------------------
973 void create_small_fireball_on_object(object *objp, fix size_scale, int sound_flag)
974 {
975         fix                     size;
976         vms_vector      pos, rand_vec;
977         int                     segnum;
978
979         pos = objp->pos;
980         make_random_vector(&rand_vec);
981
982         vm_vec_scale(&rand_vec, objp->size/2);
983
984         vm_vec_add2(&pos, &rand_vec);
985
986         size = fixmul(size_scale, F1_0/2 + d_rand()*4/2);
987
988         segnum = find_point_seg(&pos, objp->segnum);
989         if (segnum != -1) {
990                 object *expl_obj;
991                 expl_obj = object_create_explosion(segnum, &pos, size, VCLIP_SMALL_EXPLOSION);
992                 if (!expl_obj)
993                         return;
994                 obj_attach(objp,expl_obj);
995                 if (d_rand() < 8192) {
996                         fix     vol = F1_0/2;
997                         if (objp->type == OBJ_ROBOT)
998                                 vol *= 2;
999                         else if (sound_flag)
1000                                 digi_link_sound_to_object(SOUND_EXPLODING_WALL, objp-Objects, 0, vol);
1001                 }
1002         }
1003 }
1004
1005 //      ------------------------------------------------------------------------------------------------------------------
1006 void create_vclip_on_object(object *objp, fix size_scale, int vclip_num)
1007 {
1008         fix                     size;
1009         vms_vector      pos, rand_vec;
1010         int                     segnum;
1011
1012         pos = objp->pos;
1013         make_random_vector(&rand_vec);
1014
1015         vm_vec_scale(&rand_vec, objp->size/2);
1016
1017         vm_vec_add2(&pos, &rand_vec);
1018
1019         size = fixmul(size_scale, F1_0 + d_rand()*4);
1020
1021         segnum = find_point_seg(&pos, objp->segnum);
1022         if (segnum != -1) {
1023                 object *expl_obj;
1024                 expl_obj = object_create_explosion(segnum, &pos, size, vclip_num);
1025                 if (!expl_obj)
1026                         return;
1027
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;
1032         }
1033 }
1034
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 -- }
1044
1045 // -----------------------------------------------------------------------------
1046 //      Render an object.  Calls one of several routines based on type
1047 void render_object(object *obj)
1048 {
1049         int     mld_save;
1050
1051         if ( obj == Viewer ) return;            
1052
1053         if ( obj->type==OBJ_NONE )      {
1054                 #ifndef NDEBUG
1055                 mprintf( (1, "ERROR!!!! Bogus obj %d in seg %d is rendering!\n", obj-Objects, obj->segnum ));
1056                 Int3();
1057                 #endif
1058                 return;
1059         }
1060
1061         mld_save = Max_linear_depth;
1062         Max_linear_depth = Max_linear_depth_objects;
1063
1064         switch (obj->render_type) {
1065
1066                 case RT_NONE:   break;          //doesn't render, like the player
1067
1068                 case RT_POLYOBJ:
1069
1070                         draw_polygon_object(obj);
1071
1072                         //"warn" robot if being shot at
1073                         if (obj->type == OBJ_ROBOT)
1074                                 set_robot_location_info(obj);
1075
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);
1078
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);
1082
1083                         break;
1084
1085                 case RT_MORPH:  draw_morph_object(obj); break;
1086
1087                 case RT_FIREBALL: draw_fireball(obj); break;
1088
1089                 case RT_WEAPON_VCLIP: draw_weapon_vclip(obj); break;
1090
1091                 case RT_HOSTAGE: draw_hostage(obj); break;
1092
1093                 case RT_POWERUP: draw_powerup(obj); break;
1094
1095                 case RT_LASER: Laser_render(obj); break;
1096
1097                 default: Error("Unknown render_type <%d>",obj->render_type);
1098         }
1099
1100         #ifdef NEWDEMO
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;
1106                         }
1107                 }
1108         #endif
1109
1110         Max_linear_depth = mld_save;
1111
1112 }
1113
1114 //--unused-- void object_toggle_lock_targets()  {
1115 //--unused--    Object_draw_lock_boxes ^= 1;
1116 //--unused-- }
1117
1118 //091494: //draw target boxes for nearby robots
1119 //091494: void object_render_targets()
1120 //091494: {
1121 //091494:       g3s_point pt;
1122 //091494:       ubyte codes;
1123 //091494:       int i;
1124 //091494:       int radius,x,y;
1125 //091494:
1126 //091494:       if (Object_draw_lock_boxes==0)
1127 //091494:               return;
1128 //091494:
1129 //091494:       for (i=0; i<Object_num_close; i++ )     {
1130 //091494:                       
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);
1140 //091494:                       }
1141 //091494:               }
1142 //091494:       }
1143 //091494:       Object_num_close=0;
1144 //091494: }
1145 //--unused-- //draw target boxes for nearby robots
1146 //--unused-- void object_render_id(object * obj)
1147 //--unused-- {
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;
1153 //--unused--
1154 //--unused--    s1 = network_get_player_name( obj-Objects );
1155 //--unused--
1156 //--unused--    if (s1)
1157 //--unused--            sprintf( s, "%s", s1 );
1158 //--unused--    else
1159 //--unused--            sprintf( s, "<%d>", obj->id );
1160 //--unused--
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 );
1171 //--unused--                    }
1172 //--unused--            }
1173 //--unused--    }
1174 //--unused-- }
1175
1176 void check_and_fix_matrix(vms_matrix *m);
1177
1178 #define vm_angvec_zero(v) (v)->p=(v)->b=(v)->h=0
1179
1180 void reset_player_object()
1181 {
1182         int i;
1183
1184         //Init physics
1185
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;
1194
1195         //Init render info
1196
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!
1201
1202         for (i=0;i<MAX_SUBMODELS;i++)
1203                 vm_angvec_zero(&ConsoleObject->rtype.pobj_info.anim_angles[i]);
1204
1205         // Clear misc
1206
1207         ConsoleObject->flags = 0;
1208
1209 }
1210
1211
1212 //make object0 the player, setting all relevant fields
1213 void init_player_object()
1214 {
1215         ConsoleObject->type = OBJ_PLAYER;
1216         ConsoleObject->id = 0;                                  //no sub-types for player
1217
1218         ConsoleObject->signature = 0;                   //player has zero, others start at 1
1219
1220         ConsoleObject->size = Polygon_models[Player_ship->model_num].rad;
1221
1222         ConsoleObject->control_type = CT_SLEW;                  //default is player slewing
1223         ConsoleObject->movement_type = MT_PHYSICS;              //change this sometime
1224
1225         ConsoleObject->lifeleft = IMMORTAL_TIME;
1226
1227         ConsoleObject->attached_obj = -1;
1228
1229         reset_player_object();
1230
1231 }
1232
1233 //sets up the free list & init player & whatever else
1234 void init_objects()
1235 {
1236         int i;
1237
1238         collide_init();
1239
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;
1244         }
1245
1246         for (i=0;i<MAX_SEGMENTS;i++)
1247                 Segments[i].objects = -1;
1248
1249         ConsoleObject = Viewer = &Objects[0];
1250
1251         init_player_object();
1252         obj_link(ConsoleObject-Objects,0);      //put in the world in segment 0
1253
1254         num_objects = 1;                                                //just the player
1255         Highest_object_index = 0;
1256
1257         
1258 }
1259
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)
1264 {
1265         int i;
1266
1267         num_objects=MAX_OBJECTS;
1268
1269         Highest_object_index = 0;
1270         Assert(Objects[0].type != OBJ_NONE);            //0 should be used
1271
1272         for (i=MAX_OBJECTS;i--;)
1273                 if (Objects[i].type == OBJ_NONE)
1274                         free_obj_list[--num_objects] = i;
1275                 else
1276                         if (i > Highest_object_index)
1277                                 Highest_object_index = i;
1278 }
1279
1280 #ifndef NDEBUG
1281 int is_object_in_seg( int segnum, int objn )
1282 {
1283         int objnum, count = 0;
1284
1285         for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next)    {
1286                 if ( count > MAX_OBJECTS )      {
1287                         Int3();
1288                         return count;
1289                 }
1290                 if ( objnum==objn ) count++;
1291         }
1292          return count;
1293 }
1294
1295 int search_all_segments_for_object( int objnum )
1296 {
1297         int i;
1298         int count = 0;
1299
1300         for (i=0; i<=Highest_segment_index; i++) {
1301                 count += is_object_in_seg( i, objnum );
1302         }
1303         return count;
1304 }
1305
1306 void johns_obj_unlink(int segnum, int objnum)
1307 {
1308         object  *obj = &Objects[objnum];
1309         segment *seg = &Segments[segnum];
1310
1311         Assert(objnum != -1);
1312
1313         if (obj->prev == -1)
1314                 seg->objects = obj->next;
1315         else
1316                 Objects[obj->prev].next = obj->next;
1317
1318         if (obj->next != -1) Objects[obj->next].prev = obj->prev;
1319 }
1320
1321 void remove_incorrect_objects()
1322 {
1323         int segnum, objnum, count;
1324
1325         for (segnum=0; segnum <= Highest_segment_index; segnum++) {
1326                 count = 0;
1327                 for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next)    {
1328                         count++;
1329                         #ifndef NDEBUG
1330                         if ( count > MAX_OBJECTS )      {
1331                                 mprintf((1, "Object list in segment %d is circular.\n", segnum ));
1332                                 Int3();
1333                         }
1334                         #endif
1335                         if (Objects[objnum].segnum != segnum )  {
1336                                 #ifndef NDEBUG
1337                                 mprintf((0, "Removing object %d from segment %d.\n", objnum, segnum ));
1338                                 Int3();
1339                                 #endif
1340                                 johns_obj_unlink(segnum,objnum);
1341                         }
1342                 }
1343         }
1344 }
1345
1346 void remove_all_objects_but( int segnum, int objnum )
1347 {
1348         int i;
1349
1350         for (i=0; i<=Highest_segment_index; i++) {
1351                 if (segnum != i )       {
1352                         if (is_object_in_seg( i, objnum ))      {
1353                                 johns_obj_unlink( i, objnum );
1354                         }
1355                 }
1356         }
1357 }
1358
1359 int check_duplicate_objects()
1360 {
1361         int i, count=0;
1362         
1363         for (i=0;i<=Highest_object_index;i++) {
1364                 if ( Objects[i].type != OBJ_NONE )      {
1365                         count = search_all_segments_for_object( i );
1366                         if ( count > 1 )        {
1367                                 #ifndef NDEBUG
1368                                 mprintf((1, "Object %d is in %d segments!\n", i, count ));
1369                                 Int3();
1370                                 #endif
1371                                 remove_all_objects_but( Objects[i].segnum,  i );
1372                                 return count;
1373                         }
1374                 }
1375         }
1376         return count;
1377 }
1378
1379 void list_seg_objects( int segnum )
1380 {
1381         int objnum, count = 0;
1382
1383         for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next)    {
1384                 count++;
1385                 if ( count > MAX_OBJECTS )      {
1386                         Int3();
1387                         return;
1388                 }
1389         }
1390         return;
1391
1392 }
1393 #endif
1394
1395 //link the object into the list for its segment
1396 void obj_link(int objnum,int segnum)
1397 {
1398         object *obj = &Objects[objnum];
1399
1400         Assert(objnum != -1);
1401
1402         Assert(obj->segnum == -1);
1403
1404         Assert(segnum>=0 && segnum<=Highest_segment_index);
1405
1406         obj->segnum = segnum;
1407         
1408         obj->next = Segments[segnum].objects;
1409         obj->prev = -1;
1410
1411         Segments[segnum].objects = objnum;
1412
1413         if (obj->next != -1) Objects[obj->next].prev = objnum;
1414         
1415         //list_seg_objects( segnum );
1416         //check_duplicate_objects();
1417
1418         Assert(Objects[0].next != 0);
1419         if (Objects[0].next == 0)
1420                 Objects[0].next = -1;
1421
1422         Assert(Objects[0].prev != 0);
1423         if (Objects[0].prev == 0)
1424                 Objects[0].prev = -1;
1425 }
1426
1427 void obj_unlink(int objnum)
1428 {
1429         object  *obj = &Objects[objnum];
1430         segment *seg = &Segments[obj->segnum];
1431
1432         Assert(objnum != -1);
1433
1434         if (obj->prev == -1)
1435                 seg->objects = obj->next;
1436         else
1437                 Objects[obj->prev].next = obj->next;
1438
1439         if (obj->next != -1) Objects[obj->next].prev = obj->prev;
1440
1441         obj->segnum = -1;
1442
1443         Assert(Objects[0].next != 0);
1444         Assert(Objects[0].prev != 0);
1445 }
1446
1447 int Object_next_signature = 1;  //player gets 0, others start at 1
1448
1449 int Debris_object_count=0;
1450
1451 int     Unused_object_slots;
1452
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)
1458 {
1459         int objnum;
1460
1461         if ( num_objects >= MAX_OBJECTS-2 ) {
1462                 int     num_freed;
1463
1464                 num_freed = free_object_slots(MAX_OBJECTS-10);
1465                 mprintf((0, " *** Freed %i objects in frame %i\n", num_freed, FrameCount));
1466         }
1467
1468         if ( num_objects >= MAX_OBJECTS ) {
1469                 #ifndef NDEBUG
1470                 mprintf((1, "Object creation failed - too many objects!\n" ));
1471                 #endif
1472                 return -1;
1473         }
1474
1475         objnum = free_obj_list[num_objects++];
1476
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;
1481         }
1482
1483 {
1484 int     i;
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++;
1489 }
1490         return objnum;
1491 }
1492
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)
1497 {
1498         free_obj_list[--num_objects] = objnum;
1499         Assert(num_objects >= 0);
1500
1501         if (objnum == Highest_object_index)
1502                 while (Objects[--Highest_object_index].type == OBJ_NONE);
1503 }
1504
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)
1509 {
1510         int     i, olind;
1511         int     obj_list[MAX_OBJECTS];
1512         int     num_already_free, num_to_free, original_num_to_free;
1513
1514         olind = 0;
1515         num_already_free = MAX_OBJECTS - Highest_object_index - 1;
1516
1517         if (MAX_OBJECTS - num_already_free < num_used)
1518                 return 0;
1519
1520         for (i=0; i<=Highest_object_index; i++) {
1521                 if (Objects[i].flags & OF_SHOULD_BE_DEAD) {
1522                         num_already_free++;
1523                         if (MAX_OBJECTS - num_already_free < num_used)
1524                                 return num_already_free;
1525                 } else
1526                         switch (Objects[i].type) {
1527                                 case OBJ_NONE:
1528                                         num_already_free++;
1529                                         if (MAX_OBJECTS - num_already_free < num_used)
1530                                                 return 0;
1531                                         break;
1532                                 case OBJ_WALL:
1533                                 case OBJ_FLARE:
1534                                         Int3();         //      This is curious.  What is an object that is a wall?
1535                                         break;
1536                                 case OBJ_FIREBALL:
1537                                 case OBJ_WEAPON:
1538                                 case OBJ_DEBRIS:
1539                                         obj_list[olind++] = i;
1540                                         break;
1541                                 case OBJ_ROBOT:
1542                                 case OBJ_HOSTAGE:
1543                                 case OBJ_PLAYER:
1544                                 case OBJ_CNTRLCEN:
1545                                 case OBJ_CLUTTER:
1546                                 case OBJ_GHOST:
1547                                 case OBJ_LIGHT:
1548                                 case OBJ_CAMERA:
1549                                 case OBJ_POWERUP:
1550                                         break;
1551                         }
1552
1553         }
1554
1555         num_to_free = MAX_OBJECTS - num_used - num_already_free;
1556         original_num_to_free = num_to_free;
1557
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;
1561         }
1562
1563         for (i=0; i<num_to_free; i++)
1564                 if (Objects[obj_list[i]].type == OBJ_DEBRIS) {
1565                         num_to_free--;
1566                         mprintf((0, "Freeing   DEBRIS object %3i\n", obj_list[i]));
1567                         Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1568                 }
1569
1570         if (!num_to_free)
1571                 return original_num_to_free;
1572
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) {
1575                         num_to_free--;
1576                         mprintf((0, "Freeing FIREBALL object %3i\n", obj_list[i]));
1577                         Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1578                 }
1579
1580         if (!num_to_free)
1581                 return original_num_to_free;
1582
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)) {
1585                         num_to_free--;
1586                         Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1587                 }
1588
1589         if (!num_to_free)
1590                 return original_num_to_free;
1591
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)) {
1594                         num_to_free--;
1595                         mprintf((0, "Freeing   WEAPON object %3i\n", obj_list[i]));
1596                         Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1597                 }
1598
1599         return original_num_to_free - num_to_free;
1600 }
1601
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)
1609 {
1610         int objnum;
1611         object *obj;
1612
1613         Assert(segnum <= Highest_segment_index);
1614         Assert (segnum >= 0);
1615         Assert(ctype <= CT_CNTRLCEN);
1616
1617         if (type==OBJ_DEBRIS && Debris_object_count>=Max_debris_objects)
1618                 return -1;
1619
1620         if (get_seg_masks(pos, segnum, 0, __FILE__, __LINE__).centermask != 0)
1621                 if ((segnum=find_point_seg(pos,segnum))==-1) {
1622                         #ifndef NDEBUG
1623                         mprintf((0,"Bad segnum in obj_create (type=%d)\n",type));
1624                         #endif
1625                         return -1;              //don't create this object
1626                 }
1627
1628         // Find next free object
1629         objnum = obj_allocate();
1630
1631         if (objnum == -1)               //no free objects
1632                 return -1;
1633
1634         Assert(Objects[objnum].type == OBJ_NONE);               //make sure unused
1635
1636         obj = &Objects[objnum];
1637
1638         Assert(obj->segnum == -1);
1639
1640         // Zero out object structure to keep weird bugs from happening
1641         // in uninitialized fields.
1642         memset( obj, 0, sizeof(object) );
1643
1644         obj->signature                          = Object_next_signature++;
1645         obj->type                                       = type;
1646         obj->id                                                 = id;
1647         obj->last_pos                           = *pos;
1648         obj->pos                                        = *pos;
1649         obj->size                                       = size;
1650         obj->flags                                      = 0;
1651         //@@if (orient != NULL)
1652         //@@    obj->orient                     = *orient;
1653
1654         obj->orient                             = orient?*orient:vmd_identity_matrix;
1655
1656         obj->control_type               = ctype;
1657         obj->movement_type              = mtype;
1658         obj->render_type                        = rtype;
1659         obj->contains_type              = -1;
1660
1661         obj->lifeleft                           = IMMORTAL_TIME;                //assume immortal
1662         obj->attached_obj                       = -1;
1663
1664         if (obj->control_type == CT_POWERUP)
1665                 obj->ctype.powerup_info.count = 1;
1666
1667         // Init physics info for this object
1668         if (obj->movement_type == MT_PHYSICS) {
1669
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);
1674
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;
1680         }
1681
1682         if (obj->render_type == RT_POLYOBJ)
1683                 obj->rtype.pobj_info.tmap_override = -1;
1684
1685         obj->shields                            = 20*F1_0;
1686
1687         segnum = find_point_seg(pos,segnum);            //find correct segment
1688
1689         Assert(segnum!=-1);
1690
1691         obj->segnum = -1;                                       //set to zero by memset, above
1692         obj_link(objnum,segnum);
1693
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;
1701         }
1702
1703         if (obj->control_type == CT_POWERUP)
1704                 obj->ctype.powerup_info.creation_time = GameTime;
1705
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;
1708
1709         #ifndef NDEBUG
1710         if (print_object_info)  
1711                 mprintf( (0, "Created object %d of type %d\n", objnum, obj->type ));
1712         #endif
1713
1714         if (obj->type == OBJ_DEBRIS)
1715                 Debris_object_count++;
1716
1717         return objnum;
1718 }
1719
1720 #ifdef EDITOR
1721 //create a copy of an object. returns new object number
1722 int obj_create_copy(int objnum, vms_vector *new_pos, int newsegnum)
1723 {
1724         int newobjnum;
1725         object *obj;
1726
1727         // Find next free object
1728         newobjnum = obj_allocate();
1729
1730         if (newobjnum == -1)
1731                 return -1;
1732
1733         obj = &Objects[newobjnum];
1734
1735         *obj = Objects[objnum];
1736
1737         obj->pos = obj->last_pos = *new_pos;
1738
1739         obj->next = obj->prev = obj->segnum = -1;
1740
1741         obj_link(newobjnum,newsegnum);
1742
1743         obj->signature                          = Object_next_signature++;
1744
1745         //we probably should initialize sub-structures here
1746
1747         return newobjnum;
1748
1749 }
1750 #endif
1751
1752 extern void newdemo_record_guided_end();
1753
1754 //remove object from the world
1755 void obj_delete(int objnum)
1756 {
1757         int pnum;
1758         object *obj = &Objects[objnum];
1759
1760         Assert(objnum != -1);
1761         Assert(objnum != 0 );
1762         Assert(obj->type != OBJ_NONE);
1763         Assert(obj != ConsoleObject);
1764
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));
1768
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;
1772                 }
1773                 else if (Newdemo_state==ND_STATE_RECORDING)
1774                         newdemo_record_guided_end();
1775                 
1776         }
1777
1778         if (obj == Viewer)              //deleting the viewer?
1779                 Viewer = ConsoleObject;                                         //..make the player the viewer
1780
1781         if (obj->flags & OF_ATTACHED)           //detach this from object
1782                 obj_detach_one(obj);
1783
1784         if (obj->attached_obj != -1)            //detach all objects from this
1785                 obj_detach_all(obj);
1786
1787         #if !defined(NDEBUG) && !defined(NMONO)
1788         if (print_object_info) mprintf( (0, "Deleting object %d of type %d\n", objnum, Objects[objnum].type ));
1789         #endif
1790
1791         if (obj->type == OBJ_DEBRIS)
1792                 Debris_object_count--;
1793
1794         obj_unlink(objnum);
1795
1796         Assert(Objects[0].next != 0);
1797
1798         obj->type = OBJ_NONE;           //unused!
1799         obj->signature = -1;
1800         obj->segnum=-1;                         // zero it!
1801
1802         obj_free(objnum);
1803 }
1804
1805 #define DEATH_SEQUENCE_LENGTH                   (F1_0*5)
1806 #define DEATH_SEQUENCE_EXPLODE_TIME     (F1_0*2)
1807
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;
1817
1818 ubyte           Control_type_save, Render_type_save;
1819 extern  int Cockpit_mode_save;  //set while in letterbox or rear view, or -1
1820
1821 //      ------------------------------------------------------------------------------------------------------------------
1822 void dead_player_end(void)
1823 {
1824         if (!Player_is_dead)
1825                 return;
1826
1827         if (Newdemo_state == ND_STATE_RECORDING)
1828                 newdemo_record_restore_cockpit();
1829
1830         Player_is_dead = 0;
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;
1839
1840         Assert((Control_type_save == CT_FLYING) || (Control_type_save == CT_SLEW));
1841
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;
1846
1847 }
1848
1849 //      ------------------------------------------------------------------------------------------------------------------
1850 //      Camera is less than size of player away from
1851 void set_camera_pos(vms_vector *camera_pos, object *objp)
1852 {
1853         int     count = 0;
1854         fix     camera_player_dist;
1855         fix     far_scale;
1856
1857         camera_player_dist = vm_vec_dist_quick(camera_pos, &objp->pos);
1858
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;
1862                 fvi_query       fq;
1863                 fvi_info                hit_data;
1864                 vms_vector      local_p1;
1865
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;
1869
1870                 hit_data.hit_type = HIT_WALL;
1871                 far_scale = F1_0;
1872
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);
1877
1878                         fq.p0 = &objp->pos;
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.
1882
1883                         fq.p1 = &local_p1;
1884                         fq.startseg = objp->segnum;
1885                         fq.rad = 0;
1886                         fq.thisobjnum = objp-Objects;
1887                         fq.ignore_obj_list = NULL;
1888                         fq.flags = 0;
1889                         find_vector_intersection( &fq, &hit_data);
1890
1891                         if (hit_data.hit_type == HIT_NONE) {
1892                                 *camera_pos = closer_p1;
1893                         } else {
1894                                 make_random_vector(&player_camera_vec);
1895                                 far_scale = 3*F1_0/2;
1896                         }
1897                 }
1898         }
1899 }
1900
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;
1905
1906 //      ------------------------------------------------------------------------------------------------------------------
1907 void dead_player_frame(void)
1908 {
1909         fix     time_dead;
1910         vms_vector      fvec;
1911
1912         if (Player_is_dead) {
1913                 time_dead = GameTime - Player_time_of_death;
1914
1915                 //      If unable to create camera at time of death, create now.
1916                 if (Dead_player_camera == Viewer_save) {
1917                         int             objnum;
1918                         object  *player = &Objects[Players[Player_num].objnum];
1919
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);
1922
1923                         mprintf((0, "Creating new dead player camera.\n"));
1924                         if (objnum != -1)
1925                                 Viewer = Dead_player_camera = &Objects[objnum];
1926                         else {
1927                                 mprintf((1, "Can't create dead player camera.\n"));
1928                                 Int3();
1929                         }
1930                 }               
1931
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;
1935
1936                 Camera_to_player_dist_goal = min(time_dead*8, F1_0*20) + ConsoleObject->size;
1937
1938                 set_camera_pos(&Dead_player_camera->pos, ConsoleObject);
1939
1940                 // the following line uncommented by WraithX, 4-12-00
1941                 if (time_dead < DEATH_SEQUENCE_EXPLODE_TIME + F1_0 * 2)
1942                 {
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;
1946
1947                         // the following "if" added by WraithX to get rid of camera "wiggle"
1948                         if (Dead_player_camera->mtype.phys_info.flags & PF_WIGGLE)
1949                         {
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
1952
1953                 // the following line uncommented by WraithX, 4-12-00
1954                 }
1955                 else
1956                 {
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
1961                 }
1962                 // end addition by WX
1963
1964                 if (time_dead > DEATH_SEQUENCE_EXPLODE_TIME) {
1965                         if (!Player_exploded) {
1966
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);
1971                         else
1972                                 HUD_init_message(TXT_SHIP_DESTROYED_0);
1973
1974                                 #ifdef TACTILE
1975                                         if (TactileStick)
1976                                          {
1977                                           ClearForces();
1978                                          }
1979                                 #endif
1980                                 Player_exploded = 1;
1981 #ifdef NETWORK
1982                                 if (Game_mode & GM_NETWORK)
1983                                  {
1984                                         AdjustMineSpawn ();
1985                                         multi_cap_objects();
1986                                  }
1987 #endif
1988                                 
1989                                 drop_player_eggs(ConsoleObject);
1990                                 Player_eggs_dropped = 1;
1991                                 #ifdef NETWORK
1992                                 if (Game_mode & GM_MULTI)
1993                                 {
1994                                         //multi_send_position(Players[Player_num].objnum);
1995                                         multi_send_player_explode(MULTI_PLAYER_EXPLODE);
1996                                 }
1997                                 #endif
1998
1999                                 explode_badass_player(ConsoleObject);
2000
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;
2007                         }
2008                 } else {
2009                         if (d_rand() < FrameTime*4) {
2010                                 #ifdef NETWORK
2011                                 if (Game_mode & GM_MULTI)
2012                                         multi_send_create_explosion(Player_num);
2013                                 #endif
2014                                 create_small_fireball_on_object(ConsoleObject, F1_0, 1);
2015                         }
2016                 }
2017
2018
2019                 if (Death_sequence_aborted) { //time_dead > DEATH_SEQUENCE_LENGTH) {
2020                         if (!Player_eggs_dropped) {
2021                         
2022 #ifdef NETWORK
2023                                 if (Game_mode & GM_NETWORK)
2024                                  {
2025                                         AdjustMineSpawn();
2026                                         multi_cap_objects();
2027                                  }
2028 #endif
2029                                 
2030                                 drop_player_eggs(ConsoleObject);
2031                                 Player_eggs_dropped = 1;
2032                                 #ifdef NETWORK
2033                                 if (Game_mode & GM_MULTI)
2034                                 {
2035                                         //multi_send_position(Players[Player_num].objnum);
2036                                         multi_send_player_explode(MULTI_PLAYER_EXPLODE);
2037                                 }
2038                                 #endif
2039                         }
2040
2041                         DoPlayerDead();         //kill_player();
2042                 }
2043         }
2044 }
2045
2046
2047 void AdjustMineSpawn()
2048  {
2049    if (!(Game_mode & GM_NETWORK))
2050                 return;  // No need for this function in any other mode
2051
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;
2057  }
2058
2059
2060
2061 int Killed_in_frame = -1;
2062 short Killed_objnum = -1;
2063 extern char Multi_killed_yourself;
2064
2065 //      ------------------------------------------------------------------------------------------------------------------
2066 void start_player_death_sequence(object *player)
2067 {
2068         int     objnum;
2069
2070         Assert(player == ConsoleObject);
2071         if ((Player_is_dead != 0) || (Dead_player_camera != NULL))
2072                 return;
2073
2074         //Assert(Player_is_dead == 0);
2075         //Assert(Dead_player_camera == NULL);
2076
2077         reset_rear_view();
2078
2079         if (!(Game_mode & GM_MULTI))
2080                 HUD_clear_messages();
2081
2082         Killed_in_frame = FrameCount;
2083         Killed_objnum = player-Objects;
2084         Death_sequence_aborted = 0;
2085
2086         #ifdef NETWORK
2087         if (Game_mode & GM_MULTI)
2088         {
2089                 multi_send_kill(Players[Player_num].objnum);
2090
2091 //              If Hoard, increase number of orbs by 1
2092 //    Only if you haven't killed yourself
2093 //              This prevents cheating
2094
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]++;
2099         
2100         }
2101         #endif
2102         
2103         PaletteRedAdd = 40;
2104         Player_is_dead = 1;
2105    #ifdef TACTILE
2106     if (TactileStick)
2107           Buffeting (70);
2108         #endif
2109
2110         //Players[Player_num].flags &= ~(PLAYER_FLAGS_AFTERBURNER);
2111
2112         vm_vec_zero(&player->mtype.phys_info.rotthrust);
2113         vm_vec_zero(&player->mtype.phys_info.thrust);
2114
2115         Player_time_of_death = GameTime;
2116
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;
2120         if (objnum != -1)
2121                 Viewer = Dead_player_camera = &Objects[objnum];
2122         else {
2123                 mprintf((1, "Can't create dead player camera.\n"));
2124                 Int3();
2125                 Dead_player_camera = Viewer;
2126         }
2127
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();
2133
2134         Player_flags_save = player->flags;
2135         Control_type_save = player->control_type;
2136         Render_type_save = player->render_type;
2137
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;
2142
2143         PALETTE_FLASH_SET(0,0,0);
2144 }
2145
2146 //      ------------------------------------------------------------------------------------------------------------------
2147 void obj_delete_all_that_should_be_dead()
2148 {
2149         int i;
2150         object *objp;
2151         int             local_dead_player_object=-1;
2152
2153         // Move all objects
2154         objp = Objects;
2155
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;
2164                                         } else
2165                                                 Int3(); //      Contact Mike: Illegal, killed player twice in this frame!
2166                                                                         // Ok to continue, won't start death sequence again!
2167                                         // kill_player();
2168                                 }
2169                         } else {                                        
2170                                 obj_delete(i);
2171                         }
2172                 }
2173                 objp++;
2174         }
2175 }
2176
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)
2180 {
2181
2182         Assert((objnum >= 0) && (objnum <= Highest_object_index));
2183         Assert((newsegnum <= Highest_segment_index) && (newsegnum >= 0));
2184
2185         obj_unlink(objnum);
2186
2187         obj_link(objnum,newsegnum);
2188         
2189 #ifndef NDEBUG
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"));
2192 #endif
2193 }
2194
2195 //process a continuously-spinning object
2196 void
2197 spin_object(object *obj)
2198 {
2199         vms_angvec rotangs;
2200         vms_matrix rotmat, new_pm;
2201
2202         Assert(obj->movement_type == MT_SPINNING);
2203
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);
2207
2208         vm_angles_2_matrix(&rotmat,&rotangs);
2209
2210         vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat);
2211         obj->orient = new_pm;
2212
2213         check_and_fix_matrix(&obj->orient);
2214 }
2215
2216 int Drop_afterburner_blob_flag;         //ugly hack
2217 extern void multi_send_drop_blobs(char);
2218 extern void fuelcen_check_for_goal (segment *);
2219
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);
2223
2224 //      Time at which this object last created afterburner blobs.
2225 fix     Last_afterburner_time[MAX_OBJECTS];
2226
2227 //--------------------------------------------------------------------
2228 //move an object for the current frame
2229 void object_move_one( object * obj )
2230 {
2231
2232         #ifndef DEMO_ONLY
2233
2234         int     previous_segment = obj->segnum;
2235
2236         obj->last_pos = obj->pos;                       // Save the current position
2237
2238         if ((obj->type==OBJ_PLAYER) && (Player_num==obj->id))   {
2239                 fix fuel, shields;
2240                 
2241 #ifdef NETWORK
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]);
2246 #endif
2247
2248                 fuel=fuelcen_give_fuel( &Segments[obj->segnum], INITIAL_ENERGY-Players[Player_num].energy );
2249                 if (fuel > 0 )  {
2250                         Players[Player_num].energy += fuel;
2251                 }
2252
2253                 shields = repaircen_give_shields( &Segments[obj->segnum], INITIAL_ENERGY-Players[Player_num].energy );
2254                 if (shields > 0) {
2255                         Players[Player_num].shields += shields;
2256                 }
2257         }
2258
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
2264         }
2265
2266         Drop_afterburner_blob_flag = 0;
2267
2268         switch (obj->control_type) {
2269
2270                 case CT_NONE: break;
2271
2272                 case CT_FLYING:
2273
2274                         #if !defined(NDEBUG) && !defined(NMONO)
2275                         if (print_object_info>1) mprintf( (0, "Moving player object #%d\n", obj-Objects ));
2276                         #endif
2277
2278                         read_flying_controls( obj );
2279
2280                         break;
2281
2282                 case CT_REPAIRCEN: Int3();      // -- hey! these are no longer supported!! -- do_repair_sequence(obj); break;
2283
2284                 case CT_POWERUP: do_powerup_frame(obj); break;
2285         
2286                 case CT_MORPH:                  //morph implies AI
2287                         do_morph_frame(obj);
2288                         //NOTE: FALLS INTO AI HERE!!!!
2289
2290                 case CT_AI:
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 ));
2296 #endif
2297                         do_ai_frame(obj);
2298                         break;
2299
2300                 case CT_WEAPON:         Laser_do_weapon_sequence(obj); break;
2301                 case CT_EXPLOSION:      do_explosion_sequence(obj); break;
2302
2303                 #ifndef RELEASE
2304                 case CT_SLEW:
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
2309                         }
2310                         slew_frame(0 );         // Does velocity addition for us.
2311                         break;
2312                 #endif
2313
2314
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!!!
2319 //                      break;
2320
2321                 case CT_DEBRIS: do_debris_frame(obj); break;
2322
2323                 case CT_LIGHT: break;           //doesn't do anything
2324
2325                 case CT_REMOTE: break;          //movement is handled in com_process_input
2326
2327                 case CT_CNTRLCEN: do_controlcen_frame(obj); break;
2328
2329                 default:
2330
2331 #ifdef __DJGPP__
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);
2333 #else
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);
2335 #endif
2336
2337                         break;
2338
2339         }
2340
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);
2347         }
2348
2349         // the following ( if !dead) statement added by WraithX
2350         if (!Player_is_dead)
2351         {
2352         if (obj->type == OBJ_NONE || obj->flags&OF_SHOULD_BE_DEAD)
2353                 return;                 //object has been deleted
2354         }// end addition by WraithX
2355
2356         switch (obj->movement_type) {
2357
2358                 case MT_NONE:                   break;                                                          //this doesn't move
2359
2360                 case MT_PHYSICS:                do_physics_sim(obj);    break;  //move by physics
2361
2362                 case MT_SPINNING:               spin_object(obj); break;
2363
2364         }
2365
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)  {
2369
2370                 if (previous_segment != obj->segnum) {
2371                         int     connect_side,i;
2372 #ifdef NETWORK
2373                         int     old_level = Current_level_num;
2374 #endif
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);
2379                                 #ifndef NDEBUG
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();
2383                                 }
2384                                 #endif
2385
2386                                 //maybe we've gone on to the next level.  if so, bail!
2387 #ifdef NETWORK
2388                                 if (Current_level_num != old_level)
2389                                         return;
2390 #endif
2391                         }
2392                 }
2393
2394                 {
2395                         int sidemask,under_lavafall=0;
2396                         static int lavafall_hiss_playing[MAX_PLAYERS]={0};
2397
2398                         sidemask = get_seg_masks(&obj->pos, obj->segnum, obj->size, __FILE__, __LINE__).sidemask;
2399                         if (sidemask) {
2400                                 int sidenum,bit,wall_num;
2401         
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) {
2404                                                 int type;
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;
2407                                                         under_lavafall = 1;
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;
2411                                                         }
2412                                                 }
2413                                         }
2414                         }
2415         
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;
2419                         }
2420                 }
2421         }
2422
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) {
2426                         int     connect_side;
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;
2436                                 }
2437                         }
2438                 }
2439         }
2440
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
2444 #ifdef NETWORK
2445                 if (Game_mode & GM_MULTI)
2446                         multi_send_drop_blobs(Player_num);
2447 #endif
2448                 Drop_afterburner_blob_flag = 0;
2449         }
2450
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;
2455
2456                 if (vel > F1_0*200)
2457                         delay = F1_0/16;
2458                 else if (vel > F1_0*40)
2459                         delay = fixdiv(F1_0*13,vel);
2460                 else
2461                         delay = F1_0/4;
2462
2463                 lifetime = (delay * 3)/2;
2464                 if (!(Game_mode & GM_MULTI)) {
2465                         delay /= 2;
2466                         lifetime *= 2;
2467                 }
2468
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;
2472                 }
2473         }
2474
2475         #else
2476                 obj++;          //kill warning
2477         #endif          //DEMO_ONLY
2478 }
2479
2480 int     Max_used_objects = MAX_OBJECTS - 20;
2481
2482 //--------------------------------------------------------------------
2483 //move all objects for the current frame
2484 void object_move_all()
2485 {
2486         int i;
2487         object *objp;
2488
2489 // -- mprintf((0, "Frame %i: %i/%i objects used.\n", FrameCount, num_objects, MAX_OBJECTS));
2490
2491 //      check_duplicate_objects();
2492 //      remove_incorrect_objects();
2493
2494         if (Highest_object_index > Max_used_objects)
2495                 free_object_slots(Max_used_objects);            //      Free all possible object slots.
2496
2497         obj_delete_all_that_should_be_dead();
2498
2499         if (Auto_leveling_on)
2500                 ConsoleObject->mtype.phys_info.flags |= PF_LEVELLING;
2501         else
2502                 ConsoleObject->mtype.phys_info.flags &= ~PF_LEVELLING;
2503
2504         // Move all objects
2505         objp = Objects;
2506
2507         #ifndef DEMO_ONLY
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 );
2511                 }
2512                 objp++;
2513         }
2514         #else
2515                 i=0;    //kill warning
2516         #endif
2517
2518 //      check_duplicate_objects();
2519 //      remove_incorrect_objects();
2520
2521 }
2522
2523
2524 //--unused-- // -----------------------------------------------------------
2525 //--unused-- // Moved here from eobject.c on 02/09/94 by MK.
2526 //--unused-- int find_last_obj(int i)
2527 //--unused-- {
2528 //--unused--    for (i=MAX_OBJECTS;--i>=0;)
2529 //--unused--            if (Objects[i].type != OBJ_NONE) break;
2530 //--unused--
2531 //--unused--    return i;
2532 //--unused--
2533 //--unused-- }
2534
2535
2536 //make object array non-sparse
2537 void compress_objects(void)
2538 {
2539         int start_i;    //,last_i;
2540
2541         //last_i = find_last_obj(MAX_OBJECTS);
2542
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++)
2546
2547                 if (Objects[start_i].type == OBJ_NONE) {
2548
2549                         int     segnum_copy;
2550
2551                         segnum_copy = Objects[Highest_object_index].segnum;
2552
2553                         obj_unlink(Highest_object_index);
2554
2555                         Objects[start_i] = Objects[Highest_object_index];
2556
2557                         #ifdef EDITOR
2558                         if (Cur_object_index == Highest_object_index)
2559                                 Cur_object_index = start_i;
2560                         #endif
2561
2562                         Objects[Highest_object_index].type = OBJ_NONE;
2563
2564                         obj_link(start_i,segnum_copy);
2565
2566                         while (Objects[--Highest_object_index].type == OBJ_NONE);
2567
2568                         //last_i = find_last_obj(last_i);
2569                         
2570                 }
2571
2572         reset_objects(num_objects);
2573
2574 }
2575
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)
2579 {
2580         int i;
2581
2582         num_objects = n_objs;
2583
2584         Assert(num_objects>0);
2585
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;
2590         }
2591
2592         Highest_object_index = num_objects-1;
2593
2594         Debris_object_count = 0;
2595 }
2596
2597 //Tries to find a segment for an object, using find_point_seg()
2598 int find_object_seg(object * obj )
2599 {
2600         return find_point_seg(&obj->pos,obj->segnum);
2601 }
2602
2603
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 )
2608 {
2609         int newseg;
2610
2611         newseg = find_object_seg(obj);
2612
2613         if (newseg == -1)
2614                 return 0;
2615
2616         if ( newseg != obj->segnum )
2617                 obj_relink(obj-Objects, newseg );
2618
2619         return 1;
2620 }
2621
2622
2623 //go through all objects and make sure they have the correct segment numbers
2624 void
2625 fix_object_segs()
2626 {
2627         int i;
2628
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"));
2633                                 Int3();
2634                                 compute_segment_center(&Objects[i].pos,&Segments[Objects[i].segnum]);
2635                         }
2636 }
2637
2638
2639 //--unused-- void object_use_new_object_list( object * new_list )
2640 //--unused-- {
2641 //--unused--    int i, segnum;
2642 //--unused--    object *obj;
2643 //--unused--
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;
2647 //--unused--    }
2648 //--unused--    // Then, erase all the objects
2649 //--unused--    reset_objects(1);
2650 //--unused--
2651 //--unused--    // Fill in the object array
2652 //--unused--    memcpy( Objects, new_list, sizeof(object)*MAX_OBJECTS );
2653 //--unused--
2654 //--unused--    Highest_object_index=-1;
2655 //--unused--
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;
2667 //--unused--            }
2668 //--unused--    }
2669 //--unused--    
2670 //--unused-- }
2671
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)
2676 {
2677         int objnum;
2678         object *obj;
2679
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)) {
2686
2687                         #ifndef NDEBUG
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));
2690                         #endif
2691                         obj_delete(objnum);
2692                 }
2693                 #ifndef NDEBUG
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));
2696                 #endif
2697 }
2698
2699 //attaches an object, such as a fireball, to another object, such as a robot
2700 void obj_attach(object *parent,object *sub)
2701 {
2702         Assert(sub->type == OBJ_FIREBALL);
2703         Assert(sub->control_type == CT_EXPLOSION);
2704
2705         Assert(sub->ctype.expl_info.next_attach==-1);
2706         Assert(sub->ctype.expl_info.prev_attach==-1);
2707
2708         Assert(parent->attached_obj==-1 || Objects[parent->attached_obj].ctype.expl_info.prev_attach==-1);
2709
2710         sub->ctype.expl_info.next_attach = parent->attached_obj;
2711
2712         if (sub->ctype.expl_info.next_attach != -1)
2713                 Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach = sub-Objects;
2714
2715         parent->attached_obj = sub-Objects;
2716
2717         sub->ctype.expl_info.attach_parent = parent-Objects;
2718         sub->flags |= OF_ATTACHED;
2719
2720         Assert(sub->ctype.expl_info.next_attach != sub-Objects);
2721         Assert(sub->ctype.expl_info.prev_attach != sub-Objects);
2722 }
2723
2724 //dettaches one object
2725 void obj_detach_one(object *sub)
2726 {
2727         Assert(sub->flags & OF_ATTACHED);
2728         Assert(sub->ctype.expl_info.attach_parent != -1);
2729
2730         if ((Objects[sub->ctype.expl_info.attach_parent].type == OBJ_NONE) || (Objects[sub->ctype.expl_info.attach_parent].attached_obj == -1))
2731         {
2732                 sub->flags &= ~OF_ATTACHED;
2733                 return;
2734         }
2735
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;
2739         }
2740
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;
2744         }
2745         else {
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;
2748         }
2749
2750         sub->ctype.expl_info.next_attach = sub->ctype.expl_info.prev_attach = -1;
2751         sub->flags &= ~OF_ATTACHED;
2752
2753 }
2754
2755 //dettaches all objects from this object
2756 void obj_detach_all(object *parent)
2757 {
2758         while (parent->attached_obj != -1)
2759                 obj_detach_one(&Objects[parent->attached_obj]);
2760 }
2761
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)
2764 {
2765         int objnum;
2766
2767         Assert(Marker_model_num != -1);
2768
2769         objnum = obj_create(OBJ_MARKER, marker_num, segnum, pos, orient, Polygon_models[Marker_model_num].rad, CT_NONE, MT_NONE, RT_POLYOBJ);
2770
2771         if (objnum >= 0) {
2772                 object *obj = &Objects[objnum];
2773
2774                 obj->rtype.pobj_info.model_num = Marker_model_num;
2775
2776                 vm_vec_copy_scale(&obj->mtype.spin_rate,&obj->orient.uvec,F1_0/2);
2777
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;
2780         }
2781
2782         return objnum;  
2783 }
2784
2785 extern int Ai_last_missile_camera;
2786
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)
2790 {
2791         int     i;
2792
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"));
2796                 return;
2797         }
2798
2799         Ai_last_missile_camera = viewer-Objects;
2800
2801         for (i=0; i<Window_rendered_data[window_num].num_objects; i++) {
2802                 int     objnum;
2803                 object *objp;
2804                 int     fcval = FrameCount & 3;
2805
2806                 objnum = Window_rendered_data[window_num].rendered_objects[i];
2807                 if ((objnum & 3) == fcval) {
2808                         objp = &Objects[objnum];
2809         
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;
2818                                         }
2819                                 }
2820                         }
2821                 }
2822         }
2823 }
2824
2825
2826
2827
2828
2829