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