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