2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
24 #include <string.h> // for memset
85 #include "editor/editor.h"
88 void obj_detach_all(object *parent);
89 void obj_detach_one(object *sub);
90 int free_object_slots(int num_used);
96 extern sbyte WasRecorded[MAX_OBJECTS];
98 ubyte CollisionResult[MAX_OBJECT_TYPES][MAX_OBJECT_TYPES];
100 object *ConsoleObject; //the object that is the player
102 static short free_obj_list[MAX_OBJECTS];
108 //info on the various types of objects
110 object Object_minus_one;
113 object Objects[MAX_OBJECTS];
115 int Highest_object_index=0;
116 int Highest_ever_object_index=0;
118 // grs_bitmap *robot_bms[MAX_ROBOT_BITMAPS]; //all bitmaps for all robots
120 // int robot_bm_nums[MAX_ROBOT_TYPES]; //starting bitmap num for each robot
121 // int robot_n_bitmaps[MAX_ROBOT_TYPES]; //how many bitmaps for each robot
123 // char *robot_names[MAX_ROBOT_TYPES]; //name of each robot
125 //--unused-- int Num_robot_types=0;
127 int print_object_info = 0;
128 //@@int Object_viewer = 0;
130 //object * Slew_object = NULL; // Object containing slew object info.
132 //--unused-- int Player_controller_type = 0;
134 window_rendered_data Window_rendered_data[MAX_RENDERED_WINDOWS];
136 #if defined(EDITOR) || !defined(NDEBUG)
137 char Object_type_names[MAX_OBJECT_TYPES][9] = {
158 //set viewer object to next object in array
159 void object_goto_next_viewer()
161 int i, start_obj = 0;
163 start_obj = OBJECT_NUMBER(Viewer); // get viewer object number
165 for (i=0;i<=Highest_object_index;i++) {
168 if (start_obj > Highest_object_index ) start_obj = 0;
170 if (Objects[start_obj].type != OBJ_NONE ) {
171 Viewer = &Objects[start_obj];
176 Error( "Couldn't find a viewer object!" );
180 //set viewer object to next object in array
181 void object_goto_prev_viewer()
183 int i, start_obj = 0;
185 start_obj = OBJECT_NUMBER(Viewer); // get viewer object number
187 for (i=0; i<=Highest_object_index; i++) {
190 if (start_obj < 0 ) start_obj = Highest_object_index;
192 if (Objects[start_obj].type != OBJ_NONE ) {
193 Viewer = &Objects[start_obj];
198 Error( "Couldn't find a viewer object!" );
203 object *obj_find_first_of_type (int type)
207 for (i=0;i<=Highest_object_index;i++)
208 if (Objects[i].type==type)
209 return (&Objects[i]);
210 return ((object *)NULL);
213 int obj_return_num_of_type (int type)
217 for (i=0;i<=Highest_object_index;i++)
218 if (Objects[i].type==type)
222 int obj_return_num_of_typeid (int type,int id)
226 for (i=0;i<=Highest_object_index;i++)
227 if (Objects[i].type==type && Objects[i].id==id)
232 int global_orientation = 0;
234 //draw an object that has one bitmap & doesn't rotate
235 void draw_object_blob(object *obj,bitmap_index bmi)
238 grs_bitmap * bm = &GameBitmaps[bmi.index];
241 if (obj->type == OBJ_FIREBALL)
242 orientation = OBJECT_NUMBER(obj) & 7;
244 orientation = global_orientation;
246 PIGGY_PAGE_IN( bmi );
248 if (bm->bm_w > bm->bm_h)
250 g3_draw_bitmap(&obj->pos,obj->size,fixmuldiv(obj->size,bm->bm_h,bm->bm_w),bm, orientation);
254 g3_draw_bitmap(&obj->pos,fixmuldiv(obj->size,bm->bm_w,bm->bm_h),obj->size,bm, orientation);
257 //draw an object that is a texture-mapped rod
258 void draw_object_tmap_rod(object *obj,bitmap_index bitmapi,int lighted)
260 grs_bitmap * bitmap = &GameBitmaps[bitmapi.index];
263 vms_vector delta,top_v,bot_v;
264 g3s_point top_p,bot_p;
266 PIGGY_PAGE_IN(bitmapi);
268 bitmap->bm_handle = bitmapi.index;
270 vm_vec_copy_scale(&delta,&obj->orient.uvec,obj->size);
272 vm_vec_add(&top_v,&obj->pos,&delta);
273 vm_vec_sub(&bot_v,&obj->pos,&delta);
275 g3_rotate_point(&top_p,&top_v);
276 g3_rotate_point(&bot_p,&bot_v);
279 light = compute_object_light(obj,&top_p.p3_vec);
283 g3_draw_rod_tmap(bitmap,&bot_p,obj->size,&top_p,obj->size,light);
286 int Linear_tmap_polygon_objects = 1;
288 extern fix Max_thrust;
290 //used for robot engine glow
291 #define MAX_VELOCITY i2f(50)
293 //function that takes the same parms as draw_tmap, but renders as flat poly
294 //we need this to do the cloaked effect
295 extern void draw_tmap_flat();
297 //what darkening level to use when cloaked
298 #define CLOAKED_FADE_LEVEL 28
300 #define CLOAK_FADEIN_DURATION_PLAYER F2_0
301 #define CLOAK_FADEOUT_DURATION_PLAYER F2_0
303 #define CLOAK_FADEIN_DURATION_ROBOT F1_0
304 #define CLOAK_FADEOUT_DURATION_ROBOT F1_0
306 //do special cloaked render
307 void draw_cloaked_object(object *obj,fix light,fix *glow,fix cloak_start_time,fix cloak_end_time)
309 fix cloak_delta_time,total_cloaked_time;
310 fix light_scale=F1_0;
312 int fading=0; //if true, fading, else cloaking
313 fix Cloak_fadein_duration=F1_0;
314 fix Cloak_fadeout_duration=F1_0;
317 total_cloaked_time = cloak_end_time-cloak_start_time;
321 Cloak_fadein_duration = CLOAK_FADEIN_DURATION_PLAYER;
322 Cloak_fadeout_duration = CLOAK_FADEOUT_DURATION_PLAYER;
325 Cloak_fadein_duration = CLOAK_FADEIN_DURATION_ROBOT;
326 Cloak_fadeout_duration = CLOAK_FADEOUT_DURATION_ROBOT;
329 Int3(); // Contact Mike: Unexpected object type in draw_cloaked_object.
332 cloak_delta_time = GameTime - cloak_start_time;
334 if (cloak_delta_time < Cloak_fadein_duration/2) {
336 light_scale = fixdiv(Cloak_fadein_duration/2 - cloak_delta_time,Cloak_fadein_duration/2);
340 else if (cloak_delta_time < Cloak_fadein_duration) {
342 cloak_value = f2i(fixdiv(cloak_delta_time - Cloak_fadein_duration/2,Cloak_fadein_duration/2) * CLOAKED_FADE_LEVEL);
344 } else if (GameTime < cloak_end_time-Cloak_fadeout_duration) {
345 static int cloak_delta=0,cloak_dir=1;
346 static fix cloak_timer=0;
348 //note, if more than one cloaked object is visible at once, the
349 //pulse rate will change!
351 cloak_timer -= FrameTime;
352 while (cloak_timer < 0) {
354 cloak_timer += Cloak_fadeout_duration/12;
356 cloak_delta += cloak_dir;
358 if (cloak_delta==0 || cloak_delta==4)
359 cloak_dir = -cloak_dir;
362 cloak_value = CLOAKED_FADE_LEVEL - cloak_delta;
364 } else if (GameTime < cloak_end_time-Cloak_fadeout_duration/2) {
366 cloak_value = f2i(fixdiv(total_cloaked_time - Cloak_fadeout_duration/2 - cloak_delta_time,Cloak_fadeout_duration/2) * CLOAKED_FADE_LEVEL);
370 light_scale = fixdiv(Cloak_fadeout_duration/2 - (total_cloaked_time - cloak_delta_time),Cloak_fadeout_duration/2);
376 fix new_light,save_glow;
377 bitmap_index * alt_textures = NULL;
380 if ( obj->rtype.pobj_info.alt_textures > 0 )
381 alt_textures = multi_player_textures[obj->rtype.pobj_info.alt_textures-1];
384 new_light = fixmul(light,light_scale);
386 glow[0] = fixmul(glow[0],light_scale);
387 draw_polygon_model(&obj->pos,
389 (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
390 obj->rtype.pobj_info.model_num,obj->rtype.pobj_info.subobj_flags,
397 Gr_scanline_darkening_level = cloak_value;
398 gr_setcolor(BM_XRGB(0,0,0)); //set to black (matters for s3)
399 g3_set_special_render(draw_tmap_flat,NULL,NULL); //use special flat drawer
400 draw_polygon_model(&obj->pos,
402 (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
403 obj->rtype.pobj_info.model_num,obj->rtype.pobj_info.subobj_flags,
407 g3_set_special_render(NULL,NULL,NULL);
408 Gr_scanline_darkening_level = GR_FADE_LEVELS;
413 //draw an object which renders as a polygon model
414 void draw_polygon_object(object *obj)
418 fix engine_glow_value[2]; //element 0 is for engine glow, 1 for headlight
420 light = compute_object_light(obj,NULL);
422 // If option set for bright players in netgame, brighten them!
424 if (Game_mode & GM_MULTI)
425 if (Netgame.BrightPlayers)
429 //make robots brighter according to robot glow field
430 if (obj->type == OBJ_ROBOT)
431 light += (Robot_info[obj->id].glow<<12); //convert 4:4 to 16:16
433 if (obj->type == OBJ_WEAPON)
434 if (obj->id == FLARE_ID)
437 if (obj->type == OBJ_MARKER)
441 imsave = Interpolation_method;
442 if (Linear_tmap_polygon_objects)
443 Interpolation_method = 1;
445 //set engine glow value
446 engine_glow_value[0] = f1_0/5;
447 if (obj->movement_type == MT_PHYSICS) {
449 if (obj->mtype.phys_info.flags & PF_USES_THRUST && obj->type==OBJ_PLAYER && obj->id==Player_num) {
450 fix thrust_mag = vm_vec_mag_quick(&obj->mtype.phys_info.thrust);
451 engine_glow_value[0] += (fixdiv(thrust_mag,Player_ship->max_thrust)*4)/5;
454 fix speed = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
455 engine_glow_value[0] += (fixdiv(speed,MAX_VELOCITY)*3)/5;
459 //set value for player headlight
460 if (obj->type == OBJ_PLAYER) {
461 if (Players[obj->id].flags & PLAYER_FLAGS_HEADLIGHT && !Endlevel_sequence)
462 if (Players[obj->id].flags & PLAYER_FLAGS_HEADLIGHT_ON)
463 engine_glow_value[1] = -2; //draw white!
465 engine_glow_value[1] = -1; //draw normal color (grey)
467 engine_glow_value[1] = -3; //don't draw
470 if (obj->rtype.pobj_info.tmap_override != -1) {
472 polymodel *pm = &Polygon_models[obj->rtype.pobj_info.model_num];
474 bitmap_index bm_ptrs[12];
478 Assert(pm->n_textures<=12);
480 for (i=0;i<12;i++) //fill whole array, in case simple model needs more
481 bm_ptrs[i] = Textures[obj->rtype.pobj_info.tmap_override];
483 draw_polygon_model(&obj->pos,
485 (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
486 obj->rtype.pobj_info.model_num,
487 obj->rtype.pobj_info.subobj_flags,
494 if (obj->type==OBJ_PLAYER && (Players[obj->id].flags&PLAYER_FLAGS_CLOAKED))
495 draw_cloaked_object(obj,light,engine_glow_value,Players[obj->id].cloak_time,Players[obj->id].cloak_time+CLOAK_TIME_MAX);
496 else if ((obj->type == OBJ_ROBOT) && (obj->ctype.ai_info.CLOAKED)) {
497 if (Robot_info[obj->id].boss_flag)
498 draw_cloaked_object(obj,light,engine_glow_value, Boss_cloak_start_time, Boss_cloak_end_time);
500 draw_cloaked_object(obj,light,engine_glow_value, GameTime-F1_0*10, GameTime+F1_0*10);
502 bitmap_index * alt_textures = NULL;
505 if ( obj->rtype.pobj_info.alt_textures > 0 )
506 alt_textures = multi_player_textures[obj->rtype.pobj_info.alt_textures-1];
509 // Snipers get bright when they fire.
510 if (Ai_local_info[OBJECT_NUMBER(obj)].next_fire < F1_0/8) {
511 if (obj->ctype.ai_info.behavior == AIB_SNIPE)
512 light = 2*light + F1_0;
515 draw_polygon_model(&obj->pos,
517 (vms_angvec *)&obj->rtype.pobj_info.anim_angles,obj->rtype.pobj_info.model_num,
518 obj->rtype.pobj_info.subobj_flags,
522 if (obj->type == OBJ_WEAPON && (Weapon_info[obj->id].model_num_inner > -1 )) {
523 fix dist_to_eye = vm_vec_dist_quick(&Viewer->pos, &obj->pos);
524 if (dist_to_eye < Simple_model_threshhold_scale * F1_0*2)
525 draw_polygon_model(&obj->pos,
527 (vms_angvec *)&obj->rtype.pobj_info.anim_angles,
528 Weapon_info[obj->id].model_num_inner,
529 obj->rtype.pobj_info.subobj_flags,
537 Interpolation_method = imsave;
541 //------------------------------------------------------------------------------
542 // These variables are used to keep a list of the 3 closest robots to the viewer.
543 // The code works like this: Every time render object is called with a polygon model,
544 // it finds the distance of that robot to the viewer. If this distance if within 10
545 // segments of the viewer, it does the following: If there aren't already 3 robots in
546 // the closet-robots list, it just sticks that object into the list along with its distance.
547 // If the list already contains 3 robots, then it finds the robot in that list that is
548 // farthest from the viewer. If that object is farther than the object currently being
549 // rendered, then the new object takes over that far object's slot. *Then* after all
550 // objects are rendered, object_render_targets is called an it draws a target on top
551 // of all the objects.
553 //091494: #define MAX_CLOSE_ROBOTS 3
554 //--unused-- static int Object_draw_lock_boxes = 0;
555 //091494: static int Object_num_close = 0;
556 //091494: static object * Object_close_ones[MAX_CLOSE_ROBOTS];
557 //091494: static fix Object_close_distance[MAX_CLOSE_ROBOTS];
559 //091494: set_close_objects(object *obj)
563 //091494: if ( (obj->type != OBJ_ROBOT) || (Object_draw_lock_boxes==0) )
566 //091494: // The following code keeps a list of the 10 closest robots to the
567 //091494: // viewer. See comments in front of this function for how this works.
568 //091494: dist = vm_vec_dist( &obj->pos, &Viewer->pos );
569 //091494: if ( dist < i2f(20*10) ) {
570 //091494: if ( Object_num_close < MAX_CLOSE_ROBOTS ) {
571 //091494: Object_close_ones[Object_num_close] = obj;
572 //091494: Object_close_distance[Object_num_close] = dist;
573 //091494: Object_num_close++;
575 //091494: int i, farthest_robot;
576 //091494: fix farthest_distance;
577 //091494: // Find the farthest robot in the list
578 //091494: farthest_robot = 0;
579 //091494: farthest_distance = Object_close_distance[0];
580 //091494: for (i=1; i<Object_num_close; i++ ) {
581 //091494: if ( Object_close_distance[i] > farthest_distance ) {
582 //091494: farthest_distance = Object_close_distance[i];
583 //091494: farthest_robot = i;
586 //091494: // If this object is closer to the viewer than
587 //091494: // the farthest in the list, replace the farthest with this object.
588 //091494: if ( farthest_distance > dist ) {
589 //091494: Object_close_ones[farthest_robot] = obj;
590 //091494: Object_close_distance[farthest_robot] = dist;
596 int Player_fired_laser_this_frame=-1;
600 // -----------------------------------------------------------------------------
601 //this routine checks to see if an robot rendered near the middle of
602 //the screen, and if so and the player had fired, "warns" the robot
603 void set_robot_location_info(object *objp)
605 if (Player_fired_laser_this_frame != -1) {
608 g3_rotate_point(&temp,&objp->pos);
610 if (temp.p3_codes & CC_BEHIND) //robot behind the screen
613 //the code below to check for object near the center of the screen
614 //completely ignores z, which may not be good
616 if ((abs(temp.p3_x) < F1_0*4) && (abs(temp.p3_y) < F1_0*4)) {
617 objp->ctype.ai_info.danger_laser_num = Player_fired_laser_this_frame;
618 objp->ctype.ai_info.danger_laser_signature = Objects[Player_fired_laser_this_frame].signature;
625 // ------------------------------------------------------------------------------------------------------------------
626 void create_small_fireball_on_object(object *objp, fix size_scale, int sound_flag)
629 vms_vector pos, rand_vec;
633 make_random_vector(&rand_vec);
635 vm_vec_scale(&rand_vec, objp->size/2);
637 vm_vec_add2(&pos, &rand_vec);
639 size = fixmul(size_scale, F1_0/2 + d_rand()*4/2);
641 segnum = find_point_seg(&pos, objp->segnum);
644 expl_obj = object_create_explosion(segnum, &pos, size, VCLIP_SMALL_EXPLOSION);
647 obj_attach(objp,expl_obj);
648 if (d_rand() < 8192) {
650 if (objp->type == OBJ_ROBOT)
653 digi_link_sound_to_object(SOUND_EXPLODING_WALL, OBJECT_NUMBER(objp), 0, vol);
658 // ------------------------------------------------------------------------------------------------------------------
659 void create_vclip_on_object(object *objp, fix size_scale, int vclip_num)
662 vms_vector pos, rand_vec;
666 make_random_vector(&rand_vec);
668 vm_vec_scale(&rand_vec, objp->size/2);
670 vm_vec_add2(&pos, &rand_vec);
672 size = fixmul(size_scale, F1_0 + d_rand()*4);
674 segnum = find_point_seg(&pos, objp->segnum);
677 expl_obj = object_create_explosion(segnum, &pos, size, vclip_num);
681 expl_obj->movement_type = MT_PHYSICS;
682 expl_obj->mtype.phys_info.velocity.x = objp->mtype.phys_info.velocity.x/2;
683 expl_obj->mtype.phys_info.velocity.y = objp->mtype.phys_info.velocity.y/2;
684 expl_obj->mtype.phys_info.velocity.z = objp->mtype.phys_info.velocity.z/2;
688 // -- mk, 02/05/95 -- #define VCLIP_INVULNERABILITY_EFFECT VCLIP_SMALL_EXPLOSION
689 // -- mk, 02/05/95 --
690 // -- mk, 02/05/95 -- // -----------------------------------------------------------------------------
691 // -- mk, 02/05/95 -- void do_player_invulnerability_effect(object *objp)
692 // -- mk, 02/05/95 -- {
693 // -- mk, 02/05/95 -- if (d_rand() < FrameTime*8) {
694 // -- mk, 02/05/95 -- create_vclip_on_object(objp, F1_0, VCLIP_INVULNERABILITY_EFFECT);
695 // -- mk, 02/05/95 -- }
696 // -- mk, 02/05/95 -- }
698 // -----------------------------------------------------------------------------
699 // Render an object. Calls one of several routines based on type
700 void render_object(object *obj)
704 if ( obj == Viewer ) return;
706 if ( obj->type==OBJ_NONE ) {
708 mprintf( (1, "ERROR!!!! Bogus obj %d in seg %d is rendering!\n", OBJECT_NUMBER(obj), obj->segnum) );
714 mld_save = Max_linear_depth;
715 Max_linear_depth = Max_linear_depth_objects;
717 switch (obj->render_type) {
719 case RT_NONE: break; //doesn't render, like the player
723 draw_polygon_object(obj);
725 //"warn" robot if being shot at
726 if (obj->type == OBJ_ROBOT)
727 set_robot_location_info(obj);
729 //JOHN SAID TO: if ( (obj->type==OBJ_PLAYER) && ((keyd_pressed[KEY_W]) || (keyd_pressed[KEY_I])))
730 //JOHN SAID TO: object_render_id(obj);
732 // -- mk, 02/05/95 -- if (obj->type == OBJ_PLAYER)
733 // -- mk, 02/05/95 -- if (Players[obj->id].flags & PLAYER_FLAGS_INVULNERABLE)
734 // -- mk, 02/05/95 -- do_player_invulnerability_effect(obj);
738 case RT_MORPH: draw_morph_object(obj); break;
740 case RT_FIREBALL: draw_fireball(obj); break;
742 case RT_WEAPON_VCLIP: draw_weapon_vclip(obj); break;
744 case RT_HOSTAGE: draw_hostage(obj); break;
746 case RT_POWERUP: draw_powerup(obj); break;
748 case RT_LASER: Laser_render(obj); break;
750 default: Error("Unknown render_type <%d>",obj->render_type);
754 if ( obj->render_type != RT_NONE )
755 if ( Newdemo_state == ND_STATE_RECORDING ) {
756 if (!WasRecorded[OBJECT_NUMBER(obj)]) {
757 newdemo_record_render_object(obj);
758 WasRecorded[OBJECT_NUMBER(obj)] = 1;
763 Max_linear_depth = mld_save;
767 //--unused-- void object_toggle_lock_targets() {
768 //--unused-- Object_draw_lock_boxes ^= 1;
771 //091494: //draw target boxes for nearby robots
772 //091494: void object_render_targets()
774 //091494: g3s_point pt;
775 //091494: ubyte codes;
777 //091494: int radius,x,y;
779 //091494: if (Object_draw_lock_boxes==0)
782 //091494: for (i=0; i<Object_num_close; i++ ) {
784 //091494: codes = g3_rotate_point(&pt, &Object_close_ones[i]->pos );
785 //091494: if ( !(codes & CC_BEHIND) ) {
786 //091494: g3_project_point(&pt);
787 //091494: if (pt.p3_flags & PF_PROJECTED) {
788 //091494: x = f2i(pt.p3_sx);
789 //091494: y = f2i(pt.p3_sy);
790 //091494: radius = f2i(fixdiv((grd_curcanv->cv_bitmap.bm_w*Object_close_ones[i]->size)/8,pt.z));
791 //091494: gr_setcolor( BM_XRGB(0,31,0) );
792 //091494: gr_box(x-radius,y-radius,x+radius,y+radius);
796 //091494: Object_num_close=0;
798 //--unused-- //draw target boxes for nearby robots
799 //--unused-- void object_render_id(object * obj)
801 //--unused-- g3s_point pt;
802 //--unused-- ubyte codes;
803 //--unused-- int x,y;
804 //--unused-- int w, h, aw;
805 //--unused-- char s[20], *s1;
807 //--unused-- s1 = network_get_player_name( OBJECT_NUMBER(obj) );
810 //--unused-- sprintf( s, "%s", s1 );
812 //--unused-- sprintf( s, "<%d>", obj->id );
814 //--unused-- codes = g3_rotate_point(&pt, &obj->pos );
815 //--unused-- if ( !(codes & CC_BEHIND) ) {
816 //--unused-- g3_project_point(&pt);
817 //--unused-- if (pt.p3_flags & PF_PROJECTED) {
818 //--unused-- gr_get_string_size( s, &w, &h, &aw );
819 //--unused-- x = f2i(pt.p3_sx) - w/2;
820 //--unused-- y = f2i(pt.p3_sy) - h/2;
821 //--unused-- if ( x>= 0 && y>=0 && (x+w)<=grd_curcanv->cv_bitmap.bm_w && (y+h)<grd_curcanv->cv_bitmap.bm_h ) {
822 //--unused-- gr_set_fontcolor( BM_XRGB(0,31,0), -1 );
823 //--unused-- gr_string( x, y, s );
829 void check_and_fix_matrix(vms_matrix *m);
831 #define vm_angvec_zero(v) (v)->p=(v)->b=(v)->h=0
833 void reset_player_object()
839 vm_vec_zero(&ConsoleObject->mtype.phys_info.velocity);
840 vm_vec_zero(&ConsoleObject->mtype.phys_info.thrust);
841 vm_vec_zero(&ConsoleObject->mtype.phys_info.rotvel);
842 vm_vec_zero(&ConsoleObject->mtype.phys_info.rotthrust);
843 ConsoleObject->mtype.phys_info.brakes = ConsoleObject->mtype.phys_info.turnroll = 0;
844 ConsoleObject->mtype.phys_info.mass = Player_ship->mass;
845 ConsoleObject->mtype.phys_info.drag = Player_ship->drag;
846 ConsoleObject->mtype.phys_info.flags |= PF_TURNROLL | PF_LEVELLING | PF_WIGGLE | PF_USES_THRUST;
850 ConsoleObject->render_type = RT_POLYOBJ;
851 ConsoleObject->rtype.pobj_info.model_num = Player_ship->model_num; //what model is this?
852 ConsoleObject->rtype.pobj_info.subobj_flags = 0; //zero the flags
853 ConsoleObject->rtype.pobj_info.tmap_override = -1; //no tmap override!
855 for (i=0;i<MAX_SUBMODELS;i++)
856 vm_angvec_zero(&ConsoleObject->rtype.pobj_info.anim_angles[i]);
860 ConsoleObject->flags = 0;
865 //make object0 the player, setting all relevant fields
866 void init_player_object()
868 ConsoleObject->type = OBJ_PLAYER;
869 ConsoleObject->id = 0; //no sub-types for player
871 ConsoleObject->signature = 0; //player has zero, others start at 1
873 ConsoleObject->size = Polygon_models[Player_ship->model_num].rad;
875 ConsoleObject->control_type = CT_SLEW; //default is player slewing
876 ConsoleObject->movement_type = MT_PHYSICS; //change this sometime
878 ConsoleObject->lifeleft = IMMORTAL_TIME;
880 ConsoleObject->attached_obj = -1;
882 reset_player_object();
886 //sets up the free list & init player & whatever else
893 for (i=0;i<MAX_OBJECTS;i++) {
894 free_obj_list[i] = i;
895 Objects[i].type = OBJ_NONE;
896 Objects[i].segnum = -1;
899 for (i=0;i<MAX_SEGMENTS;i++)
900 Segments[i].objects = -1;
902 ConsoleObject = Viewer = &Objects[0];
904 init_player_object();
905 obj_link(OBJECT_NUMBER(ConsoleObject), 0); // put in the world in segment 0
907 num_objects = 1; //just the player
908 Highest_object_index = 0;
913 //after calling init_object(), the network code has grabbed specific
914 //object slots without allocating them. Go though the objects & build
915 //the free list, then set the apporpriate globals
916 void special_reset_objects(void)
920 num_objects=MAX_OBJECTS;
922 Highest_object_index = 0;
923 Assert(Objects[0].type != OBJ_NONE); //0 should be used
925 for (i=MAX_OBJECTS;i--;)
926 if (Objects[i].type == OBJ_NONE)
927 free_obj_list[--num_objects] = i;
929 if (i > Highest_object_index)
930 Highest_object_index = i;
934 int is_object_in_seg( int segnum, int objn )
936 int objnum, count = 0;
938 for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next) {
939 if ( count > MAX_OBJECTS ) {
943 if ( objnum==objn ) count++;
948 int search_all_segments_for_object( int objnum )
953 for (i=0; i<=Highest_segment_index; i++) {
954 count += is_object_in_seg( i, objnum );
959 void johns_obj_unlink(int segnum, int objnum)
961 object *obj = &Objects[objnum];
962 segment *seg = &Segments[segnum];
964 Assert(objnum != -1);
967 seg->objects = obj->next;
969 Objects[obj->prev].next = obj->next;
971 if (obj->next != -1) Objects[obj->next].prev = obj->prev;
974 void remove_incorrect_objects()
976 int segnum, objnum, count;
978 for (segnum=0; segnum <= Highest_segment_index; segnum++) {
980 for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next) {
983 if ( count > MAX_OBJECTS ) {
984 mprintf((1, "Object list in segment %d is circular.\n", segnum ));
988 if (Objects[objnum].segnum != segnum ) {
990 mprintf((0, "Removing object %d from segment %d.\n", objnum, segnum ));
993 johns_obj_unlink(segnum,objnum);
999 void remove_all_objects_but( int segnum, int objnum )
1003 for (i=0; i<=Highest_segment_index; i++) {
1005 if (is_object_in_seg( i, objnum )) {
1006 johns_obj_unlink( i, objnum );
1012 int check_duplicate_objects()
1016 for (i=0;i<=Highest_object_index;i++) {
1017 if ( Objects[i].type != OBJ_NONE ) {
1018 count = search_all_segments_for_object( i );
1021 mprintf((1, "Object %d is in %d segments!\n", i, count ));
1024 remove_all_objects_but( Objects[i].segnum, i );
1032 void list_seg_objects( int segnum )
1034 int objnum, count = 0;
1036 for (objnum=Segments[segnum].objects;objnum!=-1;objnum=Objects[objnum].next) {
1038 if ( count > MAX_OBJECTS ) {
1048 //link the object into the list for its segment
1049 void obj_link(int objnum,int segnum)
1051 object *obj = &Objects[objnum];
1053 Assert(objnum != -1);
1055 Assert(obj->segnum == -1);
1057 Assert(segnum>=0 && segnum<=Highest_segment_index);
1059 obj->segnum = segnum;
1061 obj->next = Segments[segnum].objects;
1064 Segments[segnum].objects = objnum;
1066 if (obj->next != -1) Objects[obj->next].prev = objnum;
1068 //list_seg_objects( segnum );
1069 //check_duplicate_objects();
1071 Assert(Objects[0].next != 0);
1072 if (Objects[0].next == 0)
1073 Objects[0].next = -1;
1075 Assert(Objects[0].prev != 0);
1076 if (Objects[0].prev == 0)
1077 Objects[0].prev = -1;
1080 void obj_unlink(int objnum)
1082 object *obj = &Objects[objnum];
1083 segment *seg = &Segments[obj->segnum];
1085 Assert(objnum != -1);
1087 if (obj->prev == -1)
1088 seg->objects = obj->next;
1090 Objects[obj->prev].next = obj->next;
1092 if (obj->next != -1) Objects[obj->next].prev = obj->prev;
1096 Assert(Objects[0].next != 0);
1097 Assert(Objects[0].prev != 0);
1100 int Object_next_signature = 1; //player gets 0, others start at 1
1102 int Debris_object_count=0;
1104 int Unused_object_slots;
1106 //returns the number of a free object, updating Highest_object_index.
1107 //Generally, obj_create() should be called to get an object, since it
1108 //fills in important fields and does the linking.
1109 //returns -1 if no free objects
1110 int obj_allocate(void)
1114 if ( num_objects >= MAX_OBJECTS-2 ) {
1117 num_freed = free_object_slots(MAX_OBJECTS-10);
1118 mprintf((0, " *** Freed %i objects in frame %i\n", num_freed, FrameCount));
1121 if ( num_objects >= MAX_OBJECTS ) {
1123 mprintf((1, "Object creation failed - too many objects!\n" ));
1128 objnum = free_obj_list[num_objects++];
1130 if (objnum > Highest_object_index) {
1131 Highest_object_index = objnum;
1132 if (Highest_object_index > Highest_ever_object_index)
1133 Highest_ever_object_index = Highest_object_index;
1138 Unused_object_slots=0;
1139 for (i=0; i<=Highest_object_index; i++)
1140 if (Objects[i].type == OBJ_NONE)
1141 Unused_object_slots++;
1146 //frees up an object. Generally, obj_delete() should be called to get
1147 //rid of an object. This function deallocates the object entry after
1148 //the object has been unlinked
1149 void obj_free(int objnum)
1151 free_obj_list[--num_objects] = objnum;
1152 Assert(num_objects >= 0);
1154 if (objnum == Highest_object_index)
1155 while (Objects[--Highest_object_index].type == OBJ_NONE);
1158 //-----------------------------------------------------------------------------
1159 // Scan the object list, freeing down to num_used objects
1160 // Returns number of slots freed.
1161 int free_object_slots(int num_used)
1164 int obj_list[MAX_OBJECTS];
1165 int num_already_free, num_to_free, original_num_to_free;
1168 num_already_free = MAX_OBJECTS - Highest_object_index - 1;
1170 if (MAX_OBJECTS - num_already_free < num_used)
1173 for (i=0; i<=Highest_object_index; i++) {
1174 if (Objects[i].flags & OF_SHOULD_BE_DEAD) {
1176 if (MAX_OBJECTS - num_already_free < num_used)
1177 return num_already_free;
1179 switch (Objects[i].type) {
1182 if (MAX_OBJECTS - num_already_free < num_used)
1187 Int3(); // This is curious. What is an object that is a wall?
1192 obj_list[olind++] = i;
1208 num_to_free = MAX_OBJECTS - num_used - num_already_free;
1209 original_num_to_free = num_to_free;
1211 if (num_to_free > olind) {
1212 mprintf((1, "Warning: Asked to free %i objects, but can only free %i.\n", num_to_free, olind));
1213 num_to_free = olind;
1216 for (i=0; i<num_to_free; i++)
1217 if (Objects[obj_list[i]].type == OBJ_DEBRIS) {
1219 mprintf((0, "Freeing DEBRIS object %3i\n", obj_list[i]));
1220 Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1224 return original_num_to_free;
1226 for (i=0; i<num_to_free; i++)
1227 if (Objects[obj_list[i]].type == OBJ_FIREBALL && Objects[obj_list[i]].ctype.expl_info.delete_objnum==-1) {
1229 mprintf((0, "Freeing FIREBALL object %3i\n", obj_list[i]));
1230 Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1234 return original_num_to_free;
1236 for (i=0; i<num_to_free; i++)
1237 if ((Objects[obj_list[i]].type == OBJ_WEAPON) && (Objects[obj_list[i]].id == FLARE_ID)) {
1239 Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1243 return original_num_to_free;
1245 for (i=0; i<num_to_free; i++)
1246 if ((Objects[obj_list[i]].type == OBJ_WEAPON) && (Objects[obj_list[i]].id != FLARE_ID)) {
1248 mprintf((0, "Freeing WEAPON object %3i\n", obj_list[i]));
1249 Objects[obj_list[i]].flags |= OF_SHOULD_BE_DEAD;
1252 return original_num_to_free - num_to_free;
1255 //-----------------------------------------------------------------------------
1256 //initialize a new object. adds to the list for the given segment
1257 //note that segnum is really just a suggestion, since this routine actually
1258 //searches for the correct segment
1259 //returns the object number
1260 int obj_create(ubyte type,ubyte id,int segnum,vms_vector *pos,
1261 vms_matrix *orient,fix size,ubyte ctype,ubyte mtype,ubyte rtype)
1266 Assert(segnum <= Highest_segment_index);
1267 Assert (segnum >= 0);
1268 Assert(ctype <= CT_CNTRLCEN);
1270 if (type==OBJ_DEBRIS && Debris_object_count>=Max_debris_objects)
1273 if (get_seg_masks(pos, segnum, 0, __FILE__, __LINE__).centermask != 0)
1274 if ((segnum=find_point_seg(pos,segnum))==-1) {
1276 mprintf((0,"Bad segnum in obj_create (type=%d)\n",type));
1278 return -1; //don't create this object
1281 // Find next free object
1282 objnum = obj_allocate();
1284 if (objnum == -1) //no free objects
1287 Assert(Objects[objnum].type == OBJ_NONE); //make sure unused
1289 obj = &Objects[objnum];
1291 Assert(obj->segnum == -1);
1293 // Zero out object structure to keep weird bugs from happening
1294 // in uninitialized fields.
1295 memset( obj, 0, sizeof(object) );
1297 obj->signature = Object_next_signature++;
1300 obj->last_pos = *pos;
1304 //@@if (orient != NULL)
1305 //@@ obj->orient = *orient;
1307 obj->orient = orient?*orient:vmd_identity_matrix;
1309 obj->control_type = ctype;
1310 obj->movement_type = mtype;
1311 obj->render_type = rtype;
1312 obj->contains_type = -1;
1314 obj->lifeleft = IMMORTAL_TIME; //assume immortal
1315 obj->attached_obj = -1;
1317 if (obj->control_type == CT_POWERUP)
1318 obj->ctype.powerup_info.count = 1;
1320 // Init physics info for this object
1321 if (obj->movement_type == MT_PHYSICS) {
1323 vm_vec_zero(&obj->mtype.phys_info.velocity);
1324 vm_vec_zero(&obj->mtype.phys_info.thrust);
1325 vm_vec_zero(&obj->mtype.phys_info.rotvel);
1326 vm_vec_zero(&obj->mtype.phys_info.rotthrust);
1328 obj->mtype.phys_info.mass = 0;
1329 obj->mtype.phys_info.drag = 0;
1330 obj->mtype.phys_info.brakes = 0;
1331 obj->mtype.phys_info.turnroll = 0;
1332 obj->mtype.phys_info.flags = 0;
1335 if (obj->render_type == RT_POLYOBJ)
1336 obj->rtype.pobj_info.tmap_override = -1;
1338 obj->shields = 20*F1_0;
1340 segnum = find_point_seg(pos,segnum); //find correct segment
1344 obj->segnum = -1; //set to zero by memset, above
1345 obj_link(objnum,segnum);
1347 // Set (or not) persistent bit in phys_info.
1348 if (obj->type == OBJ_WEAPON) {
1349 Assert(obj->control_type == CT_WEAPON);
1350 obj->mtype.phys_info.flags |= (Weapon_info[obj->id].persistent*PF_PERSISTENT);
1351 obj->ctype.laser_info.creation_time = GameTime;
1352 obj->ctype.laser_info.last_hitobj = -1;
1353 obj->ctype.laser_info.multiplier = F1_0;
1356 if (obj->control_type == CT_POWERUP)
1357 obj->ctype.powerup_info.creation_time = GameTime;
1359 if (obj->control_type == CT_EXPLOSION)
1360 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
1363 if (print_object_info)
1364 mprintf( (0, "Created object %d of type %d\n", objnum, obj->type ));
1367 if (obj->type == OBJ_DEBRIS)
1368 Debris_object_count++;
1374 //create a copy of an object. returns new object number
1375 int obj_create_copy(int objnum, vms_vector *new_pos, int newsegnum)
1380 // Find next free object
1381 newobjnum = obj_allocate();
1383 if (newobjnum == -1)
1386 obj = &Objects[newobjnum];
1388 *obj = Objects[objnum];
1390 obj->pos = obj->last_pos = *new_pos;
1392 obj->next = obj->prev = obj->segnum = -1;
1394 obj_link(newobjnum,newsegnum);
1396 obj->signature = Object_next_signature++;
1398 //we probably should initialize sub-structures here
1405 extern void newdemo_record_guided_end();
1407 //remove object from the world
1408 void obj_delete(int objnum)
1411 object *obj = &Objects[objnum];
1413 Assert(objnum != -1);
1414 Assert(objnum != 0 );
1415 Assert(obj->type != OBJ_NONE);
1416 Assert(obj != ConsoleObject);
1418 if (obj->type==OBJ_WEAPON && obj->id==GUIDEDMISS_ID && obj->ctype.laser_info.parent_type==OBJ_PLAYER)
1420 pnum=Objects[obj->ctype.laser_info.parent_num].id;
1421 mprintf ((0,"Deleting a guided missile! Player %d\n\n",pnum));
1423 if (pnum!=Player_num) {
1424 mprintf ((0,"deleting missile that belongs to %d (%s)!\n",pnum,Players[pnum].callsign));
1425 Guided_missile[pnum]=NULL;
1427 else if (Newdemo_state==ND_STATE_RECORDING)
1428 newdemo_record_guided_end();
1432 if (obj == Viewer) //deleting the viewer?
1433 Viewer = ConsoleObject; //..make the player the viewer
1435 if (obj->flags & OF_ATTACHED) //detach this from object
1436 obj_detach_one(obj);
1438 if (obj->attached_obj != -1) //detach all objects from this
1439 obj_detach_all(obj);
1441 #if !defined(NDEBUG) && !defined(NMONO)
1442 if (print_object_info) mprintf( (0, "Deleting object %d of type %d\n", objnum, Objects[objnum].type ));
1445 if (obj->type == OBJ_DEBRIS)
1446 Debris_object_count--;
1450 Assert(Objects[0].next != 0);
1452 obj->type = OBJ_NONE; //unused!
1453 obj->signature = -1;
1454 obj->segnum=-1; // zero it!
1459 #define DEATH_SEQUENCE_LENGTH (F1_0*5)
1460 #define DEATH_SEQUENCE_EXPLODE_TIME (F1_0*2)
1462 int Player_is_dead = 0; // If !0, then player is dead, but game continues so he can watch.
1463 object *Dead_player_camera = NULL; // Object index of object watching deader.
1464 fix Player_time_of_death; // Time at which player died.
1465 object *Viewer_save;
1466 int Player_flags_save;
1467 int Player_exploded = 0;
1468 int Death_sequence_aborted=0;
1469 int Player_eggs_dropped=0;
1470 fix Camera_to_player_dist_goal=F1_0*4;
1472 ubyte Control_type_save, Render_type_save;
1473 extern int Cockpit_mode_save; //set while in letterbox or rear view, or -1
1475 // ------------------------------------------------------------------------------------------------------------------
1476 void dead_player_end(void)
1478 if (!Player_is_dead)
1481 if (Newdemo_state == ND_STATE_RECORDING)
1482 newdemo_record_restore_cockpit();
1485 Player_exploded = 0;
1486 obj_delete(OBJECT_NUMBER(Dead_player_camera));
1487 Dead_player_camera = NULL;
1488 select_cockpit(Cockpit_mode_save);
1489 Cockpit_mode_save = -1;
1490 Viewer = Viewer_save;
1491 ConsoleObject->type = OBJ_PLAYER;
1492 ConsoleObject->flags = Player_flags_save;
1494 Assert((Control_type_save == CT_FLYING) || (Control_type_save == CT_SLEW));
1496 ConsoleObject->control_type = Control_type_save;
1497 ConsoleObject->render_type = Render_type_save;
1498 Players[Player_num].flags &= ~PLAYER_FLAGS_INVULNERABLE;
1499 Player_eggs_dropped = 0;
1503 // ------------------------------------------------------------------------------------------------------------------
1504 // Camera is less than size of player away from
1505 void set_camera_pos(vms_vector *camera_pos, object *objp)
1508 fix camera_player_dist;
1511 camera_player_dist = vm_vec_dist_quick(camera_pos, &objp->pos);
1513 if (camera_player_dist < Camera_to_player_dist_goal) { //2*objp->size) {
1514 // Camera is too close to player object, so move it away.
1515 vms_vector player_camera_vec;
1518 vms_vector local_p1;
1520 vm_vec_sub(&player_camera_vec, camera_pos, &objp->pos);
1521 if ((player_camera_vec.x == 0) && (player_camera_vec.y == 0) && (player_camera_vec.z == 0))
1522 player_camera_vec.x += F1_0/16;
1524 hit_data.hit_type = HIT_WALL;
1527 while ((hit_data.hit_type != HIT_NONE) && (count++ < 6)) {
1528 vms_vector closer_p1;
1529 vm_vec_normalize_quick(&player_camera_vec);
1530 vm_vec_scale(&player_camera_vec, Camera_to_player_dist_goal);
1533 vm_vec_add(&closer_p1, &objp->pos, &player_camera_vec); // This is the actual point we want to put the camera at.
1534 vm_vec_scale(&player_camera_vec, far_scale); // ...but find a point 50% further away...
1535 vm_vec_add(&local_p1, &objp->pos, &player_camera_vec); // ...so we won't have to do as many cuts.
1538 fq.startseg = objp->segnum;
1540 fq.thisobjnum = OBJECT_NUMBER(objp);
1541 fq.ignore_obj_list = NULL;
1543 find_vector_intersection( &fq, &hit_data);
1545 if (hit_data.hit_type == HIT_NONE) {
1546 *camera_pos = closer_p1;
1548 make_random_vector(&player_camera_vec);
1549 far_scale = 3*F1_0/2;
1555 extern void drop_player_eggs(object *objp);
1556 extern int get_explosion_vclip(object *obj,int stage);
1557 extern void multi_cap_objects();
1558 extern int Proximity_dropped,Smartmines_dropped;
1560 // ------------------------------------------------------------------------------------------------------------------
1561 void dead_player_frame(void)
1566 if (Player_is_dead) {
1567 time_dead = GameTime - Player_time_of_death;
1569 // If unable to create camera at time of death, create now.
1570 if (Dead_player_camera == Viewer_save) {
1572 object *player = &Objects[Players[Player_num].objnum];
1574 // this next line was changed by WraithX, instead of CT_FLYING, it was CT_NONE: instead of MT_PHYSICS, it was MT_NONE.
1575 objnum = obj_create(OBJ_CAMERA, 0, player->segnum, &player->pos, &player->orient, 0, CT_FLYING, MT_PHYSICS, RT_NONE);
1577 mprintf((0, "Creating new dead player camera.\n"));
1579 Viewer = Dead_player_camera = &Objects[objnum];
1581 mprintf((1, "Can't create dead player camera.\n"));
1586 ConsoleObject->mtype.phys_info.rotvel.x = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/4;
1587 ConsoleObject->mtype.phys_info.rotvel.y = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/2;
1588 ConsoleObject->mtype.phys_info.rotvel.z = max(0, DEATH_SEQUENCE_EXPLODE_TIME - time_dead)/3;
1590 Camera_to_player_dist_goal = min(time_dead*8, F1_0*20) + ConsoleObject->size;
1592 set_camera_pos(&Dead_player_camera->pos, ConsoleObject);
1594 // the following line uncommented by WraithX, 4-12-00
1595 if (time_dead < DEATH_SEQUENCE_EXPLODE_TIME + F1_0 * 2)
1597 vm_vec_sub(&fvec, &ConsoleObject->pos, &Dead_player_camera->pos);
1598 vm_vector_2_matrix(&Dead_player_camera->orient, &fvec, NULL, NULL);
1599 Dead_player_camera->mtype.phys_info = ConsoleObject->mtype.phys_info;
1601 // the following "if" added by WraithX to get rid of camera "wiggle"
1602 if (Dead_player_camera->mtype.phys_info.flags & PF_WIGGLE)
1604 Dead_player_camera->mtype.phys_info.flags = (Dead_player_camera->mtype.phys_info.flags & ~PF_WIGGLE);
1605 }// end "if" added by WraithX, 4/13/00
1607 // the following line uncommented by WraithX, 4-12-00
1611 // the following line uncommented by WraithX, 4-11-00
1612 Dead_player_camera->movement_type = MT_PHYSICS;
1613 //Dead_player_camera->mtype.phys_info.rotvel.y = F1_0/8;
1614 // the following line uncommented by WraithX, 4-12-00
1616 // end addition by WX
1618 if (time_dead > DEATH_SEQUENCE_EXPLODE_TIME) {
1619 if (!Player_exploded) {
1621 if (Players[Player_num].hostages_on_board > 1)
1622 HUD_init_message(TXT_SHIP_DESTROYED_2, Players[Player_num].hostages_on_board);
1623 else if (Players[Player_num].hostages_on_board == 1)
1624 HUD_init_message(TXT_SHIP_DESTROYED_1);
1626 HUD_init_message(TXT_SHIP_DESTROYED_0);
1634 Player_exploded = 1;
1636 if (Game_mode & GM_NETWORK)
1639 multi_cap_objects();
1643 drop_player_eggs(ConsoleObject);
1644 Player_eggs_dropped = 1;
1646 if (Game_mode & GM_MULTI)
1648 //multi_send_position(Players[Player_num].objnum);
1649 multi_send_player_explode(MULTI_PLAYER_EXPLODE);
1653 explode_badass_player(ConsoleObject);
1655 //is this next line needed, given the badass call above?
1656 explode_object(ConsoleObject,0);
1657 ConsoleObject->flags &= ~OF_SHOULD_BE_DEAD; //don't really kill player
1658 ConsoleObject->render_type = RT_NONE; //..just make him disappear
1659 ConsoleObject->type = OBJ_GHOST; //..and kill intersections
1660 Players[Player_num].flags &= ~PLAYER_FLAGS_HEADLIGHT_ON;
1663 if (d_rand() < FrameTime*4) {
1665 if (Game_mode & GM_MULTI)
1666 multi_send_create_explosion(Player_num);
1668 create_small_fireball_on_object(ConsoleObject, F1_0, 1);
1673 if (Death_sequence_aborted) { //time_dead > DEATH_SEQUENCE_LENGTH) {
1674 if (!Player_eggs_dropped) {
1677 if (Game_mode & GM_NETWORK)
1680 multi_cap_objects();
1684 drop_player_eggs(ConsoleObject);
1685 Player_eggs_dropped = 1;
1687 if (Game_mode & GM_MULTI)
1689 //multi_send_position(Players[Player_num].objnum);
1690 multi_send_player_explode(MULTI_PLAYER_EXPLODE);
1695 DoPlayerDead(); //kill_player();
1701 void AdjustMineSpawn()
1703 if (!(Game_mode & GM_NETWORK))
1704 return; // No need for this function in any other mode
1706 if (!(Game_mode & GM_HOARD))
1707 Players[Player_num].secondary_ammo[PROXIMITY_INDEX]+=Proximity_dropped;
1708 Players[Player_num].secondary_ammo[SMART_MINE_INDEX]+=Smartmines_dropped;
1709 Proximity_dropped=0;
1710 Smartmines_dropped=0;
1715 int Killed_in_frame = -1;
1716 short Killed_objnum = -1;
1717 extern char Multi_killed_yourself;
1719 // ------------------------------------------------------------------------------------------------------------------
1720 void start_player_death_sequence(object *player)
1724 Assert(player == ConsoleObject);
1725 if ((Player_is_dead != 0) || (Dead_player_camera != NULL))
1728 //Assert(Player_is_dead == 0);
1729 //Assert(Dead_player_camera == NULL);
1733 if (!(Game_mode & GM_MULTI))
1734 HUD_clear_messages();
1736 Killed_in_frame = FrameCount;
1737 Killed_objnum = OBJECT_NUMBER(player);
1738 Death_sequence_aborted = 0;
1741 if (Game_mode & GM_MULTI)
1743 multi_send_kill(Players[Player_num].objnum);
1745 // If Hoard, increase number of orbs by 1
1746 // Only if you haven't killed yourself
1747 // This prevents cheating
1749 if (Game_mode & GM_HOARD)
1750 if (Players[Player_num].secondary_ammo[PROXIMITY_INDEX]<12)
1751 if (!Multi_killed_yourself)
1752 Players[Player_num].secondary_ammo[PROXIMITY_INDEX]++;
1764 //Players[Player_num].flags &= ~(PLAYER_FLAGS_AFTERBURNER);
1766 vm_vec_zero(&player->mtype.phys_info.rotthrust);
1767 vm_vec_zero(&player->mtype.phys_info.thrust);
1769 Player_time_of_death = GameTime;
1771 // this next line was changed by WraithX, instead of CT_FLYING, it was CT_NONE: instead of MT_PHYSICS, it was MT_NONE.
1772 objnum = obj_create(OBJ_CAMERA, 0, player->segnum, &player->pos, &player->orient, 0, CT_FLYING, MT_PHYSICS, RT_NONE);
1773 Viewer_save = Viewer;
1775 Viewer = Dead_player_camera = &Objects[objnum];
1777 mprintf((1, "Can't create dead player camera.\n"));
1779 Dead_player_camera = Viewer;
1782 if (Cockpit_mode_save == -1) //if not already saved
1783 Cockpit_mode_save = Cockpit_mode;
1784 select_cockpit(CM_LETTERBOX);
1785 if (Newdemo_state == ND_STATE_RECORDING)
1786 newdemo_record_letterbox();
1788 Player_flags_save = player->flags;
1789 Control_type_save = player->control_type;
1790 Render_type_save = player->render_type;
1792 player->flags &= ~OF_SHOULD_BE_DEAD;
1793 // Players[Player_num].flags |= PLAYER_FLAGS_INVULNERABLE;
1794 player->control_type = CT_FLYING; // change from CT_NONE to CT_FLYING by WraithX
1795 player->shields = F1_0*1000;
1797 PALETTE_FLASH_SET(0,0,0);
1800 // ------------------------------------------------------------------------------------------------------------------
1801 void obj_delete_all_that_should_be_dead()
1805 int local_dead_player_object=-1;
1810 for (i=0;i<=Highest_object_index;i++) {
1811 if ((objp->type!=OBJ_NONE) && (objp->flags&OF_SHOULD_BE_DEAD) ) {
1812 Assert(!(objp->type==OBJ_FIREBALL && objp->ctype.expl_info.delete_time!=-1));
1813 if (objp->type==OBJ_PLAYER) {
1814 if ( objp->id == Player_num ) {
1815 if (local_dead_player_object == -1) {
1816 start_player_death_sequence(objp);
1817 local_dead_player_object = OBJECT_NUMBER(objp);
1819 Int3(); // Contact Mike: Illegal, killed player twice in this frame!
1820 // Ok to continue, won't start death sequence again!
1831 //when an object has moved into a new segment, this function unlinks it
1832 //from its old segment, and links it into the new segment
1833 void obj_relink(int objnum,int newsegnum)
1836 Assert((objnum >= 0) && (objnum <= Highest_object_index));
1837 Assert((newsegnum <= Highest_segment_index) && (newsegnum >= 0));
1841 obj_link(objnum,newsegnum);
1844 if (get_seg_masks(&Objects[objnum].pos, Objects[objnum].segnum, 0, __FILE__, __LINE__).centermask != 0)
1845 mprintf((1, "obj_relink violates seg masks.\n"));
1849 //process a continuously-spinning object
1851 spin_object(object *obj)
1854 vms_matrix rotmat, new_pm;
1856 Assert(obj->movement_type == MT_SPINNING);
1858 rotangs.p = fixmul(obj->mtype.spin_rate.x,FrameTime);
1859 rotangs.h = fixmul(obj->mtype.spin_rate.y,FrameTime);
1860 rotangs.b = fixmul(obj->mtype.spin_rate.z,FrameTime);
1862 vm_angles_2_matrix(&rotmat,&rotangs);
1864 vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat);
1865 obj->orient = new_pm;
1867 check_and_fix_matrix(&obj->orient);
1870 int Drop_afterburner_blob_flag; //ugly hack
1871 extern void multi_send_drop_blobs(char);
1872 extern void fuelcen_check_for_goal (segment *);
1874 //see if wall is volatile, and if so, cause damage to player
1875 //returns true if player is in lava
1876 int check_volatile_wall(object *obj,int segnum,int sidenum,vms_vector *hitpt);
1878 // Time at which this object last created afterburner blobs.
1879 fix Last_afterburner_time[MAX_OBJECTS];
1881 //--------------------------------------------------------------------
1882 //move an object for the current frame
1883 void object_move_one( object * obj )
1888 int previous_segment = obj->segnum;
1890 obj->last_pos = obj->pos; // Save the current position
1892 if ((obj->type==OBJ_PLAYER) && (Player_num==obj->id)) {
1896 if (Game_mode & GM_CAPTURE)
1897 fuelcen_check_for_goal (&Segments[obj->segnum]);
1898 if (Game_mode & GM_HOARD)
1899 fuelcen_check_for_hoard_goal (&Segments[obj->segnum]);
1902 fuel=fuelcen_give_fuel( &Segments[obj->segnum], INITIAL_ENERGY-Players[Player_num].energy );
1904 Players[Player_num].energy += fuel;
1907 shields = repaircen_give_shields( &Segments[obj->segnum], INITIAL_ENERGY-Players[Player_num].energy );
1909 Players[Player_num].shields += shields;
1913 if (obj->lifeleft != IMMORTAL_TIME) { //if not immortal...
1914 // Ok, this is a big hack by MK.
1915 // If you want an object to last for exactly one frame, then give it a lifeleft of ONE_FRAME_TIME.
1916 if (obj->lifeleft != ONE_FRAME_TIME)
1917 obj->lifeleft -= FrameTime; //...inevitable countdown towards death
1920 Drop_afterburner_blob_flag = 0;
1922 switch (obj->control_type) {
1924 case CT_NONE: break;
1928 #if !defined(NDEBUG) && !defined(NMONO)
1929 if (print_object_info > 1) mprintf( (0, "Moving player object #%d\n", OBJECT_NUMBER(obj)) );
1932 read_flying_controls( obj );
1936 case CT_REPAIRCEN: Int3(); // -- hey! these are no longer supported!! -- do_repair_sequence(obj); break;
1938 case CT_POWERUP: do_powerup_frame(obj); break;
1940 case CT_MORPH: //morph implies AI
1941 do_morph_frame(obj);
1942 //NOTE: FALLS INTO AI HERE!!!!
1945 //NOTE LINK TO CT_MORPH ABOVE!!!
1946 if (Game_suspended & SUSP_ROBOTS) return;
1947 #if !defined(NDEBUG) && !defined(NMONO)
1948 if (print_object_info>1)
1949 mprintf( (0, "AI: Moving robot object #%d\n", OBJECT_NUMBER(obj)) );
1954 case CT_WEAPON: Laser_do_weapon_sequence(obj); break;
1955 case CT_EXPLOSION: do_explosion_sequence(obj); break;
1959 if ( keyd_pressed[KEY_PAD5] ) slew_stop( obj );
1960 if ( keyd_pressed[KEY_NUMLOCK] ) {
1961 slew_reset_orient( obj );
1962 * (ubyte *) 0x417 &= ~0x20; //kill numlock
1964 slew_frame(0 ); // Does velocity addition for us.
1969 // case CT_FLYTHROUGH:
1970 // do_flythrough(obj,0); // HACK:do_flythrough should operate on an object!!!!
1971 // //check_object_seg(obj);
1972 // return; // DON'T DO THE REST OF OBJECT STUFF SINCE THIS IS A SPECIAL CASE!!!
1975 case CT_DEBRIS: do_debris_frame(obj); break;
1977 case CT_LIGHT: break; //doesn't do anything
1979 case CT_REMOTE: break; //movement is handled in com_process_input
1981 case CT_CNTRLCEN: do_controlcen_frame(obj); break;
1985 Error("Unknown control type %d in object %i, sig/type/id = %i/%i/%i", obj->control_type, OBJECT_NUMBER(obj), obj->signature, obj->type, obj->id);
1991 if (obj->lifeleft < 0 ) { // We died of old age
1992 obj->flags |= OF_SHOULD_BE_DEAD;
1993 if ( obj->type==OBJ_WEAPON && Weapon_info[obj->id].damage_radius )
1994 explode_badass_weapon(obj,&obj->pos);
1995 else if ( obj->type==OBJ_ROBOT) //make robots explode
1996 explode_object(obj,0);
1999 if (obj->type == OBJ_NONE)
2000 return; // object has been deleted
2002 // stay around if !dead, for WraithX's death-cam
2003 if (!Player_is_dead && obj->flags&OF_SHOULD_BE_DEAD)
2006 switch (obj->movement_type) {
2008 case MT_NONE: break; //this doesn't move
2010 case MT_PHYSICS: do_physics_sim(obj); break; //move by physics
2012 case MT_SPINNING: spin_object(obj); break;
2016 // If player and moved to another segment, see if hit any triggers.
2017 // also check in player under a lavafall
2018 if (obj->type == OBJ_PLAYER && obj->movement_type==MT_PHYSICS) {
2020 if (previous_segment != obj->segnum) {
2023 int old_level = Current_level_num;
2025 for (i=0;i<n_phys_segs-1;i++) {
2026 connect_side = find_connect_side(&Segments[phys_seglist[i+1]], &Segments[phys_seglist[i]]);
2027 if (connect_side != -1)
2028 check_trigger(&Segments[phys_seglist[i]], connect_side, OBJECT_NUMBER(obj), 0);
2030 else { // segments are not directly connected, so do binary subdivision until you find connected segments.
2031 mprintf((1, "UNCONNECTED SEGMENTS %d,%d\n",phys_seglist[i+1],phys_seglist[i]));
2032 // -- Unnecessary, MK, 09/04/95 -- Int3();
2036 //maybe we've gone on to the next level. if so, bail!
2038 if (Current_level_num != old_level)
2045 int sidemask,under_lavafall=0;
2046 static int lavafall_hiss_playing[MAX_PLAYERS]={0};
2048 sidemask = get_seg_masks(&obj->pos, obj->segnum, obj->size, __FILE__, __LINE__).sidemask;
2050 int sidenum,bit,wall_num;
2052 for (sidenum=0,bit=1;sidenum<6;bit<<=1,sidenum++)
2053 if ((sidemask & bit) && ((wall_num=Segments[obj->segnum].sides[sidenum].wall_num)!=-1) && Walls[wall_num].type==WALL_ILLUSION) {
2055 if ((type=check_volatile_wall(obj,obj->segnum,sidenum,&obj->pos))!=0) {
2056 int sound = (type==1)?SOUND_LAVAFALL_HISS:SOUND_SHIP_IN_WATERFALL;
2058 if (!lavafall_hiss_playing[obj->id]) {
2059 digi_link_sound_to_object3( sound, OBJECT_NUMBER(obj), 1, F1_0, i2f(256), -1, -1 );
2060 lavafall_hiss_playing[obj->id] = 1;
2066 if (!under_lavafall && lavafall_hiss_playing[obj->id]) {
2067 digi_kill_sound_linked_to_object( OBJECT_NUMBER(obj) );
2068 lavafall_hiss_playing[obj->id] = 0;
2073 //see if guided missile has flown through exit trigger
2074 if (obj==Guided_missile[Player_num] && obj->signature==Guided_missile_sig[Player_num]) {
2075 if (previous_segment != obj->segnum) {
2077 connect_side = find_connect_side(&Segments[obj->segnum], &Segments[previous_segment]);
2078 if (connect_side != -1) {
2079 int wall_num,trigger_num;
2080 wall_num = Segments[previous_segment].sides[connect_side].wall_num;
2081 if ( wall_num != -1 ) {
2082 trigger_num = Walls[wall_num].trigger;
2083 if (trigger_num != -1)
2084 if (Triggers[trigger_num].type == TT_EXIT)
2085 Guided_missile[Player_num]->lifeleft = 0;
2091 if (Drop_afterburner_blob_flag) {
2092 Assert(obj==ConsoleObject);
2093 drop_afterburner_blobs(obj, 2, i2f(5)/2, -1); // -1 means use default lifetime
2095 if (Game_mode & GM_MULTI)
2096 multi_send_drop_blobs(Player_num);
2098 Drop_afterburner_blob_flag = 0;
2101 if ((obj->type == OBJ_WEAPON) && (Weapon_info[obj->id].afterburner_size)) {
2102 int objnum = OBJECT_NUMBER(obj);
2103 fix vel = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
2104 fix delay, lifetime;
2108 else if (vel > F1_0*40)
2109 delay = fixdiv(F1_0*13,vel);
2113 lifetime = (delay * 3)/2;
2114 if (!(Game_mode & GM_MULTI)) {
2119 if ((Last_afterburner_time[objnum] + delay < GameTime) || (Last_afterburner_time[objnum] > GameTime)) {
2120 drop_afterburner_blobs(obj, 1, i2f(Weapon_info[obj->id].afterburner_size)/16, lifetime);
2121 Last_afterburner_time[objnum] = GameTime;
2126 obj++; //kill warning
2130 int Max_used_objects = MAX_OBJECTS - 20;
2132 //--------------------------------------------------------------------
2133 //move all objects for the current frame
2134 void object_move_all()
2139 // -- mprintf((0, "Frame %i: %i/%i objects used.\n", FrameCount, num_objects, MAX_OBJECTS));
2141 // check_duplicate_objects();
2142 // remove_incorrect_objects();
2144 if (Highest_object_index > Max_used_objects)
2145 free_object_slots(Max_used_objects); // Free all possible object slots.
2147 obj_delete_all_that_should_be_dead();
2149 if (Auto_leveling_on)
2150 ConsoleObject->mtype.phys_info.flags |= PF_LEVELLING;
2152 ConsoleObject->mtype.phys_info.flags &= ~PF_LEVELLING;
2158 for (i=0;i<=Highest_object_index;i++) {
2159 if ( (objp->type != OBJ_NONE) && (!(objp->flags&OF_SHOULD_BE_DEAD)) ) {
2160 object_move_one( objp );
2168 // check_duplicate_objects();
2169 // remove_incorrect_objects();
2174 //--unused-- // -----------------------------------------------------------
2175 //--unused-- // Moved here from eobject.c on 02/09/94 by MK.
2176 //--unused-- int find_last_obj(int i)
2178 //--unused-- for (i=MAX_OBJECTS;--i>=0;)
2179 //--unused-- if (Objects[i].type != OBJ_NONE) break;
2181 //--unused-- return i;
2186 //make object array non-sparse
2187 void compress_objects(void)
2189 int start_i; //,last_i;
2191 //last_i = find_last_obj(MAX_OBJECTS);
2193 // Note: It's proper to do < (rather than <=) Highest_object_index here because we
2194 // are just removing gaps, and the last object can't be a gap.
2195 for (start_i=0;start_i<Highest_object_index;start_i++)
2197 if (Objects[start_i].type == OBJ_NONE) {
2201 segnum_copy = Objects[Highest_object_index].segnum;
2203 obj_unlink(Highest_object_index);
2205 Objects[start_i] = Objects[Highest_object_index];
2208 if (Cur_object_index == Highest_object_index)
2209 Cur_object_index = start_i;
2212 Objects[Highest_object_index].type = OBJ_NONE;
2214 obj_link(start_i,segnum_copy);
2216 while (Objects[--Highest_object_index].type == OBJ_NONE);
2218 //last_i = find_last_obj(last_i);
2222 reset_objects(num_objects);
2226 //called after load. Takes number of objects, and objects should be
2227 //compressed. resets free list, marks unused objects as unused
2228 void reset_objects(int n_objs)
2232 num_objects = n_objs;
2234 Assert(num_objects>0);
2236 for (i=num_objects;i<MAX_OBJECTS;i++) {
2237 free_obj_list[i] = i;
2238 Objects[i].type = OBJ_NONE;
2239 Objects[i].segnum = -1;
2242 Highest_object_index = num_objects-1;
2244 Debris_object_count = 0;
2247 //Tries to find a segment for an object, using find_point_seg()
2248 int find_object_seg(object * obj )
2250 return find_point_seg(&obj->pos,obj->segnum);
2254 //If an object is in a segment, set its segnum field and make sure it's
2255 //properly linked. If not in any segment, returns 0, else 1.
2256 //callers should generally use find_vector_intersection()
2257 int update_object_seg(object * obj )
2261 newseg = find_object_seg(obj);
2266 if ( newseg != obj->segnum )
2267 obj_relink( OBJECT_NUMBER(obj), newseg );
2273 //go through all objects and make sure they have the correct segment numbers
2279 for (i=0;i<=Highest_object_index;i++)
2280 if (Objects[i].type != OBJ_NONE)
2281 if (update_object_seg(&Objects[i]) == 0) {
2282 mprintf((1,"Cannot find segment for object %d in fix_object_segs()\n"));
2284 compute_segment_center(&Objects[i].pos,&Segments[Objects[i].segnum]);
2289 //--unused-- void object_use_new_object_list( object * new_list )
2291 //--unused-- int i, segnum;
2292 //--unused-- object *obj;
2294 //--unused-- // First, unlink all the old objects for the segments array
2295 //--unused-- for (segnum=0; segnum <= Highest_segment_index; segnum++) {
2296 //--unused-- Segments[segnum].objects = -1;
2298 //--unused-- // Then, erase all the objects
2299 //--unused-- reset_objects(1);
2301 //--unused-- // Fill in the object array
2302 //--unused-- memcpy( Objects, new_list, sizeof(object)*MAX_OBJECTS );
2304 //--unused-- Highest_object_index=-1;
2306 //--unused-- // Relink 'em
2307 //--unused-- for (i=0; i<MAX_OBJECTS; i++ ) {
2308 //--unused-- obj = &Objects[i];
2309 //--unused-- if ( obj->type != OBJ_NONE ) {
2310 //--unused-- num_objects++;
2311 //--unused-- Highest_object_index = i;
2312 //--unused-- segnum = obj->segnum;
2313 //--unused-- obj->next = obj->prev = obj->segnum = -1;
2314 //--unused-- obj_link(i,segnum);
2315 //--unused-- } else {
2316 //--unused-- obj->next = obj->prev = obj->segnum = -1;
2322 //delete objects, such as weapons & explosions, that shouldn't stay between levels
2323 // Changed by MK on 10/15/94, don't remove proximity bombs.
2324 //if clear_all is set, clear even proximity bombs
2325 void clear_transient_objects(int clear_all)
2330 for (objnum=0,obj=&Objects[0];objnum<=Highest_object_index;objnum++,obj++)
2331 if (((obj->type == OBJ_WEAPON) && !(Weapon_info[obj->id].flags&WIF_PLACABLE) && (clear_all || ((obj->id != PROXIMITY_ID) && (obj->id != SUPERPROX_ID)))) ||
2332 obj->type == OBJ_FIREBALL ||
2333 obj->type == OBJ_DEBRIS ||
2334 obj->type == OBJ_DEBRIS ||
2335 (obj->type!=OBJ_NONE && obj->flags & OF_EXPLODING)) {
2338 if (Objects[objnum].lifeleft > i2f(2))
2339 mprintf((0,"Note: Clearing object %d (type=%d, id=%d) with lifeleft=%x\n",objnum,Objects[objnum].type,Objects[objnum].id,Objects[objnum].lifeleft));
2344 else if (Objects[objnum].type!=OBJ_NONE && Objects[objnum].lifeleft < i2f(2))
2345 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));
2349 //attaches an object, such as a fireball, to another object, such as a robot
2350 void obj_attach(object *parent,object *sub)
2352 Assert(sub->type == OBJ_FIREBALL);
2353 Assert(sub->control_type == CT_EXPLOSION);
2355 Assert(sub->ctype.expl_info.next_attach==-1);
2356 Assert(sub->ctype.expl_info.prev_attach==-1);
2358 Assert(parent->attached_obj==-1 || Objects[parent->attached_obj].ctype.expl_info.prev_attach==-1);
2360 sub->ctype.expl_info.next_attach = parent->attached_obj;
2362 if (sub->ctype.expl_info.next_attach != -1)
2363 Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach = OBJECT_NUMBER(sub);
2365 parent->attached_obj = OBJECT_NUMBER(sub);
2367 sub->ctype.expl_info.attach_parent = OBJECT_NUMBER(parent);
2368 sub->flags |= OF_ATTACHED;
2370 Assert(sub->ctype.expl_info.next_attach != OBJECT_NUMBER(sub));
2371 Assert(sub->ctype.expl_info.prev_attach != OBJECT_NUMBER(sub));
2374 //dettaches one object
2375 void obj_detach_one(object *sub)
2377 Assert(sub->flags & OF_ATTACHED);
2378 Assert(sub->ctype.expl_info.attach_parent != -1);
2380 if ((Objects[sub->ctype.expl_info.attach_parent].type == OBJ_NONE) || (Objects[sub->ctype.expl_info.attach_parent].attached_obj == -1))
2382 sub->flags &= ~OF_ATTACHED;
2386 if (sub->ctype.expl_info.next_attach != -1) {
2387 Assert(Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach = OBJECT_NUMBER(sub));
2388 Objects[sub->ctype.expl_info.next_attach].ctype.expl_info.prev_attach = sub->ctype.expl_info.prev_attach;
2391 if (sub->ctype.expl_info.prev_attach != -1) {
2392 Assert(Objects[sub->ctype.expl_info.prev_attach].ctype.expl_info.next_attach = OBJECT_NUMBER(sub));
2393 Objects[sub->ctype.expl_info.prev_attach].ctype.expl_info.next_attach = sub->ctype.expl_info.next_attach;
2396 Assert(Objects[sub->ctype.expl_info.attach_parent].attached_obj = OBJECT_NUMBER(sub));
2397 Objects[sub->ctype.expl_info.attach_parent].attached_obj = sub->ctype.expl_info.next_attach;
2400 sub->ctype.expl_info.next_attach = sub->ctype.expl_info.prev_attach = -1;
2401 sub->flags &= ~OF_ATTACHED;
2405 //dettaches all objects from this object
2406 void obj_detach_all(object *parent)
2408 while (parent->attached_obj != -1)
2409 obj_detach_one(&Objects[parent->attached_obj]);
2412 //creates a marker object in the world. returns the object number
2413 int drop_marker_object(vms_vector *pos,int segnum,vms_matrix *orient, int marker_num)
2417 Assert(Marker_model_num != -1);
2419 objnum = obj_create(OBJ_MARKER, marker_num, segnum, pos, orient, Polygon_models[Marker_model_num].rad, CT_NONE, MT_NONE, RT_POLYOBJ);
2422 object *obj = &Objects[objnum];
2424 obj->rtype.pobj_info.model_num = Marker_model_num;
2426 vm_vec_copy_scale(&obj->mtype.spin_rate,&obj->orient.uvec,F1_0/2);
2428 // MK, 10/16/95: Using lifeleft to make it flash, thus able to trim lightlevel from all objects.
2429 obj->lifeleft = IMMORTAL_TIME - 1;
2435 extern int Ai_last_missile_camera;
2437 // *viewer is a viewer, probably a missile.
2438 // wake up all robots that were rendered last frame subject to some constraints.
2439 void wake_up_rendered_objects(object *viewer, int window_num)
2443 // Make sure that we are processing current data.
2444 if (FrameCount != Window_rendered_data[window_num].frame) {
2445 mprintf((1, "Warning: Called wake_up_rendered_objects with a bogus window.\n"));
2449 Ai_last_missile_camera = OBJECT_NUMBER(viewer);
2451 for (i=0; i<Window_rendered_data[window_num].num_objects; i++) {
2454 int fcval = FrameCount & 3;
2456 objnum = Window_rendered_data[window_num].rendered_objects[i];
2457 if ((objnum & 3) == fcval) {
2458 objp = &Objects[objnum];
2460 if (objp->type == OBJ_ROBOT) {
2461 if (vm_vec_dist_quick(&viewer->pos, &objp->pos) < F1_0*100) {
2462 ai_local *ailp = &Ai_local_info[objnum];
2463 if (ailp->player_awareness_type == 0) {
2464 objp->ctype.ai_info.SUB_FLAGS |= SUB_FLAGS_CAMERA_AWAKE;
2465 ailp->player_awareness_type = PA_WEAPON_ROBOT_COLLISION;
2466 ailp->player_awareness_time = F1_0*3;
2467 ailp->previous_visibility = 2;