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