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