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