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