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