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