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