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