]> icculus.org git repositories - btb/d2x.git/blob - main/object.c
fix level number position
[btb/d2x.git] / main / object.c
1 /* $Id: object.c,v 1.9 2003-10-04 03:14:47 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).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                         objnum = obj_create(OBJ_CAMERA, 0, player->segnum, &player->pos, &player->orient, 0, CT_NONE, MT_NONE, RT_NONE);
1921
1922                         mprintf((0, "Creating new dead player camera.\n"));
1923                         if (objnum != -1)
1924                                 Viewer = Dead_player_camera = &Objects[objnum];
1925                         else {
1926                                 mprintf((1, "Can't create dead player camera.\n"));
1927                                 Int3();
1928                         }
1929                 }               
1930
1931                 ConsoleObject->mtype.phys_info.rotvel.x = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/4;
1932                 ConsoleObject->mtype.phys_info.rotvel.y = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/2;
1933                 ConsoleObject->mtype.phys_info.rotvel.z = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/3;
1934
1935                 Camera_to_player_dist_goal = min(time_dead*8, F1_0*20) + ConsoleObject->size;
1936
1937                 set_camera_pos(&Dead_player_camera->pos, ConsoleObject);
1938
1939 //              if (time_dead < DEATH_SEQUENCE_EXPLODE_TIME+F1_0*2) {
1940                         vm_vec_sub(&fvec, &ConsoleObject->pos, &Dead_player_camera->pos);
1941                         vm_vector_2_matrix(&Dead_player_camera->orient, &fvec, NULL, NULL);
1942 //              } else {
1943 //                      Dead_player_camera->movement_type = MT_PHYSICS;
1944 //                      Dead_player_camera->mtype.phys_info.rotvel.y = F1_0/8;
1945 //              }
1946
1947                 if (time_dead > DEATH_SEQUENCE_EXPLODE_TIME) {
1948                         if (!Player_exploded) {
1949
1950                         if (Players[Player_num].hostages_on_board > 1)
1951                                 HUD_init_message(TXT_SHIP_DESTROYED_2, Players[Player_num].hostages_on_board);
1952                         else if (Players[Player_num].hostages_on_board == 1)
1953                                 HUD_init_message(TXT_SHIP_DESTROYED_1);
1954                         else
1955                                 HUD_init_message(TXT_SHIP_DESTROYED_0);
1956
1957                                 #ifdef TACTILE
1958                                         if (TactileStick)
1959                                          {
1960                                           ClearForces();
1961                                          }
1962                                 #endif
1963                                 Player_exploded = 1;
1964 #ifdef NETWORK
1965                                 if (Game_mode & GM_NETWORK)
1966                                  {
1967                                         AdjustMineSpawn ();
1968                                         multi_cap_objects();
1969                                  }
1970 #endif
1971                                 
1972                                 drop_player_eggs(ConsoleObject);
1973                                 Player_eggs_dropped = 1;
1974                                 #ifdef NETWORK
1975                                 if (Game_mode & GM_MULTI)
1976                                 {
1977                                         //multi_send_position(Players[Player_num].objnum);
1978                                         multi_send_player_explode(MULTI_PLAYER_EXPLODE);
1979                                 }
1980                                 #endif
1981
1982                                 explode_badass_player(ConsoleObject);
1983
1984                                 //is this next line needed, given the badass call above?
1985                                 explode_object(ConsoleObject,0);
1986                                 ConsoleObject->flags &= ~OF_SHOULD_BE_DEAD;             //don't really kill player
1987                                 ConsoleObject->render_type = RT_NONE;                           //..just make him disappear
1988                                 ConsoleObject->type = OBJ_GHOST;                                                //..and kill intersections
1989                                 Players[Player_num].flags &= ~PLAYER_FLAGS_HEADLIGHT_ON;
1990                         }
1991                 } else {
1992                         if (d_rand() < FrameTime*4) {
1993                                 #ifdef NETWORK
1994                                 if (Game_mode & GM_MULTI)
1995                                         multi_send_create_explosion(Player_num);
1996                                 #endif
1997                                 create_small_fireball_on_object(ConsoleObject, F1_0, 1);
1998                         }
1999                 }
2000
2001
2002                 if (Death_sequence_aborted) { //time_dead > DEATH_SEQUENCE_LENGTH) {
2003                         if (!Player_eggs_dropped) {
2004                         
2005 #ifdef NETWORK
2006                                 if (Game_mode & GM_NETWORK)
2007                                  {
2008                                         AdjustMineSpawn();
2009                                         multi_cap_objects();
2010                                  }
2011 #endif
2012                                 
2013                                 drop_player_eggs(ConsoleObject);
2014                                 Player_eggs_dropped = 1;
2015                                 #ifdef NETWORK
2016                                 if (Game_mode & GM_MULTI)
2017                                 {
2018                                         //multi_send_position(Players[Player_num].objnum);
2019                                         multi_send_player_explode(MULTI_PLAYER_EXPLODE);
2020                                 }
2021                                 #endif
2022                         }
2023
2024                         DoPlayerDead();         //kill_player();
2025                 }
2026         }
2027 }
2028
2029
2030 void AdjustMineSpawn()
2031  {
2032    if (!(Game_mode & GM_NETWORK))
2033                 return;  // No need for this function in any other mode
2034
2035    if (!(Game_mode & GM_HOARD))
2036                 Players[Player_num].secondary_ammo[PROXIMITY_INDEX]+=Proximity_dropped;
2037    Players[Player_num].secondary_ammo[SMART_MINE_INDEX]+=Smartmines_dropped;
2038         Proximity_dropped=0;
2039         Smartmines_dropped=0;
2040  }
2041
2042
2043
2044 int Killed_in_frame = -1;
2045 short Killed_objnum = -1;
2046 extern char Multi_killed_yourself;
2047
2048 //      ------------------------------------------------------------------------------------------------------------------
2049 void start_player_death_sequence(object *player)
2050 {
2051         int     objnum;
2052
2053         Assert(player == ConsoleObject);
2054         if ((Player_is_dead != 0) || (Dead_player_camera != NULL))
2055                 return;
2056
2057         //Assert(Player_is_dead == 0);
2058         //Assert(Dead_player_camera == NULL);
2059
2060         reset_rear_view();
2061
2062         if (!(Game_mode & GM_MULTI))
2063                 HUD_clear_messages();
2064
2065         Killed_in_frame = FrameCount;
2066         Killed_objnum = player-Objects;
2067         Death_sequence_aborted = 0;
2068
2069         #ifdef NETWORK
2070         if (Game_mode & GM_MULTI)
2071         {
2072                 multi_send_kill(Players[Player_num].objnum);
2073
2074 //              If Hoard, increase number of orbs by 1
2075 //    Only if you haven't killed yourself
2076 //              This prevents cheating
2077
2078                 if (Game_mode & GM_HOARD)
2079                  if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]<12)
2080                         if (!Multi_killed_yourself)
2081                                 Players[Player_num].secondary_ammo[PROXIMITY_INDEX]++;
2082         
2083         }
2084         #endif
2085         
2086         PaletteRedAdd = 40;
2087         Player_is_dead = 1;
2088    #ifdef TACTILE
2089     if (TactileStick)
2090           Buffeting (70);
2091         #endif
2092
2093         //Players[Player_num].flags &= ~(PLAYER_FLAGS_AFTERBURNER);
2094
2095         vm_vec_zero(&player->mtype.phys_info.rotthrust);
2096         vm_vec_zero(&player->mtype.phys_info.thrust);
2097
2098         Player_time_of_death = GameTime;
2099
2100         objnum = obj_create(OBJ_CAMERA, 0, player->segnum, &player->pos, &player->orient, 0, CT_NONE, MT_NONE, RT_NONE);
2101         Viewer_save = Viewer;
2102         if (objnum != -1)
2103                 Viewer = Dead_player_camera = &Objects[objnum];
2104         else {
2105                 mprintf((1, "Can't create dead player camera.\n"));
2106                 Int3();
2107                 Dead_player_camera = Viewer;
2108         }
2109
2110         if (Cockpit_mode_save == -1)            //if not already saved
2111                 Cockpit_mode_save = Cockpit_mode;
2112         select_cockpit(CM_LETTERBOX);
2113         if (Newdemo_state == ND_STATE_RECORDING)
2114                 newdemo_record_letterbox();
2115
2116         Player_flags_save = player->flags;
2117         Control_type_save = player->control_type;
2118         Render_type_save = player->render_type;
2119
2120         player->flags &= ~OF_SHOULD_BE_DEAD;
2121 //      Players[Player_num].flags |= PLAYER_FLAGS_INVULNERABLE;
2122         player->control_type = CT_NONE;
2123         player->shields = F1_0*1000;
2124
2125         PALETTE_FLASH_SET(0,0,0);
2126 }
2127
2128 //      ------------------------------------------------------------------------------------------------------------------
2129 void obj_delete_all_that_should_be_dead()
2130 {
2131         int i;
2132         object *objp;
2133         int             local_dead_player_object=-1;
2134
2135         // Move all objects
2136         objp = Objects;
2137
2138         for (i=0;i<=Highest_object_index;i++) {
2139                 if ((objp->type!=OBJ_NONE) && (objp->flags&OF_SHOULD_BE_DEAD) ) {
2140                         Assert(!(objp->type==OBJ_FIREBALL && objp->ctype.expl_info.delete_time!=-1));
2141                         if (objp->type==OBJ_PLAYER) {
2142                                 if ( objp->id == Player_num ) {
2143                                         if (local_dead_player_object == -1) {
2144                                                 start_player_death_sequence(objp);
2145                                                 local_dead_player_object = objp-Objects;
2146                                         } else
2147                                                 Int3(); //      Contact Mike: Illegal, killed player twice in this frame!
2148                                                                         // Ok to continue, won't start death sequence again!
2149                                         // kill_player();
2150                                 }
2151                         } else {                                        
2152                                 obj_delete(i);
2153                         }
2154                 }
2155                 objp++;
2156         }
2157 }
2158
2159 //when an object has moved into a new segment, this function unlinks it
2160 //from its old segment, and links it into the new segment
2161 void obj_relink(int objnum,int newsegnum)
2162 {
2163
2164         Assert((objnum >= 0) && (objnum <= Highest_object_index));
2165         Assert((newsegnum <= Highest_segment_index) && (newsegnum >= 0));
2166
2167         obj_unlink(objnum);
2168
2169         obj_link(objnum,newsegnum);
2170         
2171 #ifndef NDEBUG
2172         if (get_seg_masks(&Objects[objnum].pos,Objects[objnum].segnum,0).centermask!=0)
2173                 mprintf((1, "obj_relink violates seg masks.\n"));
2174 #endif
2175 }
2176
2177 //process a continuously-spinning object
2178 void
2179 spin_object(object *obj)
2180 {
2181         vms_angvec rotangs;
2182         vms_matrix rotmat, new_pm;
2183
2184         Assert(obj->movement_type == MT_SPINNING);
2185
2186         rotangs.p = fixmul(obj->mtype.spin_rate.x,FrameTime);
2187         rotangs.h = fixmul(obj->mtype.spin_rate.y,FrameTime);
2188         rotangs.b = fixmul(obj->mtype.spin_rate.z,FrameTime);
2189
2190         vm_angles_2_matrix(&rotmat,&rotangs);
2191
2192         vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat);
2193         obj->orient = new_pm;
2194
2195         check_and_fix_matrix(&obj->orient);
2196 }
2197
2198 int Drop_afterburner_blob_flag;         //ugly hack
2199 extern void multi_send_drop_blobs(char);
2200 extern void fuelcen_check_for_goal (segment *);
2201
2202 //see if wall is volatile, and if so, cause damage to player
2203 //returns true if player is in lava
2204 int check_volatile_wall(object *obj,int segnum,int sidenum,vms_vector *hitpt);
2205
2206 //      Time at which this object last created afterburner blobs.
2207 fix     Last_afterburner_time[MAX_OBJECTS];
2208
2209 //--------------------------------------------------------------------
2210 //move an object for the current frame
2211 void object_move_one( object * obj )
2212 {
2213
2214         #ifndef DEMO_ONLY
2215
2216         int     previous_segment = obj->segnum;
2217
2218         obj->last_pos = obj->pos;                       // Save the current position
2219
2220         if ((obj->type==OBJ_PLAYER) && (Player_num==obj->id))   {
2221                 fix fuel;
2222                 
2223 #ifdef NETWORK
2224       if (Game_mode & GM_CAPTURE)
2225                          fuelcen_check_for_goal (&Segments[obj->segnum]);
2226       if (Game_mode & GM_HOARD)
2227                          fuelcen_check_for_hoard_goal (&Segments[obj->segnum]);
2228 #endif
2229
2230                 fuel=fuelcen_give_fuel( &Segments[obj->segnum], INITIAL_ENERGY-Players[Player_num].energy );
2231                 if (fuel > 0 )  {
2232                         Players[Player_num].energy += fuel;
2233
2234                 }
2235         }
2236
2237         if (obj->lifeleft != IMMORTAL_TIME) {   //if not immortal...
2238                 //      Ok, this is a big hack by MK.
2239                 //      If you want an object to last for exactly one frame, then give it a lifeleft of ONE_FRAME_TIME.
2240                 if (obj->lifeleft != ONE_FRAME_TIME)
2241                         obj->lifeleft -= FrameTime;             //...inevitable countdown towards death
2242         }
2243
2244         Drop_afterburner_blob_flag = 0;
2245
2246         switch (obj->control_type) {
2247
2248                 case CT_NONE: break;
2249
2250                 case CT_FLYING:
2251
2252                         #if !defined(NDEBUG) && !defined(NMONO)
2253                         if (print_object_info>1) mprintf( (0, "Moving player object #%d\n", obj-Objects ));
2254                         #endif
2255
2256                         read_flying_controls( obj );
2257
2258                         break;
2259
2260                 case CT_REPAIRCEN: Int3();      // -- hey! these are no longer supported!! -- do_repair_sequence(obj); break;
2261
2262                 case CT_POWERUP: do_powerup_frame(obj); break;
2263         
2264                 case CT_MORPH:                  //morph implies AI
2265                         do_morph_frame(obj);
2266                         //NOTE: FALLS INTO AI HERE!!!!
2267
2268                 case CT_AI:
2269                         //NOTE LINK TO CT_MORPH ABOVE!!!
2270                         if (Game_suspended & SUSP_ROBOTS) return;
2271                         #if !defined(NDEBUG) && !defined(NMONO)
2272                         if (print_object_info>1) mprintf( (0, "AI: Moving robot object #%d\n",obj-Objects ));
2273                         #endif
2274                         do_ai_frame(obj);
2275                         break;
2276
2277                 case CT_WEAPON:         Laser_do_weapon_sequence(obj); break;
2278                 case CT_EXPLOSION:      do_explosion_sequence(obj); break;
2279
2280                 #ifndef RELEASE
2281                 case CT_SLEW:
2282                         if ( keyd_pressed[KEY_PAD5] ) slew_stop( obj );
2283                         if ( keyd_pressed[KEY_NUMLOCK] )                {
2284                                 slew_reset_orient( obj );
2285                                 * (ubyte *) 0x417 &= ~0x20;             //kill numlock
2286                         }
2287                         slew_frame(0 );         // Does velocity addition for us.
2288                         break;
2289                 #endif
2290
2291
2292 //              case CT_FLYTHROUGH:
2293 //                      do_flythrough(obj,0);                   // HACK:do_flythrough should operate on an object!!!!
2294 //                      //check_object_seg(obj);
2295 //                      return; // DON'T DO THE REST OF OBJECT STUFF SINCE THIS IS A SPECIAL CASE!!!
2296 //                      break;
2297
2298                 case CT_DEBRIS: do_debris_frame(obj); break;
2299
2300                 case CT_LIGHT: break;           //doesn't do anything
2301
2302                 case CT_REMOTE: break;          //movement is handled in com_process_input
2303
2304                 case CT_CNTRLCEN: do_controlcen_frame(obj); break;
2305
2306                 default:
2307
2308 #ifdef __DJGPP__
2309                         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);
2310 #else
2311                         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);
2312 #endif
2313
2314                         break;
2315
2316         }
2317
2318         if (obj->lifeleft < 0 ) {               // We died of old age
2319                 obj->flags |= OF_SHOULD_BE_DEAD;
2320                 if ( obj->type==OBJ_WEAPON && Weapon_info[obj->id].damage_radius )
2321                         explode_badass_weapon(obj,&obj->pos);
2322                 else if ( obj->type==OBJ_ROBOT) //make robots explode
2323                         explode_object(obj,0);
2324         }
2325
2326         if (obj->type == OBJ_NONE || obj->flags&OF_SHOULD_BE_DEAD)
2327                 return;                 //object has been deleted
2328
2329         switch (obj->movement_type) {
2330
2331                 case MT_NONE:                   break;                                                          //this doesn't move
2332
2333                 case MT_PHYSICS:                do_physics_sim(obj);    break;  //move by physics
2334
2335                 case MT_SPINNING:               spin_object(obj); break;
2336
2337         }
2338
2339         //      If player and moved to another segment, see if hit any triggers.
2340         // also check in player under a lavafall
2341         if (obj->type == OBJ_PLAYER && obj->movement_type==MT_PHYSICS)  {
2342
2343                 if (previous_segment != obj->segnum) {
2344                         int     connect_side,i;
2345 #ifdef NETWORK
2346                         int     old_level = Current_level_num;
2347 #endif
2348                         for (i=0;i<n_phys_segs-1;i++) {
2349                                 connect_side = find_connect_side(&Segments[phys_seglist[i+1]], &Segments[phys_seglist[i]]);
2350                                 if (connect_side != -1)
2351                                         check_trigger(&Segments[phys_seglist[i]], connect_side, obj-Objects,0);
2352                                 #ifndef NDEBUG
2353                                 else {  // segments are not directly connected, so do binary subdivision until you find connected segments.
2354                                         mprintf((1, "UNCONNECTED SEGMENTS %d,%d\n",phys_seglist[i+1],phys_seglist[i]));
2355                                         // -- Unnecessary, MK, 09/04/95 -- Int3();
2356                                 }
2357                                 #endif
2358
2359                                 //maybe we've gone on to the next level.  if so, bail!
2360 #ifdef NETWORK
2361                                 if (Current_level_num != old_level)
2362                                         return;
2363 #endif
2364                         }
2365                 }
2366
2367                 {
2368                         int sidemask,under_lavafall=0;
2369                         static int lavafall_hiss_playing[MAX_PLAYERS]={0};
2370
2371                         sidemask = get_seg_masks(&obj->pos,obj->segnum,obj->size).sidemask;
2372                         if (sidemask) {
2373                                 int sidenum,bit,wall_num;
2374         
2375                                 for (sidenum=0,bit=1;sidenum<6;bit<<=1,sidenum++)
2376                                         if ((sidemask & bit) && ((wall_num=Segments[obj->segnum].sides[sidenum].wall_num)!=-1) && Walls[wall_num].type==WALL_ILLUSION) {
2377                                                 int type;
2378                                                 if ((type=check_volatile_wall(obj,obj->segnum,sidenum,&obj->pos))!=0) {
2379                                                         int sound = (type==1)?SOUND_LAVAFALL_HISS:SOUND_SHIP_IN_WATERFALL;
2380                                                         under_lavafall = 1;
2381                                                         if (!lavafall_hiss_playing[obj->id]) {
2382                                                                 digi_link_sound_to_object3( sound, obj-Objects, 1, F1_0, i2f(256), -1, -1);
2383                                                                 lavafall_hiss_playing[obj->id] = 1;
2384                                                         }
2385                                                 }
2386                                         }
2387                         }
2388         
2389                         if (!under_lavafall && lavafall_hiss_playing[obj->id]) {
2390                                 digi_kill_sound_linked_to_object( obj-Objects);
2391                                 lavafall_hiss_playing[obj->id] = 0;
2392                         }
2393                 }
2394         }
2395
2396         //see if guided missile has flown through exit trigger
2397         if (obj==Guided_missile[Player_num] && obj->signature==Guided_missile_sig[Player_num]) {
2398                 if (previous_segment != obj->segnum) {
2399                         int     connect_side;
2400                         connect_side = find_connect_side(&Segments[obj->segnum], &Segments[previous_segment]);
2401                         if (connect_side != -1) {
2402                                 int wall_num,trigger_num;
2403                                 wall_num = Segments[previous_segment].sides[connect_side].wall_num;
2404                                 if ( wall_num != -1 ) {
2405                                         trigger_num = Walls[wall_num].trigger;
2406                                         if (trigger_num != -1)
2407                                                 if (Triggers[trigger_num].type == TT_EXIT)
2408                                                         Guided_missile[Player_num]->lifeleft = 0;
2409                                 }
2410                         }
2411                 }
2412         }
2413
2414         if (Drop_afterburner_blob_flag) {
2415                 Assert(obj==ConsoleObject);
2416                 drop_afterburner_blobs(obj, 2, i2f(5)/2, -1);   //      -1 means use default lifetime
2417 #ifdef NETWORK
2418                 if (Game_mode & GM_MULTI)
2419                         multi_send_drop_blobs(Player_num);
2420 #endif
2421                 Drop_afterburner_blob_flag = 0;
2422         }
2423
2424         if ((obj->type == OBJ_WEAPON) && (Weapon_info[obj->id].afterburner_size)) {
2425                 int     objnum = obj-Objects;
2426                 fix     vel = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
2427                 fix     delay, lifetime;
2428
2429                 if (vel > F1_0*200)
2430                         delay = F1_0/16;
2431                 else if (vel > F1_0*40)
2432                         delay = fixdiv(F1_0*13,vel);
2433                 else
2434                         delay = F1_0/4;
2435
2436                 lifetime = (delay * 3)/2;
2437                 if (!(Game_mode & GM_MULTI)) {
2438                         delay /= 2;
2439                         lifetime *= 2;
2440                 }
2441
2442                 if ((Last_afterburner_time[objnum] + delay < GameTime) || (Last_afterburner_time[objnum] > GameTime)) {
2443                         drop_afterburner_blobs(obj, 1, i2f(Weapon_info[obj->id].afterburner_size)/16, lifetime);
2444                         Last_afterburner_time[objnum] = GameTime;
2445                 }
2446         }
2447
2448         #else
2449                 obj++;          //kill warning
2450         #endif          //DEMO_ONLY
2451 }
2452
2453 int     Max_used_objects = MAX_OBJECTS - 20;
2454
2455 //--------------------------------------------------------------------
2456 //move all objects for the current frame
2457 void object_move_all()
2458 {
2459         int i;
2460         object *objp;
2461
2462 // -- mprintf((0, "Frame %i: %i/%i objects used.\n", FrameCount, num_objects, MAX_OBJECTS));
2463
2464 //      check_duplicate_objects();
2465 //      remove_incorrect_objects();
2466
2467         if (Highest_object_index > Max_used_objects)
2468                 free_object_slots(Max_used_objects);            //      Free all possible object slots.
2469
2470         obj_delete_all_that_should_be_dead();
2471
2472         if (Auto_leveling_on)
2473                 ConsoleObject->mtype.phys_info.flags |= PF_LEVELLING;
2474         else
2475                 ConsoleObject->mtype.phys_info.flags &= ~PF_LEVELLING;
2476
2477         // Move all objects
2478         objp = Objects;
2479
2480         #ifndef DEMO_ONLY
2481         for (i=0;i<=Highest_object_index;i++) {
2482                 if ( (objp->type != OBJ_NONE) && (!(objp->flags&OF_SHOULD_BE_DEAD)) )   {
2483                         object_move_one( objp );
2484                 }
2485                 objp++;
2486         }
2487         #else
2488                 i=0;    //kill warning
2489         #endif
2490
2491 //      check_duplicate_objects();
2492 //      remove_incorrect_objects();
2493
2494 }
2495
2496
2497 //--unused-- // -----------------------------------------------------------
2498 //--unused-- // Moved here from eobject.c on 02/09/94 by MK.
2499 //--unused-- int find_last_obj(int i)
2500 //--unused-- {
2501 //--unused--    for (i=MAX_OBJECTS;--i>=0;)
2502 //--unused--            if (Objects[i].type != OBJ_NONE) break;
2503 //--unused--
2504 //--unused--    return i;
2505 //--unused--
2506 //--unused-- }
2507
2508
2509 //make object array non-sparse
2510 void compress_objects(void)
2511 {
2512         int start_i;    //,last_i;
2513
2514         //last_i = find_last_obj(MAX_OBJECTS);
2515
2516         //      Note: It's proper to do < (rather than <=) Highest_object_index here because we
2517         //      are just removing gaps, and the last object can't be a gap.
2518         for (start_i=0;start_i<Highest_object_index;start_i++)
2519
2520                 if (Objects[start_i].type == OBJ_NONE) {
2521
2522                         int     segnum_copy;
2523
2524                         segnum_copy = Objects[Highest_object_index].segnum;
2525
2526                         obj_unlink(Highest_object_index);
2527
2528                         Objects[start_i] = Objects[Highest_object_index];
2529
2530                         #ifdef EDITOR
2531                         if (Cur_object_index == Highest_object_index)
2532                                 Cur_object_index = start_i;
2533                         #endif
2534
2535                         Objects[Highest_object_index].type = OBJ_NONE;
2536
2537                         obj_link(start_i,segnum_copy);
2538
2539                         while (Objects[--Highest_object_index].type == OBJ_NONE);
2540
2541                         //last_i = find_last_obj(last_i);
2542                         
2543                 }
2544
2545         reset_objects(num_objects);
2546
2547 }
2548
2549 //called after load.  Takes number of objects,  and objects should be
2550 //compressed.  resets free list, marks unused objects as unused
2551 void reset_objects(int n_objs)
2552 {
2553         int i;
2554
2555         num_objects = n_objs;
2556
2557         Assert(num_objects>0);
2558
2559         for (i=num_objects;i<MAX_OBJECTS;i++) {
2560                 free_obj_list[i] = i;
2561                 Objects[i].type = OBJ_NONE;
2562                 Objects[i].segnum = -1;
2563         }
2564
2565         Highest_object_index = num_objects-1;
2566
2567         Debris_object_count = 0;
2568 }
2569
2570 //Tries to find a segment for an object, using find_point_seg()
2571 int find_object_seg(object * obj )
2572 {
2573         return find_point_seg(&obj->pos,obj->segnum);
2574 }
2575
2576
2577 //If an object is in a segment, set its segnum field and make sure it's
2578 //properly linked.  If not in any segment, returns 0, else 1.
2579 //callers should generally use find_vector_intersection()
2580 int update_object_seg(object * obj )
2581 {
2582         int newseg;
2583
2584         newseg = find_object_seg(obj);
2585
2586         if (newseg == -1)
2587                 return 0;
2588
2589         if ( newseg != obj->segnum )
2590                 obj_relink(obj-Objects, newseg );
2591
2592         return 1;
2593 }
2594
2595
2596 //go through all objects and make sure they have the correct segment numbers
2597 void
2598 fix_object_segs()
2599 {
2600         int i;
2601
2602         for (i=0;i<=Highest_object_index;i++)
2603                 if (Objects[i].type != OBJ_NONE)
2604                         if (update_object_seg(&Objects[i]) == 0) {
2605                                 mprintf((1,"Cannot find segment for object %d in fix_object_segs()\n"));
2606                                 Int3();
2607                                 compute_segment_center(&Objects[i].pos,&Segments[Objects[i].segnum]);
2608                         }
2609 }
2610
2611
2612 //--unused-- void object_use_new_object_list( object * new_list )
2613 //--unused-- {
2614 //--unused--    int i, segnum;
2615 //--unused--    object *obj;
2616 //--unused--
2617 //--unused--    // First, unlink all the old objects for the segments array
2618 //--unused--    for (segnum=0; segnum <= Highest_segment_index; segnum++) {
2619 //--unused--            Segments[segnum].objects = -1;
2620 //--unused--    }
2621 //--unused--    // Then, erase all the objects
2622 //--unused--    reset_objects(1);
2623 //--unused--
2624 //--unused--    // Fill in the object array
2625 //--unused--    memcpy( Objects, new_list, sizeof(object)*MAX_OBJECTS );
2626 //--unused--
2627 //--unused--    Highest_object_index=-1;
2628 //--unused--
2629 //--unused--    // Relink 'em
2630 //--unused--    for (i=0; i<MAX_OBJECTS; i++ )  {
2631 //--unused--            obj = &Objects[i];
2632 //--unused--            if ( obj->type != OBJ_NONE )    {
2633 //--unused--                    num_objects++;
2634 //--unused--                    Highest_object_index = i;
2635 //--unused--                    segnum = obj->segnum;
2636 //--unused--                    obj->next = obj->prev = obj->segnum = -1;
2637 //--unused--                    obj_link(i,segnum);
2638 //--unused--            } else {
2639 //--unused--                    obj->next = obj->prev = obj->segnum = -1;
2640 //--unused--            }
2641 //--unused--    }
2642 //--unused--    
2643 //--unused-- }
2644
2645 //delete objects, such as weapons & explosions, that shouldn't stay between levels
2646 //      Changed by MK on 10/15/94, don't remove proximity bombs.
2647 //if clear_all is set, clear even proximity bombs
2648 void clear_transient_objects(int clear_all)
2649 {
2650         int objnum;
2651         object *obj;
2652
2653         for (objnum=0,obj=&Objects[0];objnum<=Highest_object_index;objnum++,obj++)
2654                 if (((obj->type == OBJ_WEAPON) && !(Weapon_info[obj->id].flags&WIF_PLACABLE) && (clear_all || ((obj->id != PROXIMITY_ID) && (obj->id != SUPERPROX_ID)))) ||
2655                          obj->type == OBJ_FIREBALL ||
2656                          obj->type == OBJ_DEBRIS ||
2657                          obj->type == OBJ_DEBRIS ||
2658                          (obj->type!=OBJ_NONE && obj->flags & OF_EXPLODING)) {
2659
2660                         #ifndef NDEBUG
2661                         if (Objects[objnum].lifeleft > i2f(2))
2662                                 mprintf((0,"Note: Clearing object %d (type=%d, id=%d) with lifeleft=%x\n",objnum,Objects[objnum].type,Objects[objnum].id,Objects[objnum].lifeleft));
2663                         #endif
2664                         obj_delete(objnum);
2665                 }
2666                 #ifndef NDEBUG
2667                  else if (Objects[objnum].type!=OBJ_NONE && Objects[objnum].lifeleft < i2f(2))
2668                         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));
2669                 #endif
2670 }
2671
2672 //attaches an object, such as a fireball, to another object, such as a robot
2673 void obj_attach(object *parent,object *sub)
2674 {
2675         Assert(sub->type == OBJ_FIREBALL);
2676         Assert(sub->control_type == CT_EXPLOSION);
2677
2678         Assert(sub->ctype.expl_info.next_attach==-1);
2679         Assert(sub->ctype.expl_info.prev_attach==-1);
2680
2681         Assert(parent->attached_obj==-1 || Objects[parent->attached_obj].ctype.expl_info.prev_attach==-1);
2682
2683         sub->ctype.expl_info.next_attach = parent->attached_obj;
2684
2685         if (sub->ctype.expl_info.next_attach != -1)
2686                 Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach = sub-Objects;
2687
2688         parent->attached_obj = sub-Objects;
2689
2690         sub->ctype.expl_info.attach_parent = parent-Objects;
2691         sub->flags |= OF_ATTACHED;
2692
2693         Assert(sub->ctype.expl_info.next_attach != sub-Objects);
2694         Assert(sub->ctype.expl_info.prev_attach != sub-Objects);
2695 }
2696
2697 //dettaches one object
2698 void obj_detach_one(object *sub)
2699 {
2700         Assert(sub->flags & OF_ATTACHED);
2701         Assert(sub->ctype.expl_info.attach_parent != -1);
2702
2703         if ((Objects[sub->ctype.expl_info.attach_parent].type == OBJ_NONE) || (Objects[sub->ctype.expl_info.attach_parent].attached_obj == -1))
2704         {
2705                 sub->flags &= ~OF_ATTACHED;
2706                 return;
2707         }
2708
2709         if (sub->ctype.expl_info.next_attach != -1) {
2710                 Assert(Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach=sub-Objects);
2711                 Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach = sub->ctype.expl_info.prev_attach;
2712         }
2713
2714         if (sub->ctype.expl_info.prev_attach != -1) {
2715                 Assert(Objects[sub->ctype.expl_info.prev_attach].ctype.expl_info.next_attach=sub-Objects);
2716                 Objects[sub->ctype.expl_info.prev_attach].ctype.expl_info.next_attach = sub->ctype.expl_info.next_attach;
2717         }
2718         else {
2719                 Assert(Objects[sub->ctype.expl_info.attach_parent].attached_obj=sub-Objects);
2720                 Objects[sub->ctype.expl_info.attach_parent].attached_obj = sub->ctype.expl_info.next_attach;
2721         }
2722
2723         sub->ctype.expl_info.next_attach = sub->ctype.expl_info.prev_attach = -1;
2724         sub->flags &= ~OF_ATTACHED;
2725
2726 }
2727
2728 //dettaches all objects from this object
2729 void obj_detach_all(object *parent)
2730 {
2731         while (parent->attached_obj != -1)
2732                 obj_detach_one(&Objects[parent->attached_obj]);
2733 }
2734
2735 //creates a marker object in the world.  returns the object number
2736 int drop_marker_object(vms_vector *pos,int segnum,vms_matrix *orient, int marker_num)
2737 {
2738         int objnum;
2739
2740         Assert(Marker_model_num != -1);
2741
2742         objnum = obj_create(OBJ_MARKER, marker_num, segnum, pos, orient, Polygon_models[Marker_model_num].rad, CT_NONE, MT_NONE, RT_POLYOBJ);
2743
2744         if (objnum >= 0) {
2745                 object *obj = &Objects[objnum];
2746
2747                 obj->rtype.pobj_info.model_num = Marker_model_num;
2748
2749                 vm_vec_copy_scale(&obj->mtype.spin_rate,&obj->orient.uvec,F1_0/2);
2750
2751                 //      MK, 10/16/95: Using lifeleft to make it flash, thus able to trim lightlevel from all objects.
2752                 obj->lifeleft = IMMORTAL_TIME - 1;
2753         }
2754
2755         return objnum;  
2756 }
2757
2758 extern int Ai_last_missile_camera;
2759
2760 //      *viewer is a viewer, probably a missile.
2761 //      wake up all robots that were rendered last frame subject to some constraints.
2762 void wake_up_rendered_objects(object *viewer, int window_num)
2763 {
2764         int     i;
2765
2766         //      Make sure that we are processing current data.
2767         if (FrameCount != Window_rendered_data[window_num].frame) {
2768                 mprintf((1, "Warning: Called wake_up_rendered_objects with a bogus window.\n"));
2769                 return;
2770         }
2771
2772         Ai_last_missile_camera = viewer-Objects;
2773
2774         for (i=0; i<Window_rendered_data[window_num].num_objects; i++) {
2775                 int     objnum;
2776                 object *objp;
2777                 int     fcval = FrameCount & 3;
2778
2779                 objnum = Window_rendered_data[window_num].rendered_objects[i];
2780                 if ((objnum & 3) == fcval) {
2781                         objp = &Objects[objnum];
2782         
2783                         if (objp->type == OBJ_ROBOT) {
2784                                 if (vm_vec_dist_quick(&viewer->pos, &objp->pos) < F1_0*100) {
2785                                         ai_local                *ailp = &Ai_local_info[objnum];
2786                                         if (ailp->player_awareness_type == 0) {
2787                                                 objp->ctype.ai_info.SUB_FLAGS |= SUB_FLAGS_CAMERA_AWAKE;
2788                                                 ailp->player_awareness_type = PA_WEAPON_ROBOT_COLLISION;
2789                                                 ailp->player_awareness_time = F1_0*3;
2790                                                 ailp->previous_visibility = 2;
2791                                         }
2792                                 }
2793                         }
2794                 }
2795         }
2796 }
2797
2798
2799
2800
2801
2802