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