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