]> icculus.org git repositories - btb/d2x.git/blob - main/object.c
allow the editor to be built with NDEBUG
[btb/d2x.git] / main / object.c
1 /* $Id: object.c,v 1.20 2006-03-05 12:19:42 chris 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  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <conf.h>
23 #endif
24
25 #include <string.h>     // for memset
26 #include <stdio.h>
27
28 #include "inferno.h"
29 #include "game.h"
30 #include "gr.h"
31 #include "stdlib.h"
32 #include "bm.h"
33 //#include "error.h"
34 #include "mono.h"
35 #include "3d.h"
36 #include "segment.h"
37 #include "texmap.h"
38 #include "laser.h"
39 #include "key.h"
40 #include "gameseg.h"
41 #include "textures.h"
42
43 #include "object.h"
44 #include "physics.h"
45 #include "slew.h"               
46 #include "render.h"
47 #include "wall.h"
48 #include "vclip.h"
49 #include "polyobj.h"
50 #include "fireball.h"
51 #include "laser.h"
52 #include "error.h"
53 #include "ai.h"
54 #include "hostage.h"
55 #include "morph.h"
56 #include "cntrlcen.h"
57 #include "powerup.h"
58 #include "fuelcen.h"
59 #include "endlevel.h"
60
61 #include "sounds.h"
62 #include "collide.h"
63
64 #include "lighting.h"
65 #include "newdemo.h"
66 #include "player.h"
67 #include "weapon.h"
68 #ifdef NETWORK
69 #include "network.h"
70 #endif
71 #include "newmenu.h"
72 #include "gauges.h"
73 #include "multi.h"
74 #include "menu.h"
75 #include "args.h"
76 #include "text.h"
77 #include "piggy.h"
78 #include "switch.h"
79 #include "gameseq.h"
80
81 #ifdef TACTILE
82 #include "tactile.h"
83 #endif
84
85 #ifdef EDITOR
86 #include "editor/editor.h"
87 #endif
88
89 void obj_detach_all(object *parent);
90 void obj_detach_one(object *sub);
91 int free_object_slots(int num_used);
92
93 /*
94  *  Global variables
95  */
96
97 extern sbyte WasRecorded[MAX_OBJECTS];
98
99 ubyte CollisionResult[MAX_OBJECT_TYPES][MAX_OBJECT_TYPES];
100
101 object *ConsoleObject;                                  //the object that is the player
102
103 static short free_obj_list[MAX_OBJECTS];
104
105 //Data for objects
106
107 // -- Object stuff
108
109 //info on the various types of objects
110 #ifndef NDEBUG
111 object  Object_minus_one;
112 #endif
113
114 object Objects[MAX_OBJECTS];
115 int num_objects=0;
116 int Highest_object_index=0;
117 int Highest_ever_object_index=0;
118
119 // grs_bitmap *robot_bms[MAX_ROBOT_BITMAPS];    //all bitmaps for all robots
120
121 // int robot_bm_nums[MAX_ROBOT_TYPES];          //starting bitmap num for each robot
122 // int robot_n_bitmaps[MAX_ROBOT_TYPES];                //how many bitmaps for each robot
123
124 // char *robot_names[MAX_ROBOT_TYPES];          //name of each robot
125
126 //--unused-- int Num_robot_types=0;
127
128 int print_object_info = 0;
129 //@@int Object_viewer = 0;
130
131 //object * Slew_object = NULL;  // Object containing slew object info.
132
133 //--unused-- int Player_controller_type = 0;
134
135 window_rendered_data Window_rendered_data[MAX_RENDERED_WINDOWS];
136
137 #if defined(EDITOR) || !defined(NDEBUG)
138 char    Object_type_names[MAX_OBJECT_TYPES][9] = {
139         "WALL    ",
140         "FIREBALL",
141         "ROBOT   ",
142         "HOSTAGE ",
143         "PLAYER  ",
144         "WEAPON  ",
145         "CAMERA  ",
146         "POWERUP ",
147         "DEBRIS  ",
148         "CNTRLCEN",
149         "FLARE   ",
150         "CLUTTER ",
151         "GHOST   ",
152         "LIGHT   ",
153         "COOP    ",
154         "MARKER  ",
155 };
156 #endif
157
158 #ifndef RELEASE
159 //set viewer object to next object in array
160 void object_goto_next_viewer()
161 {
162         int i, start_obj = 0;
163
164         start_obj = Viewer - Objects;           //get viewer object number
165         
166         for (i=0;i<=Highest_object_index;i++) {
167
168                 start_obj++;
169                 if (start_obj > Highest_object_index ) start_obj = 0;
170
171                 if (Objects[start_obj].type != OBJ_NONE )       {
172                         Viewer = &Objects[start_obj];
173                         return;
174                 }
175         }
176
177         Error( "Couldn't find a viewer object!" );
178
179 }
180
181 //set viewer object to next object in array
182 void object_goto_prev_viewer()
183 {
184         int i, start_obj = 0;
185
186         start_obj = Viewer - Objects;           //get viewer object number
187         
188         for (i=0; i<=Highest_object_index; i++) {
189
190                 start_obj--;
191                 if (start_obj < 0 ) start_obj = Highest_object_index;
192
193                 if (Objects[start_obj].type != OBJ_NONE )       {
194                         Viewer = &Objects[start_obj];
195                         return;
196                 }
197         }
198
199         Error( "Couldn't find a viewer object!" );
200
201 }
202 #endif
203
204 object *obj_find_first_of_type (int type)
205  {
206   int i;
207
208   for (i=0;i<=Highest_object_index;i++)
209         if (Objects[i].type==type)
210          return (&Objects[i]);
211   return ((object *)NULL);
212  }
213
214 int obj_return_num_of_type (int type)
215  {
216   int i,count=0;
217
218   for (i=0;i<=Highest_object_index;i++)
219         if (Objects[i].type==type)
220          count++;
221   return (count);
222  }
223 int obj_return_num_of_typeid (int type,int id)
224  {
225   int i,count=0;
226
227   for (i=0;i<=Highest_object_index;i++)
228         if (Objects[i].type==type && Objects[i].id==id)
229          count++;
230   return (count);
231  }
232
233 int global_orientation = 0;
234
235 //draw an object that has one bitmap & doesn't rotate
236 void draw_object_blob(object *obj,bitmap_index bmi)
237 {
238         int     orientation=0;
239         grs_bitmap * bm = &GameBitmaps[bmi.index];
240
241
242         if (obj->type == OBJ_FIREBALL)
243                 orientation = (obj-Objects) & 7;
244
245         orientation = global_orientation;
246
247         PIGGY_PAGE_IN( bmi );
248
249         if (bm->bm_w > bm->bm_h)
250
251                 g3_draw_bitmap(&obj->pos,obj->size,fixmuldiv(obj->size,bm->bm_h,bm->bm_w),bm, orientation);
252
253         else
254
255                 g3_draw_bitmap(&obj->pos,fixmuldiv(obj->size,bm->bm_w,bm->bm_h),obj->size,bm, orientation);
256 }
257
258 //draw an object that is a texture-mapped rod
259 void draw_object_tmap_rod(object *obj,bitmap_index bitmapi,int lighted)
260 {
261         grs_bitmap * bitmap = &GameBitmaps[bitmapi.index];
262         fix light;
263
264         vms_vector delta,top_v,bot_v;
265         g3s_point top_p,bot_p;
266
267         PIGGY_PAGE_IN(bitmapi);
268
269    bitmap->bm_handle = bitmapi.index;
270
271         vm_vec_copy_scale(&delta,&obj->orient.uvec,obj->size);
272
273         vm_vec_add(&top_v,&obj->pos,&delta);
274         vm_vec_sub(&bot_v,&obj->pos,&delta);
275
276         g3_rotate_point(&top_p,&top_v);
277         g3_rotate_point(&bot_p,&bot_v);
278
279         if (lighted)
280                 light = compute_object_light(obj,&top_p.p3_vec);
281         else
282                 light = f1_0;
283
284         g3_draw_rod_tmap(bitmap,&bot_p,obj->size,&top_p,obj->size,light);
285 }
286
287 int     Linear_tmap_polygon_objects = 1;
288
289 extern fix Max_thrust;
290
291 //used for robot engine glow
292 #define MAX_VELOCITY i2f(50)
293
294 //function that takes the same parms as draw_tmap, but renders as flat poly
295 //we need this to do the cloaked effect
296 extern void draw_tmap_flat();
297
298 //what darkening level to use when cloaked
299 #define CLOAKED_FADE_LEVEL              28
300
301 #define CLOAK_FADEIN_DURATION_PLAYER    F2_0
302 #define CLOAK_FADEOUT_DURATION_PLAYER   F2_0
303
304 #define CLOAK_FADEIN_DURATION_ROBOT     F1_0
305 #define CLOAK_FADEOUT_DURATION_ROBOT    F1_0
306
307 //do special cloaked render
308 void draw_cloaked_object(object *obj,fix light,fix *glow,fix cloak_start_time,fix cloak_end_time)
309 {
310         fix cloak_delta_time,total_cloaked_time;
311         fix light_scale=F1_0;
312         int cloak_value=0;
313         int fading=0;           //if true, fading, else cloaking
314         fix     Cloak_fadein_duration=F1_0;
315         fix     Cloak_fadeout_duration=F1_0;
316
317
318         total_cloaked_time = cloak_end_time-cloak_start_time;
319
320         switch (obj->type) {
321                 case OBJ_PLAYER:
322                         Cloak_fadein_duration = CLOAK_FADEIN_DURATION_PLAYER;
323                         Cloak_fadeout_duration = CLOAK_FADEOUT_DURATION_PLAYER;
324                         break;
325                 case OBJ_ROBOT:
326                         Cloak_fadein_duration = CLOAK_FADEIN_DURATION_ROBOT;
327                         Cloak_fadeout_duration = CLOAK_FADEOUT_DURATION_ROBOT;
328                         break;
329                 default:
330                         Int3();         //      Contact Mike: Unexpected object type in draw_cloaked_object.
331         }
332
333         cloak_delta_time = GameTime - cloak_start_time;
334
335         if (cloak_delta_time < Cloak_fadein_duration/2) {
336
337                 light_scale = fixdiv(Cloak_fadein_duration/2 - cloak_delta_time,Cloak_fadein_duration/2);
338                 fading = 1;
339
340         }
341         else if (cloak_delta_time < Cloak_fadein_duration) {
342
343                 cloak_value = f2i(fixdiv(cloak_delta_time - Cloak_fadein_duration/2,Cloak_fadein_duration/2) * CLOAKED_FADE_LEVEL);
344
345         } else if (GameTime < cloak_end_time-Cloak_fadeout_duration) {
346                 static int cloak_delta=0,cloak_dir=1;
347                 static fix cloak_timer=0;
348
349                 //note, if more than one cloaked object is visible at once, the
350                 //pulse rate will change!
351
352                 cloak_timer -= FrameTime;
353                 while (cloak_timer < 0) {
354
355                         cloak_timer += Cloak_fadeout_duration/12;
356
357                         cloak_delta += cloak_dir;
358
359                         if (cloak_delta==0 || cloak_delta==4)
360                                 cloak_dir = -cloak_dir;
361                 }
362
363                 cloak_value = CLOAKED_FADE_LEVEL - cloak_delta;
364         
365         } else if (GameTime < cloak_end_time-Cloak_fadeout_duration/2) {
366
367                 cloak_value = f2i(fixdiv(total_cloaked_time - Cloak_fadeout_duration/2 - cloak_delta_time,Cloak_fadeout_duration/2) * CLOAKED_FADE_LEVEL);
368
369         } else {
370
371                 light_scale = fixdiv(Cloak_fadeout_duration/2 - (total_cloaked_time - cloak_delta_time),Cloak_fadeout_duration/2);
372                 fading = 1;
373         }
374
375
376         if (fading) {
377                 fix new_light,save_glow;
378                 bitmap_index * alt_textures = NULL;
379
380 #ifdef NETWORK
381                 if ( obj->rtype.pobj_info.alt_textures > 0 )
382                         alt_textures = multi_player_textures[obj->rtype.pobj_info.alt_textures-1];
383 #endif
384
385                 new_light = fixmul(light,light_scale);
386                 save_glow = glow[0];
387                 glow[0] = fixmul(glow[0],light_scale);
388                 draw_polygon_model(&obj->pos,
389                                    &obj->orient,
390                                    (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
391                                    obj->rtype.pobj_info.model_num,obj->rtype.pobj_info.subobj_flags,
392                                    new_light,
393                                    glow,
394                                    alt_textures );
395                 glow[0] = save_glow;
396         }
397         else {
398                 Gr_scanline_darkening_level = cloak_value;
399                 gr_setcolor(BM_XRGB(0,0,0));    //set to black (matters for s3)
400                 g3_set_special_render(draw_tmap_flat,NULL,NULL);                //use special flat drawer
401                 draw_polygon_model(&obj->pos,
402                                    &obj->orient,
403                                    (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
404                                    obj->rtype.pobj_info.model_num,obj->rtype.pobj_info.subobj_flags,
405                                    light,
406                                    glow,
407                                    NULL );
408                 g3_set_special_render(NULL,NULL,NULL);
409                 Gr_scanline_darkening_level = GR_FADE_LEVELS;
410         }
411
412 }
413
414 //draw an object which renders as a polygon model
415 void draw_polygon_object(object *obj)
416 {
417         fix light;
418         int     imsave;
419         fix engine_glow_value[2];               //element 0 is for engine glow, 1 for headlight
420
421         light = compute_object_light(obj,NULL);
422
423         //      If option set for bright players in netgame, brighten them!
424 #ifdef NETWORK
425         if (Game_mode & GM_MULTI)
426                 if (Netgame.BrightPlayers)
427                         light = F1_0;
428 #endif
429
430         //make robots brighter according to robot glow field
431         if (obj->type == OBJ_ROBOT)
432                 light += (Robot_info[obj->id].glow<<12);                //convert 4:4 to 16:16
433
434         if (obj->type == OBJ_WEAPON)
435                 if (obj->id == FLARE_ID)
436                         light += F1_0*2;
437
438         if (obj->type == OBJ_MARKER)
439                 light += F1_0*2;
440
441
442         imsave = Interpolation_method;
443         if (Linear_tmap_polygon_objects)
444                 Interpolation_method = 1;
445
446         //set engine glow value
447         engine_glow_value[0] = f1_0/5;
448         if (obj->movement_type == MT_PHYSICS) {
449
450                 if (obj->mtype.phys_info.flags & PF_USES_THRUST && obj->type==OBJ_PLAYER && obj->id==Player_num) {
451                         fix thrust_mag = vm_vec_mag_quick(&obj->mtype.phys_info.thrust);
452                         engine_glow_value[0] += (fixdiv(thrust_mag,Player_ship->max_thrust)*4)/5;
453                 }
454                 else {
455                         fix speed = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
456                         engine_glow_value[0] += (fixdiv(speed,MAX_VELOCITY)*3)/5;
457                 }
458         }
459
460         //set value for player headlight
461         if (obj->type == OBJ_PLAYER) {
462                 if (Players[obj->id].flags & PLAYER_FLAGS_HEADLIGHT && !Endlevel_sequence)
463                         if (Players[obj->id].flags & PLAYER_FLAGS_HEADLIGHT_ON)
464                                 engine_glow_value[1] = -2;              //draw white!
465                         else
466                                 engine_glow_value[1] = -1;              //draw normal color (grey)
467                 else
468                         engine_glow_value[1] = -3;                      //don't draw
469         }
470
471         if (obj->rtype.pobj_info.tmap_override != -1) {
472 #ifndef NDEBUG
473                 polymodel *pm = &Polygon_models[obj->rtype.pobj_info.model_num];
474 #endif
475                 bitmap_index bm_ptrs[12];
476
477                 int i;
478
479                 Assert(pm->n_textures<=12);
480
481                 for (i=0;i<12;i++)              //fill whole array, in case simple model needs more
482                         bm_ptrs[i] = Textures[obj->rtype.pobj_info.tmap_override];
483
484                 draw_polygon_model(&obj->pos,
485                                    &obj->orient,
486                                    (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
487                                    obj->rtype.pobj_info.model_num,
488                                    obj->rtype.pobj_info.subobj_flags,
489                                    light,
490                                    engine_glow_value,
491                                    bm_ptrs);
492         }
493         else {
494
495                 if (obj->type==OBJ_PLAYER && (Players[obj->id].flags&PLAYER_FLAGS_CLOAKED))
496                         draw_cloaked_object(obj,light,engine_glow_value,Players[obj->id].cloak_time,Players[obj->id].cloak_time+CLOAK_TIME_MAX);
497                 else if ((obj->type == OBJ_ROBOT) && (obj->ctype.ai_info.CLOAKED)) {
498                         if (Robot_info[obj->id].boss_flag)
499                                 draw_cloaked_object(obj,light,engine_glow_value, Boss_cloak_start_time, Boss_cloak_end_time);
500                         else
501                                 draw_cloaked_object(obj,light,engine_glow_value, GameTime-F1_0*10, GameTime+F1_0*10);
502                 } else {
503                         bitmap_index * alt_textures = NULL;
504         
505                         #ifdef NETWORK
506                         if ( obj->rtype.pobj_info.alt_textures > 0 )
507                                 alt_textures = multi_player_textures[obj->rtype.pobj_info.alt_textures-1];
508                         #endif
509
510                         //      Snipers get bright when they fire.
511                         if (Ai_local_info[obj-Objects].next_fire < F1_0/8) {
512                                 if (obj->ctype.ai_info.behavior == AIB_SNIPE)
513                                         light = 2*light + F1_0;
514                         }
515
516                         draw_polygon_model(&obj->pos,
517                                            &obj->orient,
518                                            (vms_angvec *)&obj->rtype.pobj_info.anim_angles,obj->rtype.pobj_info.model_num,
519                                            obj->rtype.pobj_info.subobj_flags,
520                                            light,
521                                            engine_glow_value,
522                                            alt_textures);
523                         if (obj->type == OBJ_WEAPON && (Weapon_info[obj->id].model_num_inner > -1 )) {
524                                 fix dist_to_eye = vm_vec_dist_quick(&Viewer->pos, &obj->pos);
525                                 if (dist_to_eye < Simple_model_threshhold_scale * F1_0*2)
526                                         draw_polygon_model(&obj->pos,
527                                                            &obj->orient,
528                                                            (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
529                                                            Weapon_info[obj->id].model_num_inner,
530                                                            obj->rtype.pobj_info.subobj_flags,
531                                                            light,
532                                                            engine_glow_value,
533                                                            alt_textures);
534                         }
535                 }
536         }
537
538         Interpolation_method = imsave;
539
540 }
541
542 //------------------------------------------------------------------------------
543 // These variables are used to keep a list of the 3 closest robots to the viewer.
544 // The code works like this: Every time render object is called with a polygon model,
545 // it finds the distance of that robot to the viewer.  If this distance if within 10
546 // segments of the viewer, it does the following: If there aren't already 3 robots in
547 // the closet-robots list, it just sticks that object into the list along with its distance.
548 // If the list already contains 3 robots, then it finds the robot in that list that is
549 // farthest from the viewer. If that object is farther than the object currently being
550 // rendered, then the new object takes over that far object's slot.  *Then* after all
551 // objects are rendered, object_render_targets is called an it draws a target on top
552 // of all the objects.
553
554 //091494: #define MAX_CLOSE_ROBOTS 3
555 //--unused-- static int Object_draw_lock_boxes = 0;
556 //091494: static int Object_num_close = 0;
557 //091494: static object * Object_close_ones[MAX_CLOSE_ROBOTS];
558 //091494: static fix Object_close_distance[MAX_CLOSE_ROBOTS];
559
560 //091494: set_close_objects(object *obj)
561 //091494: {
562 //091494:       fix dist;
563 //091494:
564 //091494:       if ( (obj->type != OBJ_ROBOT) || (Object_draw_lock_boxes==0) )  
565 //091494:               return;
566 //091494:
567 //091494:       // The following code keeps a list of the 10 closest robots to the
568 //091494:       // viewer.  See comments in front of this function for how this works.
569 //091494:       dist = vm_vec_dist( &obj->pos, &Viewer->pos );
570 //091494:       if ( dist < i2f(20*10) )        {                               
571 //091494:               if ( Object_num_close < MAX_CLOSE_ROBOTS )      {
572 //091494:                       Object_close_ones[Object_num_close] = obj;
573 //091494:                       Object_close_distance[Object_num_close] = dist;
574 //091494:                       Object_num_close++;
575 //091494:               } else {
576 //091494:                       int i, farthest_robot;
577 //091494:                       fix farthest_distance;
578 //091494:                       // Find the farthest robot in the list
579 //091494:                       farthest_robot = 0;
580 //091494:                       farthest_distance = Object_close_distance[0];
581 //091494:                       for (i=1; i<Object_num_close; i++ )     {
582 //091494:                               if ( Object_close_distance[i] > farthest_distance )     {
583 //091494:                                       farthest_distance = Object_close_distance[i];
584 //091494:                                       farthest_robot = i;
585 //091494:                               }
586 //091494:                       }
587 //091494:                       // If this object is closer to the viewer than
588 //091494:                       // the farthest in the list, replace the farthest with this object.
589 //091494:                       if ( farthest_distance > dist ) {
590 //091494:                               Object_close_ones[farthest_robot] = obj;
591 //091494:                               Object_close_distance[farthest_robot] = dist;
592 //091494:                       }
593 //091494:               }
594 //091494:       }
595 //091494: }
596
597 int     Player_fired_laser_this_frame=-1;
598
599
600
601 // -----------------------------------------------------------------------------
602 //this routine checks to see if an robot rendered near the middle of
603 //the screen, and if so and the player had fired, "warns" the robot
604 void set_robot_location_info(object *objp)
605 {
606         if (Player_fired_laser_this_frame != -1) {
607                 g3s_point temp;
608
609                 g3_rotate_point(&temp,&objp->pos);
610
611                 if (temp.p3_codes & CC_BEHIND)          //robot behind the screen
612                         return;
613
614                 //the code below to check for object near the center of the screen
615                 //completely ignores z, which may not be good
616
617                 if ((abs(temp.p3_x) < F1_0*4) && (abs(temp.p3_y) < F1_0*4)) {
618                         objp->ctype.ai_info.danger_laser_num = Player_fired_laser_this_frame;
619                         objp->ctype.ai_info.danger_laser_signature = Objects[Player_fired_laser_this_frame].signature;
620                 }
621         }
622
623
624 }
625
626 //      ------------------------------------------------------------------------------------------------------------------
627 void create_small_fireball_on_object(object *objp, fix size_scale, int sound_flag)
628 {
629         fix                     size;
630         vms_vector      pos, rand_vec;
631         int                     segnum;
632
633         pos = objp->pos;
634         make_random_vector(&rand_vec);
635
636         vm_vec_scale(&rand_vec, objp->size/2);
637
638         vm_vec_add2(&pos, &rand_vec);
639
640         size = fixmul(size_scale, F1_0/2 + d_rand()*4/2);
641
642         segnum = find_point_seg(&pos, objp->segnum);
643         if (segnum != -1) {
644                 object *expl_obj;
645                 expl_obj = object_create_explosion(segnum, &pos, size, VCLIP_SMALL_EXPLOSION);
646                 if (!expl_obj)
647                         return;
648                 obj_attach(objp,expl_obj);
649                 if (d_rand() < 8192) {
650                         fix     vol = F1_0/2;
651                         if (objp->type == OBJ_ROBOT)
652                                 vol *= 2;
653                         else if (sound_flag)
654                                 digi_link_sound_to_object(SOUND_EXPLODING_WALL, objp-Objects, 0, vol);
655                 }
656         }
657 }
658
659 //      ------------------------------------------------------------------------------------------------------------------
660 void create_vclip_on_object(object *objp, fix size_scale, int vclip_num)
661 {
662         fix                     size;
663         vms_vector      pos, rand_vec;
664         int                     segnum;
665
666         pos = objp->pos;
667         make_random_vector(&rand_vec);
668
669         vm_vec_scale(&rand_vec, objp->size/2);
670
671         vm_vec_add2(&pos, &rand_vec);
672
673         size = fixmul(size_scale, F1_0 + d_rand()*4);
674
675         segnum = find_point_seg(&pos, objp->segnum);
676         if (segnum != -1) {
677                 object *expl_obj;
678                 expl_obj = object_create_explosion(segnum, &pos, size, vclip_num);
679                 if (!expl_obj)
680                         return;
681
682                 expl_obj->movement_type = MT_PHYSICS;
683                 expl_obj->mtype.phys_info.velocity.x = objp->mtype.phys_info.velocity.x/2;
684                 expl_obj->mtype.phys_info.velocity.y = objp->mtype.phys_info.velocity.y/2;
685                 expl_obj->mtype.phys_info.velocity.z = objp->mtype.phys_info.velocity.z/2;
686         }
687 }
688
689 // -- mk, 02/05/95 -- #define   VCLIP_INVULNERABILITY_EFFECT    VCLIP_SMALL_EXPLOSION
690 // -- mk, 02/05/95 --
691 // -- mk, 02/05/95 -- // -----------------------------------------------------------------------------
692 // -- mk, 02/05/95 -- void do_player_invulnerability_effect(object *objp)
693 // -- mk, 02/05/95 -- {
694 // -- mk, 02/05/95 --   if (d_rand() < FrameTime*8) {
695 // -- mk, 02/05/95 --           create_vclip_on_object(objp, F1_0, VCLIP_INVULNERABILITY_EFFECT);
696 // -- mk, 02/05/95 --   }
697 // -- mk, 02/05/95 -- }
698
699 // -----------------------------------------------------------------------------
700 //      Render an object.  Calls one of several routines based on type
701 void render_object(object *obj)
702 {
703         int     mld_save;
704
705         if ( obj == Viewer ) return;            
706
707         if ( obj->type==OBJ_NONE )      {
708                 #ifndef NDEBUG
709                 mprintf( (1, "ERROR!!!! Bogus obj %d in seg %d is rendering!\n", obj-Objects, obj->segnum ));
710                 Int3();
711                 #endif
712                 return;
713         }
714
715         mld_save = Max_linear_depth;
716         Max_linear_depth = Max_linear_depth_objects;
717
718         switch (obj->render_type) {
719
720                 case RT_NONE:   break;          //doesn't render, like the player
721
722                 case RT_POLYOBJ:
723
724                         draw_polygon_object(obj);
725
726                         //"warn" robot if being shot at
727                         if (obj->type == OBJ_ROBOT)
728                                 set_robot_location_info(obj);
729
730 //JOHN SAID TO:                 if ( (obj->type==OBJ_PLAYER) && ((keyd_pressed[KEY_W]) || (keyd_pressed[KEY_I])))
731 //JOHN SAID TO:                         object_render_id(obj);
732
733 // -- mk, 02/05/95 --                   if (obj->type == OBJ_PLAYER)
734 // -- mk, 02/05/95 --                           if (Players[obj->id].flags & PLAYER_FLAGS_INVULNERABLE)
735 // -- mk, 02/05/95 --                                   do_player_invulnerability_effect(obj);
736
737                         break;
738
739                 case RT_MORPH:  draw_morph_object(obj); break;
740
741                 case RT_FIREBALL: draw_fireball(obj); break;
742
743                 case RT_WEAPON_VCLIP: draw_weapon_vclip(obj); break;
744
745                 case RT_HOSTAGE: draw_hostage(obj); break;
746
747                 case RT_POWERUP: draw_powerup(obj); break;
748
749                 case RT_LASER: Laser_render(obj); break;
750
751                 default: Error("Unknown render_type <%d>",obj->render_type);
752         }
753
754         #ifdef NEWDEMO
755         if ( obj->render_type != RT_NONE )
756                 if ( Newdemo_state == ND_STATE_RECORDING ) {
757                         if (!WasRecorded[obj-Objects]) {
758                                 newdemo_record_render_object(obj);
759                                 WasRecorded[obj-Objects]=1;
760                         }
761                 }
762         #endif
763
764         Max_linear_depth = mld_save;
765
766 }
767
768 //--unused-- void object_toggle_lock_targets()  {
769 //--unused--    Object_draw_lock_boxes ^= 1;
770 //--unused-- }
771
772 //091494: //draw target boxes for nearby robots
773 //091494: void object_render_targets()
774 //091494: {
775 //091494:       g3s_point pt;
776 //091494:       ubyte codes;
777 //091494:       int i;
778 //091494:       int radius,x,y;
779 //091494:
780 //091494:       if (Object_draw_lock_boxes==0)
781 //091494:               return;
782 //091494:
783 //091494:       for (i=0; i<Object_num_close; i++ )     {
784 //091494:                       
785 //091494:               codes = g3_rotate_point(&pt, &Object_close_ones[i]->pos );
786 //091494:               if ( !(codes & CC_BEHIND) )     {
787 //091494:                       g3_project_point(&pt);
788 //091494:                       if (pt.p3_flags & PF_PROJECTED) {
789 //091494:                               x = f2i(pt.p3_sx);
790 //091494:                               y = f2i(pt.p3_sy);
791 //091494:                               radius = f2i(fixdiv((grd_curcanv->cv_bitmap.bm_w*Object_close_ones[i]->size)/8,pt.z));
792 //091494:                               gr_setcolor( BM_XRGB(0,31,0) );
793 //091494:                               gr_box(x-radius,y-radius,x+radius,y+radius);
794 //091494:                       }
795 //091494:               }
796 //091494:       }
797 //091494:       Object_num_close=0;
798 //091494: }
799 //--unused-- //draw target boxes for nearby robots
800 //--unused-- void object_render_id(object * obj)
801 //--unused-- {
802 //--unused--    g3s_point pt;
803 //--unused--    ubyte codes;
804 //--unused--    int x,y;
805 //--unused--    int w, h, aw;
806 //--unused--    char s[20], *s1;
807 //--unused--
808 //--unused--    s1 = network_get_player_name( obj-Objects );
809 //--unused--
810 //--unused--    if (s1)
811 //--unused--            sprintf( s, "%s", s1 );
812 //--unused--    else
813 //--unused--            sprintf( s, "<%d>", obj->id );
814 //--unused--
815 //--unused--    codes = g3_rotate_point(&pt, &obj->pos );
816 //--unused--    if ( !(codes & CC_BEHIND) )     {
817 //--unused--            g3_project_point(&pt);
818 //--unused--            if (pt.p3_flags & PF_PROJECTED) {
819 //--unused--                    gr_get_string_size( s, &w, &h, &aw );
820 //--unused--                    x = f2i(pt.p3_sx) - w/2;
821 //--unused--                    y = f2i(pt.p3_sy) - h/2;
822 //--unused--                    if ( x>= 0 && y>=0 && (x+w)<=grd_curcanv->cv_bitmap.bm_w && (y+h)<grd_curcanv->cv_bitmap.bm_h ) {
823 //--unused--                            gr_set_fontcolor( BM_XRGB(0,31,0), -1 );
824 //--unused--                            gr_string( x, y, s );
825 //--unused--                    }
826 //--unused--            }
827 //--unused--    }
828 //--unused-- }
829
830 void check_and_fix_matrix(vms_matrix *m);
831
832 #define vm_angvec_zero(v) (v)->p=(v)->b=(v)->h=0
833
834 void reset_player_object()
835 {
836         int i;
837
838         //Init physics
839
840         vm_vec_zero(&ConsoleObject->mtype.phys_info.velocity);
841         vm_vec_zero(&ConsoleObject->mtype.phys_info.thrust);
842         vm_vec_zero(&ConsoleObject->mtype.phys_info.rotvel);
843         vm_vec_zero(&ConsoleObject->mtype.phys_info.rotthrust);
844         ConsoleObject->mtype.phys_info.brakes = ConsoleObject->mtype.phys_info.turnroll = 0;
845         ConsoleObject->mtype.phys_info.mass = Player_ship->mass;
846         ConsoleObject->mtype.phys_info.drag = Player_ship->drag;
847         ConsoleObject->mtype.phys_info.flags |= PF_TURNROLL | PF_LEVELLING | PF_WIGGLE | PF_USES_THRUST;
848
849         //Init render info
850
851         ConsoleObject->render_type = RT_POLYOBJ;
852         ConsoleObject->rtype.pobj_info.model_num = Player_ship->model_num;              //what model is this?
853         ConsoleObject->rtype.pobj_info.subobj_flags = 0;                //zero the flags
854         ConsoleObject->rtype.pobj_info.tmap_override = -1;              //no tmap override!
855
856         for (i=0;i<MAX_SUBMODELS;i++)
857                 vm_angvec_zero(&ConsoleObject->rtype.pobj_info.anim_angles[i]);
858
859         // Clear misc
860
861         ConsoleObject->flags = 0;
862
863 }
864
865
866 //make object0 the player, setting all relevant fields
867 void init_player_object()
868 {
869         ConsoleObject->type = OBJ_PLAYER;
870         ConsoleObject->id = 0;                                  //no sub-types for player
871
872         ConsoleObject->signature = 0;                   //player has zero, others start at 1
873
874         ConsoleObject->size = Polygon_models[Player_ship->model_num].rad;
875
876         ConsoleObject->control_type = CT_SLEW;                  //default is player slewing
877         ConsoleObject->movement_type = MT_PHYSICS;              //change this sometime
878
879         ConsoleObject->lifeleft = IMMORTAL_TIME;
880
881         ConsoleObject->attached_obj = -1;
882
883         reset_player_object();
884
885 }
886
887 //sets up the free list & init player & whatever else
888 void init_objects()
889 {
890         int i;
891
892         collide_init();
893
894         for (i=0;i<MAX_OBJECTS;i++) {
895                 free_obj_list[i] = i;
896                 Objects[i].type = OBJ_NONE;
897                 Objects[i].segnum = -1;
898         }
899
900         for (i=0;i<MAX_SEGMENTS;i++)
901                 Segments[i].objects = -1;
902
903         ConsoleObject = Viewer = &Objects[0];
904
905         init_player_object();
906         obj_link(ConsoleObject-Objects,0);      //put in the world in segment 0
907
908         num_objects = 1;                                                //just the player
909         Highest_object_index = 0;
910
911         
912 }
913
914 //after calling init_object(), the network code has grabbed specific
915 //object slots without allocating them.  Go though the objects & build
916 //the free list, then set the apporpriate globals
917 void special_reset_objects(void)
918 {
919         int i;
920
921         num_objects=MAX_OBJECTS;
922
923         Highest_object_index = 0;
924         Assert(Objects[0].type != OBJ_NONE);            //0 should be used
925
926         for (i=MAX_OBJECTS;i--;)
927                 if (Objects[i].type == OBJ_NONE)
928                         free_obj_list[--num_objects] = i;
929                 else
930                         if (i > Highest_object_index)
931                                 Highest_object_index = i;
932 }
933
934 #ifndef NDEBUG
935 int is_object_in_seg( int segnum, int objn )
936 {
937         int objnum, count = 0;
938
939         for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next)    {
940                 if ( count > MAX_OBJECTS )      {
941                         Int3();
942                         return count;
943                 }
944                 if ( objnum==objn ) count++;
945         }
946          return count;
947 }
948
949 int search_all_segments_for_object( int objnum )
950 {
951         int i;
952         int count = 0;
953
954         for (i=0; i<=Highest_segment_index; i++) {
955                 count += is_object_in_seg( i, objnum );
956         }
957         return count;
958 }
959
960 void johns_obj_unlink(int segnum, int objnum)
961 {
962         object  *obj = &Objects[objnum];
963         segment *seg = &Segments[segnum];
964
965         Assert(objnum != -1);
966
967         if (obj->prev == -1)
968                 seg->objects = obj->next;
969         else
970                 Objects[obj->prev].next = obj->next;
971
972         if (obj->next != -1) Objects[obj->next].prev = obj->prev;
973 }
974
975 void remove_incorrect_objects()
976 {
977         int segnum, objnum, count;
978
979         for (segnum=0; segnum <= Highest_segment_index; segnum++) {
980                 count = 0;
981                 for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next)    {
982                         count++;
983                         #ifndef NDEBUG
984                         if ( count > MAX_OBJECTS )      {
985                                 mprintf((1, "Object list in segment %d is circular.\n", segnum ));
986                                 Int3();
987                         }
988                         #endif
989                         if (Objects[objnum].segnum != segnum )  {
990                                 #ifndef NDEBUG
991                                 mprintf((0, "Removing object %d from segment %d.\n", objnum, segnum ));
992                                 Int3();
993                                 #endif
994                                 johns_obj_unlink(segnum,objnum);
995                         }
996                 }
997         }
998 }
999
1000 void remove_all_objects_but( int segnum, int objnum )
1001 {
1002         int i;
1003
1004         for (i=0; i<=Highest_segment_index; i++) {
1005                 if (segnum != i )       {
1006                         if (is_object_in_seg( i, objnum ))      {
1007                                 johns_obj_unlink( i, objnum );
1008                         }
1009                 }
1010         }
1011 }
1012
1013 int check_duplicate_objects()
1014 {
1015         int i, count=0;
1016         
1017         for (i=0;i<=Highest_object_index;i++) {
1018                 if ( Objects[i].type != OBJ_NONE )      {
1019                         count = search_all_segments_for_object( i );
1020                         if ( count > 1 )        {
1021                                 #ifndef NDEBUG
1022                                 mprintf((1, "Object %d is in %d segments!\n", i, count ));
1023                                 Int3();
1024                                 #endif
1025                                 remove_all_objects_but( Objects[i].segnum,  i );
1026                                 return count;
1027                         }
1028                 }
1029         }
1030         return count;
1031 }
1032
1033 void list_seg_objects( int segnum )
1034 {
1035         int objnum, count = 0;
1036
1037         for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next)    {
1038                 count++;
1039                 if ( count > MAX_OBJECTS )      {
1040                         Int3();
1041                         return;
1042                 }
1043         }
1044         return;
1045
1046 }
1047 #endif
1048
1049 //link the object into the list for its segment
1050 void obj_link(int objnum,int segnum)
1051 {
1052         object *obj = &Objects[objnum];
1053
1054         Assert(objnum != -1);
1055
1056         Assert(obj->segnum == -1);
1057
1058         Assert(segnum>=0 && segnum<=Highest_segment_index);
1059
1060         obj->segnum = segnum;
1061         
1062         obj->next = Segments[segnum].objects;
1063         obj->prev = -1;
1064
1065         Segments[segnum].objects = objnum;
1066
1067         if (obj->next != -1) Objects[obj->next].prev = objnum;
1068         
1069         //list_seg_objects( segnum );
1070         //check_duplicate_objects();
1071
1072         Assert(Objects[0].next != 0);
1073         if (Objects[0].next == 0)
1074                 Objects[0].next = -1;
1075
1076         Assert(Objects[0].prev != 0);
1077         if (Objects[0].prev == 0)
1078                 Objects[0].prev = -1;
1079 }
1080
1081 void obj_unlink(int objnum)
1082 {
1083         object  *obj = &Objects[objnum];
1084         segment *seg = &Segments[obj->segnum];
1085
1086         Assert(objnum != -1);
1087
1088         if (obj->prev == -1)
1089                 seg->objects = obj->next;
1090         else
1091                 Objects[obj->prev].next = obj->next;
1092
1093         if (obj->next != -1) Objects[obj->next].prev = obj->prev;
1094
1095         obj->segnum = -1;
1096
1097         Assert(Objects[0].next != 0);
1098         Assert(Objects[0].prev != 0);
1099 }
1100
1101 int Object_next_signature = 1;  //player gets 0, others start at 1
1102
1103 int Debris_object_count=0;
1104
1105 int     Unused_object_slots;
1106
1107 //returns the number of a free object, updating Highest_object_index.
1108 //Generally, obj_create() should be called to get an object, since it
1109 //fills in important fields and does the linking.
1110 //returns -1 if no free objects
1111 int obj_allocate(void)
1112 {
1113         int objnum;
1114
1115         if ( num_objects >= MAX_OBJECTS-2 ) {
1116                 int     num_freed;
1117
1118                 num_freed = free_object_slots(MAX_OBJECTS-10);
1119                 mprintf((0, " *** Freed %i objects in frame %i\n", num_freed, FrameCount));
1120         }
1121
1122         if ( num_objects >= MAX_OBJECTS ) {
1123                 #ifndef NDEBUG
1124                 mprintf((1, "Object creation failed - too many objects!\n" ));
1125                 #endif
1126                 return -1;
1127         }
1128
1129         objnum = free_obj_list[num_objects++];
1130
1131         if (objnum > Highest_object_index) {
1132                 Highest_object_index = objnum;
1133                 if (Highest_object_index > Highest_ever_object_index)
1134                         Highest_ever_object_index = Highest_object_index;
1135         }
1136
1137 {
1138 int     i;
1139 Unused_object_slots=0;
1140 for (i=0; i<=Highest_object_index; i++)
1141         if (Objects[i].type == OBJ_NONE)
1142                 Unused_object_slots++;
1143 }
1144         return objnum;
1145 }
1146
1147 //frees up an object.  Generally, obj_delete() should be called to get
1148 //rid of an object.  This function deallocates the object entry after
1149 //the object has been unlinked
1150 void obj_free(int objnum)
1151 {
1152         free_obj_list[--num_objects] = objnum;
1153         Assert(num_objects >= 0);
1154
1155         if (objnum == Highest_object_index)
1156                 while (Objects[--Highest_object_index].type == OBJ_NONE);
1157 }
1158
1159 //-----------------------------------------------------------------------------
1160 //      Scan the object list, freeing down to num_used objects
1161 //      Returns number of slots freed.
1162 int free_object_slots(int num_used)
1163 {
1164         int     i, olind;
1165         int     obj_list[MAX_OBJECTS];
1166         int     num_already_free, num_to_free, original_num_to_free;
1167
1168         olind = 0;
1169         num_already_free = MAX_OBJECTS - Highest_object_index - 1;
1170
1171         if (MAX_OBJECTS - num_already_free < num_used)
1172                 return 0;
1173
1174         for (i=0; i<=Highest_object_index; i++) {
1175                 if (Objects[i].flags & OF_SHOULD_BE_DEAD) {
1176                         num_already_free++;
1177                         if (MAX_OBJECTS - num_already_free < num_used)
1178                                 return num_already_free;
1179                 } else
1180                         switch (Objects[i].type) {
1181                                 case OBJ_NONE:
1182                                         num_already_free++;
1183                                         if (MAX_OBJECTS - num_already_free < num_used)
1184                                                 return 0;
1185                                         break;
1186                                 case OBJ_WALL:
1187                                 case OBJ_FLARE:
1188                                         Int3();         //      This is curious.  What is an object that is a wall?
1189                                         break;
1190                                 case OBJ_FIREBALL:
1191                                 case OBJ_WEAPON:
1192                                 case OBJ_DEBRIS:
1193                                         obj_list[olind++] = i;
1194                                         break;
1195                                 case OBJ_ROBOT:
1196                                 case OBJ_HOSTAGE:
1197                                 case OBJ_PLAYER:
1198                                 case OBJ_CNTRLCEN:
1199                                 case OBJ_CLUTTER:
1200                                 case OBJ_GHOST:
1201                                 case OBJ_LIGHT:
1202                                 case OBJ_CAMERA:
1203                                 case OBJ_POWERUP:
1204                                         break;
1205                         }
1206
1207         }
1208
1209         num_to_free = MAX_OBJECTS - num_used - num_already_free;
1210         original_num_to_free = num_to_free;
1211
1212         if (num_to_free > olind) {
1213                 mprintf((1, "Warning: Asked to free %i objects, but can only free %i.\n", num_to_free, olind));
1214                 num_to_free = olind;
1215         }
1216
1217         for (i=0; i<num_to_free; i++)
1218                 if (Objects[obj_list[i]].type == OBJ_DEBRIS) {
1219                         num_to_free--;
1220                         mprintf((0, "Freeing   DEBRIS object %3i\n", obj_list[i]));
1221                         Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1222                 }
1223
1224         if (!num_to_free)
1225                 return original_num_to_free;
1226
1227         for (i=0; i<num_to_free; i++)
1228                 if (Objects[obj_list[i]].type == OBJ_FIREBALL  &&  Objects[obj_list[i]].ctype.expl_info.delete_objnum==-1) {
1229                         num_to_free--;
1230                         mprintf((0, "Freeing FIREBALL object %3i\n", obj_list[i]));
1231                         Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1232                 }
1233
1234         if (!num_to_free)
1235                 return original_num_to_free;
1236
1237         for (i=0; i<num_to_free; i++)
1238                 if ((Objects[obj_list[i]].type == OBJ_WEAPON) && (Objects[obj_list[i]].id == FLARE_ID)) {
1239                         num_to_free--;
1240                         Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1241                 }
1242
1243         if (!num_to_free)
1244                 return original_num_to_free;
1245
1246         for (i=0; i<num_to_free; i++)
1247                 if ((Objects[obj_list[i]].type == OBJ_WEAPON) && (Objects[obj_list[i]].id != FLARE_ID)) {
1248                         num_to_free--;
1249                         mprintf((0, "Freeing   WEAPON object %3i\n", obj_list[i]));
1250                         Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1251                 }
1252
1253         return original_num_to_free - num_to_free;
1254 }
1255
1256 //-----------------------------------------------------------------------------
1257 //initialize a new object.  adds to the list for the given segment
1258 //note that segnum is really just a suggestion, since this routine actually
1259 //searches for the correct segment
1260 //returns the object number
1261 int obj_create(ubyte type,ubyte id,int segnum,vms_vector *pos,
1262                                 vms_matrix *orient,fix size,ubyte ctype,ubyte mtype,ubyte rtype)
1263 {
1264         int objnum;
1265         object *obj;
1266
1267         Assert(segnum <= Highest_segment_index);
1268         Assert (segnum >= 0);
1269         Assert(ctype <= CT_CNTRLCEN);
1270
1271         if (type==OBJ_DEBRIS && Debris_object_count>=Max_debris_objects)
1272                 return -1;
1273
1274         if (get_seg_masks(pos, segnum, 0, __FILE__, __LINE__).centermask != 0)
1275                 if ((segnum=find_point_seg(pos,segnum))==-1) {
1276                         #ifndef NDEBUG
1277                         mprintf((0,"Bad segnum in obj_create (type=%d)\n",type));
1278                         #endif
1279                         return -1;              //don't create this object
1280                 }
1281
1282         // Find next free object
1283         objnum = obj_allocate();
1284
1285         if (objnum == -1)               //no free objects
1286                 return -1;
1287
1288         Assert(Objects[objnum].type == OBJ_NONE);               //make sure unused
1289
1290         obj = &Objects[objnum];
1291
1292         Assert(obj->segnum == -1);
1293
1294         // Zero out object structure to keep weird bugs from happening
1295         // in uninitialized fields.
1296         memset( obj, 0, sizeof(object) );
1297
1298         obj->signature                          = Object_next_signature++;
1299         obj->type                                       = type;
1300         obj->id                                                 = id;
1301         obj->last_pos                           = *pos;
1302         obj->pos                                        = *pos;
1303         obj->size                                       = size;
1304         obj->flags                                      = 0;
1305         //@@if (orient != NULL)
1306         //@@    obj->orient                     = *orient;
1307
1308         obj->orient                             = orient?*orient:vmd_identity_matrix;
1309
1310         obj->control_type               = ctype;
1311         obj->movement_type              = mtype;
1312         obj->render_type                        = rtype;
1313         obj->contains_type              = -1;
1314
1315         obj->lifeleft                           = IMMORTAL_TIME;                //assume immortal
1316         obj->attached_obj                       = -1;
1317
1318         if (obj->control_type == CT_POWERUP)
1319                 obj->ctype.powerup_info.count = 1;
1320
1321         // Init physics info for this object
1322         if (obj->movement_type == MT_PHYSICS) {
1323
1324                 vm_vec_zero(&obj->mtype.phys_info.velocity);
1325                 vm_vec_zero(&obj->mtype.phys_info.thrust);
1326                 vm_vec_zero(&obj->mtype.phys_info.rotvel);
1327                 vm_vec_zero(&obj->mtype.phys_info.rotthrust);
1328
1329                 obj->mtype.phys_info.mass               = 0;
1330                 obj->mtype.phys_info.drag               = 0;
1331                 obj->mtype.phys_info.brakes     = 0;
1332                 obj->mtype.phys_info.turnroll   = 0;
1333                 obj->mtype.phys_info.flags              = 0;
1334         }
1335
1336         if (obj->render_type == RT_POLYOBJ)
1337                 obj->rtype.pobj_info.tmap_override = -1;
1338
1339         obj->shields                            = 20*F1_0;
1340
1341         segnum = find_point_seg(pos,segnum);            //find correct segment
1342
1343         Assert(segnum!=-1);
1344
1345         obj->segnum = -1;                                       //set to zero by memset, above
1346         obj_link(objnum,segnum);
1347
1348         //      Set (or not) persistent bit in phys_info.
1349         if (obj->type == OBJ_WEAPON) {
1350                 Assert(obj->control_type == CT_WEAPON);
1351                 obj->mtype.phys_info.flags |= (Weapon_info[obj->id].persistent*PF_PERSISTENT);
1352                 obj->ctype.laser_info.creation_time = GameTime;
1353                 obj->ctype.laser_info.last_hitobj = -1;
1354                 obj->ctype.laser_info.multiplier = F1_0;
1355         }
1356
1357         if (obj->control_type == CT_POWERUP)
1358                 obj->ctype.powerup_info.creation_time = GameTime;
1359
1360         if (obj->control_type == CT_EXPLOSION)
1361                 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
1362
1363         #ifndef NDEBUG
1364         if (print_object_info)  
1365                 mprintf( (0, "Created object %d of type %d\n", objnum, obj->type ));
1366         #endif
1367
1368         if (obj->type == OBJ_DEBRIS)
1369                 Debris_object_count++;
1370
1371         return objnum;
1372 }
1373
1374 #ifdef EDITOR
1375 //create a copy of an object. returns new object number
1376 int obj_create_copy(int objnum, vms_vector *new_pos, int newsegnum)
1377 {
1378         int newobjnum;
1379         object *obj;
1380
1381         // Find next free object
1382         newobjnum = obj_allocate();
1383
1384         if (newobjnum == -1)
1385                 return -1;
1386
1387         obj = &Objects[newobjnum];
1388
1389         *obj = Objects[objnum];
1390
1391         obj->pos = obj->last_pos = *new_pos;
1392
1393         obj->next = obj->prev = obj->segnum = -1;
1394
1395         obj_link(newobjnum,newsegnum);
1396
1397         obj->signature                          = Object_next_signature++;
1398
1399         //we probably should initialize sub-structures here
1400
1401         return newobjnum;
1402
1403 }
1404 #endif
1405
1406 extern void newdemo_record_guided_end();
1407
1408 //remove object from the world
1409 void obj_delete(int objnum)
1410 {
1411         int pnum;
1412         object *obj = &Objects[objnum];
1413
1414         Assert(objnum != -1);
1415         Assert(objnum != 0 );
1416         Assert(obj->type != OBJ_NONE);
1417         Assert(obj != ConsoleObject);
1418
1419         if (obj->type==OBJ_WEAPON && obj->id==GUIDEDMISS_ID) {
1420                 pnum=Objects[obj->ctype.laser_info.parent_num].id;
1421                 mprintf ((0,"Deleting a guided missile! Player %d\n\n",pnum));
1422
1423                 if (pnum!=Player_num) {
1424                         mprintf ((0,"deleting missile that belongs to %d (%s)!\n",pnum,Players[pnum].callsign));
1425                         Guided_missile[pnum]=NULL;
1426                 }
1427                 else if (Newdemo_state==ND_STATE_RECORDING)
1428                         newdemo_record_guided_end();
1429                 
1430         }
1431
1432         if (obj == Viewer)              //deleting the viewer?
1433                 Viewer = ConsoleObject;                                         //..make the player the viewer
1434
1435         if (obj->flags & OF_ATTACHED)           //detach this from object
1436                 obj_detach_one(obj);
1437
1438         if (obj->attached_obj != -1)            //detach all objects from this
1439                 obj_detach_all(obj);
1440
1441         #if !defined(NDEBUG) && !defined(NMONO)
1442         if (print_object_info) mprintf( (0, "Deleting object %d of type %d\n", objnum, Objects[objnum].type ));
1443         #endif
1444
1445         if (obj->type == OBJ_DEBRIS)
1446                 Debris_object_count--;
1447
1448         obj_unlink(objnum);
1449
1450         Assert(Objects[0].next != 0);
1451
1452         obj->type = OBJ_NONE;           //unused!
1453         obj->signature = -1;
1454         obj->segnum=-1;                         // zero it!
1455
1456         obj_free(objnum);
1457 }
1458
1459 #define DEATH_SEQUENCE_LENGTH                   (F1_0*5)
1460 #define DEATH_SEQUENCE_EXPLODE_TIME     (F1_0*2)
1461
1462 int             Player_is_dead = 0;                     //      If !0, then player is dead, but game continues so he can watch.
1463 object  *Dead_player_camera = NULL;     //      Object index of object watching deader.
1464 fix             Player_time_of_death;           //      Time at which player died.
1465 object  *Viewer_save;
1466 int             Player_flags_save;
1467 int             Player_exploded = 0;
1468 int             Death_sequence_aborted=0;
1469 int             Player_eggs_dropped=0;
1470 fix             Camera_to_player_dist_goal=F1_0*4;
1471
1472 ubyte           Control_type_save, Render_type_save;
1473 extern  int Cockpit_mode_save;  //set while in letterbox or rear view, or -1
1474
1475 //      ------------------------------------------------------------------------------------------------------------------
1476 void dead_player_end(void)
1477 {
1478         if (!Player_is_dead)
1479                 return;
1480
1481         if (Newdemo_state == ND_STATE_RECORDING)
1482                 newdemo_record_restore_cockpit();
1483
1484         Player_is_dead = 0;
1485         Player_exploded = 0;
1486         obj_delete(Dead_player_camera-Objects);
1487         Dead_player_camera = NULL;
1488         select_cockpit(Cockpit_mode_save);
1489         Cockpit_mode_save = -1;
1490         Viewer = Viewer_save;
1491         ConsoleObject->type = OBJ_PLAYER;
1492         ConsoleObject->flags = Player_flags_save;
1493
1494         Assert((Control_type_save == CT_FLYING) || (Control_type_save == CT_SLEW));
1495
1496         ConsoleObject->control_type = Control_type_save;
1497         ConsoleObject->render_type = Render_type_save;
1498         Players[Player_num].flags &= ~PLAYER_FLAGS_INVULNERABLE;
1499         Player_eggs_dropped = 0;
1500
1501 }
1502
1503 //      ------------------------------------------------------------------------------------------------------------------
1504 //      Camera is less than size of player away from
1505 void set_camera_pos(vms_vector *camera_pos, object *objp)
1506 {
1507         int     count = 0;
1508         fix     camera_player_dist;
1509         fix     far_scale;
1510
1511         camera_player_dist = vm_vec_dist_quick(camera_pos, &objp->pos);
1512
1513         if (camera_player_dist < Camera_to_player_dist_goal) { //2*objp->size) {
1514                 //      Camera is too close to player object, so move it away.
1515                 vms_vector      player_camera_vec;
1516                 fvi_query       fq;
1517                 fvi_info                hit_data;
1518                 vms_vector      local_p1;
1519
1520                 vm_vec_sub(&player_camera_vec, camera_pos, &objp->pos);
1521                 if ((player_camera_vec.x == 0) && (player_camera_vec.y == 0) && (player_camera_vec.z == 0))
1522                         player_camera_vec.x += F1_0/16;
1523
1524                 hit_data.hit_type = HIT_WALL;
1525                 far_scale = F1_0;
1526
1527                 while ((hit_data.hit_type != HIT_NONE) && (count++ < 6)) {
1528                         vms_vector      closer_p1;
1529                         vm_vec_normalize_quick(&player_camera_vec);
1530                         vm_vec_scale(&player_camera_vec, Camera_to_player_dist_goal);
1531
1532                         fq.p0 = &objp->pos;
1533                         vm_vec_add(&closer_p1, &objp->pos, &player_camera_vec);         //      This is the actual point we want to put the camera at.
1534                         vm_vec_scale(&player_camera_vec, far_scale);                                            //      ...but find a point 50% further away...
1535                         vm_vec_add(&local_p1, &objp->pos, &player_camera_vec);          //      ...so we won't have to do as many cuts.
1536
1537                         fq.p1 = &local_p1;
1538                         fq.startseg = objp->segnum;
1539                         fq.rad = 0;
1540                         fq.thisobjnum = objp-Objects;
1541                         fq.ignore_obj_list = NULL;
1542                         fq.flags = 0;
1543                         find_vector_intersection( &fq, &hit_data);
1544
1545                         if (hit_data.hit_type == HIT_NONE) {
1546                                 *camera_pos = closer_p1;
1547                         } else {
1548                                 make_random_vector(&player_camera_vec);
1549                                 far_scale = 3*F1_0/2;
1550                         }
1551                 }
1552         }
1553 }
1554
1555 extern void drop_player_eggs(object *objp);
1556 extern int get_explosion_vclip(object *obj,int stage);
1557 extern void multi_cap_objects();
1558 extern int Proximity_dropped,Smartmines_dropped;
1559
1560 //      ------------------------------------------------------------------------------------------------------------------
1561 void dead_player_frame(void)
1562 {
1563         fix     time_dead;
1564         vms_vector      fvec;
1565
1566         if (Player_is_dead) {
1567                 time_dead = GameTime - Player_time_of_death;
1568
1569                 //      If unable to create camera at time of death, create now.
1570                 if (Dead_player_camera == Viewer_save) {
1571                         int             objnum;
1572                         object  *player = &Objects[Players[Player_num].objnum];
1573
1574                         // this next line was changed by WraithX, instead of CT_FLYING, it was CT_NONE: instead of MT_PHYSICS, it was MT_NONE.
1575                         objnum = obj_create(OBJ_CAMERA, 0, player->segnum, &player->pos, &player->orient, 0, CT_FLYING, MT_PHYSICS, RT_NONE);
1576
1577                         mprintf((0, "Creating new dead player camera.\n"));
1578                         if (objnum != -1)
1579                                 Viewer = Dead_player_camera = &Objects[objnum];
1580                         else {
1581                                 mprintf((1, "Can't create dead player camera.\n"));
1582                                 Int3();
1583                         }
1584                 }               
1585
1586                 ConsoleObject->mtype.phys_info.rotvel.x = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/4;
1587                 ConsoleObject->mtype.phys_info.rotvel.y = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/2;
1588                 ConsoleObject->mtype.phys_info.rotvel.z = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/3;
1589
1590                 Camera_to_player_dist_goal = min(time_dead*8, F1_0*20) + ConsoleObject->size;
1591
1592                 set_camera_pos(&Dead_player_camera->pos, ConsoleObject);
1593
1594                 // the following line uncommented by WraithX, 4-12-00
1595                 if (time_dead < DEATH_SEQUENCE_EXPLODE_TIME + F1_0 * 2)
1596                 {
1597                         vm_vec_sub(&fvec, &ConsoleObject->pos, &Dead_player_camera->pos);
1598                         vm_vector_2_matrix(&Dead_player_camera->orient, &fvec, NULL, NULL);
1599                         Dead_player_camera->mtype.phys_info = ConsoleObject->mtype.phys_info;
1600
1601                         // the following "if" added by WraithX to get rid of camera "wiggle"
1602                         if (Dead_player_camera->mtype.phys_info.flags & PF_WIGGLE)
1603                         {
1604                                 Dead_player_camera->mtype.phys_info.flags = (Dead_player_camera->mtype.phys_info.flags & ~PF_WIGGLE);
1605                         }// end "if" added by WraithX, 4/13/00
1606
1607                 // the following line uncommented by WraithX, 4-12-00
1608                 }
1609                 else
1610                 {
1611                         // the following line uncommented by WraithX, 4-11-00
1612                         Dead_player_camera->movement_type = MT_PHYSICS;
1613                         //Dead_player_camera->mtype.phys_info.rotvel.y = F1_0/8;
1614                 // the following line uncommented by WraithX, 4-12-00
1615                 }
1616                 // end addition by WX
1617
1618                 if (time_dead > DEATH_SEQUENCE_EXPLODE_TIME) {
1619                         if (!Player_exploded) {
1620
1621                         if (Players[Player_num].hostages_on_board > 1)
1622                                 HUD_init_message(TXT_SHIP_DESTROYED_2, Players[Player_num].hostages_on_board);
1623                         else if (Players[Player_num].hostages_on_board == 1)
1624                                 HUD_init_message(TXT_SHIP_DESTROYED_1);
1625                         else
1626                                 HUD_init_message(TXT_SHIP_DESTROYED_0);
1627
1628                                 #ifdef TACTILE
1629                                         if (TactileStick)
1630                                          {
1631                                           ClearForces();
1632                                          }
1633                                 #endif
1634                                 Player_exploded = 1;
1635 #ifdef NETWORK
1636                                 if (Game_mode & GM_NETWORK)
1637                                  {
1638                                         AdjustMineSpawn ();
1639                                         multi_cap_objects();
1640                                  }
1641 #endif
1642                                 
1643                                 drop_player_eggs(ConsoleObject);
1644                                 Player_eggs_dropped = 1;
1645                                 #ifdef NETWORK
1646                                 if (Game_mode & GM_MULTI)
1647                                 {
1648                                         //multi_send_position(Players[Player_num].objnum);
1649                                         multi_send_player_explode(MULTI_PLAYER_EXPLODE);
1650                                 }
1651                                 #endif
1652
1653                                 explode_badass_player(ConsoleObject);
1654
1655                                 //is this next line needed, given the badass call above?
1656                                 explode_object(ConsoleObject,0);
1657                                 ConsoleObject->flags &= ~OF_SHOULD_BE_DEAD;             //don't really kill player
1658                                 ConsoleObject->render_type = RT_NONE;                           //..just make him disappear
1659                                 ConsoleObject->type = OBJ_GHOST;                                                //..and kill intersections
1660                                 Players[Player_num].flags &= ~PLAYER_FLAGS_HEADLIGHT_ON;
1661                         }
1662                 } else {
1663                         if (d_rand() < FrameTime*4) {
1664                                 #ifdef NETWORK
1665                                 if (Game_mode & GM_MULTI)
1666                                         multi_send_create_explosion(Player_num);
1667                                 #endif
1668                                 create_small_fireball_on_object(ConsoleObject, F1_0, 1);
1669                         }
1670                 }
1671
1672
1673                 if (Death_sequence_aborted) { //time_dead > DEATH_SEQUENCE_LENGTH) {
1674                         if (!Player_eggs_dropped) {
1675                         
1676 #ifdef NETWORK
1677                                 if (Game_mode & GM_NETWORK)
1678                                  {
1679                                         AdjustMineSpawn();
1680                                         multi_cap_objects();
1681                                  }
1682 #endif
1683                                 
1684                                 drop_player_eggs(ConsoleObject);
1685                                 Player_eggs_dropped = 1;
1686                                 #ifdef NETWORK
1687                                 if (Game_mode & GM_MULTI)
1688                                 {
1689                                         //multi_send_position(Players[Player_num].objnum);
1690                                         multi_send_player_explode(MULTI_PLAYER_EXPLODE);
1691                                 }
1692                                 #endif
1693                         }
1694
1695                         DoPlayerDead();         //kill_player();
1696                 }
1697         }
1698 }
1699
1700
1701 void AdjustMineSpawn()
1702  {
1703    if (!(Game_mode & GM_NETWORK))
1704                 return;  // No need for this function in any other mode
1705
1706    if (!(Game_mode & GM_HOARD))
1707                 Players[Player_num].secondary_ammo[PROXIMITY_INDEX]+=Proximity_dropped;
1708    Players[Player_num].secondary_ammo[SMART_MINE_INDEX]+=Smartmines_dropped;
1709         Proximity_dropped=0;
1710         Smartmines_dropped=0;
1711  }
1712
1713
1714
1715 int Killed_in_frame = -1;
1716 short Killed_objnum = -1;
1717 extern char Multi_killed_yourself;
1718
1719 //      ------------------------------------------------------------------------------------------------------------------
1720 void start_player_death_sequence(object *player)
1721 {
1722         int     objnum;
1723
1724         Assert(player == ConsoleObject);
1725         if ((Player_is_dead != 0) || (Dead_player_camera != NULL))
1726                 return;
1727
1728         //Assert(Player_is_dead == 0);
1729         //Assert(Dead_player_camera == NULL);
1730
1731         reset_rear_view();
1732
1733         if (!(Game_mode & GM_MULTI))
1734                 HUD_clear_messages();
1735
1736         Killed_in_frame = FrameCount;
1737         Killed_objnum = player-Objects;
1738         Death_sequence_aborted = 0;
1739
1740         #ifdef NETWORK
1741         if (Game_mode & GM_MULTI)
1742         {
1743                 multi_send_kill(Players[Player_num].objnum);
1744
1745 //              If Hoard, increase number of orbs by 1
1746 //    Only if you haven't killed yourself
1747 //              This prevents cheating
1748
1749                 if (Game_mode & GM_HOARD)
1750                  if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]<12)
1751                         if (!Multi_killed_yourself)
1752                                 Players[Player_num].secondary_ammo[PROXIMITY_INDEX]++;
1753         
1754         }
1755         #endif
1756         
1757         PaletteRedAdd = 40;
1758         Player_is_dead = 1;
1759    #ifdef TACTILE
1760     if (TactileStick)
1761           Buffeting (70);
1762         #endif
1763
1764         //Players[Player_num].flags &= ~(PLAYER_FLAGS_AFTERBURNER);
1765
1766         vm_vec_zero(&player->mtype.phys_info.rotthrust);
1767         vm_vec_zero(&player->mtype.phys_info.thrust);
1768
1769         Player_time_of_death = GameTime;
1770
1771         // this next line was changed by WraithX, instead of CT_FLYING, it was CT_NONE: instead of MT_PHYSICS, it was MT_NONE.
1772         objnum = obj_create(OBJ_CAMERA, 0, player->segnum, &player->pos, &player->orient, 0, CT_FLYING, MT_PHYSICS, RT_NONE);
1773         Viewer_save = Viewer;
1774         if (objnum != -1)
1775                 Viewer = Dead_player_camera = &Objects[objnum];
1776         else {
1777                 mprintf((1, "Can't create dead player camera.\n"));
1778                 Int3();
1779                 Dead_player_camera = Viewer;
1780         }
1781
1782         if (Cockpit_mode_save == -1)            //if not already saved
1783                 Cockpit_mode_save = Cockpit_mode;
1784         select_cockpit(CM_LETTERBOX);
1785         if (Newdemo_state == ND_STATE_RECORDING)
1786                 newdemo_record_letterbox();
1787
1788         Player_flags_save = player->flags;
1789         Control_type_save = player->control_type;
1790         Render_type_save = player->render_type;
1791
1792         player->flags &= ~OF_SHOULD_BE_DEAD;
1793 //      Players[Player_num].flags |= PLAYER_FLAGS_INVULNERABLE;
1794         player->control_type = CT_FLYING;  // change from CT_NONE to CT_FLYING by WraithX
1795         player->shields = F1_0*1000;
1796
1797         PALETTE_FLASH_SET(0,0,0);
1798 }
1799
1800 //      ------------------------------------------------------------------------------------------------------------------
1801 void obj_delete_all_that_should_be_dead()
1802 {
1803         int i;
1804         object *objp;
1805         int             local_dead_player_object=-1;
1806
1807         // Move all objects
1808         objp = Objects;
1809
1810         for (i=0;i<=Highest_object_index;i++) {
1811                 if ((objp->type!=OBJ_NONE) && (objp->flags&OF_SHOULD_BE_DEAD) ) {
1812                         Assert(!(objp->type==OBJ_FIREBALL && objp->ctype.expl_info.delete_time!=-1));
1813                         if (objp->type==OBJ_PLAYER) {
1814                                 if ( objp->id == Player_num ) {
1815                                         if (local_dead_player_object == -1) {
1816                                                 start_player_death_sequence(objp);
1817                                                 local_dead_player_object = objp-Objects;
1818                                         } else
1819                                                 Int3(); //      Contact Mike: Illegal, killed player twice in this frame!
1820                                                                         // Ok to continue, won't start death sequence again!
1821                                         // kill_player();
1822                                 }
1823                         } else {                                        
1824                                 obj_delete(i);
1825                         }
1826                 }
1827                 objp++;
1828         }
1829 }
1830
1831 //when an object has moved into a new segment, this function unlinks it
1832 //from its old segment, and links it into the new segment
1833 void obj_relink(int objnum,int newsegnum)
1834 {
1835
1836         Assert((objnum >= 0) && (objnum <= Highest_object_index));
1837         Assert((newsegnum <= Highest_segment_index) && (newsegnum >= 0));
1838
1839         obj_unlink(objnum);
1840
1841         obj_link(objnum,newsegnum);
1842         
1843 #ifndef NDEBUG
1844         if (get_seg_masks(&Objects[objnum].pos, Objects[objnum].segnum, 0, __FILE__, __LINE__).centermask != 0)
1845                 mprintf((1, "obj_relink violates seg masks.\n"));
1846 #endif
1847 }
1848
1849 //process a continuously-spinning object
1850 void
1851 spin_object(object *obj)
1852 {
1853         vms_angvec rotangs;
1854         vms_matrix rotmat, new_pm;
1855
1856         Assert(obj->movement_type == MT_SPINNING);
1857
1858         rotangs.p = fixmul(obj->mtype.spin_rate.x,FrameTime);
1859         rotangs.h = fixmul(obj->mtype.spin_rate.y,FrameTime);
1860         rotangs.b = fixmul(obj->mtype.spin_rate.z,FrameTime);
1861
1862         vm_angles_2_matrix(&rotmat,&rotangs);
1863
1864         vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat);
1865         obj->orient = new_pm;
1866
1867         check_and_fix_matrix(&obj->orient);
1868 }
1869
1870 int Drop_afterburner_blob_flag;         //ugly hack
1871 extern void multi_send_drop_blobs(char);
1872 extern void fuelcen_check_for_goal (segment *);
1873
1874 //see if wall is volatile, and if so, cause damage to player
1875 //returns true if player is in lava
1876 int check_volatile_wall(object *obj,int segnum,int sidenum,vms_vector *hitpt);
1877
1878 //      Time at which this object last created afterburner blobs.
1879 fix     Last_afterburner_time[MAX_OBJECTS];
1880
1881 //--------------------------------------------------------------------
1882 //move an object for the current frame
1883 void object_move_one( object * obj )
1884 {
1885
1886         #ifndef DEMO_ONLY
1887
1888         int     previous_segment = obj->segnum;
1889
1890         obj->last_pos = obj->pos;                       // Save the current position
1891
1892         if ((obj->type==OBJ_PLAYER) && (Player_num==obj->id))   {
1893                 fix fuel, shields;
1894                 
1895 #ifdef NETWORK
1896       if (Game_mode & GM_CAPTURE)
1897                          fuelcen_check_for_goal (&Segments[obj->segnum]);
1898       if (Game_mode & GM_HOARD)
1899                          fuelcen_check_for_hoard_goal (&Segments[obj->segnum]);
1900 #endif
1901
1902                 fuel=fuelcen_give_fuel( &Segments[obj->segnum], INITIAL_ENERGY-Players[Player_num].energy );
1903                 if (fuel > 0 )  {
1904                         Players[Player_num].energy += fuel;
1905                 }
1906
1907                 shields = repaircen_give_shields( &Segments[obj->segnum], INITIAL_ENERGY-Players[Player_num].energy );
1908                 if (shields > 0) {
1909                         Players[Player_num].shields += shields;
1910                 }
1911         }
1912
1913         if (obj->lifeleft != IMMORTAL_TIME) {   //if not immortal...
1914                 //      Ok, this is a big hack by MK.
1915                 //      If you want an object to last for exactly one frame, then give it a lifeleft of ONE_FRAME_TIME.
1916                 if (obj->lifeleft != ONE_FRAME_TIME)
1917                         obj->lifeleft -= FrameTime;             //...inevitable countdown towards death
1918         }
1919
1920         Drop_afterburner_blob_flag = 0;
1921
1922         switch (obj->control_type) {
1923
1924                 case CT_NONE: break;
1925
1926                 case CT_FLYING:
1927
1928                         #if !defined(NDEBUG) && !defined(NMONO)
1929                         if (print_object_info>1) mprintf( (0, "Moving player object #%d\n", obj-Objects ));
1930                         #endif
1931
1932                         read_flying_controls( obj );
1933
1934                         break;
1935
1936                 case CT_REPAIRCEN: Int3();      // -- hey! these are no longer supported!! -- do_repair_sequence(obj); break;
1937
1938                 case CT_POWERUP: do_powerup_frame(obj); break;
1939         
1940                 case CT_MORPH:                  //morph implies AI
1941                         do_morph_frame(obj);
1942                         //NOTE: FALLS INTO AI HERE!!!!
1943
1944                 case CT_AI:
1945                         //NOTE LINK TO CT_MORPH ABOVE!!!
1946                         if (Game_suspended & SUSP_ROBOTS) return;
1947 #if !defined(NDEBUG) && !defined(NMONO)
1948                         if (print_object_info>1)
1949                                 mprintf( (0, "AI: Moving robot object #%d\n",obj-Objects ));
1950 #endif
1951                         do_ai_frame(obj);
1952                         break;
1953
1954                 case CT_WEAPON:         Laser_do_weapon_sequence(obj); break;
1955                 case CT_EXPLOSION:      do_explosion_sequence(obj); break;
1956
1957                 #ifndef RELEASE
1958                 case CT_SLEW:
1959                         if ( keyd_pressed[KEY_PAD5] ) slew_stop( obj );
1960                         if ( keyd_pressed[KEY_NUMLOCK] )                {
1961                                 slew_reset_orient( obj );
1962                                 * (ubyte *) 0x417 &= ~0x20;             //kill numlock
1963                         }
1964                         slew_frame(0 );         // Does velocity addition for us.
1965                         break;
1966                 #endif
1967
1968
1969 //              case CT_FLYTHROUGH:
1970 //                      do_flythrough(obj,0);                   // HACK:do_flythrough should operate on an object!!!!
1971 //                      //check_object_seg(obj);
1972 //                      return; // DON'T DO THE REST OF OBJECT STUFF SINCE THIS IS A SPECIAL CASE!!!
1973 //                      break;
1974
1975                 case CT_DEBRIS: do_debris_frame(obj); break;
1976
1977                 case CT_LIGHT: break;           //doesn't do anything
1978
1979                 case CT_REMOTE: break;          //movement is handled in com_process_input
1980
1981                 case CT_CNTRLCEN: do_controlcen_frame(obj); break;
1982
1983                 default:
1984
1985 #ifdef __DJGPP__
1986                         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);
1987 #else
1988                         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);
1989 #endif
1990
1991                         break;
1992
1993         }
1994
1995         if (obj->lifeleft < 0 ) {               // We died of old age
1996                 obj->flags |= OF_SHOULD_BE_DEAD;
1997                 if ( obj->type==OBJ_WEAPON && Weapon_info[obj->id].damage_radius )
1998                         explode_badass_weapon(obj,&obj->pos);
1999                 else if ( obj->type==OBJ_ROBOT) //make robots explode
2000                         explode_object(obj,0);
2001         }
2002
2003         if (obj->type == OBJ_NONE)
2004                 return;         // object has been deleted
2005
2006         // stay around if !dead, for WraithX's death-cam
2007         if (!Player_is_dead && obj->flags&OF_SHOULD_BE_DEAD)
2008                 return;
2009
2010         switch (obj->movement_type) {
2011
2012                 case MT_NONE:                   break;                                                          //this doesn't move
2013
2014                 case MT_PHYSICS:                do_physics_sim(obj);    break;  //move by physics
2015
2016                 case MT_SPINNING:               spin_object(obj); break;
2017
2018         }
2019
2020         //      If player and moved to another segment, see if hit any triggers.
2021         // also check in player under a lavafall
2022         if (obj->type == OBJ_PLAYER && obj->movement_type==MT_PHYSICS)  {
2023
2024                 if (previous_segment != obj->segnum) {
2025                         int     connect_side,i;
2026 #ifdef NETWORK
2027                         int     old_level = Current_level_num;
2028 #endif
2029                         for (i=0;i<n_phys_segs-1;i++) {
2030                                 connect_side = find_connect_side(&Segments[phys_seglist[i+1]], &Segments[phys_seglist[i]]);
2031                                 if (connect_side != -1)
2032                                         check_trigger(&Segments[phys_seglist[i]], connect_side, obj-Objects,0);
2033                                 #ifndef NDEBUG
2034                                 else {  // segments are not directly connected, so do binary subdivision until you find connected segments.
2035                                         mprintf((1, "UNCONNECTED SEGMENTS %d,%d\n",phys_seglist[i+1],phys_seglist[i]));
2036                                         // -- Unnecessary, MK, 09/04/95 -- Int3();
2037                                 }
2038                                 #endif
2039
2040                                 //maybe we've gone on to the next level.  if so, bail!
2041 #ifdef NETWORK
2042                                 if (Current_level_num != old_level)
2043                                         return;
2044 #endif
2045                         }
2046                 }
2047
2048                 {
2049                         int sidemask,under_lavafall=0;
2050                         static int lavafall_hiss_playing[MAX_PLAYERS]={0};
2051
2052                         sidemask = get_seg_masks(&obj->pos, obj->segnum, obj->size, __FILE__, __LINE__).sidemask;
2053                         if (sidemask) {
2054                                 int sidenum,bit,wall_num;
2055         
2056                                 for (sidenum=0,bit=1;sidenum<6;bit<<=1,sidenum++)
2057                                         if ((sidemask & bit) && ((wall_num=Segments[obj->segnum].sides[sidenum].wall_num)!=-1) && Walls[wall_num].type==WALL_ILLUSION) {
2058                                                 int type;
2059                                                 if ((type=check_volatile_wall(obj,obj->segnum,sidenum,&obj->pos))!=0) {
2060                                                         int sound = (type==1)?SOUND_LAVAFALL_HISS:SOUND_SHIP_IN_WATERFALL;
2061                                                         under_lavafall = 1;
2062                                                         if (!lavafall_hiss_playing[obj->id]) {
2063                                                                 digi_link_sound_to_object3( sound, obj-Objects, 1, F1_0, i2f(256), -1, -1);
2064                                                                 lavafall_hiss_playing[obj->id] = 1;
2065                                                         }
2066                                                 }
2067                                         }
2068                         }
2069         
2070                         if (!under_lavafall && lavafall_hiss_playing[obj->id]) {
2071                                 digi_kill_sound_linked_to_object( obj-Objects);
2072                                 lavafall_hiss_playing[obj->id] = 0;
2073                         }
2074                 }
2075         }
2076
2077         //see if guided missile has flown through exit trigger
2078         if (obj==Guided_missile[Player_num] && obj->signature==Guided_missile_sig[Player_num]) {
2079                 if (previous_segment != obj->segnum) {
2080                         int     connect_side;
2081                         connect_side = find_connect_side(&Segments[obj->segnum], &Segments[previous_segment]);
2082                         if (connect_side != -1) {
2083                                 int wall_num,trigger_num;
2084                                 wall_num = Segments[previous_segment].sides[connect_side].wall_num;
2085                                 if ( wall_num != -1 ) {
2086                                         trigger_num = Walls[wall_num].trigger;
2087                                         if (trigger_num != -1)
2088                                                 if (Triggers[trigger_num].type == TT_EXIT)
2089                                                         Guided_missile[Player_num]->lifeleft = 0;
2090                                 }
2091                         }
2092                 }
2093         }
2094
2095         if (Drop_afterburner_blob_flag) {
2096                 Assert(obj==ConsoleObject);
2097                 drop_afterburner_blobs(obj, 2, i2f(5)/2, -1);   //      -1 means use default lifetime
2098 #ifdef NETWORK
2099                 if (Game_mode & GM_MULTI)
2100                         multi_send_drop_blobs(Player_num);
2101 #endif
2102                 Drop_afterburner_blob_flag = 0;
2103         }
2104
2105         if ((obj->type == OBJ_WEAPON) && (Weapon_info[obj->id].afterburner_size)) {
2106                 int     objnum = obj-Objects;
2107                 fix     vel = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
2108                 fix     delay, lifetime;
2109
2110                 if (vel > F1_0*200)
2111                         delay = F1_0/16;
2112                 else if (vel > F1_0*40)
2113                         delay = fixdiv(F1_0*13,vel);
2114                 else
2115                         delay = F1_0/4;
2116
2117                 lifetime = (delay * 3)/2;
2118                 if (!(Game_mode & GM_MULTI)) {
2119                         delay /= 2;
2120                         lifetime *= 2;
2121                 }
2122
2123                 if ((Last_afterburner_time[objnum] + delay < GameTime) || (Last_afterburner_time[objnum] > GameTime)) {
2124                         drop_afterburner_blobs(obj, 1, i2f(Weapon_info[obj->id].afterburner_size)/16, lifetime);
2125                         Last_afterburner_time[objnum] = GameTime;
2126                 }
2127         }
2128
2129         #else
2130                 obj++;          //kill warning
2131         #endif          //DEMO_ONLY
2132 }
2133
2134 int     Max_used_objects = MAX_OBJECTS - 20;
2135
2136 //--------------------------------------------------------------------
2137 //move all objects for the current frame
2138 void object_move_all()
2139 {
2140         int i;
2141         object *objp;
2142
2143 // -- mprintf((0, "Frame %i: %i/%i objects used.\n", FrameCount, num_objects, MAX_OBJECTS));
2144
2145 //      check_duplicate_objects();
2146 //      remove_incorrect_objects();
2147
2148         if (Highest_object_index > Max_used_objects)
2149                 free_object_slots(Max_used_objects);            //      Free all possible object slots.
2150
2151         obj_delete_all_that_should_be_dead();
2152
2153         if (Auto_leveling_on)
2154                 ConsoleObject->mtype.phys_info.flags |= PF_LEVELLING;
2155         else
2156                 ConsoleObject->mtype.phys_info.flags &= ~PF_LEVELLING;
2157
2158         // Move all objects
2159         objp = Objects;
2160
2161         #ifndef DEMO_ONLY
2162         for (i=0;i<=Highest_object_index;i++) {
2163                 if ( (objp->type != OBJ_NONE) && (!(objp->flags&OF_SHOULD_BE_DEAD)) )   {
2164                         object_move_one( objp );
2165                 }
2166                 objp++;
2167         }
2168         #else
2169                 i=0;    //kill warning
2170         #endif
2171
2172 //      check_duplicate_objects();
2173 //      remove_incorrect_objects();
2174
2175 }
2176
2177
2178 //--unused-- // -----------------------------------------------------------
2179 //--unused-- // Moved here from eobject.c on 02/09/94 by MK.
2180 //--unused-- int find_last_obj(int i)
2181 //--unused-- {
2182 //--unused--    for (i=MAX_OBJECTS;--i>=0;)
2183 //--unused--            if (Objects[i].type != OBJ_NONE) break;
2184 //--unused--
2185 //--unused--    return i;
2186 //--unused--
2187 //--unused-- }
2188
2189
2190 //make object array non-sparse
2191 void compress_objects(void)
2192 {
2193         int start_i;    //,last_i;
2194
2195         //last_i = find_last_obj(MAX_OBJECTS);
2196
2197         //      Note: It's proper to do < (rather than <=) Highest_object_index here because we
2198         //      are just removing gaps, and the last object can't be a gap.
2199         for (start_i=0;start_i<Highest_object_index;start_i++)
2200
2201                 if (Objects[start_i].type == OBJ_NONE) {
2202
2203                         int     segnum_copy;
2204
2205                         segnum_copy = Objects[Highest_object_index].segnum;
2206
2207                         obj_unlink(Highest_object_index);
2208
2209                         Objects[start_i] = Objects[Highest_object_index];
2210
2211                         #ifdef EDITOR
2212                         if (Cur_object_index == Highest_object_index)
2213                                 Cur_object_index = start_i;
2214                         #endif
2215
2216                         Objects[Highest_object_index].type = OBJ_NONE;
2217
2218                         obj_link(start_i,segnum_copy);
2219
2220                         while (Objects[--Highest_object_index].type == OBJ_NONE);
2221
2222                         //last_i = find_last_obj(last_i);
2223                         
2224                 }
2225
2226         reset_objects(num_objects);
2227
2228 }
2229
2230 //called after load.  Takes number of objects,  and objects should be
2231 //compressed.  resets free list, marks unused objects as unused
2232 void reset_objects(int n_objs)
2233 {
2234         int i;
2235
2236         num_objects = n_objs;
2237
2238         Assert(num_objects>0);
2239
2240         for (i=num_objects;i<MAX_OBJECTS;i++) {
2241                 free_obj_list[i] = i;
2242                 Objects[i].type = OBJ_NONE;
2243                 Objects[i].segnum = -1;
2244         }
2245
2246         Highest_object_index = num_objects-1;
2247
2248         Debris_object_count = 0;
2249 }
2250
2251 //Tries to find a segment for an object, using find_point_seg()
2252 int find_object_seg(object * obj )
2253 {
2254         return find_point_seg(&obj->pos,obj->segnum);
2255 }
2256
2257
2258 //If an object is in a segment, set its segnum field and make sure it's
2259 //properly linked.  If not in any segment, returns 0, else 1.
2260 //callers should generally use find_vector_intersection()
2261 int update_object_seg(object * obj )
2262 {
2263         int newseg;
2264
2265         newseg = find_object_seg(obj);
2266
2267         if (newseg == -1)
2268                 return 0;
2269
2270         if ( newseg != obj->segnum )
2271                 obj_relink(obj-Objects, newseg );
2272
2273         return 1;
2274 }
2275
2276
2277 //go through all objects and make sure they have the correct segment numbers
2278 void
2279 fix_object_segs()
2280 {
2281         int i;
2282
2283         for (i=0;i<=Highest_object_index;i++)
2284                 if (Objects[i].type != OBJ_NONE)
2285                         if (update_object_seg(&Objects[i]) == 0) {
2286                                 mprintf((1,"Cannot find segment for object %d in fix_object_segs()\n"));
2287                                 Int3();
2288                                 compute_segment_center(&Objects[i].pos,&Segments[Objects[i].segnum]);
2289                         }
2290 }
2291
2292
2293 //--unused-- void object_use_new_object_list( object * new_list )
2294 //--unused-- {
2295 //--unused--    int i, segnum;
2296 //--unused--    object *obj;
2297 //--unused--
2298 //--unused--    // First, unlink all the old objects for the segments array
2299 //--unused--    for (segnum=0; segnum <= Highest_segment_index; segnum++) {
2300 //--unused--            Segments[segnum].objects = -1;
2301 //--unused--    }
2302 //--unused--    // Then, erase all the objects
2303 //--unused--    reset_objects(1);
2304 //--unused--
2305 //--unused--    // Fill in the object array
2306 //--unused--    memcpy( Objects, new_list, sizeof(object)*MAX_OBJECTS );
2307 //--unused--
2308 //--unused--    Highest_object_index=-1;
2309 //--unused--
2310 //--unused--    // Relink 'em
2311 //--unused--    for (i=0; i<MAX_OBJECTS; i++ )  {
2312 //--unused--            obj = &Objects[i];
2313 //--unused--            if ( obj->type != OBJ_NONE )    {
2314 //--unused--                    num_objects++;
2315 //--unused--                    Highest_object_index = i;
2316 //--unused--                    segnum = obj->segnum;
2317 //--unused--                    obj->next = obj->prev = obj->segnum = -1;
2318 //--unused--                    obj_link(i,segnum);
2319 //--unused--            } else {
2320 //--unused--                    obj->next = obj->prev = obj->segnum = -1;
2321 //--unused--            }
2322 //--unused--    }
2323 //--unused--    
2324 //--unused-- }
2325
2326 //delete objects, such as weapons & explosions, that shouldn't stay between levels
2327 //      Changed by MK on 10/15/94, don't remove proximity bombs.
2328 //if clear_all is set, clear even proximity bombs
2329 void clear_transient_objects(int clear_all)
2330 {
2331         int objnum;
2332         object *obj;
2333
2334         for (objnum=0,obj=&Objects[0];objnum<=Highest_object_index;objnum++,obj++)
2335                 if (((obj->type == OBJ_WEAPON) && !(Weapon_info[obj->id].flags&WIF_PLACABLE) && (clear_all || ((obj->id != PROXIMITY_ID) && (obj->id != SUPERPROX_ID)))) ||
2336                          obj->type == OBJ_FIREBALL ||
2337                          obj->type == OBJ_DEBRIS ||
2338                          obj->type == OBJ_DEBRIS ||
2339                          (obj->type!=OBJ_NONE && obj->flags & OF_EXPLODING)) {
2340
2341                         #ifndef NDEBUG
2342                         if (Objects[objnum].lifeleft > i2f(2))
2343                                 mprintf((0,"Note: Clearing object %d (type=%d, id=%d) with lifeleft=%x\n",objnum,Objects[objnum].type,Objects[objnum].id,Objects[objnum].lifeleft));
2344                         #endif
2345                         obj_delete(objnum);
2346                 }
2347                 #ifndef NDEBUG
2348                  else if (Objects[objnum].type!=OBJ_NONE && Objects[objnum].lifeleft < i2f(2))
2349                         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));
2350                 #endif
2351 }
2352
2353 //attaches an object, such as a fireball, to another object, such as a robot
2354 void obj_attach(object *parent,object *sub)
2355 {
2356         Assert(sub->type == OBJ_FIREBALL);
2357         Assert(sub->control_type == CT_EXPLOSION);
2358
2359         Assert(sub->ctype.expl_info.next_attach==-1);
2360         Assert(sub->ctype.expl_info.prev_attach==-1);
2361
2362         Assert(parent->attached_obj==-1 || Objects[parent->attached_obj].ctype.expl_info.prev_attach==-1);
2363
2364         sub->ctype.expl_info.next_attach = parent->attached_obj;
2365
2366         if (sub->ctype.expl_info.next_attach != -1)
2367                 Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach = sub-Objects;
2368
2369         parent->attached_obj = sub-Objects;
2370
2371         sub->ctype.expl_info.attach_parent = parent-Objects;
2372         sub->flags |= OF_ATTACHED;
2373
2374         Assert(sub->ctype.expl_info.next_attach != sub-Objects);
2375         Assert(sub->ctype.expl_info.prev_attach != sub-Objects);
2376 }
2377
2378 //dettaches one object
2379 void obj_detach_one(object *sub)
2380 {
2381         Assert(sub->flags & OF_ATTACHED);
2382         Assert(sub->ctype.expl_info.attach_parent != -1);
2383
2384         if ((Objects[sub->ctype.expl_info.attach_parent].type == OBJ_NONE) || (Objects[sub->ctype.expl_info.attach_parent].attached_obj == -1))
2385         {
2386                 sub->flags &= ~OF_ATTACHED;
2387                 return;
2388         }
2389
2390         if (sub->ctype.expl_info.next_attach != -1) {
2391                 Assert(Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach=sub-Objects);
2392                 Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach = sub->ctype.expl_info.prev_attach;
2393         }
2394
2395         if (sub->ctype.expl_info.prev_attach != -1) {
2396                 Assert(Objects[sub->ctype.expl_info.prev_attach].ctype.expl_info.next_attach=sub-Objects);
2397                 Objects[sub->ctype.expl_info.prev_attach].ctype.expl_info.next_attach = sub->ctype.expl_info.next_attach;
2398         }
2399         else {
2400                 Assert(Objects[sub->ctype.expl_info.attach_parent].attached_obj=sub-Objects);
2401                 Objects[sub->ctype.expl_info.attach_parent].attached_obj = sub->ctype.expl_info.next_attach;
2402         }
2403
2404         sub->ctype.expl_info.next_attach = sub->ctype.expl_info.prev_attach = -1;
2405         sub->flags &= ~OF_ATTACHED;
2406
2407 }
2408
2409 //dettaches all objects from this object
2410 void obj_detach_all(object *parent)
2411 {
2412         while (parent->attached_obj != -1)
2413                 obj_detach_one(&Objects[parent->attached_obj]);
2414 }
2415
2416 //creates a marker object in the world.  returns the object number
2417 int drop_marker_object(vms_vector *pos,int segnum,vms_matrix *orient, int marker_num)
2418 {
2419         int objnum;
2420
2421         Assert(Marker_model_num != -1);
2422
2423         objnum = obj_create(OBJ_MARKER, marker_num, segnum, pos, orient, Polygon_models[Marker_model_num].rad, CT_NONE, MT_NONE, RT_POLYOBJ);
2424
2425         if (objnum >= 0) {
2426                 object *obj = &Objects[objnum];
2427
2428                 obj->rtype.pobj_info.model_num = Marker_model_num;
2429
2430                 vm_vec_copy_scale(&obj->mtype.spin_rate,&obj->orient.uvec,F1_0/2);
2431
2432                 //      MK, 10/16/95: Using lifeleft to make it flash, thus able to trim lightlevel from all objects.
2433                 obj->lifeleft = IMMORTAL_TIME - 1;
2434         }
2435
2436         return objnum;  
2437 }
2438
2439 extern int Ai_last_missile_camera;
2440
2441 //      *viewer is a viewer, probably a missile.
2442 //      wake up all robots that were rendered last frame subject to some constraints.
2443 void wake_up_rendered_objects(object *viewer, int window_num)
2444 {
2445         int     i;
2446
2447         //      Make sure that we are processing current data.
2448         if (FrameCount != Window_rendered_data[window_num].frame) {
2449                 mprintf((1, "Warning: Called wake_up_rendered_objects with a bogus window.\n"));
2450                 return;
2451         }
2452
2453         Ai_last_missile_camera = viewer-Objects;
2454
2455         for (i=0; i<Window_rendered_data[window_num].num_objects; i++) {
2456                 int     objnum;
2457                 object *objp;
2458                 int     fcval = FrameCount & 3;
2459
2460                 objnum = Window_rendered_data[window_num].rendered_objects[i];
2461                 if ((objnum & 3) == fcval) {
2462                         objp = &Objects[objnum];
2463         
2464                         if (objp->type == OBJ_ROBOT) {
2465                                 if (vm_vec_dist_quick(&viewer->pos, &objp->pos) < F1_0*100) {
2466                                         ai_local                *ailp = &Ai_local_info[objnum];
2467                                         if (ailp->player_awareness_type == 0) {
2468                                                 objp->ctype.ai_info.SUB_FLAGS |= SUB_FLAGS_CAMERA_AWAKE;
2469                                                 ailp->player_awareness_type = PA_WEAPON_ROBOT_COLLISION;
2470                                                 ailp->player_awareness_time = F1_0*3;
2471                                                 ailp->previous_visibility = 2;
2472                                         }
2473                                 }
2474                         }
2475                 }
2476         }
2477 }
2478
2479
2480
2481
2482
2483