1 /* $Id: laser.c,v 1.7 2002/08/06 09:30:24 btb Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14 
15 #ifdef HAVE_CONFIG_H
16 #include <conf.h>
17 #endif
18 
19 #ifdef RCS
20 char laser_rcsid[] = "$Id: laser.c,v 1.7 2002/08/06 09:30:24 btb Exp $";
21 #endif
22 
23 #include <stdlib.h>
24 #include <stdio.h>
25 
26 #include "inferno.h"
27 #include "game.h"
28 #include "bm.h"
29 #include "object.h"
30 #include "laser.h"
31 #include "args.h"
32 #include "segment.h"
33 #include "fvi.h"
34 #include "segpoint.h"
35 #include "error.h"
36 #include "mono.h"
37 #include "key.h"
38 #include "texmap.h"
39 #include "textures.h"
40 #include "render.h"
41 #include "vclip.h"
42 #include "fireball.h"
43 #include "polyobj.h"
44 #include "robot.h"
45 #include "weapon.h"
46 #include "newdemo.h"
47 #include "timer.h"
48 #include "player.h"
49 #include "sounds.h"
50 #ifdef NETWORK
51 #include "network.h"
52 #endif
53 #include "ai.h"
54 #include "modem.h"
55 #include "powerup.h"
56 #include "multi.h"
57 #include "physics.h"
58 #include "multi.h"
59 
60 #ifdef TACTILE
61 #include "tactile.h"
62 #endif
63 
64 int Laser_rapid_fire = 0;
65 
66 
67 object *Guided_missile[MAX_PLAYERS]={NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
68 int Guided_missile_sig[MAX_PLAYERS]={-1,-1,-1,-1,-1,-1,-1,-1};
69 
70 int find_homing_object_complete(vms_vector *curpos, object *tracker, int track_obj_type1, int track_obj_type2);
71 
72 extern char Multi_is_guided;
73 extern char BounceCheat;
74 
75 extern void newdemo_record_guided_end();
76 extern void newdemo_record_guided_start();
77 
78 int find_homing_object(vms_vector *curpos, object *tracker);
79 
80 //---------------------------------------------------------------------------------
81 // Called by render code.... determines if the laser is from a robot or the
82 // player and calls the appropriate routine.
83 
Laser_render(object * obj)84 void Laser_render(object *obj)
85 {
86 
87 //	Commented out by John (sort of, typed by Mike) on 6/8/94
88 #if 0
89 	switch( obj->id )	{
90 	case WEAPON_TYPE_WEAK_LASER:
91 	case WEAPON_TYPE_STRONG_LASER:
92 	case WEAPON_TYPE_CANNON_BALL:
93 	case WEAPON_TYPE_MISSILE:
94 		break;
95 	default:
96 		Error( "Invalid weapon type in Laser_render\n" );
97 	}
98 #endif
99 
100 	switch( Weapon_info[obj->id].render_type )	{
101 	case WEAPON_RENDER_LASER:
102 		Int3();	// Not supported anymore!
103 					//Laser_draw_one(obj-Objects, Weapon_info[obj->id].bitmap );
104 		break;
105 	case WEAPON_RENDER_BLOB:
106 		draw_object_blob(obj, Weapon_info[obj->id].bitmap  );
107 		break;
108 	case WEAPON_RENDER_POLYMODEL:
109 		break;
110 	case WEAPON_RENDER_VCLIP:
111 		Int3();	//	Oops, not supported, type added by mk on 09/09/94, but not for lasers...
112 	default:
113 		Error( "Invalid weapon render type in Laser_render\n" );
114 	}
115 
116 }
117 
118 //---------------------------------------------------------------------------------
119 // Draws a texture-mapped laser bolt
120 
121 //void Laser_draw_one( int objnum, grs_bitmap * bmp )
122 //{
123 //	int t1, t2, t3;
124 //	g3s_point p1, p2;
125 //	object *obj;
126 //	vms_vector start_pos,end_pos;
127 //
128 //	obj = &Objects[objnum];
129 //
130 //	start_pos = obj->pos;
131 //	vm_vec_scale_add(&end_pos,&start_pos,&obj->orient.fvec,-Laser_length);
132 //
133 //	g3_rotate_point(&p1,&start_pos);
134 //	g3_rotate_point(&p2,&end_pos);
135 //
136 //	t1 = Lighting_on;
137 //	t2 = Interpolation_method;
138 //	t3 = Transparency_on;
139 //
140 //	Lighting_on  = 0;
141 //	//Interpolation_method = 3;	// Full perspective
142 //	Interpolation_method = 1;	// Linear
143 //	Transparency_on = 1;
144 //
145 //	//gr_setcolor( gr_getcolor(31,15,0));
146 //	//g3_draw_line_ptrs(p1,p2);
147 //	//g3_draw_rod(p1,0x2000,p2,0x2000);
148 //	//g3_draw_rod(p1,Laser_width,p2,Laser_width);
149 //	g3_draw_rod_tmap(bmp,&p2,Laser_width,&p1,Laser_width,0);
150 //	Lighting_on = t1;
151 //	Interpolation_method = t2;
152 //	Transparency_on = t3;
153 //
154 //}
155 
156 //	Changed by MK on 09/07/94
157 //	I want you to be able to blow up your own bombs.
158 //	AND...Your proximity bombs can blow you up if they're 2.0 seconds or more old.
159 //	Changed by MK on 06/06/95: Now must be 4.0 seconds old.  Much valid Net-complaining.
laser_are_related(int o1,int o2)160 int laser_are_related( int o1, int o2 )
161 {
162 	if ( (o1<0) || (o2<0) )
163 		return 0;
164 
165 	// See if o2 is the parent of o1
166 	if ( Objects[o1].type == OBJ_WEAPON  )
167 		if ( (Objects[o1].ctype.laser_info.parent_num==o2) && (Objects[o1].ctype.laser_info.parent_signature==Objects[o2].signature) )
168 		{
169 			//	o1 is a weapon, o2 is the parent of 1, so if o1 is PROXIMITY_BOMB and o2 is player, they are related only if o1 < 2.0 seconds old
170 			if ((Objects[o1].id == PHOENIX_ID && (GameTime > Objects[o1].ctype.laser_info.creation_time + F1_0/4)) ||
171 			   (Objects[o1].id == GUIDEDMISS_ID && (GameTime > Objects[o1].ctype.laser_info.creation_time + F1_0*2)) ||
172 				(((Objects[o1].id == PROXIMITY_ID) || (Objects[o1].id == SUPERPROX_ID)) && (GameTime > Objects[o1].ctype.laser_info.creation_time + F1_0*4))) {
173 				return 0;
174 			} else
175 				return 1;
176 		}
177 
178 	// See if o1 is the parent of o2
179 	if ( Objects[o2].type == OBJ_WEAPON  )
180 	{
181 		if ( (Objects[o2].ctype.laser_info.parent_num==o1) && (Objects[o2].ctype.laser_info.parent_signature==Objects[o1].signature) )
182 		{
183 			//	o2 is a weapon, o1 is the parent of 2, so if o2 is PROXIMITY_BOMB and o1 is player, they are related only if o1 < 2.0 seconds old
184 			if ((Objects[o2].id == PHOENIX_ID && (GameTime > Objects[o2].ctype.laser_info.creation_time + F1_0/4)) ||
185 			   (Objects[o2].id == GUIDEDMISS_ID && (GameTime > Objects[o2].ctype.laser_info.creation_time + F1_0*2)) ||
186 				(((Objects[o2].id == PROXIMITY_ID) || (Objects[o2].id == SUPERPROX_ID)) && (GameTime > Objects[o2].ctype.laser_info.creation_time + F1_0*4))) {
187 				return 0;
188 			} else
189 				return 1;
190 		}
191 	}
192 
193 	// They must both be weapons
194 	if ( Objects[o1].type != OBJ_WEAPON || Objects[o2].type != OBJ_WEAPON )
195 		return 0;
196 
197 	//	Here is the 09/07/94 change -- Siblings must be identical, others can hurt each other
198 	// See if they're siblings...
199 	//	MK: 06/08/95, Don't allow prox bombs to detonate for 3/4 second.  Else too likely to get toasted by your own bomb if hit by opponent.
200 	if ( Objects[o1].ctype.laser_info.parent_signature==Objects[o2].ctype.laser_info.parent_signature )
201 	{
202 		if (Objects[o1].id == PROXIMITY_ID  || Objects[o2].id == PROXIMITY_ID || Objects[o1].id == SUPERPROX_ID || Objects[o2].id == SUPERPROX_ID) {
203 			//	If neither is older than 1/2 second, then can't blow up!
204 			if ((GameTime > (Objects[o1].ctype.laser_info.creation_time + F1_0/2)) || (GameTime > (Objects[o2].ctype.laser_info.creation_time + F1_0/2)))
205 				return 0;
206 			else
207 				return 1;
208 		} else
209 			return 1;
210 	}
211 
212 	//	Anything can cause a collision with a robot super prox mine.
213 	if (Objects[o1].id == ROBOT_SUPERPROX_ID || Objects[o2].id == ROBOT_SUPERPROX_ID ||
214 		 Objects[o1].id == PROXIMITY_ID || Objects[o2].id == PROXIMITY_ID ||
215 		 Objects[o1].id == SUPERPROX_ID || Objects[o2].id == SUPERPROX_ID ||
216 		 Objects[o1].id == PMINE_ID || Objects[o2].id == PMINE_ID)
217 		return 0;
218 
219 	return 1;
220 }
221 
222 //--unused-- int Muzzle_scale=2;
223 int Laser_offset=0;
224 
do_muzzle_stuff(int segnum,vms_vector * pos)225 void do_muzzle_stuff(int segnum, vms_vector *pos)
226 {
227 	Muzzle_data[Muzzle_queue_index].create_time = timer_get_fixed_seconds();
228 	Muzzle_data[Muzzle_queue_index].segnum = segnum;
229 	Muzzle_data[Muzzle_queue_index].pos = *pos;
230 	Muzzle_queue_index++;
231 	if (Muzzle_queue_index >= MUZZLE_QUEUE_MAX)
232 		Muzzle_queue_index = 0;
233 }
234 
235 //creates a weapon object
create_weapon_object(int weapon_type,int segnum,vms_vector * position)236 int create_weapon_object(int weapon_type,int segnum,vms_vector *position)
237 {
238 	int rtype=-1;
239 	fix laser_radius = -1;
240 	int objnum;
241 	object *obj;
242 
243 	switch( Weapon_info[weapon_type].render_type )	{
244 
245 		case WEAPON_RENDER_BLOB:
246 			rtype = RT_LASER;			// Render as a laser even if blob (see render code above for explanation)
247 			laser_radius = Weapon_info[weapon_type].blob_size;
248 			break;
249 		case WEAPON_RENDER_POLYMODEL:
250 			laser_radius = 0;	//	Filled in below.
251 			rtype = RT_POLYOBJ;
252 			break;
253 		case WEAPON_RENDER_LASER:
254 			Int3(); 	// Not supported anymore
255 			break;
256 		case WEAPON_RENDER_NONE:
257 			rtype = RT_NONE;
258 			laser_radius = F1_0;
259 			break;
260 		case WEAPON_RENDER_VCLIP:
261 			rtype = RT_WEAPON_VCLIP;
262 			laser_radius = Weapon_info[weapon_type].blob_size;
263 			break;
264 		default:
265 			Error( "Invalid weapon render type in Laser_create_new\n" );
266 	}
267 
268 	Assert(laser_radius != -1);
269 	Assert(rtype != -1);
270 
271 	objnum = obj_create( OBJ_WEAPON, weapon_type, segnum, position, NULL, laser_radius, CT_WEAPON, MT_PHYSICS, rtype );
272 
273 	obj = &Objects[objnum];
274 
275 	if (Weapon_info[weapon_type].render_type == WEAPON_RENDER_POLYMODEL) {
276 		obj->rtype.pobj_info.model_num = Weapon_info[obj->id].model_num;
277 		obj->size = fixdiv(Polygon_models[obj->rtype.pobj_info.model_num].rad,Weapon_info[obj->id].po_len_to_width_ratio);
278 	}
279 
280 	obj->mtype.phys_info.mass = Weapon_info[weapon_type].mass;
281 	obj->mtype.phys_info.drag = Weapon_info[weapon_type].drag;
282 	vm_vec_zero(&obj->mtype.phys_info.thrust);
283 
284 	if (Weapon_info[weapon_type].bounce==1)
285 		obj->mtype.phys_info.flags |= PF_BOUNCE;
286 
287 	if (Weapon_info[weapon_type].bounce==2 || BounceCheat)
288 		obj->mtype.phys_info.flags |= PF_BOUNCE+PF_BOUNCES_TWICE;
289 
290 
291 	return objnum;
292 }
293 
294 extern int Doing_lighting_hack_flag;
295 
296 //	-------------------------------------------------------------------------------------------------------------------------------
297 //	***** HEY ARTISTS!! *****
298 //	Here are the constants you're looking for! --MK
299 
300 //	Change the following constants to affect the look of the omega cannon.
301 //	Changing these constants will not affect the damage done.
302 //	WARNING: If you change DESIRED_OMEGA_DIST and MAX_OMEGA_BLOBS, you don't merely change the look of the cannon,
303 //	you change its range.  If you decrease DESIRED_OMEGA_DIST, you decrease how far the gun can fire.
304 #define	MIN_OMEGA_BLOBS		3				//	No matter how close the obstruction, at this many blobs created.
305 #define	MIN_OMEGA_DIST			(F1_0*3)		//	At least this distance between blobs, unless doing so would violate MIN_OMEGA_BLOBS
306 #define	DESIRED_OMEGA_DIST	(F1_0*5)		//	This is the desired distance between blobs.  For distances > MIN_OMEGA_BLOBS*DESIRED_OMEGA_DIST, but not very large, this will apply.
307 #define	MAX_OMEGA_BLOBS		16				//	No matter how far away the obstruction, this is the maximum number of blobs.
308 #define	MAX_OMEGA_DIST			(MAX_OMEGA_BLOBS * DESIRED_OMEGA_DIST)		//	Maximum extent of lightning blobs.
309 
310 //	Additionally, several constants which apply to homing objects in general control the behavior of the Omega Cannon.
311 //	They are defined in laser.h.  They are copied here for reference.  These values are valid on 1/10/96:
312 //	If you want the Omega Cannon view cone to be different than the Homing Missile viewcone, contact MK to make the change.
313 //	(Unless you are a programmer, in which case, do it yourself!)
314 #define	OMEGA_MIN_TRACKABLE_DOT			(15*F1_0/16)		//	Larger values mean narrower cone.  F1_0 means damn near impossible.  0 means 180 degree field of view.
315 #define	OMEGA_MAX_TRACKABLE_DIST		MAX_OMEGA_DIST	//	An object must be at least this close to be tracked.
316 
317 //	Note, you don't need to change these constants.  You can control damage and energy consumption by changing the
318 //	usual bitmaps.tbl parameters.
319 #define	OMEGA_DAMAGE_SCALE			32				//	Controls how much damage is done.  This gets multiplied by FrameTime and then again by the damage specified in bitmaps.tbl in the $WEAPON line.
320 #define	OMEGA_ENERGY_CONSUMPTION	16				//	Controls how much energy is consumed.  This gets multiplied by FrameTime and then again by the energy parameter from bitmaps.tbl.
321 //	-------------------------------------------------------------------------------------------------------------------------------
322 
delete_old_omega_blobs(object * parent_objp)323 void delete_old_omega_blobs(object *parent_objp)
324 {
325 	int	i;
326 	int	parent_num;
327 	int	count = 0;
328 
329 	parent_num = parent_objp->ctype.laser_info.parent_num;
330 
331 	for (i=0; i<=Highest_object_index; i++)
332 		if (Objects[i].type == OBJ_WEAPON)
333 			if (Objects[i].id == OMEGA_ID)
334 				if (Objects[i].ctype.laser_info.parent_num == parent_num) {
335 					obj_delete(i);
336 					count++;
337 				}
338 
339 	mprintf((0, "%i Omega blobs deleted in frame %i\n", count, FrameCount));
340 }
341 
342 // ---------------------------------------------------------------------------------
create_omega_blobs(int firing_segnum,vms_vector * firing_pos,vms_vector * goal_pos,object * parent_objp)343 void create_omega_blobs(int firing_segnum, vms_vector *firing_pos, vms_vector *goal_pos, object *parent_objp)
344 {
345 	int			i, last_segnum, last_created_objnum = -1;
346 	vms_vector	vec_to_goal;
347 	fix			dist_to_goal;
348 	int			num_omega_blobs;
349 	fix			omega_blob_dist;
350 	vms_vector	omega_delta_vector;
351 	vms_vector	blob_pos, perturb_vec;
352 	fix			perturb_array[MAX_OMEGA_BLOBS];
353 
354 	if (Game_mode & GM_MULTI)
355 		delete_old_omega_blobs(parent_objp);
356 
357 	vm_vec_sub(&vec_to_goal, goal_pos, firing_pos);
358 
359 	dist_to_goal = vm_vec_normalize_quick(&vec_to_goal);
360 
361 	if (dist_to_goal < MIN_OMEGA_BLOBS * MIN_OMEGA_DIST) {
362 		omega_blob_dist = MIN_OMEGA_DIST;
363 		num_omega_blobs = dist_to_goal/omega_blob_dist;
364 		if (num_omega_blobs == 0)
365 			num_omega_blobs = 1;
366 	} else {
367 		omega_blob_dist = DESIRED_OMEGA_DIST;
368 		num_omega_blobs = dist_to_goal / omega_blob_dist;
369 		if (num_omega_blobs > MAX_OMEGA_BLOBS) {
370 			num_omega_blobs = MAX_OMEGA_BLOBS;
371 			omega_blob_dist = dist_to_goal / num_omega_blobs;
372 		} else if (num_omega_blobs < MIN_OMEGA_BLOBS) {
373 			num_omega_blobs = MIN_OMEGA_BLOBS;
374 			omega_blob_dist = dist_to_goal / num_omega_blobs;
375 		}
376 	}
377 
378 	omega_delta_vector = vec_to_goal;
379 	vm_vec_scale(&omega_delta_vector, omega_blob_dist);
380 
381 	//	Now, create all the blobs
382 	blob_pos = *firing_pos;
383 	last_segnum = firing_segnum;
384 
385 	//	If nearby, don't perturb vector.  If not nearby, start halfway out.
386 	if (dist_to_goal < MIN_OMEGA_DIST*4) {
387 		for (i=0; i<num_omega_blobs; i++)
388 			perturb_array[i] = 0;
389 	} else {
390 		vm_vec_scale_add2(&blob_pos, &omega_delta_vector, F1_0/2);	//	Put first blob half way out.
391 		for (i=0; i<num_omega_blobs/2; i++) {
392 			perturb_array[i] = F1_0*i + F1_0/4;
393 			perturb_array[num_omega_blobs-1-i] = F1_0*i;
394 		}
395 	}
396 
397 	//	Create random perturbation vector, but favor _not_ going up in player's reference.
398 	make_random_vector(&perturb_vec);
399 	vm_vec_scale_add2(&perturb_vec, &parent_objp->orient.uvec, -F1_0/2);
400 
401 	Doing_lighting_hack_flag = 1;	//	Ugly, but prevents blobs which are probably outside the mine from killing framerate.
402 
403 	for (i=0; i<num_omega_blobs; i++) {
404 		vms_vector	temp_pos;
405 		int			blob_objnum, segnum;
406 
407 		//	This will put the last blob right at the destination object, causing damage.
408 		if (i == num_omega_blobs-1)
409 			vm_vec_scale_add2(&blob_pos, &omega_delta_vector, 15*F1_0/32);	//	Move last blob another (almost) half section
410 
411 		//	Every so often, re-perturb blobs
412 		if ((i % 4) == 3) {
413 			vms_vector	temp_vec;
414 
415 			make_random_vector(&temp_vec);
416 			vm_vec_scale_add2(&perturb_vec, &temp_vec, F1_0/4);
417 		}
418 
419 		vm_vec_scale_add(&temp_pos, &blob_pos, &perturb_vec, perturb_array[i]);
420 
421 		segnum = find_point_seg(&temp_pos, last_segnum);
422 		if (segnum != -1) {
423 			object		*objp;
424 
425 			last_segnum = segnum;
426 			blob_objnum = obj_create(OBJ_WEAPON, OMEGA_ID, segnum, &temp_pos, NULL, 0, CT_WEAPON, MT_PHYSICS, RT_WEAPON_VCLIP );
427 			if (blob_objnum == -1)
428 				break;
429 
430 			last_created_objnum = blob_objnum;
431 
432 			objp = &Objects[blob_objnum];
433 
434 			objp->lifeleft = ONE_FRAME_TIME;
435 			objp->mtype.phys_info.velocity = vec_to_goal;
436 
437 			//	Only make the last one move fast, else multiple blobs might collide with target.
438 			vm_vec_scale(&objp->mtype.phys_info.velocity, F1_0*4);
439 
440 			objp->size = Weapon_info[objp->id].blob_size;
441 
442 			objp->shields = fixmul(OMEGA_DAMAGE_SCALE*FrameTime, Weapon_info[objp->id].strength[Difficulty_level]);
443 
444 			objp->ctype.laser_info.parent_type			= parent_objp->type;
445 			objp->ctype.laser_info.parent_signature	= parent_objp->signature;
446 			objp->ctype.laser_info.parent_num			= parent_objp-Objects;
447 			objp->movement_type = MT_NONE;	//	Only last one moves, that will get bashed below.
448 
449 		}
450 
451 		vm_vec_add2(&blob_pos, &omega_delta_vector);
452 
453 	}
454 
455 	//	Make last one move faster, but it's already moving at speed = F1_0*4.
456 	if (last_created_objnum != -1) {
457 		vm_vec_scale(&Objects[last_created_objnum].mtype.phys_info.velocity, Weapon_info[OMEGA_ID].speed[Difficulty_level]/4);
458 		Objects[last_created_objnum].movement_type = MT_PHYSICS;
459 	}
460 
461 	Doing_lighting_hack_flag = 0;
462 }
463 
464 #define	MIN_OMEGA_CHARGE	(MAX_OMEGA_CHARGE/8)
465 #define	OMEGA_CHARGE_SCALE	4			//	FrameTime / OMEGA_CHARGE_SCALE added to Omega_charge every frame.
466 fix	Omega_charge = MAX_OMEGA_CHARGE;
467 
468 #define	OMEGA_CHARGE_SCALE	4
469 
470 int	Last_omega_fire_frame=0;
471 
472 // ---------------------------------------------------------------------------------
473 //	Call this every frame to recharge the Omega Cannon.
omega_charge_frame(void)474 void omega_charge_frame(void)
475 {
476 	fix	delta_charge, old_omega_charge;
477 
478 	if (Omega_charge == MAX_OMEGA_CHARGE)
479 		return;
480 
481 	if (!(player_has_weapon(OMEGA_INDEX, 0) & HAS_WEAPON_FLAG))
482 		return;
483 
484 	if (Player_is_dead)
485 		return;
486 
487 	if ((Primary_weapon == OMEGA_INDEX) && (Omega_charge == 0) && (Players[Player_num].energy == 0)) {
488 		Primary_weapon--;
489 		auto_select_weapon(0);
490 	}
491 
492 	//	Don't charge while firing.
493 	if ((Last_omega_fire_frame == FrameCount) || (Last_omega_fire_frame == FrameCount-1))
494 		return;
495 
496 	if (Players[Player_num].energy) {
497 		fix	energy_used;
498 
499 		old_omega_charge = Omega_charge;
500 		Omega_charge += FrameTime/OMEGA_CHARGE_SCALE;
501 		if (Omega_charge > MAX_OMEGA_CHARGE)
502 			Omega_charge = MAX_OMEGA_CHARGE;
503 
504 		delta_charge = Omega_charge - old_omega_charge;
505 
506 		energy_used = fixmul(F1_0*190/17, delta_charge);
507 		if (Difficulty_level < 2)
508 			energy_used = fixmul(energy_used, i2f(Difficulty_level+2)/4);
509 
510 		Players[Player_num].energy -= energy_used;
511 		if (Players[Player_num].energy < 0)
512 			Players[Player_num].energy = 0;
513 	}
514 
515 
516 }
517 
518 // -- fix	Last_omega_muzzle_flash_time;
519 
520 // ---------------------------------------------------------------------------------
521 //	*objp is the object firing the omega cannon
522 //	*pos is the location from which the omega bolt starts
do_omega_stuff(object * parent_objp,vms_vector * firing_pos,object * weapon_objp)523 void do_omega_stuff(object *parent_objp, vms_vector *firing_pos, object *weapon_objp)
524 {
525 	int			lock_objnum, firing_segnum;
526 	vms_vector	goal_pos;
527 	int			pnum = parent_objp->id;
528 
529 	if (pnum == Player_num) {
530 		//	If charge >= min, or (some charge and zero energy), allow to fire.
531 		if (!((Omega_charge >= MIN_OMEGA_CHARGE) || (Omega_charge && !Players[pnum].energy))) {
532 			obj_delete(weapon_objp-Objects);
533 			return;
534 		}
535 
536 		Omega_charge -= FrameTime;
537 		if (Omega_charge < 0)
538 			Omega_charge = 0;
539 
540 		//	Ensure that the lightning cannon can be fired next frame.
541 		Next_laser_fire_time = GameTime+1;
542 
543 		Last_omega_fire_frame = FrameCount;
544 	}
545 
546 	weapon_objp->ctype.laser_info.parent_type = OBJ_PLAYER;
547 	weapon_objp->ctype.laser_info.parent_num = Players[pnum].objnum;
548 	weapon_objp->ctype.laser_info.parent_signature = Objects[Players[pnum].objnum].signature;
549 
550 	lock_objnum = find_homing_object(firing_pos, weapon_objp);
551 
552 	firing_segnum = find_point_seg(firing_pos, parent_objp->segnum);
553 
554 	//	Play sound.
555 	if ( parent_objp == Viewer )
556 		digi_play_sample( Weapon_info[weapon_objp->id].flash_sound, F1_0 );
557 	else
558 		digi_link_sound_to_pos( Weapon_info[weapon_objp->id].flash_sound, weapon_objp->segnum, 0, &weapon_objp->pos, 0, F1_0 );
559 
560 	// -- if ((Last_omega_muzzle_flash_time + F1_0/4 < GameTime) || (Last_omega_muzzle_flash_time > GameTime)) {
561 	// -- 	do_muzzle_stuff(firing_segnum, firing_pos);
562 	// -- 	Last_omega_muzzle_flash_time = GameTime;
563 	// -- }
564 
565 	//	Delete the original object.  Its only purpose in life was to determine which object to home in on.
566 	obj_delete(weapon_objp-Objects);
567 
568 	//	If couldn't lock on anything, fire straight ahead.
569 	if (lock_objnum == -1) {
570 		fvi_query	fq;
571 		fvi_info		hit_data;
572 		int			fate;
573 		vms_vector	perturb_vec, perturbed_fvec;
574 
575 		make_random_vector(&perturb_vec);
576 		vm_vec_scale_add(&perturbed_fvec, &parent_objp->orient.fvec, &perturb_vec, F1_0/16);
577 
578 		vm_vec_scale_add(&goal_pos, firing_pos, &perturbed_fvec, MAX_OMEGA_DIST);
579 		fq.startseg = firing_segnum;
580 		if (fq.startseg == -1) {
581 			mprintf((1, "Trying to fire Omega Cannon, but gun is outside mine.  Aborting!\n"));
582 			return;
583 		}
584 		fq.p0						= firing_pos;
585 		fq.p1						= &goal_pos;
586 		fq.rad					= 0;
587 		fq.thisobjnum			= parent_objp-Objects;
588 		fq.ignore_obj_list	= NULL;
589 		fq.flags					= FQ_IGNORE_POWERUPS | FQ_TRANSPOINT | FQ_CHECK_OBJS;		//what about trans walls???
590 
591 		fate = find_vector_intersection(&fq, &hit_data);
592 		if (fate != HIT_NONE) {
593 			Assert(hit_data.hit_seg != -1);		//	How can this be?  We went from inside the mine to outside without hitting anything?
594 			goal_pos = hit_data.hit_pnt;
595 		}
596 	} else
597 		goal_pos = Objects[lock_objnum].pos;
598 
599 	//	This is where we create a pile of omega blobs!
600 	create_omega_blobs(firing_segnum, firing_pos, &goal_pos, parent_objp);
601 
602 }
603 
604 // ---------------------------------------------------------------------------------
605 // Initializes a laser after Fire is pressed
606 //	Returns object number.
Laser_create_new(vms_vector * direction,vms_vector * position,int segnum,int parent,int weapon_type,int make_sound)607 int Laser_create_new( vms_vector * direction, vms_vector * position, int segnum, int parent, int weapon_type, int make_sound )
608 {
609 	int objnum;
610 	object *obj;
611 	fix parent_speed, weapon_speed;
612 	fix volume;
613 	fix laser_length=0;
614 
615 	Assert( weapon_type < N_weapon_types );
616 
617 	if ( (weapon_type<0) || (weapon_type>=N_weapon_types) )
618 		weapon_type = 0;
619 
620 	//	Don't let homing blobs make muzzle flash.
621 	if (Objects[parent].type == OBJ_ROBOT)
622 		do_muzzle_stuff(segnum, position);
623 
624 	objnum = create_weapon_object(weapon_type,segnum,position);
625 
626 	if ( objnum < 0 ) {
627 		mprintf((1, "Can't create laser - Out of objects!\n" ));
628 		return -1;
629 	}
630 
631 	obj = &Objects[objnum];
632 
633 	//	Do the special Omega Cannon stuff.  Then return on account of everything that follows does
634 	//	not apply to the Omega Cannon.
635 	if (weapon_type == OMEGA_ID) {
636 		// Create orientation matrix for tracking purposes.
637 		vm_vector_2_matrix( &obj->orient, direction, &Objects[parent].orient.uvec ,NULL);
638 
639 		if (( &Objects[parent] != Viewer ) && (Objects[parent].type != OBJ_WEAPON))	{
640 			// Muzzle flash
641 			if (Weapon_info[obj->id].flash_vclip > -1 )
642 				object_create_muzzle_flash( obj->segnum, &obj->pos, Weapon_info[obj->id].flash_size, Weapon_info[obj->id].flash_vclip );
643 		}
644 
645 		do_omega_stuff(&Objects[parent], position, obj);
646 
647 		return objnum;
648 	}
649 
650 	if (Objects[parent].type == OBJ_PLAYER) {
651 		if (weapon_type == FUSION_ID) {
652 
653 			if (Fusion_charge <= 0)
654 				obj->ctype.laser_info.multiplier = F1_0;
655 			else if (Fusion_charge <= 4*F1_0)
656 				obj->ctype.laser_info.multiplier = F1_0 + Fusion_charge/2;
657 			else
658 				obj->ctype.laser_info.multiplier = 4*F1_0;
659 
660 		} else if ((weapon_type >= LASER_ID && weapon_type <= MAX_SUPER_LASER_LEVEL) && (Players[Objects[parent].id].flags & PLAYER_FLAGS_QUAD_LASERS))
661 			obj->ctype.laser_info.multiplier = F1_0*3/4;
662 		else if (weapon_type == GUIDEDMISS_ID) {
663 			if (parent==Players[Player_num].objnum) {
664 				Guided_missile[Player_num]= obj;
665 				Guided_missile_sig[Player_num] = obj->signature;
666 				if (Newdemo_state==ND_STATE_RECORDING)
667 					newdemo_record_guided_start();
668 			}
669 		}
670 	}
671 
672 	//	Make children of smart bomb bounce so if they hit a wall right away, they
673 	//	won't detonate.  The frame interval code will clear this bit after 1/2 second.
674 	if ((weapon_type == PLAYER_SMART_HOMING_ID) || (weapon_type == SMART_MINE_HOMING_ID) || (weapon_type == ROBOT_SMART_HOMING_ID) || (weapon_type == ROBOT_SMART_MINE_HOMING_ID) || (weapon_type == EARTHSHAKER_MEGA_ID))
675 		obj->mtype.phys_info.flags |= PF_BOUNCE;
676 
677 	if (Weapon_info[weapon_type].render_type == WEAPON_RENDER_POLYMODEL)
678 		laser_length = Polygon_models[obj->rtype.pobj_info.model_num].rad * 2;
679 
680 	if (weapon_type == FLARE_ID)
681 		obj->mtype.phys_info.flags |= PF_STICK;		//this obj sticks to walls
682 
683 	obj->shields = Weapon_info[obj->id].strength[Difficulty_level];
684 
685 	// Fill in laser-specific data
686 
687 	obj->lifeleft							= Weapon_info[obj->id].lifetime;
688 	obj->ctype.laser_info.parent_type		= Objects[parent].type;
689 	obj->ctype.laser_info.parent_signature = Objects[parent].signature;
690 	obj->ctype.laser_info.parent_num			= parent;
691 
692 	//	Assign parent type to highest level creator.  This propagates parent type down from
693 	//	the original creator through weapons which create children of their own (ie, smart missile)
694 	if (Objects[parent].type == OBJ_WEAPON) {
695 		int	highest_parent = parent;
696 		int	count;
697 
698 		count = 0;
699 		while ((count++ < 10) && (Objects[highest_parent].type == OBJ_WEAPON)) {
700 			int	next_parent;
701 
702 			next_parent = Objects[highest_parent].ctype.laser_info.parent_num;
703 			if (Objects[next_parent].signature != Objects[highest_parent].ctype.laser_info.parent_signature)
704 				break;	//	Probably means parent was killed.  Just continue.
705 
706 			if (next_parent == highest_parent) {
707 				Int3();	//	Hmm, object is parent of itself.  This would seem to be bad, no?
708 				break;
709 			}
710 
711 			highest_parent = next_parent;
712 
713 			obj->ctype.laser_info.parent_num			= highest_parent;
714 			obj->ctype.laser_info.parent_type		= Objects[highest_parent].type;
715 			obj->ctype.laser_info.parent_signature = Objects[highest_parent].signature;
716 		}
717 	}
718 
719 	// Create orientation matrix so we can look from this pov
720 	//	Homing missiles also need an orientation matrix so they know if they can make a turn.
721 	if ((obj->render_type == RT_POLYOBJ) || (Weapon_info[obj->id].homing_flag))
722 		vm_vector_2_matrix( &obj->orient,direction, &Objects[parent].orient.uvec ,NULL);
723 
724 	if (( &Objects[parent] != Viewer ) && (Objects[parent].type != OBJ_WEAPON))	{
725 		// Muzzle flash
726 		if (Weapon_info[obj->id].flash_vclip > -1 )
727 			object_create_muzzle_flash( obj->segnum, &obj->pos, Weapon_info[obj->id].flash_size, Weapon_info[obj->id].flash_vclip );
728 	}
729 
730 	volume = F1_0;
731 	if (Weapon_info[obj->id].flash_sound > -1 )	{
732 		if (make_sound)	{
733 			if ( parent == (Viewer-Objects) )	{
734 				if (weapon_type == VULCAN_ID)	// Make your own vulcan gun  1/2 as loud.
735 					volume = F1_0 / 2;
736 				digi_play_sample( Weapon_info[obj->id].flash_sound, volume );
737 			} else {
738 				digi_link_sound_to_pos( Weapon_info[obj->id].flash_sound, obj->segnum, 0, &obj->pos, 0, volume );
739 			}
740 		}
741 	}
742 
743 	//	Fire the laser from the gun tip so that the back end of the laser bolt is at the gun tip.
744 	// Move 1 frame, so that the end-tip of the laser is touching the gun barrel.
745 	// This also jitters the laser a bit so that it doesn't alias.
746 	//	Don't do for weapons created by weapons.
747 	if ((Objects[parent].type == OBJ_PLAYER) && (Weapon_info[weapon_type].render_type != WEAPON_RENDER_NONE) && (weapon_type != FLARE_ID)) {
748 		vms_vector	end_pos;
749 		int			end_segnum;
750 
751 	 	vm_vec_scale_add( &end_pos, &obj->pos, direction, Laser_offset+(laser_length/2) );
752 		end_segnum = find_point_seg(&end_pos, obj->segnum);
753 		if (end_segnum != obj->segnum) {
754 			// mprintf(0, "Warning: Laser tip not in same segment as player.\n");
755 			if (end_segnum != -1) {
756 				obj->pos = end_pos;
757 				obj_relink(obj-Objects, end_segnum);
758 			} else
759 				mprintf((0, "Warning: Laser tip outside mine.  Laser not being moved to end of gun.\n"));
760 		} else
761 			obj->pos = end_pos;
762 	}
763 
764 	//	Here's where to fix the problem with objects which are moving backwards imparting higher velocity to their weaponfire.
765 	//	Find out if moving backwards.
766 	if ((weapon_type == PROXIMITY_ID) || (weapon_type == SUPERPROX_ID)) {
767 		parent_speed = vm_vec_mag_quick(&Objects[parent].mtype.phys_info.velocity);
768 		if (vm_vec_dot(&Objects[parent].mtype.phys_info.velocity, &Objects[parent].orient.fvec) < 0)
769 			parent_speed = -parent_speed;
770 	} else
771 		parent_speed = 0;
772 
773 	weapon_speed = Weapon_info[obj->id].speed[Difficulty_level];
774 	if (Weapon_info[obj->id].speedvar != 128) {
775 		fix	randval;
776 
777 		//	Get a scale factor between speedvar% and 1.0.
778 		randval = F1_0 - ((d_rand() * Weapon_info[obj->id].speedvar) >> 6);
779 		weapon_speed = fixmul(weapon_speed, randval);
780 	}
781 
782 	//	Ugly hack (too bad we're on a deadline), for homing missiles dropped by smart bomb, start them out slower.
783 	if ((obj->id == PLAYER_SMART_HOMING_ID) || (obj->id == SMART_MINE_HOMING_ID) || (obj->id == ROBOT_SMART_HOMING_ID) || (obj->id == ROBOT_SMART_MINE_HOMING_ID) || (obj->id == EARTHSHAKER_MEGA_ID))
784 		weapon_speed /= 4;
785 
786 	if (Weapon_info[obj->id].thrust != 0)
787 		weapon_speed /= 2;
788 
789 	vm_vec_copy_scale( &obj->mtype.phys_info.velocity, direction, weapon_speed + parent_speed );
790 
791 	//	Set thrust
792 	if (Weapon_info[weapon_type].thrust != 0) {
793 		obj->mtype.phys_info.thrust = obj->mtype.phys_info.velocity;
794 		vm_vec_scale(&obj->mtype.phys_info.thrust, fixdiv(Weapon_info[obj->id].thrust, weapon_speed+parent_speed));
795 	}
796 
797 	if ((obj->type == OBJ_WEAPON) && (obj->id == FLARE_ID))
798 		obj->lifeleft += (d_rand()-16384) << 2;		//	add in -2..2 seconds
799 
800 	//	mprintf( 0, "Weapon speed = %.1f (%.1f)\n", f2fl(Weapon_info[obj->id].speed[Difficulty_level] + parent_speed), f2fl(parent_speed) );
801 
802 	return objnum;
803 }
804 
805 //	-----------------------------------------------------------------------------------------------------------
806 //	Calls Laser_create_new, but takes care of the segment and point computation for you.
Laser_create_new_easy(vms_vector * direction,vms_vector * position,int parent,int weapon_type,int make_sound)807 int Laser_create_new_easy( vms_vector * direction, vms_vector * position, int parent, int weapon_type, int make_sound )
808 {
809 	fvi_query	fq;
810 	fvi_info		hit_data;
811 	object		*pobjp = &Objects[parent];
812 	int			fate;
813 
814 	//	Find segment containing laser fire position.  If the robot is straddling a segment, the position from
815 	//	which it fires may be in a different segment, which is bad news for find_vector_intersection.  So, cast
816 	//	a ray from the object center (whose segment we know) to the laser position.  Then, in the call to Laser_create_new
817 	//	use the data returned from this call to find_vector_intersection.
818 	//	Note that while find_vector_intersection is pretty slow, it is not terribly slow if the destination point is
819 	//	in the same segment as the source point.
820 
821 	fq.p0						= &pobjp->pos;
822 	fq.startseg				= pobjp->segnum;
823 	fq.p1						= position;
824 	fq.rad					= 0;
825 	fq.thisobjnum			= pobjp-Objects;
826 	fq.ignore_obj_list	= NULL;
827 	fq.flags					= FQ_TRANSWALL | FQ_CHECK_OBJS;		//what about trans walls???
828 
829 	fate = find_vector_intersection(&fq, &hit_data);
830 	if (fate != HIT_NONE  || hit_data.hit_seg==-1) {
831 		mprintf((1, "Warning: Laser from parent=%i stuck in wall or object, didn't fire!\n", parent));
832 		return -1;
833 	}
834 
835 	return Laser_create_new( direction, &hit_data.hit_pnt, hit_data.hit_seg, parent, weapon_type, make_sound );
836 
837 }
838 
839 int		Muzzle_queue_index = 0;
840 
841 muzzle_info		Muzzle_data[MUZZLE_QUEUE_MAX];
842 
843 //	-----------------------------------------------------------------------------------------------------------
844 //	Determine if two objects are on a line of sight.  If so, return true, else return false.
845 //	Calls fvi.
object_to_object_visibility(object * obj1,object * obj2,int trans_type)846 int object_to_object_visibility(object *obj1, object *obj2, int trans_type)
847 {
848 	fvi_query	fq;
849 	fvi_info		hit_data;
850 	int			fate;
851 
852 	fq.p0						= &obj1->pos;
853 	fq.startseg				= obj1->segnum;
854 	fq.p1						= &obj2->pos;
855 	fq.rad					= 0x10;
856 	fq.thisobjnum			= obj1-Objects;
857 	fq.ignore_obj_list	= NULL;
858 	fq.flags					= trans_type;
859 
860 	fate = find_vector_intersection(&fq, &hit_data);
861 
862 	if (fate == HIT_WALL)
863 		return 0;
864 	else if (fate == HIT_NONE)
865 		return 1;
866 	else
867 		Int3();		//	Contact Mike: Oops, what happened?  What is fate?
868 						// 2 = hit object (impossible), 3 = bad starting point (bad)
869 
870 	return 0;
871 }
872 
873 fix	Min_trackable_dot = MIN_TRACKABLE_DOT;
874 
875 //	-----------------------------------------------------------------------------------------------------------
876 //	Return true if weapon *tracker is able to track object Objects[track_goal], else return false.
877 //	In order for the object to be trackable, it must be within a reasonable turning radius for the missile
878 //	and it must not be obstructed by a wall.
object_is_trackable(int track_goal,object * tracker,fix * dot)879 int object_is_trackable(int track_goal, object *tracker, fix *dot)
880 {
881 	vms_vector	vector_to_goal;
882 	object		*objp;
883 
884 	if (track_goal == -1)
885 		return 0;
886 
887 	if (Game_mode & GM_MULTI_COOP)
888 		return 0;
889 
890 	objp = &Objects[track_goal];
891 
892 	//	Don't track player if he's cloaked.
893 	if ((track_goal == Players[Player_num].objnum) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
894 		return 0;
895 
896 	//	Can't track AI object if he's cloaked.
897 	if (objp->type == OBJ_ROBOT) {
898 		if (objp->ctype.ai_info.CLOAKED)
899 			return 0;
900 		//	Your missiles don't track your escort.
901 		if (Robot_info[objp->id].companion)
902 			if (tracker->ctype.laser_info.parent_type == OBJ_PLAYER)
903 				return 0;
904 	}
905 	vm_vec_sub(&vector_to_goal, &objp->pos, &tracker->pos);
906 	vm_vec_normalize_quick(&vector_to_goal);
907 	*dot = vm_vec_dot(&vector_to_goal, &tracker->orient.fvec);
908 
909 	if ((*dot < Min_trackable_dot) && (*dot > F1_0*9/10)) {
910 		// -- mprintf((0, "."));
911 		vm_vec_normalize(&vector_to_goal);
912 		*dot = vm_vec_dot(&vector_to_goal, &tracker->orient.fvec);
913 	}
914 
915 //mprintf((0, " OIT: dot=%5.2f ", f2fl(dot)));
916 
917 	// mprintf((0, "object_is_trackable: [%3i] %7.3f, min = %7.3f\n", track_goal, f2fl(dot), f2fl(Min_trackable_dot)));
918 
919 	if (*dot >= Min_trackable_dot) {
920 		int	rval;
921 		//	dot is in legal range, now see if object is visible
922 		rval =  object_to_object_visibility(tracker, objp, FQ_TRANSWALL);
923 //mprintf((0, " TRACK "));
924 		return rval;
925 	} else {
926 //mprintf((0, " LOST! "));
927 		return 0;
928 	}
929 
930 }
931 
932 extern int Robots_kill_robots_cheat;
933 
934 //	--------------------------------------------------------------------------------------------
call_find_homing_object_complete(object * tracker,vms_vector * curpos)935 int call_find_homing_object_complete(object *tracker, vms_vector *curpos)
936 {
937 	if (Game_mode & GM_MULTI) {
938 		if (tracker->ctype.laser_info.parent_type == OBJ_PLAYER) {
939 			//	It's fired by a player, so if robots present, track robot, else track player.
940 			if (Game_mode & GM_MULTI_COOP)
941 				return find_homing_object_complete( curpos, tracker, OBJ_ROBOT, -1);
942 			else
943 				return find_homing_object_complete( curpos, tracker, OBJ_PLAYER, OBJ_ROBOT);
944 		} else {
945 			int	goal2_type = -1;
946 
947 			if (Robots_kill_robots_cheat)
948 				goal2_type = OBJ_ROBOT;
949 			Assert(tracker->ctype.laser_info.parent_type == OBJ_ROBOT);
950 			return find_homing_object_complete(curpos, tracker, OBJ_PLAYER, goal2_type);
951 		}
952 	} else
953 		return find_homing_object_complete( curpos, tracker, OBJ_ROBOT, -1);
954 }
955 
956 //	--------------------------------------------------------------------------------------------
957 //	Find object to home in on.
958 //	Scan list of objects rendered last frame, find one that satisfies function of nearness to center and distance.
find_homing_object(vms_vector * curpos,object * tracker)959 int find_homing_object(vms_vector *curpos, object *tracker)
960 {
961 	int	i;
962 	fix	max_dot = -F1_0*2;
963 	int	best_objnum = -1;
964 
965 	//	Contact Mike: This is a bad and stupid thing.  Who called this routine with an illegal laser type??
966 	Assert((Weapon_info[tracker->id].homing_flag) || (tracker->id == OMEGA_ID));
967 
968 	//	Find an object to track based on game mode (eg, whether in network play) and who fired it.
969 
970 	if (Game_mode & GM_MULTI)
971 		return call_find_homing_object_complete(tracker, curpos);
972 	else {
973 		int	cur_min_trackable_dot;
974 
975 		cur_min_trackable_dot = MIN_TRACKABLE_DOT;
976 		if ((tracker->type == OBJ_WEAPON) && (tracker->id == OMEGA_ID))
977 			cur_min_trackable_dot = OMEGA_MIN_TRACKABLE_DOT;
978 
979 		//	Not in network mode.  If not fired by player, then track player.
980 		if (tracker->ctype.laser_info.parent_num != Players[Player_num].objnum) {
981 			if (!(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED))
982 				best_objnum = ConsoleObject - Objects;
983 		} else {
984 			int	window_num = -1;
985 			fix	dist, max_trackable_dist;
986 
987 			//	Find the window which has the forward view.
988 			for (i=0; i<MAX_RENDERED_WINDOWS; i++)
989 				if (Window_rendered_data[i].frame >= FrameCount-1)
990 					if (Window_rendered_data[i].viewer == ConsoleObject)
991 						if (!Window_rendered_data[i].rear_view) {
992 							window_num = i;
993 							break;
994 						}
995 
996 			//	Couldn't find suitable view from this frame, so do complete search.
997 			if (window_num == -1) {
998 				mprintf((0, "Note: Calling find_homing_object_complete because no suitable rendered window.\n"));
999 				return call_find_homing_object_complete(tracker, curpos);
1000 			}
1001 
1002 			max_trackable_dist = MAX_TRACKABLE_DIST;
1003 			if (tracker->id == OMEGA_ID)
1004 				max_trackable_dist = OMEGA_MAX_TRACKABLE_DIST;
1005 
1006 			//	Not in network mode and fired by player.
1007 			for (i=Window_rendered_data[window_num].num_objects-1; i>=0; i--) {
1008 				fix			dot; //, dist;
1009 				vms_vector	vec_to_curobj;
1010 				int			objnum = Window_rendered_data[window_num].rendered_objects[i];
1011 				object		*curobjp = &Objects[objnum];
1012 
1013 				if (objnum == Players[Player_num].objnum)
1014 					continue;
1015 
1016 				//	Can't track AI object if he's cloaked.
1017 				if (curobjp->type == OBJ_ROBOT) {
1018 					if (curobjp->ctype.ai_info.CLOAKED)
1019 						continue;
1020 
1021 					//	Your missiles don't track your escort.
1022 					if (Robot_info[curobjp->id].companion)
1023 						if (tracker->ctype.laser_info.parent_type == OBJ_PLAYER)
1024 							continue;
1025 				}
1026 
1027 				vm_vec_sub(&vec_to_curobj, &curobjp->pos, curpos);
1028 				dist = vm_vec_normalize_quick(&vec_to_curobj);
1029 				if (dist < max_trackable_dist) {
1030 					dot = vm_vec_dot(&vec_to_curobj, &tracker->orient.fvec);
1031 
1032 					// mprintf(0, "Object %i: dist = %7.3f, dot = %7.3f\n", objnum, f2fl(dist), f2fl(dot));
1033 
1034 					//	Note: This uses the constant, not-scaled-by-frametime value, because it is only used
1035 					//	to determine if an object is initially trackable.  find_homing_object is called on subsequent
1036 					//	frames to determine if the object remains trackable.
1037 					// mprintf((0, "find_homing_object:  [%3i] %7.3f, min = %7.3f\n", curobjp-Objects, f2fl(dot), f2fl(MIN_TRACKABLE_DOT)));
1038 					if (dot > cur_min_trackable_dot) {
1039 						if (dot > max_dot) {
1040 							if (object_to_object_visibility(tracker, &Objects[objnum], FQ_TRANSWALL)) {
1041 								max_dot = dot;
1042 								best_objnum = objnum;
1043 							}
1044 						}
1045 					} else if (dot > F1_0 - (F1_0 - cur_min_trackable_dot)*2) {
1046 						vm_vec_normalize(&vec_to_curobj);
1047 						dot = vm_vec_dot(&vec_to_curobj, &tracker->orient.fvec);
1048 						if (dot > cur_min_trackable_dot) {
1049 							if (dot > max_dot) {
1050 								if (object_to_object_visibility(tracker, &Objects[objnum], FQ_TRANSWALL)) {
1051 									max_dot = dot;
1052 									best_objnum = objnum;
1053 								}
1054 							}
1055 						}
1056 					}
1057 				}
1058 			}
1059 		}
1060 	}
1061 
1062 	// mprintf(0, "Selecting object #%i\n=n", best_objnum);
1063 
1064 	return best_objnum;
1065 }
1066 
1067 //	--------------------------------------------------------------------------------------------
1068 //	Find object to home in on.
1069 //	Scan list of objects rendered last frame, find one that satisfies function of nearness to center and distance.
1070 //	Can track two kinds of objects.  If you are only interested in one type, set track_obj_type2 to NULL
1071 //	Always track proximity bombs.  --MK, 06/14/95
1072 //	Make homing objects not track parent's prox bombs.
find_homing_object_complete(vms_vector * curpos,object * tracker,int track_obj_type1,int track_obj_type2)1073 int find_homing_object_complete(vms_vector *curpos, object *tracker, int track_obj_type1, int track_obj_type2)
1074 {
1075 	int	objnum;
1076 	fix	max_dot = -F1_0*2;
1077 	int	best_objnum = -1;
1078 	fix	max_trackable_dist;
1079 	fix	min_trackable_dot;
1080 
1081 	//	Contact Mike: This is a bad and stupid thing.  Who called this routine with an illegal laser type??
1082 	Assert((Weapon_info[tracker->id].homing_flag) || (tracker->id == OMEGA_ID));
1083 
1084 	max_trackable_dist = MAX_TRACKABLE_DIST;
1085 	min_trackable_dot = MIN_TRACKABLE_DOT;
1086 
1087 	if (tracker->id == OMEGA_ID) {
1088 		max_trackable_dist = OMEGA_MAX_TRACKABLE_DIST;
1089 		min_trackable_dot = OMEGA_MIN_TRACKABLE_DOT;
1090 	}
1091 
1092 	for (objnum=0; objnum<=Highest_object_index; objnum++) {
1093 		int			is_proximity = 0;
1094 		fix			dot, dist;
1095 		vms_vector	vec_to_curobj;
1096 		object		*curobjp = &Objects[objnum];
1097 
1098 		if ((curobjp->type != track_obj_type1) && (curobjp->type != track_obj_type2))
1099 		{
1100 			if ((curobjp->type == OBJ_WEAPON) && ((curobjp->id == PROXIMITY_ID) || (curobjp->id == SUPERPROX_ID))) {
1101 				if (curobjp->ctype.laser_info.parent_signature != tracker->ctype.laser_info.parent_signature)
1102 					is_proximity = 1;
1103 				else
1104 					continue;
1105 			} else
1106 				continue;
1107 		}
1108 
1109 		if (objnum == tracker->ctype.laser_info.parent_num) // Don't track shooter
1110 			continue;
1111 
1112 		//	Don't track cloaked players.
1113 		if (curobjp->type == OBJ_PLAYER)
1114 		{
1115 			if (Players[curobjp->id].flags & PLAYER_FLAGS_CLOAKED)
1116 				continue;
1117 			// Don't track teammates in team games
1118 			#ifdef NETWORK
1119 			if ((Game_mode & GM_TEAM) && (Objects[tracker->ctype.laser_info.parent_num].type == OBJ_PLAYER) && (get_team(curobjp->id) == get_team(Objects[tracker->ctype.laser_info.parent_num].id)))
1120 				continue;
1121 			#endif
1122 		}
1123 
1124 		//	Can't track AI object if he's cloaked.
1125 		if (curobjp->type == OBJ_ROBOT) {
1126 			if (curobjp->ctype.ai_info.CLOAKED)
1127 				continue;
1128 
1129 			//	Your missiles don't track your escort.
1130 			if (Robot_info[curobjp->id].companion)
1131 				if (tracker->ctype.laser_info.parent_type == OBJ_PLAYER)
1132 					continue;
1133 		}
1134 
1135 		vm_vec_sub(&vec_to_curobj, &curobjp->pos, curpos);
1136 		dist = vm_vec_mag_quick(&vec_to_curobj);
1137 
1138 		if (dist < max_trackable_dist) {
1139 			vm_vec_normalize_quick(&vec_to_curobj);
1140 			dot = vm_vec_dot(&vec_to_curobj, &tracker->orient.fvec);
1141 			if (is_proximity)
1142 				dot = ((dot << 3) + dot) >> 3;		//	I suspect Watcom would be too stupid to figure out the obvious...
1143 
1144 			//	Note: This uses the constant, not-scaled-by-frametime value, because it is only used
1145 			//	to determine if an object is initially trackable.  find_homing_object is called on subsequent
1146 			//	frames to determine if the object remains trackable.
1147 			// mprintf((0, "fho_complete:        [%3i] %7.3f, min = %7.3f\n", curobjp-Objects, f2fl(dot), f2fl(MIN_TRACKABLE_DOT)));
1148 			if (dot > min_trackable_dot) {
1149 				// mprintf(0, "Object %i: dist = %7.3f, dot = %7.3f\n", objnum, f2fl(dist), f2fl(dot));
1150 				if (dot > max_dot) {
1151 					if (object_to_object_visibility(tracker, &Objects[objnum], FQ_TRANSWALL)) {
1152 						max_dot = dot;
1153 						best_objnum = objnum;
1154 					}
1155 				}
1156 			}
1157 		}
1158 
1159 	}
1160 
1161 	// -- mprintf((0, "Selecting object #%i in find_homing_object_complete\n\n", best_objnum));
1162 
1163 	return best_objnum;
1164 }
1165 
1166 //	------------------------------------------------------------------------------------------------------------
1167 //	See if legal to keep tracking currently tracked object.  If not, see if another object is trackable.  If not, return -1,
1168 //	else return object number of tracking object.
1169 //	Computes and returns a fairly precise dot product.
track_track_goal(int track_goal,object * tracker,fix * dot)1170 int track_track_goal(int track_goal, object *tracker, fix *dot)
1171 {
1172 	//	Every 8 frames for each object, scan all objects.
1173 	if (object_is_trackable(track_goal, tracker, dot) && ((((tracker-Objects) ^ FrameCount) % 8) != 0)) {
1174 		//mprintf((0, "ttg: QO"));
1175 		return track_goal;
1176 	} else if ((((tracker-Objects) ^ FrameCount) % 4) == 0) {
1177 		int	rval = -2;
1178 
1179 		//	If player fired missile, then search for an object, if not, then give up.
1180 		if (Objects[tracker->ctype.laser_info.parent_num].type == OBJ_PLAYER) {
1181 			int	goal_type;
1182 
1183 			if (track_goal == -1)
1184 			{
1185 				if (Game_mode & GM_MULTI)
1186 				{
1187 					if (Game_mode & GM_MULTI_COOP)
1188 						rval = find_homing_object_complete( &tracker->pos, tracker, OBJ_ROBOT, -1);
1189 					else if (Game_mode & GM_MULTI_ROBOTS)		//	Not cooperative, if robots, track either robot or player
1190 						rval = find_homing_object_complete( &tracker->pos, tracker, OBJ_PLAYER, OBJ_ROBOT);
1191 					else		//	Not cooperative and no robots, track only a player
1192 						rval = find_homing_object_complete( &tracker->pos, tracker, OBJ_PLAYER, -1);
1193 				}
1194 				else
1195 					rval = find_homing_object_complete(&tracker->pos, tracker, OBJ_PLAYER, OBJ_ROBOT);
1196 			}
1197 			else
1198 			{
1199 				goal_type = Objects[tracker->ctype.laser_info.track_goal].type;
1200 				if ((goal_type == OBJ_PLAYER) || (goal_type == OBJ_ROBOT))
1201 					rval = find_homing_object_complete(&tracker->pos, tracker, goal_type, -1);
1202 				else
1203 					rval = -1;
1204 			}
1205 		}
1206 		else {
1207 			int	goal_type, goal2_type = -1;
1208 
1209 			if (Robots_kill_robots_cheat)
1210 				goal2_type = OBJ_ROBOT;
1211 
1212 			if (track_goal == -1)
1213 				rval = find_homing_object_complete(&tracker->pos, tracker, OBJ_PLAYER, goal2_type);
1214 			else {
1215 				goal_type = Objects[tracker->ctype.laser_info.track_goal].type;
1216 				rval = find_homing_object_complete(&tracker->pos, tracker, goal_type, goal2_type);
1217 			}
1218 		}
1219 
1220 		Assert(rval != -2);		//	This means it never got set which is bad!  Contact Mike.
1221 		return rval;
1222 	}
1223 
1224 //if (track_goal != -1)
1225 // mprintf((0, "Object %i not tracking anything.\n", tracker-Objects));
1226 
1227 	return -1;
1228 }
1229 
1230 //-------------- Initializes a laser after Fire is pressed -----------------
1231 
Laser_player_fire_spread_delay(object * obj,int laser_type,int gun_num,fix spreadr,fix spreadu,fix delay_time,int make_sound,int harmless)1232 void Laser_player_fire_spread_delay(object *obj, int laser_type, int gun_num, fix spreadr, fix spreadu, fix delay_time, int make_sound, int harmless)
1233 {
1234 	int			LaserSeg, Fate;
1235 	vms_vector	LaserPos, LaserDir;
1236 	fvi_query	fq;
1237 	fvi_info		hit_data;
1238 	vms_vector	gun_point, *pnt;
1239 	vms_matrix	m;
1240 	int			objnum;
1241 
1242 	create_awareness_event(obj, PA_WEAPON_WALL_COLLISION);
1243 
1244 	// Find the initial position of the laser
1245 	pnt = &Player_ship->gun_points[gun_num];
1246 
1247 	vm_copy_transpose_matrix(&m,&obj->orient);
1248 	vm_vec_rotate(&gun_point,pnt,&m);
1249 
1250 	vm_vec_add(&LaserPos,&obj->pos,&gun_point);
1251 
1252 	//	If supposed to fire at a delayed time (delay_time), then move this point backwards.
1253 	if (delay_time)
1254 		vm_vec_scale_add2(&LaserPos, &obj->orient.fvec, -fixmul(delay_time, Weapon_info[laser_type].speed[Difficulty_level]));
1255 
1256 //	do_muzzle_stuff(obj, &Pos);
1257 
1258 	//--------------- Find LaserPos and LaserSeg ------------------
1259 	fq.p0						= &obj->pos;
1260 	fq.startseg				= obj->segnum;
1261 	fq.p1						= &LaserPos;
1262 	fq.rad					= 0x10;
1263 	fq.thisobjnum			= obj-Objects;
1264 	fq.ignore_obj_list	= NULL;
1265 	fq.flags					= FQ_CHECK_OBJS | FQ_IGNORE_POWERUPS;
1266 
1267 	Fate = find_vector_intersection(&fq, &hit_data);
1268 
1269 	LaserSeg = hit_data.hit_seg;
1270 
1271 	if (LaserSeg == -1)		//some sort of annoying error
1272 		return;
1273 
1274 	//SORT OF HACK... IF ABOVE WAS CORRECT THIS WOULDNT BE NECESSARY.
1275 	if ( vm_vec_dist_quick(&LaserPos, &obj->pos) > 0x50000 )
1276 		return;
1277 
1278 	if (Fate==HIT_WALL) {
1279 		if (delay_time)
1280 			mprintf((0, "Your DELAYED laser is stuck thru a wall!\n" ));
1281 		else
1282 			mprintf((0, "Your laser is stuck thru a wall!\n" ));
1283 		return;
1284 	}
1285 
1286 	if (Fate==HIT_OBJECT) {
1287 //		if ( Objects[hit_data.hit_object].type == OBJ_ROBOT )
1288 //			Objects[hit_data.hit_object].flags |= OF_SHOULD_BE_DEAD;
1289 		mprintf((0, "Your laser is stuck in an object!\n" ));
1290 //		if ( Objects[hit_data.hit_object].type != OBJ_POWERUP )
1291 //			return;
1292 	//as of 12/6/94, we don't care if the laser is stuck in an object. We
1293 	//just fire away normally
1294 	}
1295 
1296 	//	Now, make laser spread out.
1297 	LaserDir = obj->orient.fvec;
1298 	if ((spreadr != 0) || (spreadu != 0)) {
1299 		vm_vec_scale_add2(&LaserDir, &obj->orient.rvec, spreadr);
1300 		vm_vec_scale_add2(&LaserDir, &obj->orient.uvec, spreadu);
1301 	}
1302 
1303 	objnum = Laser_create_new( &LaserDir, &LaserPos, LaserSeg, obj-Objects, laser_type, make_sound );
1304 
1305 	//	Omega cannon is a hack, not surprisingly.  Don't want to do the rest of this stuff.
1306 	if (laser_type == OMEGA_ID)
1307 		return;
1308 
1309 	if (objnum == -1)
1310 		return;
1311 
1312 #ifdef NETWORK
1313 	if (laser_type==GUIDEDMISS_ID && Multi_is_guided) {
1314 		mprintf ((0,"Guided missile %s activated!\n",Players[obj->id].callsign));
1315 		Guided_missile[obj->id]=&Objects[objnum];
1316 	}
1317 
1318 	Multi_is_guided=0;
1319 #endif
1320 
1321 	if (laser_type == CONCUSSION_ID ||
1322 			 laser_type == HOMING_ID ||
1323 			 laser_type == SMART_ID ||
1324 			 laser_type == MEGA_ID ||
1325 			 laser_type == FLASH_ID ||
1326 			 //laser_type == GUIDEDMISS_ID ||
1327 			 //laser_type == SUPERPROX_ID ||
1328 			 laser_type == MERCURY_ID ||
1329 			 laser_type == EARTHSHAKER_ID)
1330 		if (Missile_viewer == NULL && obj->id==Player_num)
1331 			Missile_viewer = &Objects[objnum];
1332 
1333 	//	If this weapon is supposed to be silent, set that bit!
1334 	if (!make_sound)
1335 		Objects[objnum].flags |= OF_SILENT;
1336 
1337 	//	If this weapon is supposed to be silent, set that bit!
1338 	if (harmless)
1339 		Objects[objnum].flags |= OF_HARMLESS;
1340 
1341 	//	If the object firing the laser is the player, then indicate the laser object so robots can dodge.
1342 	//	New by MK on 6/8/95, don't let robots evade proximity bombs, thereby decreasing uselessness of bombs.
1343 	if ((obj == ConsoleObject) && ((Objects[objnum].id != PROXIMITY_ID) && (Objects[objnum].id != SUPERPROX_ID)))
1344 		Player_fired_laser_this_frame = objnum;
1345 
1346 	if (Weapon_info[laser_type].homing_flag) {
1347 		if (obj == ConsoleObject)
1348 		{
1349 			Objects[objnum].ctype.laser_info.track_goal = find_homing_object(&LaserPos, &Objects[objnum]);
1350 			#ifdef NETWORK
1351 			Network_laser_track = Objects[objnum].ctype.laser_info.track_goal;
1352 			#endif
1353 		}
1354 		#ifdef NETWORK
1355 		else // Some other player shot the homing thing
1356 		{
1357 			Assert(Game_mode & GM_MULTI);
1358 			Objects[objnum].ctype.laser_info.track_goal = Network_laser_track;
1359 		}
1360 		#endif
1361 //		mprintf((0, "Selecting object #%i in find_homing_object_complete\n", Network_laser_track));
1362 	}
1363 }
1364 
1365 //	-----------------------------------------------------------------------------------------------------------
Laser_player_fire_spread(object * obj,int laser_type,int gun_num,fix spreadr,fix spreadu,int make_sound,int harmless)1366 void Laser_player_fire_spread(object *obj, int laser_type, int gun_num, fix spreadr, fix spreadu, int make_sound, int harmless)
1367 {
1368 	Laser_player_fire_spread_delay(obj, laser_type, gun_num, spreadr, spreadu, 0, make_sound, harmless);
1369 }
1370 
1371 
1372 //	-----------------------------------------------------------------------------------------------------------
Laser_player_fire(object * obj,int laser_type,int gun_num,int make_sound,int harmless)1373 void Laser_player_fire(object *obj, int laser_type, int gun_num, int make_sound, int harmless)
1374 {
1375 	Laser_player_fire_spread(obj, laser_type, gun_num, 0, 0, make_sound, harmless);
1376 }
1377 
1378 //	-----------------------------------------------------------------------------------------------------------
Flare_create(object * obj)1379 void Flare_create(object *obj)
1380 {
1381 	fix	energy_usage;
1382 
1383 	energy_usage = Weapon_info[FLARE_ID].energy_usage;
1384 
1385 	if (Difficulty_level < 2)
1386 		energy_usage = fixmul(energy_usage, i2f(Difficulty_level+2)/4);
1387 
1388 //	MK, 11/04/95: Allowed to fire flare even if no energy.
1389 // -- 	if (Players[Player_num].energy >= energy_usage) {
1390 		Players[Player_num].energy -= energy_usage;
1391 
1392 		if (Players[Player_num].energy <= 0) {
1393 			Players[Player_num].energy = 0;
1394 			// -- auto_select_weapon(0);
1395 		}
1396 
1397 		Laser_player_fire( obj, FLARE_ID, 6, 1, 0);
1398 
1399 		#ifdef NETWORK
1400 		if (Game_mode & GM_MULTI) {
1401 			Network_laser_fired = 1;
1402 			Network_laser_gun = FLARE_ADJUST;
1403 			Network_laser_flags = 0;
1404 			Network_laser_level = 0;
1405 		}
1406 		#endif
1407 // -- 	}
1408 
1409 }
1410 
1411 #define	HOMING_MISSILE_SCALE	16
1412 
1413 //-------------------------------------------------------------------------------------------
1414 //	Set object *objp's orientation to (or towards if I'm ambitious) its velocity.
homing_missile_turn_towards_velocity(object * objp,vms_vector * norm_vel)1415 void homing_missile_turn_towards_velocity(object *objp, vms_vector *norm_vel)
1416 {
1417 	vms_vector	new_fvec;
1418 
1419 	new_fvec = *norm_vel;
1420 
1421 	vm_vec_scale(&new_fvec, FrameTime*HOMING_MISSILE_SCALE);
1422 	vm_vec_add2(&new_fvec, &objp->orient.fvec);
1423 	vm_vec_normalize_quick(&new_fvec);
1424 
1425 //	if ((norm_vel->x == 0) && (norm_vel->y == 0) && (norm_vel->z == 0))
1426 //		return;
1427 
1428 	vm_vector_2_matrix(&objp->orient, &new_fvec, NULL, NULL);
1429 }
1430 
1431 //-------------------------------------------------------------------------------------------
1432 //sequence this laser object for this _frame_ (underscores added here to aid MK in his searching!)
Laser_do_weapon_sequence(object * obj)1433 void Laser_do_weapon_sequence(object *obj)
1434 {
1435 	Assert(obj->control_type == CT_WEAPON);
1436 
1437 	//	Ok, this is a big hack by MK.
1438 	//	If you want an object to last for exactly one frame, then give it a lifeleft of ONE_FRAME_TIME
1439 	if (obj->lifeleft == ONE_FRAME_TIME) {
1440 		if (Game_mode & GM_MULTI)
1441 			obj->lifeleft = OMEGA_MULTI_LIFELEFT;
1442 		else
1443 			obj->lifeleft = 0;
1444 		obj->render_type = RT_NONE;
1445 	}
1446 
1447 	if (obj->lifeleft < 0 ) {		// We died of old age
1448 		obj->flags |= OF_SHOULD_BE_DEAD;
1449 		if ( Weapon_info[obj->id].damage_radius )
1450 			explode_badass_weapon(obj,&obj->pos);
1451 		return;
1452 	}
1453 
1454 	//delete weapons that are not moving
1455 	if (	!((FrameCount ^ obj->signature) & 3) &&
1456 			(obj->id != FLARE_ID) &&
1457 			(Weapon_info[obj->id].speed[Difficulty_level] > 0) &&
1458 			(vm_vec_mag_quick(&obj->mtype.phys_info.velocity) < F2_0)) {
1459 		obj_delete(obj-Objects);
1460 		return;
1461 	}
1462 
1463 	if ( obj->id == FUSION_ID ) {		//always set fusion weapon to max vel
1464 
1465 		vm_vec_normalize_quick(&obj->mtype.phys_info.velocity);
1466 
1467 		vm_vec_scale(&obj->mtype.phys_info.velocity, Weapon_info[obj->id].speed[Difficulty_level]);
1468 	}
1469 
1470 // -- 	//	The Super Spreadfire (Helix) blobs travel in a sinusoidal path.  That is accomplished
1471 // -- 	//	by modifying velocity (direction) in the frame interval.
1472 // -- 	if (obj->id == SSPREADFIRE_ID) {
1473 // -- 		fix	age, sinval, cosval;
1474 // -- 		vms_vector	p, newp;
1475 // -- 		fix	speed;
1476 // --
1477 // -- 		speed = vm_vec_mag_quick(&obj->phys_info.velocity);
1478 // --
1479 // -- 		age = Weapon_info[obj->id].lifetime - obj->lifeleft;
1480 // --
1481 // -- 		fix_fast_sincos(age, &sinval, &cosval);
1482 // --
1483 // -- 		//	Note: Below code assumes x=1, y=0.  Need to scale this for values around a circle for 5 helix positions.
1484 // -- 		p.x = cosval << 3;
1485 // -- 		p.y = sinval << 3;
1486 // -- 		p.z = 0;
1487 // --
1488 // -- 		vm_vec_rotate(&newp, &p, &obj->orient);
1489 // --
1490 // -- 		vm_vec_add(&goal_point, &obj->pos, &newp);
1491 // --
1492 // -- 		vm_vec_sub(&vec_to_goal, &goal_point, obj
1493 // -- 	}
1494 
1495 
1496 	//	For homing missiles, turn towards target. (unless it's the guided missile)
1497 	if (Weapon_info[obj->id].homing_flag && !(obj->id==GUIDEDMISS_ID && obj==Guided_missile[Objects[obj->ctype.laser_info.parent_num].id] && obj->signature==Guided_missile[Objects[obj->ctype.laser_info.parent_num].id]->signature)) {
1498 		vms_vector		vector_to_object, temp_vec;
1499 		fix				dot=F1_0;
1500 		fix				speed, max_speed;
1501 
1502 		//	For first 1/2 second of life, missile flies straight.
1503 		if (obj->ctype.laser_info.creation_time + HOMING_MISSILE_STRAIGHT_TIME < GameTime) {
1504 
1505 			int	track_goal = obj->ctype.laser_info.track_goal;
1506 
1507 			//mprintf((0, "%5i: mtd=%5.2f", FrameCount, f2fl(Min_trackable_dot)));
1508 
1509 			//	If it's time to do tracking, then it's time to grow up, stop bouncing and start exploding!.
1510 			if ((obj->id == ROBOT_SMART_MINE_HOMING_ID) || (obj->id == ROBOT_SMART_HOMING_ID) || (obj->id == SMART_MINE_HOMING_ID) || (obj->id == PLAYER_SMART_HOMING_ID) || (obj->id == EARTHSHAKER_MEGA_ID)) {
1511 				// if (obj->mtype.phys_info.flags & PF_BOUNCE) mprintf(0, "Debouncing smart child %i\n", obj-Objects);
1512 				obj->mtype.phys_info.flags &= ~PF_BOUNCE;
1513 			}
1514 
1515 			//	Make sure the object we are tracking is still trackable.
1516 			track_goal = track_track_goal(track_goal, obj, &dot);
1517 
1518 			//mprintf((0, " after ttg=%3i ", track_goal));
1519 
1520 			if (track_goal == Players[Player_num].objnum) {
1521 				fix	dist_to_player;
1522 
1523 				dist_to_player = vm_vec_dist_quick(&obj->pos, &Objects[track_goal].pos);
1524 				if ((dist_to_player < Players[Player_num].homing_object_dist) || (Players[Player_num].homing_object_dist < 0))
1525 					Players[Player_num].homing_object_dist = dist_to_player;
1526 
1527 			}
1528 
1529 			if (track_goal != -1) {
1530 				vm_vec_sub(&vector_to_object, &Objects[track_goal].pos, &obj->pos);
1531 
1532 				vm_vec_normalize_quick(&vector_to_object);
1533 				temp_vec = obj->mtype.phys_info.velocity;
1534 				speed = vm_vec_normalize_quick(&temp_vec);
1535 				max_speed = Weapon_info[obj->id].speed[Difficulty_level];
1536 				if (speed+F1_0 < max_speed) {
1537 					speed += fixmul(max_speed, FrameTime/2);
1538 					if (speed > max_speed)
1539 						speed = max_speed;
1540 				}
1541 
1542 				// -- dot = vm_vec_dot(&temp_vec, &vector_to_object);
1543 //mprintf((0, " dot=%5.2f ", f2fl(dot)));
1544 				vm_vec_add2(&temp_vec, &vector_to_object);
1545 				//	The boss' smart children track better...
1546 				if (Weapon_info[obj->id].render_type != WEAPON_RENDER_POLYMODEL)
1547 					vm_vec_add2(&temp_vec, &vector_to_object);
1548 				vm_vec_normalize_quick(&temp_vec);
1549 				obj->mtype.phys_info.velocity = temp_vec;
1550 				vm_vec_scale(&obj->mtype.phys_info.velocity, speed);
1551 
1552 				//	Subtract off life proportional to amount turned.
1553 				//	For hardest turn, it will lose 2 seconds per second.
1554 				{
1555 					fix	lifelost, absdot;
1556 
1557 					absdot = abs(F1_0 - dot);
1558 
1559 					lifelost = fixmul(absdot*32, FrameTime);
1560 					obj->lifeleft -= lifelost;
1561 					// -- mprintf((0, "Missile %3i, dot = %7.3f life lost = %7.3f, life left = %7.3f\n", obj-Objects, f2fl(dot), f2fl(lifelost), f2fl(obj->lifeleft)));
1562 				}
1563 
1564 				//	Only polygon objects have visible orientation, so only they should turn.
1565 				if (Weapon_info[obj->id].render_type == WEAPON_RENDER_POLYMODEL)
1566 					homing_missile_turn_towards_velocity(obj, &temp_vec);		//	temp_vec is normalized velocity.
1567 			}
1568 		}
1569 
1570 //mprintf((0, "\n"));
1571 	}
1572 
1573 	//	Make sure weapon is not moving faster than allowed speed.
1574 	{
1575 		fix	weapon_speed;
1576 
1577 		weapon_speed = vm_vec_mag_quick(&obj->mtype.phys_info.velocity);
1578 		if (weapon_speed > Weapon_info[obj->id].speed[Difficulty_level]) {
1579 			//	Only slow down if not allowed to move.  Makes sense, huh?  Allows proxbombs to get moved by physics force. --MK, 2/13/96
1580 			if (Weapon_info[obj->id].speed[Difficulty_level]) {
1581 				fix	scale_factor;
1582 
1583 				scale_factor = fixdiv(Weapon_info[obj->id].speed[Difficulty_level], weapon_speed);
1584 				vm_vec_scale(&obj->mtype.phys_info.velocity, scale_factor);
1585 			}
1586 		}
1587 	}
1588 }
1589 
1590 fix	Last_laser_fired_time = 0;
1591 
1592 extern int Player_fired_laser_this_frame;
1593 
1594 int	Zbonkers = 0;
1595 
1596 //	--------------------------------------------------------------------------------------------------
1597 // Assumption: This is only called by the actual console player, not for network players
1598 
do_laser_firing_player(void)1599 int do_laser_firing_player(void)
1600 {
1601 	player	*plp = &Players[Player_num];
1602 	fix		energy_used;
1603 	int		ammo_used,primary_ammo;
1604 	int		weapon_index;
1605 	int		rval = 0;
1606 	int 		nfires = 1;
1607 	fix		addval;
1608 	static int Spreadfire_toggle=0;
1609 	static int Helix_orientation = 0;
1610 
1611 	if (Player_is_dead)
1612 		return 0;
1613 
1614 	weapon_index = Primary_weapon_to_weapon_info[Primary_weapon];
1615 	energy_used = Weapon_info[weapon_index].energy_usage;
1616 	if (Primary_weapon == OMEGA_INDEX)
1617 		energy_used = 0;	//	Omega consumes energy when recharging, not when firing.
1618 
1619 	if (Difficulty_level < 2)
1620 		energy_used = fixmul(energy_used, i2f(Difficulty_level+2)/4);
1621 
1622 	//	MK, 01/26/96, Helix use 2x energy in multiplayer.  bitmaps.tbl parm should have been reduced for single player.
1623 	if (weapon_index == HELIX_INDEX)
1624 		if (Game_mode & GM_MULTI)
1625 			energy_used *= 2;
1626 
1627 	ammo_used = Weapon_info[weapon_index].ammo_usage;
1628 
1629 	addval = 2*FrameTime;
1630 	if (addval > F1_0)
1631 		addval = F1_0;
1632 
1633 	if ((Last_laser_fired_time + 2*FrameTime < GameTime) || (GameTime < Last_laser_fired_time))
1634 		Next_laser_fire_time = GameTime;
1635 
1636 	Last_laser_fired_time = GameTime;
1637 
1638 	primary_ammo = (Primary_weapon == GAUSS_INDEX)?(plp->primary_ammo[VULCAN_INDEX]):(plp->primary_ammo[Primary_weapon]);
1639 
1640 	if	(!((plp->energy >= energy_used) && (primary_ammo >= ammo_used)))
1641 		auto_select_weapon(0);		//	Make sure the player can fire from this weapon.
1642 
1643 if (Zbonkers) {
1644 	Zbonkers = 0;
1645 	GameTime = 0;
1646 }
1647 
1648 	while (Next_laser_fire_time <= GameTime) {
1649 		if	((plp->energy >= energy_used) && (primary_ammo >= ammo_used)) {
1650 			int	laser_level, flags;
1651 
1652 //mprintf(0, ".");
1653 			if (Laser_rapid_fire!=0xBADA55)
1654 				Next_laser_fire_time += Weapon_info[weapon_index].fire_wait;
1655 			else
1656 				Next_laser_fire_time += F1_0/25;
1657 
1658 			laser_level = Players[Player_num].laser_level;
1659 
1660 			flags = 0;
1661 
1662 			if (Primary_weapon == SPREADFIRE_INDEX) {
1663 				if (Spreadfire_toggle)
1664 					flags |= LASER_SPREADFIRE_TOGGLED;
1665 				Spreadfire_toggle = !Spreadfire_toggle;
1666 			}
1667 
1668 			if (Primary_weapon == HELIX_INDEX) {
1669 				Helix_orientation++;
1670 				flags |= ((Helix_orientation & LASER_HELIX_MASK) << LASER_HELIX_SHIFT);
1671 			}
1672 
1673 			if (Players[Player_num].flags & PLAYER_FLAGS_QUAD_LASERS)
1674 				flags |= LASER_QUAD;
1675 
1676 			rval += do_laser_firing(Players[Player_num].objnum, Primary_weapon, laser_level, flags, nfires);
1677 
1678 			plp->energy -= (energy_used * rval) / Weapon_info[weapon_index].fire_count;
1679 			if (plp->energy < 0)
1680 				plp->energy = 0;
1681 
1682 			if ((Primary_weapon == VULCAN_INDEX) || (Primary_weapon == GAUSS_INDEX)) {
1683 				if (ammo_used > plp->primary_ammo[VULCAN_INDEX])
1684 					plp->primary_ammo[VULCAN_INDEX] = 0;
1685 				else
1686 					plp->primary_ammo[VULCAN_INDEX] -= ammo_used;
1687 			}
1688 
1689 			auto_select_weapon(0);		//	Make sure the player can fire from this weapon.
1690 
1691 		} else {
1692 			auto_select_weapon(0);		//	Make sure the player can fire from this weapon.
1693 			Next_laser_fire_time = GameTime;	//	Prevents shots-to-fire from building up.
1694 			break;	//	Couldn't fire weapon, so abort.
1695 		}
1696 	}
1697 //mprintf(0, "  fires = %i\n", rval);
1698 
1699 	Global_laser_firing_count = 0;
1700 
1701 	return rval;
1702 }
1703 
1704 // -- #define	MAX_LIGHTNING_DISTANCE	(F1_0*300)
1705 // -- #define	MAX_LIGHTNING_BLOBS		16
1706 // -- #define	LIGHTNING_BLOB_DISTANCE	(MAX_LIGHTNING_DISTANCE/MAX_LIGHTNING_BLOBS)
1707 // --
1708 // -- #define	LIGHTNING_BLOB_ID			13
1709 // --
1710 // -- #define	LIGHTNING_TIME		(F1_0/4)
1711 // -- #define	LIGHTNING_DELAY	(F1_0/8)
1712 // --
1713 // -- int	Lightning_gun_num = 1;
1714 // --
1715 // -- fix	Lightning_start_time = -F1_0*10, Lightning_last_time;
1716 // --
1717 // -- //	--------------------------------------------------------------------------------------------------
1718 // -- //	Return -1 if failed to create at least one blob.  Else return index of last blob created.
1719 // -- int create_lightning_blobs(vms_vector *direction, vms_vector *start_pos, int start_segnum, int parent)
1720 // -- {
1721 // -- 	int			i;
1722 // -- 	fvi_query	fq;
1723 // -- 	fvi_info		hit_data;
1724 // -- 	vms_vector	end_pos;
1725 // -- 	vms_vector	norm_dir;
1726 // -- 	int			fate;
1727 // -- 	int			num_blobs;
1728 // -- 	vms_vector	tvec;
1729 // -- 	fix			dist_to_hit_point;
1730 // -- 	vms_vector	point_pos, delta_pos;
1731 // -- 	int			objnum;
1732 // -- 	vms_vector	*gun_pos;
1733 // -- 	vms_matrix	m;
1734 // -- 	vms_vector	gun_pos2;
1735 // --
1736 // -- 	if (Players[Player_num].energy > F1_0)
1737 // -- 		Players[Player_num].energy -= F1_0;
1738 // --
1739 // -- 	if (Players[Player_num].energy <= F1_0) {
1740 // -- 		Players[Player_num].energy = 0;
1741 // -- 		auto_select_weapon(0);
1742 // -- 		return -1;
1743 // -- 	}
1744 // --
1745 // -- 	norm_dir = *direction;
1746 // --
1747 // -- 	vm_vec_normalize_quick(&norm_dir);
1748 // -- 	vm_vec_scale_add(&end_pos, start_pos, &norm_dir, MAX_LIGHTNING_DISTANCE);
1749 // --
1750 // -- 	fq.p0						= start_pos;
1751 // -- 	fq.startseg				= start_segnum;
1752 // -- 	fq.p1						= &end_pos;
1753 // -- 	fq.rad					= 0;
1754 // -- 	fq.thisobjnum			= parent;
1755 // -- 	fq.ignore_obj_list	= NULL;
1756 // -- 	fq.flags					= FQ_TRANSWALL | FQ_CHECK_OBJS;
1757 // --
1758 // -- 	fate = find_vector_intersection(&fq, &hit_data);
1759 // -- 	if (hit_data.hit_seg == -1) {
1760 // -- 		mprintf((1, "Warning: Lightning bolt has hit seg of -1.\n"));
1761 // -- 		return -1;
1762 // -- 	}
1763 // --
1764 // -- 	dist_to_hit_point = vm_vec_mag(vm_vec_sub(&tvec, &hit_data.hit_pnt, start_pos));
1765 // -- 	num_blobs = dist_to_hit_point/LIGHTNING_BLOB_DISTANCE;
1766 // --
1767 // -- 	if (num_blobs > MAX_LIGHTNING_BLOBS)
1768 // -- 		num_blobs = MAX_LIGHTNING_BLOBS;
1769 // --
1770 // -- 	if (num_blobs < MAX_LIGHTNING_BLOBS/4)
1771 // -- 		num_blobs = MAX_LIGHTNING_BLOBS/4;
1772 // --
1773 // -- 	// Find the initial position of the laser
1774 // -- 	gun_pos = &Player_ship->gun_points[Lightning_gun_num];
1775 // -- 	vm_copy_transpose_matrix(&m,&Objects[parent].orient);
1776 // -- 	vm_vec_rotate(&gun_pos2, gun_pos, &m);
1777 // -- 	vm_vec_add(&point_pos, &Objects[parent].pos, &gun_pos2);
1778 // --
1779 // -- 	delta_pos = norm_dir;
1780 // -- 	vm_vec_scale(&delta_pos, dist_to_hit_point/num_blobs);
1781 // --
1782 // -- 	for (i=0; i<num_blobs; i++) {
1783 // -- 		int			point_seg;
1784 // -- 		object		*obj;
1785 // --
1786 // -- 		vm_vec_add2(&point_pos, &delta_pos);
1787 // -- 		point_seg = find_point_seg(&point_pos, start_segnum);
1788 // -- 		if (point_seg == -1)	//	Hey, we thought we were creating points on a line, but we left the mine!
1789 // -- 			continue;
1790 // --
1791 // -- 		objnum = Laser_create_new( direction, &point_pos, point_seg, parent, LIGHTNING_BLOB_ID, 0 );
1792 // --
1793 // -- 		if ( objnum < 0 ) 	{
1794 // -- 			mprintf((1, "Can't create lightning blob - Out of objects!\n" ));
1795 // -- 			Int3();
1796 // -- 			return -1;
1797 // -- 		}
1798 // --
1799 // -- 		obj = &Objects[objnum];
1800 // --
1801 // -- 		digi_play_sample( Weapon_info[obj->id].flash_sound, F1_0 );
1802 // --
1803 // -- 		// -- vm_vec_scale( &obj->mtype.phys_info.velocity, F1_0/2);
1804 // --
1805 // -- 		obj->lifeleft = (LIGHTNING_TIME + LIGHTNING_DELAY)/2;
1806 // --
1807 // -- 	}
1808 // --
1809 // -- 	return objnum;
1810 // --
1811 // -- }
1812 // --
1813 // -- //	--------------------------------------------------------------------------------------------------
1814 // -- //	Lightning Cannon.
1815 // -- //	While being fired, creates path of blobs forward from player until it hits something.
1816 // -- //	Up to MAX_LIGHTNING_BLOBS blobs, spaced LIGHTNING_BLOB_DISTANCE units apart.
1817 // -- //	When the player releases the firing key, the blobs move forward.
1818 // -- void lightning_frame(void)
1819 // -- {
1820 // -- 	if ((GameTime - Lightning_start_time < LIGHTNING_TIME) && (GameTime - Lightning_start_time > 0)) {
1821 // -- 		if (GameTime - Lightning_last_time > LIGHTNING_DELAY) {
1822 // -- 			create_lightning_blobs(&ConsoleObject->orient.fvec, &ConsoleObject->pos, ConsoleObject->segnum, ConsoleObject-Objects);
1823 // -- 			Lightning_last_time = GameTime;
1824 // -- 		}
1825 // -- 	}
1826 // -- }
1827 
1828 //	--------------------------------------------------------------------------------------------------
1829 //	Object "objnum" fires weapon "weapon_num" of level "level".  (Right now (9/24/94) level is used only for type 0 laser.
1830 //	Flags are the player flags.  For network mode, set to 0.
1831 //	It is assumed that this is a player object (as in multiplayer), and therefore the gun positions are known.
1832 //	Returns number of times a weapon was fired.  This is typically 1, but might be more for low frame rates.
1833 //	More than one shot is fired with a pseudo-delay so that players on show machines can fire (for themselves
1834 //	or other players) often enough for things like the vulcan cannon.
do_laser_firing(int objnum,int weapon_num,int level,int flags,int nfires)1835 int do_laser_firing(int objnum, int weapon_num, int level, int flags, int nfires)
1836 {
1837 	object	*objp = &Objects[objnum];
1838 
1839 	switch (weapon_num) {
1840 		case LASER_INDEX: {
1841 			int weapon_num;
1842 
1843 			Laser_offset = ((F1_0*2)*(d_rand()%8))/8;
1844 
1845 			if (level <= MAX_LASER_LEVEL)
1846 				weapon_num = LASER_ID + level;
1847 			else
1848 				weapon_num = SUPER_LASER_ID + (level-MAX_LASER_LEVEL-1);
1849 
1850 			Laser_player_fire( objp, weapon_num, 0, 1, 0);
1851 			Laser_player_fire( objp, weapon_num, 1, 0, 0);
1852 
1853 			if (flags & LASER_QUAD) {
1854 				//	hideous system to make quad laser 1.5x powerful as normal laser, make every other quad laser bolt harmless
1855 				Laser_player_fire( objp, weapon_num, 2, 0, 0);
1856 				Laser_player_fire( objp, weapon_num, 3, 0, 0);
1857 			}
1858 			break;
1859 		}
1860 		case VULCAN_INDEX: {
1861 			//	Only make sound for 1/4 of vulcan bullets.
1862 			int	make_sound = 1;
1863 			//if (d_rand() > 24576)
1864 			//	make_sound = 1;
1865 			Laser_player_fire_spread( objp, VULCAN_ID, 6, d_rand()/8 - 32767/16, d_rand()/8 - 32767/16, make_sound, 0);
1866 			if (nfires > 1) {
1867 				Laser_player_fire_spread( objp, VULCAN_ID, 6, d_rand()/8 - 32767/16, d_rand()/8 - 32767/16, 0, 0);
1868 				if (nfires > 2) {
1869 					Laser_player_fire_spread( objp, VULCAN_ID, 6, d_rand()/8 - 32767/16, d_rand()/8 - 32767/16, 0, 0);
1870 				}
1871 			}
1872 			break;
1873 		}
1874 		case SPREADFIRE_INDEX:
1875 			if (flags & LASER_SPREADFIRE_TOGGLED) {
1876 				Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, F1_0/16, 0, 0, 0);
1877 				Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, -F1_0/16, 0, 0, 0);
1878 				Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, 0, 1, 0);
1879 			} else {
1880 				Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, F1_0/16, 0, 0);
1881 				Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, -F1_0/16, 0, 0);
1882 				Laser_player_fire_spread( objp, SPREADFIRE_ID, 6, 0, 0, 1, 0);
1883 			}
1884 			break;
1885 
1886 		case PLASMA_INDEX:
1887 			Laser_player_fire( objp, PLASMA_ID, 0, 1, 0);
1888 			Laser_player_fire( objp, PLASMA_ID, 1, 0, 0);
1889 			if (nfires > 1) {
1890 				Laser_player_fire_spread_delay( objp, PLASMA_ID, 0, 0, 0, FrameTime/2, 1, 0);
1891 				Laser_player_fire_spread_delay( objp, PLASMA_ID, 1, 0, 0, FrameTime/2, 0, 0);
1892 			}
1893 			break;
1894 
1895 		case FUSION_INDEX: {
1896 			vms_vector	force_vec;
1897 
1898 //			mprintf((0, "Fusion multiplier %f.\n", f2fl(Fusion_charge)));
1899 
1900 			Laser_player_fire( objp, FUSION_ID, 0, 1, 0);
1901 			Laser_player_fire( objp, FUSION_ID, 1, 1, 0);
1902 
1903 			flags = (byte)(Fusion_charge >> 12);
1904 
1905 			Fusion_charge = 0;
1906 
1907 			force_vec.x = -(objp->orient.fvec.x << 7);
1908 			force_vec.y = -(objp->orient.fvec.y << 7);
1909 			force_vec.z = -(objp->orient.fvec.z << 7);
1910 			phys_apply_force(objp, &force_vec);
1911 
1912 			force_vec.x = (force_vec.x >> 4) + d_rand() - 16384;
1913 			force_vec.y = (force_vec.y >> 4) + d_rand() - 16384;
1914 			force_vec.z = (force_vec.z >> 4) + d_rand() - 16384;
1915 			phys_apply_rot(objp, &force_vec);
1916 
1917 		}
1918 			break;
1919 		case SUPER_LASER_INDEX: {
1920 			int super_level = 3;		//make some new kind of laser eventually
1921 			Laser_player_fire( objp, super_level, 0, 1, 0);
1922 			Laser_player_fire( objp, super_level, 1, 0, 0);
1923 
1924 			if (flags & LASER_QUAD) {
1925 				//	hideous system to make quad laser 1.5x powerful as normal laser, make every other quad laser bolt harmless
1926 				Laser_player_fire( objp, super_level, 2, 0, 0);
1927 				Laser_player_fire( objp, super_level, 3, 0, 0);
1928 			}
1929 			break;
1930 		}
1931 		case GAUSS_INDEX: {
1932 			//	Only make sound for 1/4 of vulcan bullets.
1933 			int	make_sound = 1;
1934 			//if (d_rand() > 24576)
1935 			//	make_sound = 1;
1936 
1937 			Laser_player_fire_spread( objp, GAUSS_ID, 6, (d_rand()/8 - 32767/16)/5, (d_rand()/8 - 32767/16)/5, make_sound, 0);
1938 			if (nfires > 1) {
1939 				Laser_player_fire_spread( objp, GAUSS_ID, 6, (d_rand()/8 - 32767/16)/5, (d_rand()/8 - 32767/16)/5, 0, 0);
1940 				if (nfires > 2) {
1941 					Laser_player_fire_spread( objp, GAUSS_ID, 6, (d_rand()/8 - 32767/16)/5, (d_rand()/8 - 32767/16)/5, 0, 0);
1942 				}
1943 			}
1944 			break;
1945 		}
1946 		case HELIX_INDEX: {
1947 			int helix_orient;
1948 			fix spreadr,spreadu;
1949 			helix_orient = (flags >> LASER_HELIX_SHIFT) & LASER_HELIX_MASK;
1950 			switch(helix_orient) {
1951 
1952 				case 0: spreadr =  F1_0/16; spreadu = 0;       break; // Vertical
1953 				case 1: spreadr =  F1_0/17; spreadu = F1_0/42; break; //  22.5 degrees
1954 				case 2: spreadr =  F1_0/22; spreadu = F1_0/22; break; //  45   degrees
1955 				case 3: spreadr =  F1_0/42; spreadu = F1_0/17; break; //  67.5 degrees
1956 				case 4: spreadr =  0;       spreadu = F1_0/16; break; //  90   degrees
1957 				case 5: spreadr = -F1_0/42; spreadu = F1_0/17; break; // 112.5 degrees
1958 				case 6: spreadr = -F1_0/22; spreadu = F1_0/22; break; // 135   degrees
1959 				case 7: spreadr = -F1_0/17; spreadu = F1_0/42; break; // 157.5 degrees
1960 				default:
1961 					Error("Invalid helix_orientation value %x\n",helix_orient);
1962 			}
1963 
1964 			Laser_player_fire_spread( objp, HELIX_ID, 6,  0,  0, 1, 0);
1965 			Laser_player_fire_spread( objp, HELIX_ID, 6,  spreadr,  spreadu, 0, 0);
1966 			Laser_player_fire_spread( objp, HELIX_ID, 6, -spreadr, -spreadu, 0, 0);
1967 			Laser_player_fire_spread( objp, HELIX_ID, 6,  spreadr*2,  spreadu*2, 0, 0);
1968 			Laser_player_fire_spread( objp, HELIX_ID, 6, -spreadr*2, -spreadu*2, 0, 0);
1969 			break;
1970 		}
1971 
1972 		case PHOENIX_INDEX:
1973 			Laser_player_fire( objp, PHOENIX_ID, 0, 1, 0);
1974 			Laser_player_fire( objp, PHOENIX_ID, 1, 0, 0);
1975 			if (nfires > 1) {
1976 				Laser_player_fire_spread_delay( objp, PHOENIX_ID, 0, 0, 0, FrameTime/2, 1, 0);
1977 				Laser_player_fire_spread_delay( objp, PHOENIX_ID, 1, 0, 0, FrameTime/2, 0, 0);
1978 			}
1979 			break;
1980 
1981 		case OMEGA_INDEX:
1982 			Laser_player_fire( objp, OMEGA_ID, 1, 1, 0);
1983 			break;
1984 
1985 		default:
1986 			Int3();	//	Contact Yuan: Unknown Primary weapon type, setting to 0.
1987 			Primary_weapon = 0;
1988 	}
1989 
1990 	// Set values to be recognized during comunication phase, if we are the
1991 	//  one shooting
1992 #ifdef NETWORK
1993 	if ((Game_mode & GM_MULTI) && (objnum == Players[Player_num].objnum))
1994 	{
1995 		// mprintf((0, "Flags on fire: %d.\n", flags));
1996 		Network_laser_fired = nfires;
1997 		Network_laser_gun = weapon_num;
1998 		Network_laser_flags = flags;
1999 		Network_laser_level = level;
2000 	}
2001 #endif
2002 
2003 	return nfires;
2004 }
2005 
2006 #define	MAX_SMART_DISTANCE	(F1_0*150)
2007 #define	MAX_OBJDISTS			30
2008 
2009 //	-------------------------------------------------------------------------------------------
2010 //	if goal_obj == -1, then create random vector
create_homing_missile(object * objp,int goal_obj,int objtype,int make_sound)2011 int create_homing_missile(object *objp, int goal_obj, int objtype, int make_sound)
2012 {
2013 	int			objnum;
2014 	vms_vector	vector_to_goal;
2015 	vms_vector	random_vector;
2016 	//vms_vector	goal_pos;
2017 
2018 	if (goal_obj == -1) {
2019 		make_random_vector(&vector_to_goal);
2020 	} else {
2021 		vm_vec_normalized_dir_quick(&vector_to_goal, &Objects[goal_obj].pos, &objp->pos);
2022 		make_random_vector(&random_vector);
2023 		vm_vec_scale_add2(&vector_to_goal, &random_vector, F1_0/4);
2024 		vm_vec_normalize_quick(&vector_to_goal);
2025 	}
2026 
2027 	//	Create a vector towards the goal, then add some noise to it.
2028 	objnum = Laser_create_new(&vector_to_goal, &objp->pos, objp->segnum, objp-Objects, objtype, make_sound);
2029 	if (objnum == -1)
2030 		return -1;
2031 
2032 	// Fixed to make sure the right person gets credit for the kill
2033 
2034 //	Objects[objnum].ctype.laser_info.parent_num = objp->ctype.laser_info.parent_num;
2035 //	Objects[objnum].ctype.laser_info.parent_type = objp->ctype.laser_info.parent_type;
2036 //	Objects[objnum].ctype.laser_info.parent_signature = objp->ctype.laser_info.parent_signature;
2037 
2038 	Objects[objnum].ctype.laser_info.track_goal = goal_obj;
2039 
2040 	return objnum;
2041 }
2042 
2043 extern void blast_nearby_glass(object *objp, fix damage);
2044 
2045 //-----------------------------------------------------------------------------
2046 // Create the children of a smart bomb, which is a bunch of homing missiles.
create_smart_children(object * objp,int num_smart_children)2047 void create_smart_children(object *objp, int num_smart_children)
2048 {
2049 	int	parent_type, parent_num;
2050 #ifndef SHAREWARE
2051 	int	make_sound;
2052 	int	numobjs=0;
2053 	int	objlist[MAX_OBJDISTS];
2054 	int	blob_id;
2055 #endif
2056 
2057 	if (objp->type == OBJ_WEAPON) {
2058 		parent_type = objp->ctype.laser_info.parent_type;
2059 		parent_num = objp->ctype.laser_info.parent_num;
2060 	} else if (objp->type == OBJ_ROBOT) {
2061 		parent_type = OBJ_ROBOT;
2062 		parent_num = objp-Objects;
2063 	} else {
2064 		Int3();	//	Hey, what kind of object is this!?
2065 		parent_type = 0;
2066 		parent_num = 0;
2067 	}
2068 
2069 	if (objp->id == EARTHSHAKER_ID)
2070 		blast_nearby_glass(objp, Weapon_info[EARTHSHAKER_ID].strength[Difficulty_level]);
2071 
2072 #ifndef SHAREWARE
2073 #if 0
2074 // -- DEBUG --
2075 	if ((objp->type == OBJ_WEAPON) && ((objp->id == SMART_ID) || (objp->id == SUPERPROX_ID) || (objp->id == ROBOT_SUPERPROX_ID) || (objp->id == EARTHSHAKER_ID)))
2076 		Assert(Weapon_info[objp->id].children != -1);
2077 // -- DEBUG --
2078 #endif
2079 
2080 	if (((objp->type == OBJ_WEAPON) && (Weapon_info[objp->id].children != -1)) || (objp->type == OBJ_ROBOT)) {
2081 		int	i, objnum;
2082 
2083 		if (Game_mode & GM_MULTI)
2084 			d_srand(8321L);
2085 
2086 		for (objnum=0; objnum<=Highest_object_index; objnum++) {
2087 			object	*curobjp = &Objects[objnum];
2088 
2089 			if ((((curobjp->type == OBJ_ROBOT) && (!curobjp->ctype.ai_info.CLOAKED)) || (curobjp->type == OBJ_PLAYER)) && (objnum != parent_num)) {
2090 				fix	dist;
2091 
2092 				if (curobjp->type == OBJ_PLAYER)
2093 				{
2094 					if ((parent_type == OBJ_PLAYER) && (Game_mode & GM_MULTI_COOP))
2095 						continue;
2096 #ifdef NETWORK
2097 					if ((Game_mode & GM_TEAM) && (get_team(curobjp->id) == get_team(Objects[parent_num].id)))
2098 						continue;
2099 #endif
2100 
2101 					if (Players[curobjp->id].flags & PLAYER_FLAGS_CLOAKED)
2102 						continue;
2103 				}
2104 
2105 				//	Robot blobs can't track robots.
2106 				if (curobjp->type == OBJ_ROBOT) {
2107 					if (parent_type == OBJ_ROBOT)
2108 						continue;
2109 
2110 					//	Your shots won't track the buddy.
2111 					if (parent_type == OBJ_PLAYER)
2112 						if (Robot_info[curobjp->id].companion)
2113 							continue;
2114 				}
2115 
2116 				dist = vm_vec_dist_quick(&objp->pos, &curobjp->pos);
2117 				if (dist < MAX_SMART_DISTANCE) {
2118 					int	oovis;
2119 
2120 					oovis = object_to_object_visibility(objp, curobjp, FQ_TRANSWALL);
2121 
2122 					if (oovis) { //object_to_object_visibility(objp, curobjp, FQ_TRANSWALL)) {
2123 						objlist[numobjs] = objnum;
2124 						numobjs++;
2125 						if (numobjs >= MAX_OBJDISTS) {
2126 							mprintf((0, "Warning -- too many objects near smart bomb explosion.  See laser.c.\n"));
2127 							numobjs = MAX_OBJDISTS;
2128 							break;
2129 						}
2130 					}
2131 				}
2132 			}
2133 		}
2134 
2135 		//	Get type of weapon for child from parent.
2136 		if (objp->type == OBJ_WEAPON) {
2137 			blob_id = Weapon_info[objp->id].children;
2138 			Assert(blob_id != -1);		//	Hmm, missing data in bitmaps.tbl.  Need "children=NN" parameter.
2139 		} else {
2140 			Assert(objp->type == OBJ_ROBOT);
2141 			blob_id = ROBOT_SMART_HOMING_ID;
2142 		}
2143 
2144 // -- 		//determine what kind of blob to drop
2145 // -- 		//	Note: parent_type is not the type of the weapon's parent.  It is actually the type of the weapon's
2146 // -- 		//	earliest ancestor.  This deals with the issue of weapons spewing weapons which spew weapons.
2147 // -- 		switch (parent_type) {
2148 // -- 			case OBJ_WEAPON:
2149 // -- 				Int3();	//	Should this ever happen?
2150 // -- 				switch (objp->id) {
2151 // -- 					case SUPERPROX_ID:			blob_id = SMART_MINE_HOMING_ID; break;
2152 // -- 					case ROBOT_SUPERPROX_ID:	blob_id = ROBOT_SMART_MINE_HOMING_ID; break;
2153 // -- 					case EARTHSHAKER_ID:			blob_id = EARTHSHAKER_MEGA_ID; break;
2154 // -- 					default:							Int3();	//bogus id for weapon
2155 // -- 				}
2156 // -- 				break;
2157 // -- 			case OBJ_PLAYER:
2158 // -- 				switch (objp->id) {
2159 // -- 					case SUPERPROX_ID:			blob_id = SMART_MINE_HOMING_ID; break;
2160 // -- 					case ROBOT_SUPERPROX_ID:	Int3();	break;
2161 // -- 					case EARTHSHAKER_ID:			blob_id = EARTHSHAKER_MEGA_ID; break;
2162 // -- 					case SMART_ID:					blob_id = PLAYER_SMART_HOMING_ID; break;
2163 // -- 					default:							Int3();	//bogus id for weapon
2164 // -- 				}
2165 // -- 				break;
2166 // -- 			case OBJ_ROBOT:
2167 // -- 				switch (objp->id) {
2168 // -- 					case ROBOT_SUPERPROX_ID:	blob_id = ROBOT_SMART_MINE_HOMING_ID; break;
2169 // -- 					// -- case EARTHSHAKER_ID:			blob_id = EARTHSHAKER_MEGA_ID; break;
2170 // -- 					case SMART_ID:					blob_id = ROBOT_SMART_HOMING_ID; break;
2171 // -- 					default:							blob_id = ROBOT_SMART_HOMING_ID; break;
2172 // -- 				}
2173 // -- 				break;
2174 // -- 			default:					Int3();	//bogus type for parent object
2175 // -- 		}
2176 
2177 		make_sound = 1;
2178 		for (i=0; i<num_smart_children; i++) {
2179 			int objnum;
2180 			objnum = (numobjs==0)?-1:objlist[(d_rand() * numobjs) >> 15];
2181 			create_homing_missile(objp, objnum, blob_id, make_sound);
2182 			make_sound = 0;
2183 		}
2184 	}
2185 #endif
2186 }
2187 
2188 int Missile_gun = 0;
2189 
2190 //give up control of the guided missile
release_guided_missile(int player_num)2191 void release_guided_missile(int player_num)
2192 {
2193 	if (player_num == Player_num)
2194 	 {
2195 	  if (Guided_missile[player_num]==NULL)
2196 			return;
2197 
2198 		Missile_viewer = Guided_missile[player_num];
2199 #ifdef NETWORK
2200 		if (Game_mode & GM_MULTI)
2201 		 multi_send_guided_info (Guided_missile[Player_num],1);
2202 #endif
2203 		if (Newdemo_state==ND_STATE_RECORDING)
2204 		 newdemo_record_guided_end();
2205 	 }
2206 
2207 	Guided_missile[player_num] = NULL;
2208 }
2209 
2210 int Proximity_dropped=0,Smartmines_dropped=0;
2211 
2212 //	-------------------------------------------------------------------------------------------
2213 //parameter determines whether or not to do autoselect if have run out of ammo
2214 //this is needed because if you drop a bomb with the B key, you don't want
2215 //want to autoselect if the bomb isn't actually selected.
do_missile_firing(int do_autoselect)2216 void do_missile_firing(int do_autoselect)
2217 {
2218 	int gun_flag=0;
2219 
2220 	Assert(Secondary_weapon < MAX_SECONDARY_WEAPONS);
2221 
2222 	if (Guided_missile[Player_num] && Guided_missile[Player_num]->signature==Guided_missile_sig[Player_num]) {
2223 		release_guided_missile(Player_num);
2224 		Next_missile_fire_time = GameTime + Weapon_info[Secondary_weapon_to_weapon_info[Secondary_weapon]].fire_wait;
2225 		return;
2226 	}
2227 
2228 	if (!Player_is_dead && (Players[Player_num].secondary_ammo[Secondary_weapon] > 0))	{
2229 
2230 		int weapon_id,weapon_gun;
2231 
2232 		Players[Player_num].secondary_ammo[Secondary_weapon]--;
2233 
2234 		weapon_id = Secondary_weapon_to_weapon_info[Secondary_weapon];
2235 
2236 		if (Laser_rapid_fire!=0xBADA55)
2237 			Next_missile_fire_time = GameTime + Weapon_info[weapon_id].fire_wait;
2238 		else
2239 			Next_missile_fire_time = GameTime + F1_0/25;
2240 
2241 		weapon_gun = Secondary_weapon_to_gun_num[Secondary_weapon];
2242 
2243 		if (weapon_gun==4) {		//alternate left/right
2244 			weapon_gun += (gun_flag = (Missile_gun & 1));
2245 			Missile_gun++;
2246 		}
2247 
2248 		Laser_player_fire( ConsoleObject, weapon_id, weapon_gun, 1, 0);
2249 
2250 		if (Secondary_weapon == PROXIMITY_INDEX) {
2251 			if (++Proximity_dropped == 4) {
2252 				Proximity_dropped = 0;
2253 #ifdef NETWORK
2254 				maybe_drop_net_powerup(POW_PROXIMITY_WEAPON);
2255 #endif
2256 			}
2257 		}
2258 		else if (Secondary_weapon == SMART_MINE_INDEX) {
2259 			if (++Smartmines_dropped == 4) {
2260 				Smartmines_dropped = 0;
2261 #ifdef NETWORK
2262 				maybe_drop_net_powerup(POW_SMART_MINE);
2263 #endif
2264 			}
2265 		}
2266 #ifdef NETWORK
2267 		else if (Secondary_weapon != CONCUSSION_INDEX)
2268 			maybe_drop_net_powerup(Secondary_weapon_to_powerup[Secondary_weapon]);
2269 #endif
2270 
2271 		if (Secondary_weapon == MEGA_INDEX || Secondary_weapon == SMISSILE5_INDEX) {
2272 			vms_vector force_vec;
2273 
2274 			force_vec.x = -(ConsoleObject->orient.fvec.x << 7);
2275 			force_vec.y = -(ConsoleObject->orient.fvec.y << 7);
2276 			force_vec.z = -(ConsoleObject->orient.fvec.z << 7);
2277 			phys_apply_force(ConsoleObject, &force_vec);
2278 
2279 			force_vec.x = (force_vec.x >> 4) + d_rand() - 16384;
2280 			force_vec.y = (force_vec.y >> 4) + d_rand() - 16384;
2281 			force_vec.z = (force_vec.z >> 4) + d_rand() - 16384;
2282 			phys_apply_rot(ConsoleObject, &force_vec);
2283 		}
2284 
2285 #ifdef NETWORK
2286 		if (Game_mode & GM_MULTI)
2287 		{
2288 			Network_laser_fired = 1;		//how many
2289 			Network_laser_gun = Secondary_weapon + MISSILE_ADJUST;
2290 			Network_laser_flags = gun_flag;
2291 			Network_laser_level = 0;
2292 		}
2293 #endif
2294 
2295 		if (do_autoselect)
2296 			auto_select_weapon(1);		//select next missile, if this one out of ammo
2297 	}
2298 }
2299 
2300