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