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