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